DEADSOFTWARE

dbg_scale fixes; made non-sky textures always NEAREST
[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, MAPDEF, 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: TDFPoint;
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 (freeTextures: Boolean=true);
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; const oldMapPath: AnsiString=''): Boolean;
99 procedure g_Game_ChangeMap(const MapPath: String);
100 procedure g_Game_ExitLevel(const Map: AnsiString);
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 g_Game_Process_Params;
128 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
129 procedure g_Game_StepLoading();
130 procedure g_Game_ClearLoading();
131 procedure g_Game_SetDebugMode();
132 procedure DrawLoadingStat();
134 { procedure SetWinPause(Enable: Boolean); }
136 const
137 GAME_TICK = 28;
139 LOADING_SHOW_STEP = 100;
140 LOADING_INTERLINE = 20;
142 GT_NONE = 0;
143 GT_SINGLE = 1;
144 GT_CUSTOM = 2;
145 GT_SERVER = 3;
146 GT_CLIENT = 4;
148 GM_NONE = 0;
149 GM_DM = 1;
150 GM_TDM = 2;
151 GM_CTF = 3;
152 GM_COOP = 4;
153 GM_SINGLE = 5;
155 MESSAGE_DIKEY = WM_USER + 1;
157 EXIT_QUIT = 1;
158 EXIT_SIMPLE = 2;
159 EXIT_RESTART = 3;
160 EXIT_ENDLEVELSINGLE = 4;
161 EXIT_ENDLEVELCUSTOM = 5;
163 GAME_OPTION_RESERVED = 1;
164 GAME_OPTION_TEAMDAMAGE = 2;
165 GAME_OPTION_ALLOWEXIT = 4;
166 GAME_OPTION_WEAPONSTAY = 8;
167 GAME_OPTION_MONSTERS = 16;
168 GAME_OPTION_BOTVSPLAYER = 32;
169 GAME_OPTION_BOTVSMONSTER = 64;
171 STATE_NONE = 0;
172 STATE_MENU = 1;
173 STATE_FOLD = 2;
174 STATE_INTERCUSTOM = 3;
175 STATE_INTERSINGLE = 4;
176 STATE_INTERTEXT = 5;
177 STATE_INTERPIC = 6;
178 STATE_ENDPIC = 7;
179 STATE_SLIST = 8;
181 LMS_RESPAWN_NONE = 0;
182 LMS_RESPAWN_WARMUP = 1;
183 LMS_RESPAWN_FINAL = 2;
185 SPECT_NONE = 0;
186 SPECT_STATS = 1;
187 SPECT_MAPVIEW = 2;
188 SPECT_PLAYERS = 3;
190 DE_GLOBEVENT = 0;
191 DE_BFGHIT = 1;
192 DE_KILLCOMBO = 2;
194 ANNOUNCE_NONE = 0;
195 ANNOUNCE_ME = 1;
196 ANNOUNCE_MEPLUS = 2;
197 ANNOUNCE_ALL = 3;
199 CONFIG_FILENAME = 'Doom2DF.cfg';
200 LOG_FILENAME = 'Doom2DF.log';
202 TEST_MAP_NAME = '$$$_TEST_$$$';
204 STD_PLAYER_MODEL = 'Doomer';
206 var
207 gStdFont: DWORD;
208 gGameSettings: TGameSettings;
209 gPlayer1Settings: TPlayerSettings;
210 gPlayer2Settings: TPlayerSettings;
211 gGameOn: Boolean;
212 gPlayerScreenSize: TDFPoint;
213 gPlayer1ScreenCoord: TDFPoint;
214 gPlayer2ScreenCoord: TDFPoint;
215 gPlayer1: TPlayer = nil;
216 gPlayer2: TPlayer = nil;
217 gPlayerDrawn: TPlayer = nil;
218 gTime: LongWord;
219 gSwitchGameMode: Byte = GM_DM;
220 gHearPoint1, gHearPoint2: THearPoint;
221 gSoundEffectsDF: Boolean = False;
222 gSoundTriggerTime: Word = 0;
223 gAnnouncer: Byte = ANNOUNCE_NONE;
224 goodsnd: array[0..3] of TPlayableSound;
225 killsnd: array[0..3] of TPlayableSound;
226 gDefInterTime: ShortInt = -1;
227 gInterEndTime: LongWord = 0;
228 gInterTime: LongWord = 0;
229 gServInterTime: Byte = 0;
230 gGameStartTime: LongWord = 0;
231 gTotalMonsters: Integer = 0;
232 gPause: Boolean;
233 gShowTime: Boolean = True;
234 gShowFPS: Boolean = False;
235 gShowGoals: Boolean = True;
236 gShowStat: Boolean = True;
237 gShowKillMsg: Boolean = True;
238 gShowLives: Boolean = True;
239 gShowPing: Boolean = False;
240 gShowMap: Boolean = False;
241 gExit: Byte = 0;
242 gState: Byte = STATE_NONE;
243 sX, sY: Integer;
244 sWidth, sHeight: Word;
245 gSpectMode: Byte = SPECT_NONE;
246 gSpectHUD: Boolean = True;
247 gSpectKeyPress: Boolean = False;
248 gSpectX: Integer = 0;
249 gSpectY: Integer = 0;
250 gSpectStep: Byte = 8;
251 gSpectViewTwo: Boolean = False;
252 gSpectPID1: Integer = -1;
253 gSpectPID2: Integer = -1;
254 gMusic: TMusic = nil;
255 gLoadGameMode: Boolean;
256 gCheats: Boolean = False;
257 gMapOnce: Boolean = False;
258 gMapToDelete: String;
259 gTempDelete: Boolean = False;
260 gLastMap: Boolean = False;
261 gWinPosX, gWinPosY: Integer;
262 gWinSizeX, gWinSizeY: Integer;
263 gWinFrameX, gWinFrameY, gWinCaption: Integer;
264 gWinActive: Boolean = True; // by default window is active, lol
265 gResolutionChange: Boolean = False;
266 gRC_Width, gRC_Height: Word;
267 gRC_FullScreen, gRC_Maximized: Boolean;
268 gLanguageChange: Boolean = False;
269 gDebugMode: Boolean = False;
270 g_debug_Sounds: Boolean = False;
271 g_debug_Frames: Boolean = False;
272 g_debug_WinMsgs: Boolean = False;
273 g_debug_MonsterOff: Boolean = False;
274 g_debug_BotAIOff: Byte = 0;
275 g_debug_HealthBar: Boolean = False;
276 g_Debug_Player: Boolean = False;
277 gCoopMonstersKilled: Word = 0;
278 gCoopSecretsFound: Word = 0;
279 gCoopTotalMonstersKilled: Word = 0;
280 gCoopTotalSecretsFound: Word = 0;
281 gCoopTotalMonsters: Word = 0;
282 gCoopTotalSecrets: Word = 0;
283 gStatsOff: Boolean = False;
284 gStatsPressed: Boolean = False;
285 gExitByTrigger: Boolean = False;
286 gNextMap: String = '';
287 gLMSRespawn: Byte = LMS_RESPAWN_NONE;
288 gLMSRespawnTime: Cardinal = 0;
289 gLMSSoftSpawn: Boolean = False;
290 gMissionFailed: Boolean = False;
291 gVoteInProgress: Boolean = False;
292 gVotePassed: Boolean = False;
293 gVoteCommand: string = '';
294 gVoteTimer: Cardinal = 0;
295 gVoteCmdTimer: Cardinal = 0;
296 gVoteCount: Integer = 0;
297 gVoteTimeout: Cardinal = 30;
298 gVoted: Boolean = False;
299 gVotesEnabled: Boolean = True;
300 gEvents: Array of TGameEvent;
301 gDelayedEvents: Array of TDelayedEvent;
303 // move button values:
304 // bits 0-1: l/r state:
305 // 0: neither left, nor right pressed
306 // 1: left pressed
307 // 2: right pressed
308 // bits 4-5: l/r state when strafe was pressed
309 P1MoveButton: Byte = 0;
310 P2MoveButton: Byte = 0;
312 g_profile_frame_update: Boolean = false;
313 g_profile_frame_draw: Boolean = false;
314 g_profile_collision: Boolean = false;
315 g_profile_los: Boolean = false;
316 g_profile_history_size: Integer = 1000;
318 g_rlayer_back: Boolean = true;
319 g_rlayer_step: Boolean = true;
320 g_rlayer_wall: Boolean = true;
321 g_rlayer_door: Boolean = true;
322 g_rlayer_acid1: Boolean = true;
323 g_rlayer_acid2: Boolean = true;
324 g_rlayer_water: Boolean = true;
325 g_rlayer_fore: Boolean = true;
328 procedure g_ResetDynlights ();
329 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
330 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
332 function conIsCheatsEnabled (): Boolean;
335 implementation
337 uses
338 e_texture, g_textures, g_main, g_window, g_menu,
339 e_input, e_log, g_console, g_items, g_map, g_panel,
340 g_playermodel, g_gfx, g_options, g_weapons, Math,
341 g_triggers, g_monsters, e_sound, CONFIG,
342 BinEditor, g_language, g_net, SDL,
343 ENet, e_msg, g_netmsg, g_netmaster, GL, GLExt,
344 utils, sfs, g_holmes;
347 // ////////////////////////////////////////////////////////////////////////// //
348 function conIsCheatsEnabled (): Boolean;
349 begin
350 result := false;
351 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
352 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then exit;
353 result := true;
354 end;
357 // ////////////////////////////////////////////////////////////////////////// //
358 var
359 profileFrameDraw: TProfiler = nil;
362 // ////////////////////////////////////////////////////////////////////////// //
363 type
364 TDynLight = record
365 x, y, radius: Integer;
366 r, g, b, a: Single;
367 exploCount: Integer;
368 exploRadius: Integer;
369 end;
371 var
372 g_dynLights: array of TDynLight = nil;
373 g_dynLightCount: Integer = 0;
374 g_playerLight: Boolean = false;
376 procedure g_ResetDynlights ();
377 var
378 lnum, idx: Integer;
379 begin
380 if not gwin_has_stencil then begin g_dynLightCount := 0; exit; end;
381 lnum := 0;
382 for idx := 0 to g_dynLightCount-1 do
383 begin
384 if g_dynLights[idx].exploCount = -666 then
385 begin
386 // skip it
387 end
388 else
389 begin
390 // explosion
391 Inc(g_dynLights[idx].exploCount);
392 if (g_dynLights[idx].exploCount < 10) then
393 begin
394 g_dynLights[idx].radius := g_dynLights[idx].exploRadius+g_dynLights[idx].exploCount*8;
395 g_dynLights[idx].a := 0.4+g_dynLights[idx].exploCount/10;
396 if (g_dynLights[idx].a > 0.8) then g_dynLights[idx].a := 0.8;
397 if lnum <> idx then g_dynLights[lnum] := g_dynLights[idx];
398 Inc(lnum);
399 end;
400 end;
401 end;
402 g_dynLightCount := lnum;
403 end;
405 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
406 begin
407 if not gwin_has_stencil then exit;
408 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
409 g_dynLights[g_dynLightCount].x := x;
410 g_dynLights[g_dynLightCount].y := y;
411 g_dynLights[g_dynLightCount].radius := radius;
412 g_dynLights[g_dynLightCount].r := r;
413 g_dynLights[g_dynLightCount].g := g;
414 g_dynLights[g_dynLightCount].b := b;
415 g_dynLights[g_dynLightCount].a := a;
416 g_dynLights[g_dynLightCount].exploCount := -666;
417 Inc(g_dynLightCount);
418 end;
420 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
421 begin
422 if not gwin_has_stencil then exit;
423 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
424 g_dynLights[g_dynLightCount].x := x;
425 g_dynLights[g_dynLightCount].y := y;
426 g_dynLights[g_dynLightCount].radius := 0;
427 g_dynLights[g_dynLightCount].exploRadius := radius;
428 g_dynLights[g_dynLightCount].r := r;
429 g_dynLights[g_dynLightCount].g := g;
430 g_dynLights[g_dynLightCount].b := b;
431 g_dynLights[g_dynLightCount].a := 0;
432 g_dynLights[g_dynLightCount].exploCount := 0;
433 Inc(g_dynLightCount);
434 end;
437 // ////////////////////////////////////////////////////////////////////////// //
438 function calcProfilesHeight (prof: TProfiler): Integer;
439 begin
440 result := 0;
441 if (prof = nil) then exit;
442 if (length(prof.bars) = 0) then exit;
443 result := length(prof.bars)*(16+2);
444 end;
446 // returns width
447 function drawProfiles (x, y: Integer; prof: TProfiler): Integer;
448 var
449 wdt, hgt: Integer;
450 yy: Integer;
451 ii: Integer;
452 begin
453 result := 0;
454 if (prof = nil) then exit;
455 // gScreenWidth
456 if (length(prof.bars) = 0) then exit;
457 wdt := 192;
458 hgt := calcProfilesHeight(prof);
459 if (x < 0) then x := gScreenWidth-(wdt-1)+x;
460 if (y < 0) then y := gScreenHeight-(hgt-1)+y;
461 // background
462 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
463 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
464 e_DarkenQuadWH(x, y, wdt, hgt, 150);
465 // title
466 yy := y+2;
467 for ii := 0 to High(prof.bars) do
468 begin
469 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);
470 Inc(yy, 16+2);
471 end;
472 result := wdt;
473 end;
476 // ////////////////////////////////////////////////////////////////////////// //
477 type
478 TEndCustomGameStat = record
479 PlayerStat: TPlayerStatArray;
480 TeamStat: TTeamStat;
481 GameTime: LongWord;
482 GameMode: Byte;
483 Map, MapName: String;
484 end;
486 TEndSingleGameStat = record
487 PlayerStat: Array [0..1] of record
488 Kills: Integer;
489 Secrets: Integer;
490 end;
491 GameTime: LongWord;
492 TwoPlayers: Boolean;
493 TotalSecrets: Integer;
494 end;
496 TLoadingStat = record
497 CurValue: Integer;
498 MaxValue: Integer;
499 ShowCount: Integer;
500 Msgs: Array of String;
501 NextMsg: Word;
502 end;
504 TParamStrValue = record
505 Name: String;
506 Value: String;
507 end;
509 TParamStrValues = Array of TParamStrValue;
511 const
512 INTER_ACTION_TEXT = 1;
513 INTER_ACTION_PIC = 2;
514 INTER_ACTION_MUSIC = 3;
516 var
517 FPS, UPS: Word;
518 FPSCounter, UPSCounter: Word;
519 FPSTime, UPSTime: LongWord;
520 DataLoaded: Boolean = False;
521 LastScreenShot: Int64;
522 IsDrawStat: Boolean = False;
523 CustomStat: TEndCustomGameStat;
524 SingleStat: TEndSingleGameStat;
525 LoadingStat: TLoadingStat;
526 EndingGameCounter: Byte = 0;
527 MessageText: String;
528 MessageTime: Word;
529 MapList: SArray = nil;
530 MapIndex: Integer = -1;
531 MegaWAD: record
532 info: TMegaWADInfo;
533 endpic: String;
534 endmus: String;
535 res: record
536 text: Array of ShortString;
537 anim: Array of ShortString;
538 pic: Array of ShortString;
539 mus: Array of ShortString;
540 end;
541 triggers: Array of record
542 event: ShortString;
543 actions: Array of record
544 action, p1, p2: Integer;
545 end;
546 end;
547 cur_trigger: Integer;
548 cur_action: Integer;
549 end;
550 //InterPic: String;
551 InterText: record
552 lines: SArray;
553 img: String;
554 cur_line: Integer;
555 cur_char: Integer;
556 counter: Integer;
557 endtext: Boolean;
558 end;
560 function Compare(a, b: TPlayerStat): Integer;
561 begin
562 if a.Spectator then Result := 1
563 else if b.Spectator then Result := -1
564 else if a.Frags < b.Frags then Result := 1
565 else if a.Frags > b.Frags then Result := -1
566 else if a.Deaths < b.Deaths then Result := -1
567 else if a.Deaths > b.Deaths then Result := 1
568 else if a.Kills < b.Kills then Result := -1
569 else Result := 1;
570 end;
572 procedure SortGameStat(var stat: TPlayerStatArray);
573 var
574 I, J: Integer;
575 T: TPlayerStat;
576 begin
577 if stat = nil then Exit;
579 for I := High(stat) downto Low(stat) do
580 for J := Low(stat) to High(stat) - 1 do
581 if Compare(stat[J], stat[J + 1]) = 1 then
582 begin
583 T := stat[J];
584 stat[J] := stat[J + 1];
585 stat[J + 1] := T;
586 end;
587 end;
589 function g_Game_ModeToText(Mode: Byte): string;
590 begin
591 Result := '';
592 case Mode of
593 GM_DM: Result := _lc[I_MENU_GAME_TYPE_DM];
594 GM_TDM: Result := _lc[I_MENU_GAME_TYPE_TDM];
595 GM_CTF: Result := _lc[I_MENU_GAME_TYPE_CTF];
596 GM_COOP: Result := _lc[I_MENU_GAME_TYPE_COOP];
597 GM_SINGLE: Result := _lc[I_MENU_GAME_TYPE_SINGLE];
598 end;
599 end;
601 function g_Game_TextToMode(Mode: string): Byte;
602 begin
603 Result := GM_NONE;
604 Mode := UpperCase(Mode);
605 if Mode = _lc[I_MENU_GAME_TYPE_DM] then
606 begin
607 Result := GM_DM;
608 Exit;
609 end;
610 if Mode = _lc[I_MENU_GAME_TYPE_TDM] then
611 begin
612 Result := GM_TDM;
613 Exit;
614 end;
615 if Mode = _lc[I_MENU_GAME_TYPE_CTF] then
616 begin
617 Result := GM_CTF;
618 Exit;
619 end;
620 if Mode = _lc[I_MENU_GAME_TYPE_COOP] then
621 begin
622 Result := GM_COOP;
623 Exit;
624 end;
625 if Mode = _lc[I_MENU_GAME_TYPE_SINGLE] then
626 begin
627 Result := GM_SINGLE;
628 Exit;
629 end;
630 end;
632 function g_Game_IsNet(): Boolean;
633 begin
634 Result := (gGameSettings.GameType in [GT_SERVER, GT_CLIENT]);
635 end;
637 function g_Game_IsServer(): Boolean;
638 begin
639 Result := (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM, GT_SERVER]);
640 end;
642 function g_Game_IsClient(): Boolean;
643 begin
644 Result := (gGameSettings.GameType = GT_CLIENT);
645 end;
647 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
648 var
649 w: TWADFile;
650 cfg: TConfig;
651 p: Pointer;
652 len: Integer;
653 begin
654 Result.name := ExtractFileName(WAD);
655 Result.description := '';
656 Result.author := '';
658 w := TWADFile.Create();
659 w.ReadFile(WAD);
661 if not w.GetResource('INTERSCRIPT', p, len) then
662 begin
663 w.Free();
664 Exit;
665 end;
667 cfg := TConfig.CreateMem(p, len);
668 Result.name := cfg.ReadStr('megawad', 'name', ExtractFileName(WAD));
669 Result.description := cfg.ReadStr('megawad', 'description', '');
670 Result.author := cfg.ReadStr('megawad', 'author', '');
671 Result.pic := cfg.ReadStr('megawad', 'pic', '');
672 cfg.Free();
674 FreeMem(p);
675 end;
677 procedure g_Game_FreeWAD();
678 var
679 a: Integer;
680 begin
681 for a := 0 to High(MegaWAD.res.pic) do
682 if MegaWAD.res.pic[a] <> '' then
683 g_Texture_Delete(MegaWAD.res.pic[a]);
685 for a := 0 to High(MegaWAD.res.mus) do
686 if MegaWAD.res.mus[a] <> '' then
687 g_Sound_Delete(MegaWAD.res.mus[a]);
689 MegaWAD.res.pic := nil;
690 MegaWAD.res.text := nil;
691 MegaWAD.res.anim := nil;
692 MegaWAD.res.mus := nil;
693 MegaWAD.triggers := nil;
695 g_Texture_Delete('TEXTURE_endpic');
696 g_Sound_Delete('MUSIC_endmus');
698 ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
699 gGameSettings.WAD := '';
700 end;
702 procedure g_Game_LoadWAD(WAD: string);
703 var
704 w: TWADFile;
705 cfg: TConfig;
706 p: Pointer;
707 {b, }len: Integer;
708 s: string;
709 begin
710 g_Game_FreeWAD();
711 gGameSettings.WAD := WAD;
712 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then
713 Exit;
715 MegaWAD.info := g_Game_GetMegaWADInfo(MapsDir + WAD);
717 w := TWADFile.Create();
718 w.ReadFile(MapsDir + WAD);
720 if not w.GetResource('INTERSCRIPT', p, len) then
721 begin
722 w.Free();
723 Exit;
724 end;
726 cfg := TConfig.CreateMem(p, len);
728 {b := 1;
729 while True do
730 begin
731 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
732 if s = '' then Break;
733 b := b+1;
735 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
736 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
738 g_Texture_CreateWADEx(s, s);
739 end;
741 b := 1;
742 while True do
743 begin
744 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
745 if s = '' then Break;
746 b := b+1;
748 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
749 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
751 g_Music_CreateWADEx(s, s);
752 end;}
754 MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
755 if MegaWAD.endpic <> '' then
756 begin
757 s := g_ExtractWadName(MegaWAD.endpic);
758 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
759 g_Texture_CreateWADEx('TEXTURE_endpic', s+MegaWAD.endpic);
760 end;
761 MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
762 if MegaWAD.endmus <> '' then
763 begin
764 s := g_ExtractWadName(MegaWAD.endmus);
765 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
766 g_Sound_CreateWADEx('MUSIC_endmus', s+MegaWAD.endmus, True);
767 end;
769 cfg.Free();
770 FreeMem(p);
771 w.Free();
772 end;
774 {procedure start_trigger(t: string);
775 begin
776 end;
778 function next_trigger(): Boolean;
779 begin
780 end;}
782 procedure DisableCheats();
783 begin
784 MAX_RUNVEL := 8;
785 VEL_JUMP := 10;
786 gFly := False;
788 if gPlayer1 <> nil then gPlayer1.GodMode := False;
789 if gPlayer2 <> nil then gPlayer2.GodMode := False;
790 if gPlayer1 <> nil then gPlayer1.NoTarget := False;
791 if gPlayer2 <> nil then gPlayer2.NoTarget := False;
793 {$IF DEFINED(D2F_DEBUG)}
794 if gPlayer1 <> nil then gPlayer1.NoTarget := True;
795 gAimLine := g_dbg_aimline_on;
796 {$ENDIF}
797 end;
799 procedure g_Game_ExecuteEvent(Name: String);
800 var
801 a: Integer;
802 begin
803 if Name = '' then
804 Exit;
805 if gEvents = nil then
806 Exit;
807 for a := 0 to High(gEvents) do
808 if gEvents[a].Name = Name then
809 begin
810 if gEvents[a].Command <> '' then
811 g_Console_Process(gEvents[a].Command, True);
812 break;
813 end;
814 end;
816 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
817 var
818 a, n: Integer;
819 begin
820 n := -1;
821 if gDelayedEvents <> nil then
822 for a := 0 to High(gDelayedEvents) do
823 if not gDelayedEvents[a].Pending then
824 begin
825 n := a;
826 break;
827 end;
828 if n = -1 then
829 begin
830 SetLength(gDelayedEvents, Length(gDelayedEvents) + 1);
831 n := High(gDelayedEvents);
832 end;
833 gDelayedEvents[n].Pending := True;
834 gDelayedEvents[n].DEType := DEType;
835 gDelayedEvents[n].DENum := Num;
836 gDelayedEvents[n].DEStr := Str;
837 if DEType = DE_GLOBEVENT then
838 gDelayedEvents[n].Time := (GetTimer() {div 1000}) + Time
839 else
840 gDelayedEvents[n].Time := gTime + Time;
841 Result := n;
842 end;
844 procedure EndGame();
845 var
846 a: Integer;
847 FileName: string;
848 begin
849 if g_Game_IsNet and g_Game_IsServer then
850 MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed));
852 // Ñòîï èãðà:
853 gPause := False;
854 gGameOn := False;
856 g_Game_StopAllSounds(False);
858 MessageTime := 0;
859 MessageText := '';
861 EndingGameCounter := 0;
862 g_ActiveWindow := nil;
864 gLMSRespawn := LMS_RESPAWN_NONE;
865 gLMSRespawnTime := 0;
867 case gExit of
868 EXIT_SIMPLE: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
869 begin
870 g_Game_Free();
872 if gMapOnce then
873 begin // Ýòî áûë òåñò
874 g_Game_Quit();
875 end
876 else
877 begin // Âûõîä â ãëàâíîå ìåíþ
878 gMusic.SetByName('MUSIC_MENU');
879 gMusic.Play();
880 if gState <> STATE_SLIST then
881 begin
882 g_GUI_ShowWindow('MainMenu');
883 gState := STATE_MENU;
884 end else
885 begin
886 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
887 slReturnPressed := True;
888 if g_Net_Slist_Fetch(slCurrent) then
889 begin
890 if slCurrent = nil then
891 slWaitStr := _lc[I_NET_SLIST_NOSERVERS];
892 end
893 else
894 slWaitStr := _lc[I_NET_SLIST_ERROR];
895 end;
897 g_Game_ExecuteEvent('ongameend');
898 end;
899 end;
901 EXIT_RESTART: // Íà÷àòü óðîâåíü ñíà÷àëà
902 begin
903 if not g_Game_IsClient then g_Game_Restart();
904 end;
906 EXIT_ENDLEVELCUSTOM: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
907 begin
908 // Ñòàòèñòèêà Ñâîåé èãðû:
909 FileName := g_ExtractWadName(gMapInfo.Map);
911 CustomStat.GameTime := gTime;
912 CustomStat.Map := ExtractFileName(FileName)+':'+g_ExtractFileName(gMapInfo.Map); //ResName;
913 CustomStat.MapName := gMapInfo.Name;
914 CustomStat.GameMode := gGameSettings.GameMode;
915 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
916 CustomStat.TeamStat := gTeamStat;
918 CustomStat.PlayerStat := nil;
920 // Ñòàòèñòèêà èãðîêîâ:
921 if gPlayers <> nil then
922 begin
923 for a := 0 to High(gPlayers) do
924 if gPlayers[a] <> nil then
925 begin
926 SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1);
927 with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do
928 begin
929 Name := gPlayers[a].Name;
930 Frags := gPlayers[a].Frags;
931 Deaths := gPlayers[a].Death;
932 Kills := gPlayers[a].Kills;
933 Team := gPlayers[a].Team;
934 Color := gPlayers[a].Model.Color;
935 Spectator := gPlayers[a].FSpectator;
936 end;
937 end;
939 SortGameStat(CustomStat.PlayerStat);
940 end;
942 g_Game_ExecuteEvent('onmapend');
944 // Çàòóõàþùèé ýêðàí:
945 EndingGameCounter := 255;
946 gState := STATE_FOLD;
947 gInterTime := 0;
948 if gDefInterTime < 0 then
949 gInterEndTime := IfThen((gGameSettings.GameType = GT_SERVER) and (gPlayer1 = nil), 15000, 25000)
950 else
951 gInterEndTime := gDefInterTime * 1000;
952 end;
954 EXIT_ENDLEVELSINGLE: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
955 begin
956 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
957 SingleStat.GameTime := gTime;
958 SingleStat.TwoPlayers := gPlayer2 <> nil;
959 SingleStat.TotalSecrets := gSecretsCount;
960 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
961 SingleStat.PlayerStat[0].Kills := gPlayer1.MonsterKills;
962 SingleStat.PlayerStat[0].Secrets := gPlayer1.Secrets;
963 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
964 if SingleStat.TwoPlayers then
965 begin
966 SingleStat.PlayerStat[1].Kills := gPlayer2.MonsterKills;
967 SingleStat.PlayerStat[1].Secrets := gPlayer2.Secrets;
968 end;
970 g_Game_ExecuteEvent('onmapend');
972 // Åñòü åùå êàðòû:
973 if gNextMap <> '' then
974 begin
975 gMusic.SetByName('MUSIC_INTERMUS');
976 gMusic.Play();
977 gState := STATE_INTERSINGLE;
979 g_Game_ExecuteEvent('oninter');
980 end
981 else // Áîëüøå íåò êàðò
982 begin
983 // Çàòóõàþùèé ýêðàí:
984 EndingGameCounter := 255;
985 gState := STATE_FOLD;
986 end;
987 end;
988 end;
990 // Îêîí÷àíèå îáðàáîòàíî:
991 if gExit <> EXIT_QUIT then
992 gExit := 0;
993 end;
995 procedure drawTime(X, Y: Integer); inline;
996 begin
997 e_TextureFontPrint(x, y,
998 Format('%d:%.2d:%.2d', [
999 gTime div 1000 div 3600,
1000 (gTime div 1000 div 60) mod 60,
1001 gTime div 1000 mod 60
1002 ]),
1003 gStdFont);
1004 end;
1006 procedure DrawStat();
1007 var
1008 pc, x, y, w, h: Integer;
1009 w1, w2, w3, w4: Integer;
1010 a, aa: Integer;
1011 cw, ch, r, g, b, rr, gg, bb: Byte;
1012 s1, s2, s3: String;
1013 _y: Integer;
1014 stat: TPlayerStatArray;
1015 wad, map: string;
1016 mapstr: string;
1017 begin
1018 s1 := '';
1019 s2 := '';
1020 s3 := '';
1021 pc := g_Player_GetCount;
1022 e_TextureFontGetSize(gStdFont, cw, ch);
1024 w := gScreenWidth-(gScreenWidth div 5);
1025 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1026 h := 32+ch*(11+pc)
1027 else
1028 h := 40+ch*5+(ch+8)*pc;
1029 x := (gScreenWidth div 2)-(w div 2);
1030 y := (gScreenHeight div 2)-(h div 2);
1032 e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32);
1033 e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0);
1035 drawTime(x+w-78, y+8);
1037 wad := g_ExtractWadNameNoPath(gMapInfo.Map);
1038 map := g_ExtractFileName(gMapInfo.Map);
1039 mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name;
1041 case gGameSettings.GameMode of
1042 GM_DM:
1043 begin
1044 if gGameSettings.MaxLives = 0 then
1045 s1 := _lc[I_GAME_DM]
1046 else
1047 s1 := _lc[I_GAME_LMS];
1048 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
1049 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1050 end;
1052 GM_TDM:
1053 begin
1054 if gGameSettings.MaxLives = 0 then
1055 s1 := _lc[I_GAME_TDM]
1056 else
1057 s1 := _lc[I_GAME_TLMS];
1058 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
1059 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1060 end;
1062 GM_CTF:
1063 begin
1064 s1 := _lc[I_GAME_CTF];
1065 s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.GoalLimit]);
1066 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1067 end;
1069 GM_COOP:
1070 begin
1071 if gGameSettings.MaxLives = 0 then
1072 s1 := _lc[I_GAME_COOP]
1073 else
1074 s1 := _lc[I_GAME_SURV];
1075 s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters);
1076 s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount);
1077 end;
1079 else
1080 begin
1081 s1 := '';
1082 s2 := '';
1083 end;
1084 end;
1086 _y := y+8;
1087 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1);
1088 _y := _y+ch+8;
1089 e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1);
1090 _y := _y+ch+8;
1091 e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1);
1093 e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3,
1094 gStdFont, 200, 200, 200, 1);
1096 if NetMode = NET_SERVER then
1097 e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1)
1098 else
1099 if NetMode = NET_CLIENT then
1100 e_TextureFontPrintEx(x+8, y + 8,
1101 NetClientIP + ':' + IntToStr(NetClientPort), gStdFont, 255, 255, 255, 1);
1103 if pc = 0 then
1104 Exit;
1105 stat := g_Player_GetStats();
1106 SortGameStat(stat);
1108 w2 := (w-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1109 w3 := (w-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1110 w4 := w3;
1111 w1 := w-16-w2-w3-w4; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1113 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1114 begin
1115 _y := _y+ch+ch;
1117 for a := TEAM_RED to TEAM_BLUE do
1118 begin
1119 if a = TEAM_RED then
1120 begin
1121 s1 := _lc[I_GAME_TEAM_RED];
1122 r := 255;
1123 g := 0;
1124 b := 0;
1125 end
1126 else
1127 begin
1128 s1 := _lc[I_GAME_TEAM_BLUE];
1129 r := 0;
1130 g := 0;
1131 b := 255;
1132 end;
1134 e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1);
1135 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Goals),
1136 gStdFont, r, g, b, 1);
1138 _y := _y+ch+(ch div 4);
1139 e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b);
1140 _y := _y+(ch div 4);
1142 for aa := 0 to High(stat) do
1143 if stat[aa].Team = a then
1144 with stat[aa] do
1145 begin
1146 if Spectator then
1147 begin
1148 rr := r div 2;
1149 gg := g div 2;
1150 bb := b div 2;
1151 end
1152 else
1153 begin
1154 rr := r;
1155 gg := g;
1156 bb := b;
1157 end;
1158 // Èìÿ
1159 e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
1160 // Ïèíã/ïîòåðè
1161 e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1);
1162 // Ôðàãè
1163 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
1164 // Ñìåðòè
1165 e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
1166 _y := _y+ch;
1167 end;
1169 _y := _y+ch;
1170 end;
1171 end
1172 else if gGameSettings.GameMode in [GM_DM, GM_COOP] then
1173 begin
1174 _y := _y+ch+ch;
1175 e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
1176 e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1);
1177 e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
1178 e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
1180 _y := _y+ch+8;
1181 for aa := 0 to High(stat) do
1182 with stat[aa] do
1183 begin
1184 if Spectator then
1185 begin
1186 r := 127;
1187 g := 64;
1188 end
1189 else
1190 begin
1191 r := 255;
1192 g := 127;
1193 end;
1194 // Öâåò èãðîêà
1195 e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
1196 e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192);
1197 // Èìÿ
1198 e_TextureFontPrintEx(x+16+16+8, _y+4, Name, gStdFont, r, g, 0, 1);
1199 // Ïèíã/ïîòåðè
1200 e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1);
1201 // Ôðàãè
1202 e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1);
1203 // Ñìåðòè
1204 e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1);
1205 _y := _y+ch+8;
1206 end;
1207 end
1208 end;
1210 procedure g_Game_Init();
1211 var
1212 SR: TSearchRec;
1213 begin
1214 gExit := 0;
1215 gMapToDelete := '';
1216 gTempDelete := False;
1218 sfsGCDisable(); // temporary disable removing of temporary volumes
1220 try
1221 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE');
1222 g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER');
1223 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN');
1224 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU');
1226 LoadStdFont('STDTXT', 'STDFONT', gStdFont);
1227 LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
1228 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
1230 g_Game_ClearLoading();
1231 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
1232 g_Game_SetLoadingText('', 0, False);
1234 g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
1235 g_Console_Init();
1237 g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
1238 g_PlayerModel_LoadData();
1240 if FindFirst(ModelsDir+'*.wad', faAnyFile, SR) = 0 then
1241 repeat
1242 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1243 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1244 until FindNext(SR) <> 0;
1245 FindClose(SR);
1247 if FindFirst(ModelsDir+'*.pk3', faAnyFile, SR) = 0 then
1248 repeat
1249 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1250 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1251 until FindNext(SR) <> 0;
1252 FindClose(SR);
1254 if FindFirst(ModelsDir+'*.zip', faAnyFile, SR) = 0 then
1255 repeat
1256 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1257 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1258 until FindNext(SR) <> 0;
1259 FindClose(SR);
1261 gGameOn := False;
1262 gPause := False;
1263 gTime := 0;
1264 LastScreenShot := 0;
1266 {e_MouseInfo.Accel := 1.0;}
1268 g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
1269 g_Game_LoadData();
1271 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1272 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
1273 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
1274 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True);
1275 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
1277 g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
1278 g_Menu_Init();
1280 gMusic := TMusic.Create();
1281 gMusic.SetByName('MUSIC_MENU');
1282 gMusic.Play();
1284 gGameSettings.WarmupTime := 30;
1286 gState := STATE_MENU;
1288 SetLength(gEvents, 6);
1289 gEvents[0].Name := 'ongamestart';
1290 gEvents[1].Name := 'ongameend';
1291 gEvents[2].Name := 'onmapstart';
1292 gEvents[3].Name := 'onmapend';
1293 gEvents[4].Name := 'oninter';
1294 gEvents[5].Name := 'onwadend';
1295 finally
1296 sfsGCEnable(); // enable releasing unused volumes
1297 end;
1298 end;
1300 procedure g_Game_Free(freeTextures: Boolean=true);
1301 begin
1302 if NetMode = NET_CLIENT then g_Net_Disconnect();
1303 if NetMode = NET_SERVER then g_Net_Host_Die();
1305 g_Map_Free(freeTextures);
1306 g_Player_Free();
1307 g_Player_RemoveAllCorpses();
1309 gGameSettings.GameType := GT_NONE;
1310 if gGameSettings.GameMode = GM_SINGLE then
1311 gGameSettings.GameMode := GM_DM;
1312 gSwitchGameMode := gGameSettings.GameMode;
1314 gChatShow := False;
1315 gExitByTrigger := False;
1316 end;
1318 function IsActivePlayer(p: TPlayer): Boolean;
1319 begin
1320 Result := False;
1321 if p = nil then
1322 Exit;
1323 Result := (not p.FDummy) and (not p.FSpectator);
1324 end;
1326 function GetActivePlayer_ByID(ID: Integer): TPlayer;
1327 var
1328 a: Integer;
1329 begin
1330 Result := nil;
1331 if ID < 0 then
1332 Exit;
1333 if gPlayers = nil then
1334 Exit;
1335 for a := Low(gPlayers) to High(gPlayers) do
1336 if IsActivePlayer(gPlayers[a]) then
1337 begin
1338 if gPlayers[a].UID <> ID then
1339 continue;
1340 Result := gPlayers[a];
1341 break;
1342 end;
1343 end;
1345 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
1346 var
1347 a, idx: Integer;
1348 ids: Array of Word;
1349 begin
1350 Result := -1;
1351 if gPlayers = nil then
1352 Exit;
1353 SetLength(ids, 0);
1354 idx := -1;
1355 for a := Low(gPlayers) to High(gPlayers) do
1356 if IsActivePlayer(gPlayers[a]) then
1357 begin
1358 SetLength(ids, Length(ids) + 1);
1359 ids[High(ids)] := gPlayers[a].UID;
1360 if gPlayers[a].UID = Skip then
1361 idx := High(ids);
1362 end;
1363 if Length(ids) = 0 then
1364 Exit;
1365 if idx = -1 then
1366 Result := ids[0]
1367 else
1368 Result := ids[(idx + 1) mod Length(ids)];
1369 end;
1371 function GetActivePlayerID_Prev(Skip: Integer = -1): Integer;
1372 var
1373 a, idx: Integer;
1374 ids: Array of Word;
1375 begin
1376 Result := -1;
1377 if gPlayers = nil then
1378 Exit;
1379 SetLength(ids, 0);
1380 idx := -1;
1381 for a := Low(gPlayers) to High(gPlayers) do
1382 if IsActivePlayer(gPlayers[a]) then
1383 begin
1384 SetLength(ids, Length(ids) + 1);
1385 ids[High(ids)] := gPlayers[a].UID;
1386 if gPlayers[a].UID = Skip then
1387 idx := High(ids);
1388 end;
1389 if Length(ids) = 0 then
1390 Exit;
1391 if idx = -1 then
1392 Result := ids[Length(ids) - 1]
1393 else
1394 Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
1395 end;
1397 function isKeyPressed (key1: Word; key2: Word): Boolean;
1398 begin
1399 if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
1400 if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
1401 result := false;
1402 end;
1404 procedure processPlayerControls (plr: TPlayer; var ctrl: TPlayerControl; var MoveButton: Byte; p2hack: Boolean=false);
1405 var
1406 time: Word;
1407 strafeDir: Byte;
1408 i: Integer;
1409 begin
1410 if (plr = nil) then exit;
1411 if (p2hack) then time := 1000 else time := 1;
1412 strafeDir := MoveButton shr 4;
1413 MoveButton := MoveButton and $0F;
1414 with ctrl do
1415 begin
1416 if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1417 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1418 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1420 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1421 if MoveButton = 1 then plr.PressKey(KEY_LEFT, time)
1422 else if MoveButton = 2 then plr.PressKey(KEY_RIGHT, time);
1424 // if we have "strafe" key, turn off old strafe mechanics
1425 if isKeyPressed(KeyStrafe, KeyStrafe2) then
1426 begin
1427 // new strafe mechanics
1428 if (strafeDir = 0) then strafeDir := MoveButton; // start strafing
1429 // now set direction according to strafe (reversed)
1430 if (strafeDir = 2) then plr.SetDirection(D_LEFT)
1431 else if (strafeDir = 1) then plr.SetDirection(D_RIGHT);
1432 end
1433 else
1434 begin
1435 strafeDir := 0; // not strafing anymore
1436 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1437 if (MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then plr.SetDirection(D_LEFT)
1438 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1439 else if (MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then plr.SetDirection(D_RIGHT)
1440 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1441 else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1));
1442 end;
1444 // fix movebutton state
1445 MoveButton := MoveButton or (strafeDir shl 4);
1447 // Îñòàëüíûå êëàâèøè:
1448 if isKeyPressed(KeyJump, KeyJump2) then plr.PressKey(KEY_JUMP, time);
1449 if isKeyPressed(KeyUp, KeyUp2) then plr.PressKey(KEY_UP, time);
1450 if isKeyPressed(KeyDown, KeyDown2) then plr.PressKey(KEY_DOWN, time);
1451 if isKeyPressed(KeyFire, KeyFire2) then plr.PressKey(KEY_FIRE);
1452 if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then plr.PressKey(KEY_NEXTWEAPON);
1453 if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then plr.PressKey(KEY_PREVWEAPON);
1454 if isKeyPressed(KeyOpen, KeyOpen2) then plr.PressKey(KEY_OPEN);
1456 for i := 0 to High(KeyWeapon) do
1457 if isKeyPressed(KeyWeapon[i], KeyWeapon2[i]) then
1458 plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
1459 end;
1461 // HACK: add dynlight here
1462 if gwin_k8_enable_light_experiments then
1463 begin
1464 if e_KeyPressed(IK_F8) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1465 begin
1466 g_playerLight := true;
1467 end;
1468 if e_KeyPressed(IK_F9) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1469 begin
1470 g_playerLight := false;
1471 end;
1472 end;
1474 if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
1475 end;
1477 procedure g_Game_Update();
1478 var
1479 Msg: g_gui.TMessage;
1480 Time: Int64;
1481 a: Byte;
1482 w: Word;
1483 i, b: Integer;
1485 function sendMonsPos (mon: TMonster): Boolean;
1486 begin
1487 result := false; // don't stop
1488 // this will also reset "need-send" flag
1489 if mon.gncNeedSend then
1490 begin
1491 MH_SEND_MonsterPos(mon.UID);
1492 end
1493 else if (mon.MonsterType = MONSTER_BARREL) then
1494 begin
1495 if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1496 end
1497 else if (mon.MonsterState <> MONSTATE_SLEEP) then
1498 begin
1499 if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1500 end;
1501 end;
1503 function sendMonsPosUnexpected (mon: TMonster): Boolean;
1504 begin
1505 result := false; // don't stop
1506 // this will also reset "need-send" flag
1507 if mon.gncNeedSend then MH_SEND_MonsterPos(mon.UID);
1508 end;
1510 var
1511 reliableUpdate: Boolean;
1512 begin
1513 g_ResetDynlights();
1514 // Ïîðà âûêëþ÷àòü èãðó:
1515 if gExit = EXIT_QUIT then
1516 Exit;
1517 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1518 if gExit <> 0 then
1519 begin
1520 EndGame();
1521 if gExit = EXIT_QUIT then
1522 Exit;
1523 end;
1525 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî:
1526 e_PollInput();
1528 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1529 g_Console_Update();
1531 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1532 begin
1533 gExit := EXIT_SIMPLE;
1534 EndGame();
1535 Exit;
1536 end;
1538 case gState of
1539 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1540 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1541 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1542 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1543 begin
1544 if g_Game_IsNet and g_Game_IsServer then
1545 begin
1546 gInterTime := gInterTime + GAME_TICK;
1547 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1548 if a <> gServInterTime then
1549 begin
1550 gServInterTime := a;
1551 MH_SEND_TimeSync(gServInterTime);
1552 end;
1553 end;
1555 if (not g_Game_IsClient) and
1558 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1559 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1560 and (g_ActiveWindow = nil)
1562 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1564 then
1565 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1566 g_Game_StopAllSounds(True);
1568 if gMapOnce then // Ýòî áûë òåñò
1569 gExit := EXIT_SIMPLE
1570 else
1571 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1572 g_Game_ChangeMap(gNextMap)
1573 else // Ñëåäóþùåé êàðòû íåò
1574 begin
1575 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1576 begin
1577 // Âûõîä â ãëàâíîå ìåíþ:
1578 g_Game_Free;
1579 g_GUI_ShowWindow('MainMenu');
1580 gMusic.SetByName('MUSIC_MENU');
1581 gMusic.Play();
1582 gState := STATE_MENU;
1583 end else
1584 begin
1585 // Ôèíàëüíàÿ êàðòèíêà:
1586 g_Game_ExecuteEvent('onwadend');
1587 g_Game_Free();
1588 if not gMusic.SetByName('MUSIC_endmus') then
1589 gMusic.SetByName('MUSIC_STDENDMUS');
1590 gMusic.Play();
1591 gState := STATE_ENDPIC;
1592 end;
1593 g_Game_ExecuteEvent('ongameend');
1594 end;
1596 Exit;
1597 end;
1599 if gState = STATE_INTERTEXT then
1600 if InterText.counter > 0 then
1601 InterText.counter := InterText.counter - 1;
1602 end;
1604 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1605 begin
1606 if EndingGameCounter = 0 then
1607 begin
1608 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1609 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1610 begin
1611 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1612 begin
1613 g_Game_ExecuteEvent('onwadend');
1614 if not gMusic.SetByName('MUSIC_endmus') then
1615 gMusic.SetByName('MUSIC_STDENDMUS');
1616 end
1617 else
1618 gMusic.SetByName('MUSIC_ROUNDMUS');
1620 gMusic.Play();
1621 gState := STATE_INTERCUSTOM;
1622 end
1623 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1624 begin
1625 gMusic.SetByName('MUSIC_INTERMUS');
1626 gMusic.Play();
1627 gState := STATE_INTERSINGLE;
1628 end;
1629 g_Game_ExecuteEvent('oninter');
1630 end
1631 else
1632 DecMin(EndingGameCounter, 6, 0);
1633 end;
1635 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1636 begin
1637 if gMapOnce then // Ýòî áûë òåñò
1638 begin
1639 gExit := EXIT_SIMPLE;
1640 Exit;
1641 end;
1642 end;
1644 STATE_SLIST:
1645 g_Serverlist_Control(slCurrent);
1646 end;
1648 if g_Game_IsNet then
1649 if not gConsoleShow then
1650 if not gChatShow then
1651 begin
1652 if g_ActiveWindow = nil then
1653 begin
1654 if e_KeyPressed(gGameControls.GameControls.Chat) then
1655 g_Console_Chat_Switch(False)
1656 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1657 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1658 g_Console_Chat_Switch(True);
1659 end;
1660 end else
1661 if not gChatEnter then
1662 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1663 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1664 gChatEnter := True;
1666 // Ñòàòèñòèêà ïî Tab:
1667 if gGameOn then
1668 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1669 (gGameSettings.GameType <> GT_SINGLE) and
1670 e_KeyPressed(gGameControls.GameControls.Stat);
1672 // Èãðà èäåò:
1673 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1674 begin
1675 // Âðåìÿ += 28 ìèëëèñåêóíä:
1676 gTime := gTime + GAME_TICK;
1678 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1679 if MessageTime = 0 then
1680 MessageText := '';
1681 if MessageTime > 0 then
1682 MessageTime := MessageTime - 1;
1684 if (g_Game_IsServer) then
1685 begin
1686 // Áûë çàäàí ëèìèò âðåìåíè:
1687 if (gGameSettings.TimeLimit > 0) then
1688 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1689 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1690 g_Game_NextLevel();
1691 Exit;
1692 end;
1694 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1695 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1696 g_Game_RestartRound(gLMSSoftSpawn);
1698 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1699 if gVoteInProgress and (gVoteTimer < gTime) then
1700 g_Game_CheckVote
1701 else if gVotePassed and (gVoteCmdTimer < gTime) then
1702 begin
1703 g_Console_Process(gVoteCommand);
1704 gVoteCommand := '';
1705 gVotePassed := False;
1706 end;
1708 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1709 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1710 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1711 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1712 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1714 // Áûë çàäàí ëèìèò ïîáåä:
1715 if (gGameSettings.GoalLimit > 0) then
1716 begin
1717 b := 0;
1719 if gGameSettings.GameMode = GM_DM then
1720 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1721 for i := 0 to High(gPlayers) do
1722 if gPlayers[i] <> nil then
1723 if gPlayers[i].Frags > b then
1724 b := gPlayers[i].Frags;
1725 end
1726 else
1727 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1728 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1729 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1730 end;
1732 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1733 if b >= gGameSettings.GoalLimit then
1734 begin
1735 g_Game_NextLevel();
1736 Exit;
1737 end;
1738 end;
1740 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1741 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1742 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1743 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1744 begin
1745 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1746 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1747 end // if not console
1748 else
1749 begin
1750 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1751 end;
1752 // process weapon switch queue
1753 end; // if server
1755 // Íàáëþäàòåëü
1756 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1757 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1758 begin
1759 if not gSpectKeyPress then
1760 begin
1761 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
1762 begin
1763 // switch spect mode
1764 case gSpectMode of
1765 SPECT_NONE: ; // not spectator
1766 SPECT_STATS,
1767 SPECT_MAPVIEW: Inc(gSpectMode);
1768 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1769 end;
1770 gSpectKeyPress := True;
1771 end;
1772 if gSpectMode = SPECT_MAPVIEW then
1773 begin
1774 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1775 gSpectX := Max(gSpectX - gSpectStep, 0);
1776 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1777 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1778 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1779 gSpectY := Max(gSpectY - gSpectStep, 0);
1780 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1781 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1782 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1783 begin
1784 // decrease step
1785 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1786 gSpectKeyPress := True;
1787 end;
1788 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1789 begin
1790 // increase step
1791 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1792 gSpectKeyPress := True;
1793 end;
1794 end;
1795 if gSpectMode = SPECT_PLAYERS then
1796 begin
1797 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1798 begin
1799 // add second view
1800 gSpectViewTwo := True;
1801 gSpectKeyPress := True;
1802 end;
1803 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1804 begin
1805 // remove second view
1806 gSpectViewTwo := False;
1807 gSpectKeyPress := True;
1808 end;
1809 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1810 begin
1811 // prev player (view 1)
1812 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1813 gSpectKeyPress := True;
1814 end;
1815 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1816 begin
1817 // next player (view 1)
1818 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1819 gSpectKeyPress := True;
1820 end;
1821 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1822 begin
1823 // prev player (view 2)
1824 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1825 gSpectKeyPress := True;
1826 end;
1827 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1828 begin
1829 // next player (view 2)
1830 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1831 gSpectKeyPress := True;
1832 end;
1833 end;
1834 end
1835 else
1836 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1837 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1838 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1839 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1840 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1841 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1842 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1843 gSpectKeyPress := False;
1844 end;
1846 // Îáíîâëÿåì âñå îñòàëüíîå:
1847 g_Map_Update();
1848 g_Items_Update();
1849 g_Triggers_Update();
1850 g_Weapon_Update();
1851 g_Monsters_Update();
1852 g_GFX_Update();
1853 g_Player_UpdateAll();
1854 g_Player_UpdatePhysicalObjects();
1856 // server: send newly spawned monsters unconditionally
1857 if (gGameSettings.GameType = GT_SERVER) then
1858 begin
1859 if (Length(gMonstersSpawned) > 0) then
1860 begin
1861 for I := 0 to High(gMonstersSpawned) do MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
1862 SetLength(gMonstersSpawned, 0);
1863 end;
1864 end;
1866 if (gSoundTriggerTime > 8) then
1867 begin
1868 g_Game_UpdateTriggerSounds();
1869 gSoundTriggerTime := 0;
1870 end
1871 else
1872 begin
1873 Inc(gSoundTriggerTime);
1874 end;
1876 if (NetMode = NET_SERVER) then
1877 begin
1878 Inc(NetTimeToUpdate);
1879 Inc(NetTimeToReliable);
1881 // send monster updates
1882 if (NetTimeToReliable >= NetRelupdRate) or (NetTimeToUpdate >= NetUpdateRate) then
1883 begin
1884 // send all monsters (periodic sync)
1885 reliableUpdate := (NetTimeToReliable >= NetRelupdRate);
1887 for I := 0 to High(gPlayers) do
1888 begin
1889 if (gPlayers[I] <> nil) then MH_SEND_PlayerPos(reliableUpdate, gPlayers[I].UID);
1890 end;
1892 g_Mons_ForEach(sendMonsPos);
1894 if reliableUpdate then
1895 begin
1896 NetTimeToReliable := 0;
1897 NetTimeToUpdate := NetUpdateRate;
1898 end
1899 else
1900 begin
1901 NetTimeToUpdate := 0;
1902 end;
1903 end
1904 else
1905 begin
1906 // send only mosters with some unexpected changes
1907 g_Mons_ForEach(sendMonsPosUnexpected);
1908 end;
1910 // send unexpected platform changes
1911 g_Map_NetSendInterestingPanels();
1913 if NetUseMaster then
1914 begin
1915 if gTime >= NetTimeToMaster then
1916 begin
1917 if (NetMHost = nil) or (NetMPeer = nil) then
1918 begin
1919 if not g_Net_Slist_Connect then g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
1920 end;
1922 g_Net_Slist_Update;
1923 NetTimeToMaster := gTime + NetMasterRate;
1924 end;
1925 end;
1926 end
1927 else if (NetMode = NET_CLIENT) then
1928 begin
1929 MC_SEND_PlayerPos();
1930 end;
1931 end; // if gameOn ...
1933 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
1934 if g_ActiveWindow <> nil then
1935 begin
1936 w := e_GetFirstKeyPressed();
1938 if (w <> IK_INVALID) then
1939 begin
1940 Msg.Msg := MESSAGE_DIKEY;
1941 Msg.wParam := w;
1942 g_ActiveWindow.OnMessage(Msg);
1943 end;
1945 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
1946 if g_ActiveWindow <> nil then
1947 g_ActiveWindow.Update();
1949 // Íóæíî ñìåíèòü ðàçðåøåíèå:
1950 if gResolutionChange then
1951 begin
1952 e_WriteLog('Changing resolution', MSG_NOTIFY);
1953 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
1954 gResolutionChange := False;
1955 end;
1957 // Íóæíî ñìåíèòü ÿçûê:
1958 if gLanguageChange then
1959 begin
1960 //e_WriteLog('Read language file', MSG_NOTIFY);
1961 //g_Language_Load(DataDir + gLanguage + '.txt');
1962 g_Language_Set(gLanguage);
1963 g_Menu_Reset();
1964 gLanguageChange := False;
1965 end;
1966 end;
1968 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
1969 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
1970 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
1971 begin
1972 g_TakeScreenShot();
1973 LastScreenShot := GetTimer();
1974 end;
1976 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
1977 if e_KeyPressed(IK_F10) and
1978 gGameOn and
1979 (not gConsoleShow) and
1980 (g_ActiveWindow = nil) then
1981 begin
1982 KeyPress(IK_F10);
1983 end;
1985 Time := GetTimer() {div 1000};
1987 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
1988 if gDelayedEvents <> nil then
1989 for a := 0 to High(gDelayedEvents) do
1990 if gDelayedEvents[a].Pending and
1992 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
1993 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
1994 ) then
1995 begin
1996 case gDelayedEvents[a].DEType of
1997 DE_GLOBEVENT:
1998 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
1999 DE_BFGHIT:
2000 if gGameOn then
2001 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
2002 DE_KILLCOMBO:
2003 if gGameOn then
2004 begin
2005 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
2006 if g_Game_IsNet and g_Game_IsServer then
2007 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
2008 end;
2009 end;
2010 gDelayedEvents[a].Pending := False;
2011 end;
2013 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2014 UPSCounter := UPSCounter + 1;
2015 if Time - UPSTime >= 1000 then
2016 begin
2017 UPS := UPSCounter;
2018 UPSCounter := 0;
2019 UPSTime := Time;
2020 end;
2022 if gGameOn then
2023 begin
2024 g_Weapon_AddDynLights();
2025 g_Items_AddDynLights();
2026 end;
2027 end;
2029 procedure g_Game_LoadData();
2030 begin
2031 if DataLoaded then Exit;
2033 e_WriteLog('Loading game data...', MSG_NOTIFY);
2035 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2036 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2037 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2038 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2039 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2040 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2041 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_RB', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
2042 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
2043 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
2044 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
2045 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
2046 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
2047 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2048 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2049 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2050 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2051 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2052 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2053 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2054 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2055 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2056 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2057 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2058 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2059 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2060 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2061 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2062 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2063 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2064 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2065 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2066 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2067 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2068 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2070 goodsnd[0] := TPlayableSound.Create();
2071 goodsnd[1] := TPlayableSound.Create();
2072 goodsnd[2] := TPlayableSound.Create();
2073 goodsnd[3] := TPlayableSound.Create();
2075 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2076 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2077 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2078 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2080 killsnd[0] := TPlayableSound.Create();
2081 killsnd[1] := TPlayableSound.Create();
2082 killsnd[2] := TPlayableSound.Create();
2083 killsnd[3] := TPlayableSound.Create();
2085 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2086 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2087 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2088 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2090 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2091 g_Items_LoadData();
2093 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2094 g_Weapon_LoadData();
2096 g_Monsters_LoadData();
2098 DataLoaded := True;
2099 end;
2101 procedure g_Game_FreeData();
2102 begin
2103 if not DataLoaded then Exit;
2105 g_Items_FreeData();
2106 g_Weapon_FreeData();
2107 g_Monsters_FreeData();
2109 e_WriteLog('Releasing game data...', MSG_NOTIFY);
2111 g_Texture_Delete('NOTEXTURE');
2112 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2113 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2114 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2115 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2116 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2117 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2118 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2119 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2120 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2121 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2122 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2123 g_Frames_DeleteByName('FRAMES_TELEPORT');
2124 g_Sound_Delete('SOUND_GAME_TELEPORT');
2125 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2126 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2127 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2128 g_Sound_Delete('SOUND_GAME_BULK1');
2129 g_Sound_Delete('SOUND_GAME_BULK2');
2130 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2131 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2132 g_Sound_Delete('SOUND_GAME_SWITCH1');
2133 g_Sound_Delete('SOUND_GAME_SWITCH0');
2135 goodsnd[0].Free();
2136 goodsnd[1].Free();
2137 goodsnd[2].Free();
2138 goodsnd[3].Free();
2140 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2141 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2142 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2143 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2145 killsnd[0].Free();
2146 killsnd[1].Free();
2147 killsnd[2].Free();
2148 killsnd[3].Free();
2150 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2151 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2152 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2153 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2155 DataLoaded := False;
2156 end;
2158 procedure DrawCustomStat();
2159 var
2160 pc, x, y, w, _y,
2161 w1, w2, w3,
2162 t, p, m: Integer;
2163 ww1, hh1: Word;
2164 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2165 s1, s2, topstr: String;
2166 begin
2167 e_TextureFontGetSize(gStdFont, ww2, hh2);
2169 e_PollInput();
2170 if e_KeyPressed(IK_TAB) then
2171 begin
2172 if not gStatsPressed then
2173 begin
2174 gStatsOff := not gStatsOff;
2175 gStatsPressed := True;
2176 end;
2177 end
2178 else
2179 gStatsPressed := False;
2181 if gStatsOff then
2182 begin
2183 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2184 w := (Length(s1) * ww2) div 2;
2185 x := gScreenWidth div 2 - w;
2186 y := 8;
2187 e_TextureFontPrint(x, y, s1, gStdFont);
2188 Exit;
2189 end;
2191 if (gGameSettings.GameMode = GM_COOP) then
2192 begin
2193 if gMissionFailed then
2194 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2195 else
2196 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2197 end
2198 else
2199 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2201 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2202 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2204 if g_Game_IsNet then
2205 begin
2206 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2207 if not gChatShow then
2208 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2209 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2210 end;
2212 if g_Game_IsClient then
2213 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2214 else
2215 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2216 if not gChatShow then
2217 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2218 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2220 x := 32;
2221 y := 16+hh1+16;
2223 w := gScreenWidth-x*2;
2225 w2 := (w-16) div 6;
2226 w3 := w2;
2227 w1 := w-16-w2-w3;
2229 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2230 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2232 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2234 case CustomStat.GameMode of
2235 GM_DM:
2236 begin
2237 if gGameSettings.MaxLives = 0 then
2238 s1 := _lc[I_GAME_DM]
2239 else
2240 s1 := _lc[I_GAME_LMS];
2241 end;
2242 GM_TDM:
2243 begin
2244 if gGameSettings.MaxLives = 0 then
2245 s1 := _lc[I_GAME_TDM]
2246 else
2247 s1 := _lc[I_GAME_TLMS];
2248 end;
2249 GM_CTF: s1 := _lc[I_GAME_CTF];
2250 GM_COOP:
2251 begin
2252 if gGameSettings.MaxLives = 0 then
2253 s1 := _lc[I_GAME_COOP]
2254 else
2255 s1 := _lc[I_GAME_SURV];
2256 end;
2257 else s1 := '';
2258 end;
2260 _y := y+16;
2261 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2262 _y := _y+8;
2264 _y := _y+16;
2265 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2266 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2268 _y := _y+16;
2269 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2270 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2271 (CustomStat.GameTime div 1000 div 60) mod 60,
2272 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2274 pc := Length(CustomStat.PlayerStat);
2275 if pc = 0 then Exit;
2277 if CustomStat.GameMode = GM_COOP then
2278 begin
2279 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2280 _y := _y+32;
2281 s2 := _lc[I_GAME_MONSTERS];
2282 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2283 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2284 _y := _y+16;
2285 s2 := _lc[I_GAME_SECRETS];
2286 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2287 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2288 if gLastMap then
2289 begin
2290 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2291 _y := _y-16;
2292 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2293 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2294 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2295 _y := _y+16;
2296 s2 := _lc[I_GAME_SECRETS_TOTAL];
2297 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2298 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2299 end;
2300 end;
2302 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2303 begin
2304 _y := _y+16+16;
2306 with CustomStat do
2307 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2308 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2309 else s1 := _lc[I_GAME_WIN_DRAW];
2311 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2312 _y := _y+40;
2314 for t := TEAM_RED to TEAM_BLUE do
2315 begin
2316 if t = TEAM_RED then
2317 begin
2318 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2319 gStdFont, 255, 0, 0, 1);
2320 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2321 gStdFont, 255, 0, 0, 1);
2322 r := 255;
2323 g := 0;
2324 b := 0;
2325 end
2326 else
2327 begin
2328 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2329 gStdFont, 0, 0, 255, 1);
2330 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2331 gStdFont, 0, 0, 255, 1);
2332 r := 0;
2333 g := 0;
2334 b := 255;
2335 end;
2337 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2338 _y := _y+24;
2340 for p := 0 to High(CustomStat.PlayerStat) do
2341 if CustomStat.PlayerStat[p].Team = t then
2342 with CustomStat.PlayerStat[p] do
2343 begin
2344 if Spectator then
2345 begin
2346 rr := r div 2;
2347 gg := g div 2;
2348 bb := b div 2;
2349 end
2350 else
2351 begin
2352 rr := r;
2353 gg := g;
2354 bb := b;
2355 end;
2356 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2357 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2358 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2359 _y := _y+24;
2360 end;
2362 _y := _y+16+16;
2363 end;
2364 end
2365 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2366 begin
2367 _y := _y+40;
2368 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2369 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2370 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2372 _y := _y+24;
2373 for p := 0 to High(CustomStat.PlayerStat) do
2374 with CustomStat.PlayerStat[p] do
2375 begin
2376 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2378 if Spectator then
2379 r := 127
2380 else
2381 r := 255;
2383 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2384 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2385 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2386 _y := _y+24;
2387 end;
2388 end;
2389 end;
2391 procedure DrawSingleStat();
2392 var
2393 tm, key_x, val_x, y: Integer;
2394 w1, w2, h: Word;
2395 s1, s2: String;
2397 procedure player_stat(n: Integer);
2398 var
2399 kpm: Real;
2401 begin
2402 // "Kills: # / #":
2403 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2404 s2 := Format(' %d', [gTotalMonsters]);
2406 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2407 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2408 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2409 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2410 s1 := s1 + '/';
2411 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2412 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2414 // "Kills-per-minute: ##.#":
2415 s1 := _lc[I_MENU_INTER_KPM];
2416 if tm > 0 then
2417 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2418 else
2419 kpm := SingleStat.PlayerStat[n].Kills;
2420 s2 := Format(' %.1f', [kpm]);
2422 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2423 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2425 // "Secrets found: # / #":
2426 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2427 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2429 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2430 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2431 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2432 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2433 s1 := s1 + '/';
2434 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2435 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2436 end;
2438 begin
2439 // "Level Complete":
2440 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2441 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2443 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2444 s1 := _lc[I_MENU_INTER_KPM];
2445 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2446 Inc(w1, 16);
2447 s1 := ' 9999.9';
2448 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2450 key_x := (gScreenWidth-w1-w2) div 2;
2451 val_x := key_x + w1;
2453 // "Time: #:##:##":
2454 tm := SingleStat.GameTime div 1000;
2455 s1 := _lc[I_MENU_INTER_TIME];
2456 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2458 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2459 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2461 if SingleStat.TwoPlayers then
2462 begin
2463 // "Player 1":
2464 s1 := _lc[I_MENU_PLAYER_1];
2465 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2466 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2468 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2469 y := 176;
2470 player_stat(0);
2472 // "Player 2":
2473 s1 := _lc[I_MENU_PLAYER_2];
2474 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2475 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2477 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2478 y := 336;
2479 player_stat(1);
2480 end
2481 else
2482 begin
2483 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2484 y := 128;
2485 player_stat(0);
2486 end;
2487 end;
2489 procedure DrawLoadingStat();
2490 var
2491 ww, hh: Word;
2492 xx, yy, i: Integer;
2493 s: String;
2494 begin
2495 if Length(LoadingStat.Msgs) = 0 then
2496 Exit;
2498 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2499 yy := (gScreenHeight div 3);
2500 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2501 xx := (gScreenWidth div 3);
2503 with LoadingStat do
2504 for i := 0 to NextMsg-1 do
2505 begin
2506 if (i = (NextMsg-1)) and (MaxValue > 0) then
2507 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2508 else
2509 s := Msgs[i];
2511 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2512 yy := yy + LOADING_INTERLINE;
2513 end;
2514 end;
2516 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2517 var
2518 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2520 function monDraw (mon: TMonster): Boolean;
2521 begin
2522 result := false; // don't stop
2523 with mon do
2524 begin
2525 if alive then
2526 begin
2527 // Ëåâûé âåðõíèé óãîë
2528 aX := Obj.X div ScaleSz + 1;
2529 aY := Obj.Y div ScaleSz + 1;
2530 // Ðàçìåðû
2531 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2532 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2533 // Ïðàâûé íèæíèé óãîë
2534 aX2 := aX + aX2 - 1;
2535 aY2 := aY + aY2 - 1;
2536 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2537 end;
2538 end;
2539 end;
2541 begin
2542 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2543 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2544 begin
2545 Scale := 1;
2546 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2547 ScaleSz := 16 div Scale;
2548 // Ðàçìåðû ìèíè-êàðòû:
2549 aX := max(gMapInfo.Width div ScaleSz, 1);
2550 aY := max(gMapInfo.Height div ScaleSz, 1);
2551 // Ðàìêà êàðòû:
2552 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2554 if gWalls <> nil then
2555 begin
2556 // Ðèñóåì ñòåíû:
2557 for a := 0 to High(gWalls) do
2558 with gWalls[a] do
2559 if PanelType <> 0 then
2560 begin
2561 // Ëåâûé âåðõíèé óãîë:
2562 aX := X div ScaleSz;
2563 aY := Y div ScaleSz;
2564 // Ðàçìåðû:
2565 aX2 := max(Width div ScaleSz, 1);
2566 aY2 := max(Height div ScaleSz, 1);
2567 // Ïðàâûé íèæíèé óãîë:
2568 aX2 := aX + aX2 - 1;
2569 aY2 := aY + aY2 - 1;
2571 case PanelType of
2572 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2573 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2574 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2575 end;
2576 end;
2577 end;
2578 if gSteps <> nil then
2579 begin
2580 // Ðèñóåì ñòóïåíè:
2581 for a := 0 to High(gSteps) do
2582 with gSteps[a] do
2583 if PanelType <> 0 then
2584 begin
2585 // Ëåâûé âåðõíèé óãîë:
2586 aX := X div ScaleSz;
2587 aY := Y div ScaleSz;
2588 // Ðàçìåðû:
2589 aX2 := max(Width div ScaleSz, 1);
2590 aY2 := max(Height div ScaleSz, 1);
2591 // Ïðàâûé íèæíèé óãîë:
2592 aX2 := aX + aX2 - 1;
2593 aY2 := aY + aY2 - 1;
2595 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2596 end;
2597 end;
2598 if gLifts <> nil then
2599 begin
2600 // Ðèñóåì ëèôòû:
2601 for a := 0 to High(gLifts) do
2602 with gLifts[a] do
2603 if PanelType <> 0 then
2604 begin
2605 // Ëåâûé âåðõíèé óãîë:
2606 aX := X div ScaleSz;
2607 aY := Y div ScaleSz;
2608 // Ðàçìåðû:
2609 aX2 := max(Width div ScaleSz, 1);
2610 aY2 := max(Height div ScaleSz, 1);
2611 // Ïðàâûé íèæíèé óãîë:
2612 aX2 := aX + aX2 - 1;
2613 aY2 := aY + aY2 - 1;
2615 case LiftType of
2616 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2617 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2618 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2619 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2620 end;
2621 end;
2622 end;
2623 if gWater <> nil then
2624 begin
2625 // Ðèñóåì âîäó:
2626 for a := 0 to High(gWater) do
2627 with gWater[a] do
2628 if PanelType <> 0 then
2629 begin
2630 // Ëåâûé âåðõíèé óãîë:
2631 aX := X div ScaleSz;
2632 aY := Y div ScaleSz;
2633 // Ðàçìåðû:
2634 aX2 := max(Width div ScaleSz, 1);
2635 aY2 := max(Height div ScaleSz, 1);
2636 // Ïðàâûé íèæíèé óãîë:
2637 aX2 := aX + aX2 - 1;
2638 aY2 := aY + aY2 - 1;
2640 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2641 end;
2642 end;
2643 if gAcid1 <> nil then
2644 begin
2645 // Ðèñóåì êèñëîòó 1:
2646 for a := 0 to High(gAcid1) do
2647 with gAcid1[a] do
2648 if PanelType <> 0 then
2649 begin
2650 // Ëåâûé âåðõíèé óãîë:
2651 aX := X div ScaleSz;
2652 aY := Y div ScaleSz;
2653 // Ðàçìåðû:
2654 aX2 := max(Width div ScaleSz, 1);
2655 aY2 := max(Height div ScaleSz, 1);
2656 // Ïðàâûé íèæíèé óãîë:
2657 aX2 := aX + aX2 - 1;
2658 aY2 := aY + aY2 - 1;
2660 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2661 end;
2662 end;
2663 if gAcid2 <> nil then
2664 begin
2665 // Ðèñóåì êèñëîòó 2:
2666 for a := 0 to High(gAcid2) do
2667 with gAcid2[a] do
2668 if PanelType <> 0 then
2669 begin
2670 // Ëåâûé âåðõíèé óãîë:
2671 aX := X div ScaleSz;
2672 aY := Y div ScaleSz;
2673 // Ðàçìåðû:
2674 aX2 := max(Width div ScaleSz, 1);
2675 aY2 := max(Height div ScaleSz, 1);
2676 // Ïðàâûé íèæíèé óãîë:
2677 aX2 := aX + aX2 - 1;
2678 aY2 := aY + aY2 - 1;
2680 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2681 end;
2682 end;
2683 if gPlayers <> nil then
2684 begin
2685 // Ðèñóåì èãðîêîâ:
2686 for a := 0 to High(gPlayers) do
2687 if gPlayers[a] <> nil then with gPlayers[a] do
2688 if alive then begin
2689 // Ëåâûé âåðõíèé óãîë:
2690 aX := Obj.X div ScaleSz + 1;
2691 aY := Obj.Y div ScaleSz + 1;
2692 // Ðàçìåðû:
2693 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2694 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2695 // Ïðàâûé íèæíèé óãîë:
2696 aX2 := aX + aX2 - 1;
2697 aY2 := aY + aY2 - 1;
2699 if gPlayers[a] = p then
2700 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2701 else
2702 case Team of
2703 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2704 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2705 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2706 end;
2707 end;
2708 end;
2709 // Ðèñóåì ìîíñòðîâ
2710 g_Mons_ForEach(monDraw);
2711 end;
2712 end;
2715 procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor);
2716 begin
2717 if not hasAmbient then exit;
2718 e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
2719 end;
2722 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2723 //FIXME: broken for splitscreen mode
2724 procedure renderDynLightsInternal ();
2725 var
2726 //hasAmbient: Boolean;
2727 //ambColor: TDFColor;
2728 lln: Integer;
2729 lx, ly, lrad: Integer;
2730 scxywh: array[0..3] of GLint;
2731 wassc: Boolean;
2732 begin
2733 if e_NoGraphics then exit;
2735 //TODO: lights should be in separate grid, i think
2736 // but on the other side: grid may be slower for dynlights, as their lifetime is short
2737 if (not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit;
2739 // rendering mode
2740 //ambColor := gCurrentMap['light_ambient'].rgba;
2741 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
2743 { // this will multiply incoming color to alpha from framebuffer
2744 glEnable(GL_BLEND);
2745 glBlendFunc(GL_DST_ALPHA, GL_ONE);
2748 (*
2749 * light rendering: (INVALID!)
2750 * glStencilFunc(GL_EQUAL, 0, $ff);
2751 * for each light:
2752 * glClear(GL_STENCIL_BUFFER_BIT);
2753 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2754 * draw shadow volume into stencil buffer
2755 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2756 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
2757 * turn off blending
2758 * draw color-less quad with light alpha (WARNING! don't touch color!)
2759 * glEnable(GL_BLEND);
2760 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
2761 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
2762 *)
2764 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
2765 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
2767 // setup OpenGL parameters
2768 glStencilMask($FFFFFFFF);
2769 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
2770 glEnable(GL_STENCIL_TEST);
2771 glEnable(GL_SCISSOR_TEST);
2772 glClear(GL_STENCIL_BUFFER_BIT);
2773 glStencilFunc(GL_EQUAL, 0, $ff);
2775 for lln := 0 to g_dynLightCount-1 do
2776 begin
2777 lx := g_dynLights[lln].x;
2778 ly := g_dynLights[lln].y;
2779 lrad := g_dynLights[lln].radius;
2780 if (lrad < 3) then continue;
2782 if (lx-sX+lrad < 0) then continue;
2783 if (ly-sY+lrad < 0) then continue;
2784 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
2785 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
2787 // set scissor to optimize drawing
2788 if (g_dbg_scale = 1.0) then
2789 begin
2790 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
2791 end
2792 else
2793 begin
2794 glScissor(0, 0, gWinSizeX, gWinSizeY);
2795 end;
2796 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
2797 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
2798 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2799 // draw extruded panels
2800 glDisable(GL_TEXTURE_2D);
2801 glDisable(GL_BLEND);
2802 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
2803 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
2804 // render light texture
2805 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2806 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
2807 // blend it
2808 glEnable(GL_BLEND);
2809 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2810 glEnable(GL_TEXTURE_2D);
2811 // color and opacity
2812 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
2813 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
2814 glBegin(GL_QUADS);
2815 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
2816 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
2817 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
2818 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
2819 glEnd();
2820 end;
2822 // done
2823 glDisable(GL_STENCIL_TEST);
2824 glDisable(GL_BLEND);
2825 glDisable(GL_SCISSOR_TEST);
2826 //glScissor(0, 0, sWidth, sHeight);
2828 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
2829 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
2830 end;
2833 function fixViewportForScale (): Boolean;
2834 var
2835 nx0, ny0, nw, nh: Integer;
2836 begin
2837 result := false;
2838 if (g_dbg_scale <> 1.0) then
2839 begin
2840 result := true;
2841 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
2842 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
2843 nw := round(sWidth/g_dbg_scale);
2844 nh := round(sHeight/g_dbg_scale);
2845 sX := nx0;
2846 sY := ny0;
2847 sWidth := nw;
2848 sHeight := nh;
2849 end;
2850 end;
2853 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2854 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
2855 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
2856 type
2857 TDrawCB = procedure ();
2859 var
2860 hasAmbient: Boolean;
2861 ambColor: TDFColor;
2862 doAmbient: Boolean = false;
2864 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
2865 var
2866 tagmask: Integer;
2867 pan: TPanel;
2868 begin
2869 profileFrameDraw.sectionBegin(profname);
2870 if gdbg_map_use_accel_render then
2871 begin
2872 tagmask := panelTypeToTag(panType);
2873 while (gDrawPanelList.count > 0) do
2874 begin
2875 pan := TPanel(gDrawPanelList.front());
2876 if ((pan.tag and tagmask) = 0) then break;
2877 if doDraw then pan.Draw(doAmbient, ambColor);
2878 gDrawPanelList.popFront();
2879 end;
2880 end
2881 else
2882 begin
2883 if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor);
2884 end;
2885 profileFrameDraw.sectionEnd();
2886 end;
2888 procedure drawOther (profname: AnsiString; cb: TDrawCB);
2889 begin
2890 profileFrameDraw.sectionBegin(profname);
2891 if assigned(cb) then cb();
2892 profileFrameDraw.sectionEnd();
2893 end;
2895 begin
2896 profileFrameDraw.sectionBegin('total');
2898 // our accelerated renderer will collect all panels to gDrawPanelList
2899 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
2900 profileFrameDraw.sectionBegin('collect');
2901 if gdbg_map_use_accel_render then
2902 begin
2903 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
2904 end;
2905 profileFrameDraw.sectionEnd();
2907 profileFrameDraw.sectionBegin('skyback');
2908 g_Map_DrawBack(backXOfs, backYOfs);
2909 profileFrameDraw.sectionEnd();
2911 if setTransMatrix then
2912 begin
2913 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
2914 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
2915 glTranslatef(-sX, -sY, 0);
2916 end;
2918 // rendering mode
2919 ambColor := gCurrentMap['light_ambient'].rgba;
2920 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
2923 if hasAmbient then
2924 begin
2925 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
2926 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
2927 glClear(GL_COLOR_BUFFER_BIT);
2928 end;
2930 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
2933 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
2934 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
2935 drawOther('items', @g_Items_Draw);
2936 drawOther('weapons', @g_Weapon_Draw);
2937 drawOther('shells', @g_Player_DrawShells);
2938 drawOther('drawall', @g_Player_DrawAll);
2939 drawOther('corpses', @g_Player_DrawCorpses);
2940 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
2941 drawOther('monsters', @g_Monsters_Draw);
2942 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
2943 drawOther('gfx', @g_GFX_Draw);
2944 drawOther('flags', @g_Map_DrawFlags);
2945 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
2946 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
2947 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
2948 drawOther('dynlights', @renderDynLightsInternal);
2950 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
2951 begin
2952 renderAmbientQuad(hasAmbient, ambColor);
2953 end;
2955 doAmbient := true;
2956 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
2959 if g_debug_HealthBar then
2960 begin
2961 g_Monsters_DrawHealth();
2962 g_Player_DrawHealth();
2963 end;
2965 profileFrameDraw.mainEnd(); // map rendering
2966 end;
2969 procedure DrawMapView(x, y, w, h: Integer);
2971 var
2972 bx, by: Integer;
2973 begin
2974 glPushMatrix();
2976 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2977 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2979 sX := x;
2980 sY := y;
2981 sWidth := w;
2982 sHeight := h;
2984 fixViewportForScale();
2985 renderMapInternal(-bx, -by, true);
2987 glPopMatrix();
2988 end;
2991 procedure DrawPlayer(p: TPlayer);
2992 var
2993 px, py, a, b, c, d: Integer;
2994 //R: TRect;
2995 begin
2996 if (p = nil) or (p.FDummy) then
2997 begin
2998 glPushMatrix();
2999 g_Map_DrawBack(0, 0);
3000 glPopMatrix();
3001 Exit;
3002 end;
3004 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3005 profileFrameDraw.mainBegin(g_profile_frame_draw);
3007 gPlayerDrawn := p;
3009 glPushMatrix();
3011 px := p.GameX + PLAYER_RECT_CX;
3012 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
3014 if (g_dbg_scale = 1.0) then
3015 begin
3016 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3017 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3019 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3020 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3022 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3023 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3024 begin
3025 // hcenter
3026 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3027 end;
3029 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3030 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3031 begin
3032 // vcenter
3033 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3034 end;
3035 end
3036 else
3037 begin
3038 // scaled, ignore level bounds
3039 a := -px+(gPlayerScreenSize.X div 2);
3040 b := -py+(gPlayerScreenSize.Y div 2);
3041 end;
3043 if p.IncCam <> 0 then
3044 begin
3045 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
3046 begin
3047 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
3048 begin
3049 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
3050 end;
3051 end;
3053 if py < gPlayerScreenSize.Y div 2 then
3054 begin
3055 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
3056 begin
3057 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
3058 end;
3059 end;
3061 if p.IncCam < 0 then
3062 begin
3063 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
3064 end;
3066 if p.IncCam > 0 then
3067 begin
3068 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
3069 end;
3070 end;
3072 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
3073 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
3074 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
3076 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
3077 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
3078 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
3080 sX := -a;
3081 sY := -(b+p.IncCam);
3082 sWidth := gPlayerScreenSize.X;
3083 sHeight := gPlayerScreenSize.Y;
3085 //glTranslatef(a, b+p.IncCam, 0);
3087 if (p = gPlayer1) then g_Holmes_plrViewSize(sWidth, sHeight);
3089 fixViewportForScale();
3090 p.viewPortX := sX;
3091 p.viewPortY := sY;
3092 p.viewPortW := sWidth;
3093 p.viewPortH := sHeight;
3095 if (p = gPlayer1) then g_Holmes_plrViewPos(sX, sY);
3097 renderMapInternal(-c, -d, true);
3099 if p.FSpectator then
3100 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3101 p.GameY + PLAYER_RECT_CY - 4,
3102 'X', gStdFont, 255, 255, 255, 1, True);
3104 for a := 0 to High(gCollideMap) do
3105 for b := 0 to High(gCollideMap[a]) do
3106 begin
3107 d := 0;
3108 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3109 d := d + 1;
3110 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3111 d := d + 2;
3113 case d of
3114 1: e_DrawPoint(1, b, a, 200, 200, 200);
3115 2: e_DrawPoint(1, b, a, 64, 64, 255);
3116 3: e_DrawPoint(1, b, a, 255, 0, 255);
3117 end;
3118 end;
3121 glPopMatrix();
3123 p.DrawPain();
3124 p.DrawPickup();
3125 p.DrawRulez();
3126 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3127 if g_Debug_Player then
3128 g_Player_DrawDebug(p);
3129 p.DrawGUI();
3130 end;
3132 procedure drawProfilers ();
3133 var
3134 px: Integer = -1;
3135 py: Integer = -1;
3136 begin
3137 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
3138 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3139 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3140 end;
3142 procedure g_Game_Draw();
3143 var
3144 ID: DWORD;
3145 w, h: Word;
3146 ww, hh: Byte;
3147 Time: Int64;
3148 back: string;
3149 plView1, plView2: TPlayer;
3150 Split: Boolean;
3151 begin
3152 if gExit = EXIT_QUIT then Exit;
3154 Time := GetTimer() {div 1000};
3155 FPSCounter := FPSCounter+1;
3156 if Time - FPSTime >= 1000 then
3157 begin
3158 FPS := FPSCounter;
3159 FPSCounter := 0;
3160 FPSTime := Time;
3161 end;
3163 if gGameOn or (gState = STATE_FOLD) then
3164 begin
3165 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3166 begin
3167 gSpectMode := SPECT_NONE;
3168 if not gRevertPlayers then
3169 begin
3170 plView1 := gPlayer1;
3171 plView2 := gPlayer2;
3172 end
3173 else
3174 begin
3175 plView1 := gPlayer2;
3176 plView2 := gPlayer1;
3177 end;
3178 end
3179 else
3180 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3181 begin
3182 gSpectMode := SPECT_NONE;
3183 if gPlayer2 = nil then
3184 plView1 := gPlayer1
3185 else
3186 plView1 := gPlayer2;
3187 plView2 := nil;
3188 end
3189 else
3190 begin
3191 plView1 := nil;
3192 plView2 := nil;
3193 end;
3195 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3196 gSpectMode := SPECT_STATS;
3198 if gSpectMode = SPECT_PLAYERS then
3199 if gPlayers <> nil then
3200 begin
3201 plView1 := GetActivePlayer_ByID(gSpectPID1);
3202 if plView1 = nil then
3203 begin
3204 gSpectPID1 := GetActivePlayerID_Next();
3205 plView1 := GetActivePlayer_ByID(gSpectPID1);
3206 end;
3207 if gSpectViewTwo then
3208 begin
3209 plView2 := GetActivePlayer_ByID(gSpectPID2);
3210 if plView2 = nil then
3211 begin
3212 gSpectPID2 := GetActivePlayerID_Next();
3213 plView2 := GetActivePlayer_ByID(gSpectPID2);
3214 end;
3215 end;
3216 end;
3218 if gSpectMode = SPECT_MAPVIEW then
3219 begin
3220 // Ðåæèì ïðîñìîòðà êàðòû
3221 Split := False;
3222 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3223 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3224 gHearPoint1.Active := True;
3225 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3226 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3227 gHearPoint2.Active := False;
3228 end
3229 else
3230 begin
3231 Split := (plView1 <> nil) and (plView2 <> nil);
3233 // Òî÷êè ñëóõà èãðîêîâ
3234 if plView1 <> nil then
3235 begin
3236 gHearPoint1.Active := True;
3237 gHearPoint1.Coords.X := plView1.GameX;
3238 gHearPoint1.Coords.Y := plView1.GameY;
3239 end else
3240 gHearPoint1.Active := False;
3241 if plView2 <> nil then
3242 begin
3243 gHearPoint2.Active := True;
3244 gHearPoint2.Coords.X := plView2.GameX;
3245 gHearPoint2.Coords.Y := plView2.GameY;
3246 end else
3247 gHearPoint2.Active := False;
3249 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3250 gPlayerScreenSize.X := gScreenWidth-196;
3251 if Split then
3252 begin
3253 gPlayerScreenSize.Y := gScreenHeight div 2;
3254 if gScreenHeight mod 2 = 0 then
3255 Dec(gPlayerScreenSize.Y);
3256 end
3257 else
3258 gPlayerScreenSize.Y := gScreenHeight;
3260 if Split then
3261 if gScreenHeight mod 2 = 0 then
3262 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3263 else
3264 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3266 DrawPlayer(plView1);
3267 gPlayer1ScreenCoord.X := sX;
3268 gPlayer1ScreenCoord.Y := sY;
3270 if Split then
3271 begin
3272 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3274 DrawPlayer(plView2);
3275 gPlayer2ScreenCoord.X := sX;
3276 gPlayer2ScreenCoord.Y := sY;
3277 end;
3279 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3281 if Split then
3282 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3283 end;
3285 if MessageText <> '' then
3286 begin
3287 w := 0;
3288 h := 0;
3289 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3290 if Split then
3291 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3292 (gScreenHeight div 2)-(h div 2), MessageText)
3293 else
3294 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3295 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3296 end;
3298 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3300 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3301 begin
3302 // Draw spectator GUI
3303 ww := 0;
3304 hh := 0;
3305 e_TextureFontGetSize(gStdFont, ww, hh);
3306 case gSpectMode of
3307 SPECT_STATS:
3308 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3309 SPECT_MAPVIEW:
3310 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3311 SPECT_PLAYERS:
3312 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3313 end;
3314 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3315 if gSpectMode = SPECT_MAPVIEW then
3316 begin
3317 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3318 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3319 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3320 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3321 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3322 end;
3323 if gSpectMode = SPECT_PLAYERS then
3324 begin
3325 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3326 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3327 if gSpectViewTwo then
3328 begin
3329 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3330 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3331 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3332 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3333 end
3334 else
3335 begin
3336 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3337 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3338 end;
3339 end;
3340 end;
3341 end;
3343 if gPause and gGameOn and (g_ActiveWindow = nil) then
3344 begin
3345 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3346 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3348 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3349 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3350 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3351 end;
3353 if not gGameOn then
3354 begin
3355 if (gState = STATE_MENU) then
3356 begin
3357 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3358 begin
3359 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3360 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3361 end;
3362 // F3 at menu will show game loading dialog
3363 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3364 if (g_ActiveWindow <> nil) then
3365 begin
3366 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3367 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3368 end
3369 else
3370 begin
3371 // F3 at titlepic will show game loading dialog
3372 if e_KeyPressed(IK_F3) then
3373 begin
3374 g_Menu_Show_LoadMenu(true);
3375 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3376 end;
3377 end;
3378 end;
3380 if gState = STATE_FOLD then
3381 begin
3382 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3383 end;
3385 if gState = STATE_INTERCUSTOM then
3386 begin
3387 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3388 begin
3389 back := 'TEXTURE_endpic';
3390 if not g_Texture_Get(back, ID) then
3391 back := _lc[I_TEXTURE_ENDPIC];
3392 end
3393 else
3394 back := 'INTER';
3396 if g_Texture_Get(back, ID) then
3397 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3398 else
3399 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3401 DrawCustomStat();
3403 if g_ActiveWindow <> nil then
3404 begin
3405 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3406 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3407 end;
3408 end;
3410 if gState = STATE_INTERSINGLE then
3411 begin
3412 if EndingGameCounter > 0 then
3413 begin
3414 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3415 end
3416 else
3417 begin
3418 back := 'INTER';
3420 if g_Texture_Get(back, ID) then
3421 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3422 else
3423 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3425 DrawSingleStat();
3427 if g_ActiveWindow <> nil then
3428 begin
3429 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3430 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3431 end;
3432 end;
3433 end;
3435 if gState = STATE_ENDPIC then
3436 begin
3437 ID := DWORD(-1);
3438 if not g_Texture_Get('TEXTURE_endpic', ID) then
3439 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3441 if ID <> DWORD(-1) then
3442 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3443 else
3444 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3446 if g_ActiveWindow <> nil then
3447 begin
3448 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3449 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3450 end;
3451 end;
3453 if gState = STATE_SLIST then
3454 begin
3455 if g_Texture_Get('MENU_BACKGROUND', ID) then
3456 begin
3457 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3458 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3459 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3460 end;
3461 g_Serverlist_Draw(slCurrent);
3462 end;
3463 end;
3465 if g_ActiveWindow <> nil then
3466 begin
3467 if gGameOn then
3468 begin
3469 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3470 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3471 end;
3472 g_ActiveWindow.Draw();
3473 end;
3475 // draw inspector
3476 if (g_holmes_enabled) then g_Holmes_Draw();
3478 g_Console_Draw();
3480 if g_debug_Sounds and gGameOn then
3481 begin
3482 for w := 0 to High(e_SoundsArray) do
3483 for h := 0 to e_SoundsArray[w].nRefs do
3484 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3485 end;
3487 if gShowFPS then
3488 begin
3489 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3490 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3491 end;
3493 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3494 drawTime(gScreenWidth-72, gScreenHeight-16);
3496 if gGameOn then drawProfilers();
3498 g_Holmes_DrawUI();
3499 end;
3501 procedure g_Game_Quit();
3502 begin
3503 g_Game_StopAllSounds(True);
3504 gMusic.Free();
3505 g_Game_SaveOptions();
3506 g_Game_FreeData();
3507 g_PlayerModel_FreeData();
3508 g_Texture_DeleteAll();
3509 g_Frames_DeleteAll();
3510 g_Menu_Free();
3512 if NetInitDone then g_Net_Free;
3514 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3515 if gMapToDelete <> '' then
3516 g_Game_DeleteTestMap();
3518 gExit := EXIT_QUIT;
3519 PushExitEvent();
3520 end;
3522 procedure g_FatalError(Text: String);
3523 begin
3524 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3525 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3527 gExit := EXIT_SIMPLE;
3528 end;
3530 procedure g_SimpleError(Text: String);
3531 begin
3532 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3533 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3534 end;
3536 procedure g_Game_SetupScreenSize();
3537 const
3538 RES_FACTOR = 4.0 / 3.0;
3539 var
3540 s: Single;
3541 rf: Single;
3542 bw, bh: Word;
3543 begin
3544 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3545 gPlayerScreenSize.X := gScreenWidth-196;
3546 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3547 gPlayerScreenSize.Y := gScreenHeight div 2
3548 else
3549 gPlayerScreenSize.Y := gScreenHeight;
3551 // Ðàçìåð çàäíåãî ïëàíà:
3552 if BackID <> DWORD(-1) then
3553 begin
3554 s := SKY_STRETCH;
3555 if (gScreenWidth*s > gMapInfo.Width) or
3556 (gScreenHeight*s > gMapInfo.Height) then
3557 begin
3558 gBackSize.X := gScreenWidth;
3559 gBackSize.Y := gScreenHeight;
3560 end
3561 else
3562 begin
3563 e_GetTextureSize(BackID, @bw, @bh);
3564 rf := Single(bw) / Single(bh);
3565 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3566 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3567 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3568 if (s < 1.0) then s := 1.0;
3569 gBackSize.X := Round(bw*s);
3570 gBackSize.Y := Round(bh*s);
3571 end;
3572 end;
3573 end;
3575 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3576 begin
3577 g_Window_SetSize(newWidth, newHeight, nowFull);
3578 end;
3580 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3581 begin
3582 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3583 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3584 Exit;
3585 if gPlayer1 = nil then
3586 begin
3587 if g_Game_IsClient then
3588 begin
3589 if NetPlrUID1 > -1 then
3590 begin
3591 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3592 gPlayer1 := g_Player_Get(NetPlrUID1);
3593 end;
3594 Exit;
3595 end;
3597 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3598 Team := gPlayer1Settings.Team;
3600 // Ñîçäàíèå ïåðâîãî èãðîêà:
3601 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3602 gPlayer1Settings.Color,
3603 Team, False));
3604 if gPlayer1 = nil then
3605 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3606 else
3607 begin
3608 gPlayer1.Name := gPlayer1Settings.Name;
3609 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3610 if g_Game_IsServer and g_Game_IsNet then
3611 MH_SEND_PlayerCreate(gPlayer1.UID);
3612 gPlayer1.Respawn(False, True);
3614 if g_Game_IsNet and NetUseMaster then
3615 g_Net_Slist_Update;
3616 end;
3618 Exit;
3619 end;
3620 if gPlayer2 = nil then
3621 begin
3622 if g_Game_IsClient then
3623 begin
3624 if NetPlrUID2 > -1 then
3625 gPlayer2 := g_Player_Get(NetPlrUID2);
3626 Exit;
3627 end;
3629 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3630 Team := gPlayer2Settings.Team;
3632 // Ñîçäàíèå âòîðîãî èãðîêà:
3633 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3634 gPlayer2Settings.Color,
3635 Team, False));
3636 if gPlayer2 = nil then
3637 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3638 else
3639 begin
3640 gPlayer2.Name := gPlayer2Settings.Name;
3641 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3642 if g_Game_IsServer and g_Game_IsNet then
3643 MH_SEND_PlayerCreate(gPlayer2.UID);
3644 gPlayer2.Respawn(False, True);
3646 if g_Game_IsNet and NetUseMaster then
3647 g_Net_Slist_Update;
3648 end;
3650 Exit;
3651 end;
3652 end;
3654 procedure g_Game_RemovePlayer();
3655 var
3656 Pl: TPlayer;
3657 begin
3658 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3659 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3660 Exit;
3661 Pl := gPlayer2;
3662 if Pl <> nil then
3663 begin
3664 if g_Game_IsServer then
3665 begin
3666 Pl.Lives := 0;
3667 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3668 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3669 g_Player_Remove(Pl.UID);
3671 if g_Game_IsNet and NetUseMaster then
3672 g_Net_Slist_Update;
3673 end else
3674 gPlayer2 := nil;
3675 Exit;
3676 end;
3677 Pl := gPlayer1;
3678 if Pl <> nil then
3679 begin
3680 if g_Game_IsServer then
3681 begin
3682 Pl.Lives := 0;
3683 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3684 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3685 g_Player_Remove(Pl.UID);
3687 if g_Game_IsNet and NetUseMaster then
3688 g_Net_Slist_Update;
3689 end else
3690 begin
3691 gPlayer1 := nil;
3692 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3693 end;
3694 Exit;
3695 end;
3696 end;
3698 procedure g_Game_Spectate();
3699 begin
3700 g_Game_RemovePlayer();
3701 if gPlayer1 <> nil then
3702 g_Game_RemovePlayer();
3703 end;
3705 procedure g_Game_SpectateCenterView();
3706 begin
3707 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3708 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3709 end;
3711 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3712 var
3713 i, nPl: Integer;
3714 begin
3715 g_Game_Free();
3717 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3719 g_Game_ClearLoading();
3721 // Íàñòðîéêè èãðû:
3722 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3723 gAimLine := False;
3724 gShowMap := False;
3725 gGameSettings.GameType := GT_SINGLE;
3726 gGameSettings.MaxLives := 0;
3727 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3728 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3729 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3730 gSwitchGameMode := GM_SINGLE;
3732 g_Game_ExecuteEvent('ongamestart');
3734 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3735 g_Game_SetupScreenSize();
3737 // Ñîçäàíèå ïåðâîãî èãðîêà:
3738 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3739 gPlayer1Settings.Color,
3740 gPlayer1Settings.Team, False));
3741 if gPlayer1 = nil then
3742 begin
3743 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3744 Exit;
3745 end;
3747 gPlayer1.Name := gPlayer1Settings.Name;
3748 nPl := 1;
3750 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3751 if TwoPlayers then
3752 begin
3753 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3754 gPlayer2Settings.Color,
3755 gPlayer2Settings.Team, False));
3756 if gPlayer2 = nil then
3757 begin
3758 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3759 Exit;
3760 end;
3762 gPlayer2.Name := gPlayer2Settings.Name;
3763 Inc(nPl);
3764 end;
3766 // Çàãðóçêà è çàïóñê êàðòû:
3767 if not g_Game_StartMap(MAP, True) then
3768 begin
3769 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3770 Exit;
3771 end;
3773 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3774 g_Player_Init();
3776 // Ñîçäàåì áîòîâ:
3777 for i := nPl+1 to nPlayers do
3778 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3779 end;
3781 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3782 TimeLimit, GoalLimit: Word;
3783 MaxLives: Byte;
3784 Options: LongWord; nPlayers: Byte);
3785 var
3786 i, nPl: Integer;
3787 begin
3788 g_Game_Free();
3790 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3792 g_Game_ClearLoading();
3794 // Íàñòðîéêè èãðû:
3795 gGameSettings.GameType := GT_CUSTOM;
3796 gGameSettings.GameMode := GameMode;
3797 gSwitchGameMode := GameMode;
3798 gGameSettings.TimeLimit := TimeLimit;
3799 gGameSettings.GoalLimit := GoalLimit;
3800 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3801 gGameSettings.Options := Options;
3803 gCoopTotalMonstersKilled := 0;
3804 gCoopTotalSecretsFound := 0;
3805 gCoopTotalMonsters := 0;
3806 gCoopTotalSecrets := 0;
3807 gAimLine := False;
3808 gShowMap := False;
3810 g_Game_ExecuteEvent('ongamestart');
3812 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3813 g_Game_SetupScreenSize();
3815 // Ðåæèì íàáëþäàòåëÿ:
3816 if nPlayers = 0 then
3817 begin
3818 gPlayer1 := nil;
3819 gPlayer2 := nil;
3820 end;
3822 nPl := 0;
3823 if nPlayers >= 1 then
3824 begin
3825 // Ñîçäàíèå ïåðâîãî èãðîêà:
3826 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3827 gPlayer1Settings.Color,
3828 gPlayer1Settings.Team, False));
3829 if gPlayer1 = nil then
3830 begin
3831 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3832 Exit;
3833 end;
3835 gPlayer1.Name := gPlayer1Settings.Name;
3836 Inc(nPl);
3837 end;
3839 if nPlayers >= 2 then
3840 begin
3841 // Ñîçäàíèå âòîðîãî èãðîêà:
3842 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3843 gPlayer2Settings.Color,
3844 gPlayer2Settings.Team, False));
3845 if gPlayer2 = nil then
3846 begin
3847 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3848 Exit;
3849 end;
3851 gPlayer2.Name := gPlayer2Settings.Name;
3852 Inc(nPl);
3853 end;
3855 // Çàãðóçêà è çàïóñê êàðòû:
3856 if not g_Game_StartMap(Map, True) then
3857 begin
3858 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3859 Exit;
3860 end;
3862 // Íåò òî÷åê ïîÿâëåíèÿ:
3863 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3864 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3865 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3866 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3867 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3868 begin
3869 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3870 Exit;
3871 end;
3873 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3874 g_Player_Init();
3876 // Ñîçäàåì áîòîâ:
3877 for i := nPl+1 to nPlayers do
3878 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3879 end;
3881 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3882 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3883 Options: LongWord; nPlayers: Byte;
3884 IPAddr: LongWord; Port: Word);
3885 begin
3886 g_Game_Free();
3888 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3890 g_Game_ClearLoading();
3892 // Íàñòðîéêè èãðû:
3893 gGameSettings.GameType := GT_SERVER;
3894 gGameSettings.GameMode := GameMode;
3895 gSwitchGameMode := GameMode;
3896 gGameSettings.TimeLimit := TimeLimit;
3897 gGameSettings.GoalLimit := GoalLimit;
3898 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3899 gGameSettings.Options := Options;
3901 gCoopTotalMonstersKilled := 0;
3902 gCoopTotalSecretsFound := 0;
3903 gCoopTotalMonsters := 0;
3904 gCoopTotalSecrets := 0;
3905 gAimLine := False;
3906 gShowMap := False;
3908 g_Game_ExecuteEvent('ongamestart');
3910 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3911 g_Game_SetupScreenSize();
3913 // Ðåæèì íàáëþäàòåëÿ:
3914 if nPlayers = 0 then
3915 begin
3916 gPlayer1 := nil;
3917 gPlayer2 := nil;
3918 end;
3920 if nPlayers >= 1 then
3921 begin
3922 // Ñîçäàíèå ïåðâîãî èãðîêà:
3923 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3924 gPlayer1Settings.Color,
3925 gPlayer1Settings.Team, False));
3926 if gPlayer1 = nil then
3927 begin
3928 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3929 Exit;
3930 end;
3932 gPlayer1.Name := gPlayer1Settings.Name;
3933 end;
3935 if nPlayers >= 2 then
3936 begin
3937 // Ñîçäàíèå âòîðîãî èãðîêà:
3938 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3939 gPlayer2Settings.Color,
3940 gPlayer2Settings.Team, False));
3941 if gPlayer2 = nil then
3942 begin
3943 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3944 Exit;
3945 end;
3947 gPlayer2.Name := gPlayer2Settings.Name;
3948 end;
3950 // Ñòàðòóåì ñåðâåð
3951 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3952 begin
3953 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3954 Exit;
3955 end;
3957 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3959 // Çàãðóçêà è çàïóñê êàðòû:
3960 if not g_Game_StartMap(Map, True) then
3961 begin
3962 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3963 Exit;
3964 end;
3966 // Íåò òî÷åê ïîÿâëåíèÿ:
3967 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3968 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3969 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3970 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3971 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3972 begin
3973 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3974 Exit;
3975 end;
3977 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3978 g_Player_Init();
3980 NetState := NET_STATE_GAME;
3981 end;
3983 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3984 var
3985 Map: String;
3986 WadName: string;
3987 Ptr: Pointer;
3988 T: Cardinal;
3989 MID: Byte;
3990 State: Byte;
3991 OuterLoop: Boolean;
3992 newResPath: string;
3993 InMsg: TMsg;
3994 begin
3995 g_Game_Free();
3997 State := 0;
3998 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3999 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
4001 g_Game_ClearLoading();
4003 // Íàñòðîéêè èãðû:
4004 gGameSettings.GameType := GT_CLIENT;
4006 gCoopTotalMonstersKilled := 0;
4007 gCoopTotalSecretsFound := 0;
4008 gCoopTotalMonsters := 0;
4009 gCoopTotalSecrets := 0;
4010 gAimLine := False;
4011 gShowMap := False;
4013 g_Game_ExecuteEvent('ongamestart');
4015 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4016 g_Game_SetupScreenSize();
4018 NetState := NET_STATE_AUTH;
4020 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4021 // Ñòàðòóåì êëèåíò
4022 if not g_Net_Connect(Addr, Port) then
4023 begin
4024 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4025 NetState := NET_STATE_NONE;
4026 Exit;
4027 end;
4029 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4030 MC_SEND_Info(PW);
4031 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4033 OuterLoop := True;
4034 while OuterLoop do
4035 begin
4036 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4037 begin
4038 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4039 begin
4040 Ptr := NetEvent.packet^.data;
4041 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4042 continue;
4044 MID := InMsg.ReadByte();
4046 if (MID = NET_MSG_INFO) and (State = 0) then
4047 begin
4048 NetMyID := InMsg.ReadByte();
4049 NetPlrUID1 := InMsg.ReadWord();
4051 WadName := InMsg.ReadString();
4052 Map := InMsg.ReadString();
4054 gWADHash := InMsg.ReadMD5();
4056 gGameSettings.GameMode := InMsg.ReadByte();
4057 gSwitchGameMode := gGameSettings.GameMode;
4058 gGameSettings.GoalLimit := InMsg.ReadWord();
4059 gGameSettings.TimeLimit := InMsg.ReadWord();
4060 gGameSettings.MaxLives := InMsg.ReadByte();
4061 gGameSettings.Options := InMsg.ReadLongWord();
4062 T := InMsg.ReadLongWord();
4064 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4065 if newResPath = '' then
4066 begin
4067 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4068 newResPath := g_Res_DownloadWAD(WadName);
4069 if newResPath = '' then
4070 begin
4071 g_FatalError(_lc[I_NET_ERR_HASH]);
4072 enet_packet_destroy(NetEvent.packet);
4073 NetState := NET_STATE_NONE;
4074 Exit;
4075 end;
4076 end;
4077 newResPath := ExtractRelativePath(MapsDir, newResPath);
4079 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4080 gPlayer1Settings.Color,
4081 gPlayer1Settings.Team, False));
4083 if gPlayer1 = nil then
4084 begin
4085 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4087 enet_packet_destroy(NetEvent.packet);
4088 NetState := NET_STATE_NONE;
4089 Exit;
4090 end;
4092 gPlayer1.Name := gPlayer1Settings.Name;
4093 gPlayer1.UID := NetPlrUID1;
4094 gPlayer1.Reset(True);
4096 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4097 begin
4098 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4100 enet_packet_destroy(NetEvent.packet);
4101 NetState := NET_STATE_NONE;
4102 Exit;
4103 end;
4105 gTime := T;
4107 State := 1;
4108 OuterLoop := False;
4109 enet_packet_destroy(NetEvent.packet);
4110 break;
4111 end
4112 else
4113 enet_packet_destroy(NetEvent.packet);
4114 end
4115 else
4116 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4117 begin
4118 State := 0;
4119 if (NetEvent.data <= NET_DISC_MAX) then
4120 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4121 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4122 OuterLoop := False;
4123 Break;
4124 end;
4125 end;
4127 ProcessLoading(true);
4129 e_PollInput();
4131 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
4132 begin
4133 State := 0;
4134 break;
4135 end;
4136 end;
4138 if State <> 1 then
4139 begin
4140 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4141 NetState := NET_STATE_NONE;
4142 Exit;
4143 end;
4145 gLMSRespawn := LMS_RESPAWN_NONE;
4146 gLMSRespawnTime := 0;
4148 g_Player_Init();
4149 NetState := NET_STATE_GAME;
4150 MC_SEND_FullStateRequest;
4151 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
4152 end;
4154 procedure g_Game_SaveOptions();
4155 begin
4156 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4157 end;
4159 procedure g_Game_ChangeMap(const MapPath: String);
4160 var
4161 Force: Boolean;
4162 begin
4163 g_Game_ClearLoading();
4165 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4166 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4167 if gExitByTrigger then
4168 begin
4169 Force := False;
4170 gExitByTrigger := False;
4171 end;
4172 if not g_Game_StartMap(MapPath, Force) then
4173 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4174 end;
4176 procedure g_Game_Restart();
4177 var
4178 Map: string;
4179 begin
4180 if g_Game_IsClient then
4181 Exit;
4182 map := g_ExtractFileName(gMapInfo.Map);
4184 MessageTime := 0;
4185 gGameOn := False;
4186 g_Game_ClearLoading();
4187 g_Game_StartMap(Map, True, gCurrentMapFileName);
4188 end;
4190 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4191 var
4192 NewWAD, ResName: String;
4193 I: Integer;
4194 begin
4195 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4196 g_Player_RemoveAllCorpses();
4198 if (not g_Game_IsClient) and
4199 (gSwitchGameMode <> gGameSettings.GameMode) and
4200 (gGameSettings.GameMode <> GM_SINGLE) then
4201 begin
4202 if gSwitchGameMode = GM_CTF then
4203 gGameSettings.MaxLives := 0;
4204 gGameSettings.GameMode := gSwitchGameMode;
4205 Force := True;
4206 end else
4207 gSwitchGameMode := gGameSettings.GameMode;
4209 g_Player_ResetTeams();
4211 if isWadPath(Map) then
4212 begin
4213 NewWAD := g_ExtractWadName(Map);
4214 ResName := g_ExtractFileName(Map);
4215 if g_Game_IsServer then
4216 begin
4217 gWADHash := MD5File(MapsDir + NewWAD);
4218 g_Game_LoadWAD(NewWAD);
4219 end else
4220 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4221 g_Game_ClientWAD(NewWAD, gWADHash);
4222 end else
4223 ResName := Map;
4225 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4226 if Result then
4227 begin
4228 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4230 gState := STATE_NONE;
4231 g_ActiveWindow := nil;
4232 gGameOn := True;
4234 DisableCheats();
4235 ResetTimer();
4237 if gGameSettings.GameMode = GM_CTF then
4238 begin
4239 g_Map_ResetFlag(FLAG_RED);
4240 g_Map_ResetFlag(FLAG_BLUE);
4241 // CTF, à ôëàãîâ íåò:
4242 if not g_Map_HaveFlagPoints() then
4243 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4244 end;
4245 end
4246 else
4247 begin
4248 gState := STATE_MENU;
4249 gGameOn := False;
4250 end;
4252 gExit := 0;
4253 gPause := False;
4254 gTime := 0;
4255 NetTimeToUpdate := 1;
4256 NetTimeToReliable := 0;
4257 NetTimeToMaster := NetMasterRate;
4258 gLMSRespawn := LMS_RESPAWN_NONE;
4259 gLMSRespawnTime := 0;
4260 gMissionFailed := False;
4261 gNextMap := '';
4263 gCoopMonstersKilled := 0;
4264 gCoopSecretsFound := 0;
4266 gVoteInProgress := False;
4267 gVotePassed := False;
4268 gVoteCount := 0;
4269 gVoted := False;
4271 gStatsOff := False;
4273 if not gGameOn then Exit;
4275 g_Game_SpectateCenterView();
4277 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4278 begin
4279 gLMSRespawn := LMS_RESPAWN_WARMUP;
4280 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4281 gLMSSoftSpawn := True;
4282 if NetMode = NET_SERVER then
4283 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4284 else
4285 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4286 end;
4288 if NetMode = NET_SERVER then
4289 begin
4290 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4292 // Ìàñòåðñåðâåð
4293 if NetUseMaster then
4294 begin
4295 if (NetMHost = nil) or (NetMPeer = nil) then
4296 if not g_Net_Slist_Connect then
4297 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4299 g_Net_Slist_Update;
4300 end;
4302 if NetClients <> nil then
4303 for I := 0 to High(NetClients) do
4304 if NetClients[I].Used then
4305 begin
4306 NetClients[I].Voted := False;
4307 if NetClients[I].RequestedFullUpdate then
4308 begin
4309 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4310 NetClients[I].RequestedFullUpdate := False;
4311 end;
4312 end;
4314 g_Net_UnbanNonPermHosts();
4315 end;
4317 if gLastMap then
4318 begin
4319 gCoopTotalMonstersKilled := 0;
4320 gCoopTotalSecretsFound := 0;
4321 gCoopTotalMonsters := 0;
4322 gCoopTotalSecrets := 0;
4323 gLastMap := False;
4324 end;
4326 g_Game_ExecuteEvent('onmapstart');
4327 end;
4329 procedure SetFirstLevel();
4330 begin
4331 gNextMap := '';
4333 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4334 if MapList = nil then
4335 Exit;
4337 SortSArray(MapList);
4338 gNextMap := MapList[Low(MapList)];
4340 MapList := nil;
4341 end;
4343 procedure g_Game_ExitLevel(const Map: AnsiString);
4344 begin
4345 gNextMap := Map;
4347 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4348 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4349 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4350 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4352 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4353 if gGameSettings.GameType = GT_SINGLE then
4354 gExit := EXIT_ENDLEVELSINGLE
4355 else // Âûøëè â âûõîä â Ñâîåé èãðå
4356 begin
4357 gExit := EXIT_ENDLEVELCUSTOM;
4358 if gGameSettings.GameMode = GM_COOP then
4359 g_Player_RememberAll;
4361 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4362 begin
4363 gLastMap := True;
4364 if gGameSettings.GameMode = GM_COOP then
4365 gStatsOff := True;
4367 gStatsPressed := True;
4368 gNextMap := 'MAP01';
4370 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4371 g_Game_NextLevel;
4373 if g_Game_IsNet then
4374 begin
4375 MH_SEND_GameStats();
4376 MH_SEND_CoopStats();
4377 end;
4378 end;
4379 end;
4380 end;
4382 procedure g_Game_RestartLevel();
4383 var
4384 Map: string;
4385 begin
4386 if gGameSettings.GameMode = GM_SINGLE then
4387 begin
4388 g_Game_Restart();
4389 Exit;
4390 end;
4391 gExit := EXIT_ENDLEVELCUSTOM;
4392 Map := g_ExtractFileName(gMapInfo.Map);
4393 gNextMap := Map;
4394 end;
4396 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4397 var
4398 gWAD: String;
4399 begin
4400 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4401 Exit;
4402 if not g_Game_IsClient then
4403 Exit;
4404 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4405 if gWAD = '' then
4406 begin
4407 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4408 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4409 if gWAD = '' then
4410 begin
4411 g_Game_Free();
4412 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4413 Exit;
4414 end;
4415 end;
4416 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4417 g_Game_LoadWAD(NewWAD);
4418 end;
4420 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4421 var
4422 i, n, nb, nr: Integer;
4424 function monRespawn (mon: TMonster): Boolean;
4425 begin
4426 result := false; // don't stop
4427 if not mon.FNoRespawn then mon.Respawn();
4428 end;
4430 begin
4431 if not g_Game_IsServer then Exit;
4432 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4433 gLMSRespawn := LMS_RESPAWN_NONE;
4434 gLMSRespawnTime := 0;
4435 MessageTime := 0;
4437 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4438 begin
4439 gMissionFailed := True;
4440 g_Game_RestartLevel;
4441 Exit;
4442 end;
4444 n := 0; nb := 0; nr := 0;
4445 for i := Low(gPlayers) to High(gPlayers) do
4446 if (gPlayers[i] <> nil) and
4447 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4448 (gPlayers[i] is TBot)) then
4449 begin
4450 Inc(n);
4451 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4452 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4453 end;
4455 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4456 begin
4457 // wait a second until the fuckers finally decide to join
4458 gLMSRespawn := LMS_RESPAWN_WARMUP;
4459 gLMSRespawnTime := gTime + 1000;
4460 gLMSSoftSpawn := NoMapRestart;
4461 Exit;
4462 end;
4464 g_Player_RemoveAllCorpses;
4465 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4466 if g_Game_IsNet then
4467 MH_SEND_GameEvent(NET_EV_LMS_START);
4469 for i := Low(gPlayers) to High(gPlayers) do
4470 begin
4471 if gPlayers[i] = nil then continue;
4472 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4473 // don't touch normal spectators
4474 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4475 begin
4476 gPlayers[i].FNoRespawn := True;
4477 gPlayers[i].Lives := 0;
4478 if g_Game_IsNet then
4479 MH_SEND_PlayerStats(gPlayers[I].UID);
4480 continue;
4481 end;
4482 gPlayers[i].FNoRespawn := False;
4483 gPlayers[i].Lives := gGameSettings.MaxLives;
4484 gPlayers[i].Respawn(False, True);
4485 if gGameSettings.GameMode = GM_COOP then
4486 begin
4487 gPlayers[i].Frags := 0;
4488 gPlayers[i].RecallState;
4489 end;
4490 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4491 gPlayer1 := g_Player_Get(gLMSPID1);
4492 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4493 gPlayer2 := g_Player_Get(gLMSPID2);
4494 end;
4496 g_Items_RestartRound();
4499 g_Mons_ForEach(monRespawn);
4501 gLMSSoftSpawn := False;
4502 end;
4504 function g_Game_GetFirstMap(WAD: String): String;
4505 begin
4506 Result := '';
4508 MapList := g_Map_GetMapsList(WAD);
4509 if MapList = nil then
4510 Exit;
4512 SortSArray(MapList);
4513 Result := MapList[Low(MapList)];
4515 if not g_Map_Exist(WAD + ':\' + Result) then
4516 Result := '';
4518 MapList := nil;
4519 end;
4521 function g_Game_GetNextMap(): String;
4522 var
4523 I: Integer;
4524 Map: string;
4525 begin
4526 Result := '';
4528 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4529 if MapList = nil then
4530 Exit;
4532 Map := g_ExtractFileName(gMapInfo.Map);
4534 SortSArray(MapList);
4535 MapIndex := -255;
4536 for I := Low(MapList) to High(MapList) do
4537 if Map = MapList[I] then
4538 begin
4539 MapIndex := I;
4540 Break;
4541 end;
4543 if MapIndex <> -255 then
4544 begin
4545 if MapIndex = High(MapList) then
4546 Result := MapList[Low(MapList)]
4547 else
4548 Result := MapList[MapIndex + 1];
4550 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4551 end;
4553 MapList := nil;
4554 end;
4556 procedure g_Game_NextLevel();
4557 begin
4558 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4559 gExit := EXIT_ENDLEVELCUSTOM
4560 else
4561 begin
4562 gExit := EXIT_ENDLEVELSINGLE;
4563 Exit;
4564 end;
4566 if gNextMap <> '' then Exit;
4567 gNextMap := g_Game_GetNextMap();
4568 end;
4570 function g_Game_IsTestMap(): Boolean;
4571 begin
4572 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4573 end;
4575 procedure g_Game_DeleteTestMap();
4576 var
4577 a: Integer;
4578 //MapName: AnsiString;
4579 WadName: string;
4581 WAD: TWADFile;
4582 MapList: SArray;
4583 time: Integer;
4585 begin
4586 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
4587 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
4588 if (a = 0) then exit;
4590 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
4591 WadName := Copy(gMapToDelete, 1, a+3);
4592 Delete(gMapToDelete, 1, a+5);
4593 gMapToDelete := UpperCase(gMapToDelete);
4594 //MapName := '';
4595 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4598 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4599 if MapName <> TEST_MAP_NAME then
4600 Exit;
4602 if not gTempDelete then
4603 begin
4604 time := g_GetFileTime(WadName);
4605 WAD := TWADFile.Create();
4607 // ×èòàåì Wad-ôàéë:
4608 if not WAD.ReadFile(WadName) then
4609 begin // Íåò òàêîãî WAD-ôàéëà
4610 WAD.Free();
4611 Exit;
4612 end;
4614 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4615 WAD.CreateImage();
4616 MapList := WAD.GetResourcesList('');
4618 if MapList <> nil then
4619 for a := 0 to High(MapList) do
4620 if MapList[a] = MapName then
4621 begin
4622 // Óäàëÿåì è ñîõðàíÿåì:
4623 WAD.RemoveResource('', MapName);
4624 WAD.SaveTo(WadName);
4625 Break;
4626 end;
4628 WAD.Free();
4629 g_SetFileTime(WadName, time);
4630 end else
4632 if gTempDelete then DeleteFile(WadName);
4633 end;
4635 procedure GameCVars(P: SArray);
4636 var
4637 a, b: Integer;
4638 stat: TPlayerStatArray;
4639 cmd, s: string;
4640 config: TConfig;
4641 begin
4642 stat := nil;
4643 cmd := LowerCase(P[0]);
4644 if cmd = 'r_showfps' then
4645 begin
4646 if (Length(P) > 1) and
4647 ((P[1] = '1') or (P[1] = '0')) then
4648 gShowFPS := (P[1][1] = '1');
4650 if gShowFPS then
4651 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4652 else
4653 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4654 end
4655 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4656 begin
4657 with gGameSettings do
4658 begin
4659 if (Length(P) > 1) and
4660 ((P[1] = '1') or (P[1] = '0')) then
4661 begin
4662 if (P[1][1] = '1') then
4663 Options := Options or GAME_OPTION_TEAMDAMAGE
4664 else
4665 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4666 end;
4668 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4669 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4670 else
4671 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4673 if g_Game_IsNet then MH_SEND_GameSettings;
4674 end;
4675 end
4676 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4677 begin
4678 with gGameSettings do
4679 begin
4680 if (Length(P) > 1) and
4681 ((P[1] = '1') or (P[1] = '0')) then
4682 begin
4683 if (P[1][1] = '1') then
4684 Options := Options or GAME_OPTION_WEAPONSTAY
4685 else
4686 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4687 end;
4689 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4690 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4691 else
4692 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4694 if g_Game_IsNet then MH_SEND_GameSettings;
4695 end;
4696 end
4697 else if cmd = 'g_gamemode' then
4698 begin
4699 a := g_Game_TextToMode(P[1]);
4700 if a = GM_SINGLE then a := GM_COOP;
4701 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4702 begin
4703 gSwitchGameMode := a;
4704 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4705 (gState = STATE_INTERSINGLE) then
4706 gSwitchGameMode := GM_SINGLE;
4707 if not gGameOn then
4708 gGameSettings.GameMode := gSwitchGameMode;
4709 end;
4710 if gSwitchGameMode = gGameSettings.GameMode then
4711 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4712 [g_Game_ModeToText(gGameSettings.GameMode)]))
4713 else
4714 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4715 [g_Game_ModeToText(gGameSettings.GameMode),
4716 g_Game_ModeToText(gSwitchGameMode)]));
4717 end
4718 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4719 begin
4720 with gGameSettings do
4721 begin
4722 if (Length(P) > 1) and
4723 ((P[1] = '1') or (P[1] = '0')) then
4724 begin
4725 if (P[1][1] = '1') then
4726 Options := Options or GAME_OPTION_ALLOWEXIT
4727 else
4728 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4729 end;
4731 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4732 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4733 else
4734 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4735 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4737 if g_Game_IsNet then MH_SEND_GameSettings;
4738 end;
4739 end
4740 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4741 begin
4742 with gGameSettings do
4743 begin
4744 if (Length(P) > 1) and
4745 ((P[1] = '1') or (P[1] = '0')) then
4746 begin
4747 if (P[1][1] = '1') then
4748 Options := Options or GAME_OPTION_MONSTERS
4749 else
4750 Options := Options and (not GAME_OPTION_MONSTERS);
4751 end;
4753 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4754 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4755 else
4756 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4757 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4759 if g_Game_IsNet then MH_SEND_GameSettings;
4760 end;
4761 end
4762 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4763 begin
4764 with gGameSettings do
4765 begin
4766 if (Length(P) > 1) and
4767 ((P[1] = '1') or (P[1] = '0')) then
4768 begin
4769 if (P[1][1] = '1') then
4770 Options := Options or GAME_OPTION_BOTVSPLAYER
4771 else
4772 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4773 end;
4775 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4776 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4777 else
4778 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4780 if g_Game_IsNet then MH_SEND_GameSettings;
4781 end;
4782 end
4783 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4784 begin
4785 with gGameSettings do
4786 begin
4787 if (Length(P) > 1) and
4788 ((P[1] = '1') or (P[1] = '0')) then
4789 begin
4790 if (P[1][1] = '1') then
4791 Options := Options or GAME_OPTION_BOTVSMONSTER
4792 else
4793 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4794 end;
4796 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4797 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4798 else
4799 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4801 if g_Game_IsNet then MH_SEND_GameSettings;
4802 end;
4803 end
4804 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4805 begin
4806 if Length(P) > 1 then
4807 begin
4808 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4809 gGameSettings.WarmupTime := 30
4810 else
4811 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4812 end;
4814 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4815 [gGameSettings.WarmupTime]));
4816 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4817 end
4818 else if cmd = 'net_interp' then
4819 begin
4820 if (Length(P) > 1) then
4821 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4823 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4824 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4825 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4826 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4827 config.Free();
4828 end
4829 else if cmd = 'net_forceplayerupdate' then
4830 begin
4831 if (Length(P) > 1) and
4832 ((P[1] = '1') or (P[1] = '0')) then
4833 NetForcePlayerUpdate := (P[1][1] = '1');
4835 if NetForcePlayerUpdate then
4836 g_Console_Add('net_forceplayerupdate = 1')
4837 else
4838 g_Console_Add('net_forceplayerupdate = 0');
4839 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4840 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4841 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4842 config.Free();
4843 end
4844 else if cmd = 'net_predictself' then
4845 begin
4846 if (Length(P) > 1) and
4847 ((P[1] = '1') or (P[1] = '0')) then
4848 NetPredictSelf := (P[1][1] = '1');
4850 if NetPredictSelf then
4851 g_Console_Add('net_predictself = 1')
4852 else
4853 g_Console_Add('net_predictself = 0');
4854 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4855 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4856 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4857 config.Free();
4858 end
4859 else if cmd = 'sv_name' then
4860 begin
4861 if (Length(P) > 1) and (Length(P[1]) > 0) then
4862 begin
4863 NetServerName := P[1];
4864 if Length(NetServerName) > 64 then
4865 SetLength(NetServerName, 64);
4866 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4867 g_Net_Slist_Update;
4868 end;
4870 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4871 end
4872 else if cmd = 'sv_passwd' then
4873 begin
4874 if (Length(P) > 1) and (Length(P[1]) > 0) then
4875 begin
4876 NetPassword := P[1];
4877 if Length(NetPassword) > 24 then
4878 SetLength(NetPassword, 24);
4879 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4880 g_Net_Slist_Update;
4881 end;
4883 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4884 end
4885 else if cmd = 'sv_maxplrs' then
4886 begin
4887 if (Length(P) > 1) then
4888 begin
4889 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4890 if g_Game_IsServer and g_Game_IsNet then
4891 begin
4892 b := 0;
4893 for a := 0 to High(NetClients) do
4894 if NetClients[a].Used then
4895 begin
4896 Inc(b);
4897 if b > NetMaxClients then
4898 begin
4899 s := g_Player_Get(NetClients[a].Player).Name;
4900 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4901 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4902 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4903 end;
4904 end;
4905 if NetUseMaster then
4906 g_Net_Slist_Update;
4907 end;
4908 end;
4910 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4911 end
4912 else if cmd = 'sv_public' then
4913 begin
4914 if (Length(P) > 1) then
4915 begin
4916 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4917 if g_Game_IsServer and g_Game_IsNet then
4918 if NetUseMaster then
4919 begin
4920 if NetMPeer = nil then
4921 if not g_Net_Slist_Connect() then
4922 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4923 g_Net_Slist_Update();
4924 end
4925 else
4926 if NetMPeer <> nil then
4927 g_Net_Slist_Disconnect();
4928 end;
4930 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4931 end
4932 else if cmd = 'sv_intertime' then
4933 begin
4934 if (Length(P) > 1) then
4935 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4937 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4938 end
4939 else if cmd = 'p1_name' then
4940 begin
4941 if (Length(P) > 1) and gGameOn then
4942 begin
4943 if g_Game_IsClient then
4944 begin
4945 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4946 MC_SEND_PlayerSettings;
4947 end
4948 else
4949 if gPlayer1 <> nil then
4950 begin
4951 gPlayer1.Name := b_Text_Unformat(P[1]);
4952 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4953 end
4954 else
4955 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4956 end;
4957 end
4958 else if cmd = 'p2_name' then
4959 begin
4960 if (Length(P) > 1) and gGameOn then
4961 begin
4962 if g_Game_IsClient then
4963 begin
4964 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4965 MC_SEND_PlayerSettings;
4966 end
4967 else
4968 if gPlayer2 <> nil then
4969 begin
4970 gPlayer2.Name := b_Text_Unformat(P[1]);
4971 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4972 end
4973 else
4974 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4975 end;
4976 end
4977 else if cmd = 'p1_color' then
4978 begin
4979 if Length(P) > 3 then
4980 if g_Game_IsClient then
4981 begin
4982 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4983 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4984 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4985 MC_SEND_PlayerSettings;
4986 end
4987 else
4988 if gPlayer1 <> nil then
4989 begin
4990 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4991 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4992 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4993 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4994 end
4995 else
4996 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4997 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4998 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4999 end
5000 else if (cmd = 'p2_color') and not g_Game_IsNet then
5001 begin
5002 if Length(P) > 3 then
5003 if g_Game_IsClient then
5004 begin
5005 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5006 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5007 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5008 MC_SEND_PlayerSettings;
5009 end
5010 else
5011 if gPlayer2 <> nil then
5012 begin
5013 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5014 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5015 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5016 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5017 end
5018 else
5019 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5020 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5021 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5022 end
5023 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5024 begin
5025 if cmd = 'r_showtime' then
5026 begin
5027 if (Length(P) > 1) and
5028 ((P[1] = '1') or (P[1] = '0')) then
5029 gShowTime := (P[1][1] = '1');
5031 if gShowTime then
5032 g_Console_Add(_lc[I_MSG_TIME_ON])
5033 else
5034 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5035 end
5036 else if cmd = 'r_showscore' then
5037 begin
5038 if (Length(P) > 1) and
5039 ((P[1] = '1') or (P[1] = '0')) then
5040 gShowGoals := (P[1][1] = '1');
5042 if gShowGoals then
5043 g_Console_Add(_lc[I_MSG_SCORE_ON])
5044 else
5045 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5046 end
5047 else if cmd = 'r_showstat' then
5048 begin
5049 if (Length(P) > 1) and
5050 ((P[1] = '1') or (P[1] = '0')) then
5051 gShowStat := (P[1][1] = '1');
5053 if gShowStat then
5054 g_Console_Add(_lc[I_MSG_STATS_ON])
5055 else
5056 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5057 end
5058 else if cmd = 'r_showkillmsg' then
5059 begin
5060 if (Length(P) > 1) and
5061 ((P[1] = '1') or (P[1] = '0')) then
5062 gShowKillMsg := (P[1][1] = '1');
5064 if gShowKillMsg then
5065 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5066 else
5067 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5068 end
5069 else if cmd = 'r_showlives' then
5070 begin
5071 if (Length(P) > 1) and
5072 ((P[1] = '1') or (P[1] = '0')) then
5073 gShowLives := (P[1][1] = '1');
5075 if gShowLives then
5076 g_Console_Add(_lc[I_MSG_LIVES_ON])
5077 else
5078 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5079 end
5080 else if cmd = 'r_showspect' then
5081 begin
5082 if (Length(P) > 1) and
5083 ((P[1] = '1') or (P[1] = '0')) then
5084 gSpectHUD := (P[1][1] = '1');
5086 if gSpectHUD then
5087 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5088 else
5089 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5090 end
5091 else if cmd = 'r_showping' then
5092 begin
5093 if (Length(P) > 1) and
5094 ((P[1] = '1') or (P[1] = '0')) then
5095 gShowPing := (P[1][1] = '1');
5097 if gShowPing then
5098 g_Console_Add(_lc[I_MSG_PING_ON])
5099 else
5100 g_Console_Add(_lc[I_MSG_PING_OFF]);
5101 end
5102 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5103 begin
5104 if Length(P) > 1 then
5105 begin
5106 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5107 gGameSettings.GoalLimit := 0
5108 else
5109 begin
5110 b := 0;
5112 if gGameSettings.GameMode = GM_DM then
5113 begin // DM
5114 stat := g_Player_GetStats();
5115 if stat <> nil then
5116 for a := 0 to High(stat) do
5117 if stat[a].Frags > b then
5118 b := stat[a].Frags;
5119 end
5120 else // TDM/CTF
5121 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5123 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5124 end;
5126 if g_Game_IsNet then MH_SEND_GameSettings;
5127 end;
5129 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5130 end
5131 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5132 begin
5133 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5134 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5136 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5137 [gGameSettings.TimeLimit div 3600,
5138 (gGameSettings.TimeLimit div 60) mod 60,
5139 gGameSettings.TimeLimit mod 60]));
5140 if g_Game_IsNet then MH_SEND_GameSettings;
5141 end
5142 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5143 begin
5144 if Length(P) > 1 then
5145 begin
5146 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5147 gGameSettings.MaxLives := 0
5148 else
5149 begin
5150 b := 0;
5151 stat := g_Player_GetStats();
5152 if stat <> nil then
5153 for a := 0 to High(stat) do
5154 if stat[a].Lives > b then
5155 b := stat[a].Lives;
5156 gGameSettings.MaxLives :=
5157 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5158 end;
5159 end;
5161 g_Console_Add(Format(_lc[I_MSG_LIVES],
5162 [gGameSettings.MaxLives]));
5163 if g_Game_IsNet then MH_SEND_GameSettings;
5164 end;
5165 end;
5166 end;
5169 procedure DebugCommands(P: SArray);
5170 var
5171 a, b: Integer;
5172 cmd: string;
5173 //pt: TDFPoint;
5174 mon: TMonster;
5175 begin
5176 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5177 if gDebugMode then
5178 begin
5179 cmd := LowerCase(P[0]);
5180 if cmd = 'd_window' then
5181 begin
5182 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5183 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5184 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5185 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5186 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5187 end
5188 else if cmd = 'd_sounds' then
5189 begin
5190 if (Length(P) > 1) and
5191 ((P[1] = '1') or (P[1] = '0')) then
5192 g_Debug_Sounds := (P[1][1] = '1');
5194 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5195 end
5196 else if cmd = 'd_frames' then
5197 begin
5198 if (Length(P) > 1) and
5199 ((P[1] = '1') or (P[1] = '0')) then
5200 g_Debug_Frames := (P[1][1] = '1');
5202 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5203 end
5204 else if cmd = 'd_winmsg' then
5205 begin
5206 if (Length(P) > 1) and
5207 ((P[1] = '1') or (P[1] = '0')) then
5208 g_Debug_WinMsgs := (P[1][1] = '1');
5210 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5211 end
5212 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5213 begin
5214 if (Length(P) > 1) and
5215 ((P[1] = '1') or (P[1] = '0')) then
5216 g_Debug_MonsterOff := (P[1][1] = '1');
5218 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5219 end
5220 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5221 begin
5222 if Length(P) > 1 then
5223 case P[1][1] of
5224 '0': g_debug_BotAIOff := 0;
5225 '1': g_debug_BotAIOff := 1;
5226 '2': g_debug_BotAIOff := 2;
5227 '3': g_debug_BotAIOff := 3;
5228 end;
5230 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5231 end
5232 else if cmd = 'd_monster' then
5233 begin
5234 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5235 if Length(P) < 2 then
5236 begin
5237 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5238 g_Console_Add('ID | Name');
5239 for b := MONSTER_DEMON to MONSTER_MAN do
5240 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5241 end else
5242 begin
5243 a := StrToIntDef(P[1], 0);
5244 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5245 a := g_Mons_TypeIdByName(P[1]);
5247 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5248 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5249 else
5250 begin
5251 with gPlayer1.Obj do
5252 begin
5253 mon := g_Monsters_Create(a,
5254 X + Rect.X + (Rect.Width div 2),
5255 Y + Rect.Y + Rect.Height,
5256 gPlayer1.Direction, True);
5257 end;
5258 if (Length(P) > 2) and (mon <> nil) then
5259 mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5260 end;
5261 end;
5262 end
5263 else if (cmd = 'd_health') then
5264 begin
5265 if (Length(P) > 1) and
5266 ((P[1] = '1') or (P[1] = '0')) then
5267 g_debug_HealthBar := (P[1][1] = '1');
5269 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5270 end
5271 else if (cmd = 'd_player') then
5272 begin
5273 if (Length(P) > 1) and
5274 ((P[1] = '1') or (P[1] = '0')) then
5275 g_debug_Player := (P[1][1] = '1');
5277 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5278 end
5279 else if (cmd = 'd_joy') then
5280 begin
5281 for a := 1 to 8 do
5282 g_Console_Add(e_JoystickStateToString(a));
5283 end;
5284 end
5285 else
5286 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5287 end;
5290 procedure GameCheats(P: SArray);
5291 var
5292 cmd: string;
5293 f, a: Integer;
5294 plr: TPlayer;
5295 begin
5296 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5297 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5298 begin
5299 g_Console_Add('not available');
5300 exit;
5301 end;
5302 plr := gPlayer1;
5303 if plr = nil then
5304 begin
5305 g_Console_Add('where is the player?!');
5306 exit;
5307 end;
5308 cmd := LowerCase(P[0]);
5309 // god
5310 if cmd = 'god' then
5311 begin
5312 plr.GodMode := not plr.GodMode;
5313 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5314 exit;
5315 end;
5316 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5317 if cmd = 'give' then
5318 begin
5319 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5320 for f := 1 to High(P) do
5321 begin
5322 cmd := LowerCase(P[f]);
5323 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5324 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5325 if cmd = 'exit' then
5326 begin
5327 if gTriggers <> nil then
5328 begin
5329 for a := 0 to High(gTriggers) do
5330 begin
5331 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5332 begin
5333 g_Console_Add('player left the map');
5334 gExitByTrigger := True;
5335 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5336 g_Game_ExitLevel(gTriggers[a].tgcMap);
5337 break;
5338 end;
5339 end;
5340 end;
5341 continue;
5342 end;
5344 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5345 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5346 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5347 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5348 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5350 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5351 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5353 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5354 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;
5356 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5357 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5359 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5360 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5362 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5363 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5365 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5366 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5367 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5369 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5370 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5371 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5372 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;
5373 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5374 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5376 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;
5377 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;
5378 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;
5379 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;
5380 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;
5381 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;
5383 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5384 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;
5386 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;
5387 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;
5389 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5391 if cmd = 'ammo' then
5392 begin
5393 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5394 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5395 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5396 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5397 plr.GiveItem(ITEM_AMMO_FUELCAN);
5398 g_Console_Add('player got some ammo');
5399 continue;
5400 end;
5402 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5403 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5405 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5406 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5408 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5409 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5411 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5412 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5414 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5416 if cmd = 'weapons' then
5417 begin
5418 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5419 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5420 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5421 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5422 plr.GiveItem(ITEM_WEAPON_PLASMA);
5423 plr.GiveItem(ITEM_WEAPON_BFG);
5424 g_Console_Add('player got weapons');
5425 continue;
5426 end;
5428 if cmd = 'keys' then
5429 begin
5430 plr.GiveItem(ITEM_KEY_RED);
5431 plr.GiveItem(ITEM_KEY_GREEN);
5432 plr.GiveItem(ITEM_KEY_BLUE);
5433 g_Console_Add('player got all keys');
5434 continue;
5435 end;
5437 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5438 end;
5439 exit;
5440 end;
5441 // open
5442 if cmd = 'open' then
5443 begin
5444 g_Console_Add('player activated sesame');
5445 g_Triggers_OpenAll();
5446 exit;
5447 end;
5448 // fly
5449 if cmd = 'fly' then
5450 begin
5451 gFly := not gFly;
5452 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5453 exit;
5454 end;
5455 // noclip
5456 if cmd = 'noclip' then
5457 begin
5458 plr.SwitchNoClip;
5459 g_Console_Add('wall hardeness adjusted');
5460 exit;
5461 end;
5462 // notarget
5463 if cmd = 'notarget' then
5464 begin
5465 plr.NoTarget := not plr.NoTarget;
5466 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5467 exit;
5468 end;
5469 // noreload
5470 if cmd = 'noreload' then
5471 begin
5472 plr.NoReload := not plr.NoReload;
5473 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5474 exit;
5475 end;
5476 // speedy
5477 if cmd = 'speedy' then
5478 begin
5479 MAX_RUNVEL := 32-MAX_RUNVEL;
5480 g_Console_Add('speed adjusted');
5481 exit;
5482 end;
5483 // jumpy
5484 if cmd = 'jumpy' then
5485 begin
5486 VEL_JUMP := 30-VEL_JUMP;
5487 g_Console_Add('jump height adjusted');
5488 exit;
5489 end;
5490 // automap
5491 if cmd = 'automap' then
5492 begin
5493 gShowMap := not gShowMap;
5494 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5495 exit;
5496 end;
5497 // aimline
5498 if cmd = 'aimline' then
5499 begin
5500 gAimLine := not gAimLine;
5501 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5502 exit;
5503 end;
5504 end;
5506 procedure GameCommands(P: SArray);
5507 var
5508 a, b: Integer;
5509 s, pw: String;
5510 chstr: string;
5511 cmd: string;
5512 pl: pTNetClient = nil;
5513 plr: TPlayer;
5514 prt: Word;
5515 nm: Boolean;
5516 listen: LongWord;
5517 begin
5518 // Îáùèå êîìàíäû:
5519 cmd := LowerCase(P[0]);
5520 chstr := '';
5521 if (cmd = 'quit') or
5522 (cmd = 'exit') then
5523 begin
5524 g_Game_Free();
5525 g_Game_Quit();
5526 Exit;
5527 end
5528 else if cmd = 'pause' then
5529 begin
5530 if (g_ActiveWindow = nil) then
5531 g_Game_Pause(not gPause);
5532 end
5533 else if cmd = 'endgame' then
5534 gExit := EXIT_SIMPLE
5535 else if cmd = 'restart' then
5536 begin
5537 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5538 begin
5539 if g_Game_IsClient then
5540 begin
5541 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5542 Exit;
5543 end;
5544 g_Game_Restart();
5545 end else
5546 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5547 end
5548 else if cmd = 'kick' then
5549 begin
5550 if g_Game_IsServer then
5551 begin
5552 if Length(P) < 2 then
5553 begin
5554 g_Console_Add('kick <name>');
5555 Exit;
5556 end;
5557 if P[1] = '' then
5558 begin
5559 g_Console_Add('kick <name>');
5560 Exit;
5561 end;
5563 if g_Game_IsNet then
5564 pl := g_Net_Client_ByName(P[1]);
5565 if (pl <> nil) then
5566 begin
5567 s := g_Net_ClientName_ByID(pl^.ID);
5568 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5569 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5570 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5571 if NetUseMaster then
5572 g_Net_Slist_Update;
5573 end else if gPlayers <> nil then
5574 for a := Low(gPlayers) to High(gPlayers) do
5575 if gPlayers[a] <> nil then
5576 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5577 begin
5578 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5579 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5580 continue;
5581 gPlayers[a].Lives := 0;
5582 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5583 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5584 g_Player_Remove(gPlayers[a].UID);
5585 if NetUseMaster then
5586 g_Net_Slist_Update;
5587 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5588 g_Bot_MixNames();
5589 end;
5590 end else
5591 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5592 end
5593 else if cmd = 'kick_id' then
5594 begin
5595 if g_Game_IsServer and g_Game_IsNet then
5596 begin
5597 if Length(P) < 2 then
5598 begin
5599 g_Console_Add('kick_id <client ID>');
5600 Exit;
5601 end;
5602 if P[1] = '' then
5603 begin
5604 g_Console_Add('kick_id <client ID>');
5605 Exit;
5606 end;
5608 a := StrToIntDef(P[1], 0);
5609 if (NetClients <> nil) and (a <= High(NetClients)) then
5610 begin
5611 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5612 begin
5613 s := g_Net_ClientName_ByID(NetClients[a].ID);
5614 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5615 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5616 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5617 if NetUseMaster then
5618 g_Net_Slist_Update;
5619 end;
5620 end;
5621 end else
5622 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5623 end
5624 else if cmd = 'ban' then
5625 begin
5626 if g_Game_IsServer and g_Game_IsNet then
5627 begin
5628 if Length(P) < 2 then
5629 begin
5630 g_Console_Add('ban <name>');
5631 Exit;
5632 end;
5633 if P[1] = '' then
5634 begin
5635 g_Console_Add('ban <name>');
5636 Exit;
5637 end;
5639 pl := g_Net_Client_ByName(P[1]);
5640 if (pl <> nil) then
5641 begin
5642 s := g_Net_ClientName_ByID(pl^.ID);
5643 g_Net_BanHost(pl^.Peer^.address.host, False);
5644 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
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 else
5650 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5651 end else
5652 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5653 end
5654 else if cmd = 'ban_id' then
5655 begin
5656 if g_Game_IsServer and g_Game_IsNet then
5657 begin
5658 if Length(P) < 2 then
5659 begin
5660 g_Console_Add('ban_id <client ID>');
5661 Exit;
5662 end;
5663 if P[1] = '' then
5664 begin
5665 g_Console_Add('ban_id <client ID>');
5666 Exit;
5667 end;
5669 a := StrToIntDef(P[1], 0);
5670 if (NetClients <> nil) and (a <= High(NetClients)) then
5671 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5672 begin
5673 s := g_Net_ClientName_ByID(NetClients[a].ID);
5674 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5675 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5676 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5677 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5678 if NetUseMaster then
5679 g_Net_Slist_Update;
5680 end;
5681 end else
5682 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5683 end
5684 else if cmd = 'permban' then
5685 begin
5686 if g_Game_IsServer and g_Game_IsNet then
5687 begin
5688 if Length(P) < 2 then
5689 begin
5690 g_Console_Add('permban <name>');
5691 Exit;
5692 end;
5693 if P[1] = '' then
5694 begin
5695 g_Console_Add('permban <name>');
5696 Exit;
5697 end;
5699 pl := g_Net_Client_ByName(P[1]);
5700 if (pl <> nil) then
5701 begin
5702 s := g_Net_ClientName_ByID(pl^.ID);
5703 g_Net_BanHost(pl^.Peer^.address.host);
5704 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5705 g_Net_SaveBanList();
5706 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5707 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5708 if NetUseMaster then
5709 g_Net_Slist_Update;
5710 end else
5711 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5712 end else
5713 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5714 end
5715 else if cmd = 'permban_id' then
5716 begin
5717 if g_Game_IsServer and g_Game_IsNet then
5718 begin
5719 if Length(P) < 2 then
5720 begin
5721 g_Console_Add('permban_id <client ID>');
5722 Exit;
5723 end;
5724 if P[1] = '' then
5725 begin
5726 g_Console_Add('permban_id <client ID>');
5727 Exit;
5728 end;
5730 a := StrToIntDef(P[1], 0);
5731 if (NetClients <> nil) and (a <= High(NetClients)) then
5732 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5733 begin
5734 s := g_Net_ClientName_ByID(NetClients[a].ID);
5735 g_Net_BanHost(NetClients[a].Peer^.address.host);
5736 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5737 g_Net_SaveBanList();
5738 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5739 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5740 if NetUseMaster then
5741 g_Net_Slist_Update;
5742 end;
5743 end else
5744 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5745 end
5746 else if cmd = 'unban' then
5747 begin
5748 if g_Game_IsServer and g_Game_IsNet then
5749 begin
5750 if Length(P) < 2 then
5751 begin
5752 g_Console_Add('unban <IP Address>');
5753 Exit;
5754 end;
5755 if P[1] = '' then
5756 begin
5757 g_Console_Add('unban <IP Address>');
5758 Exit;
5759 end;
5761 if g_Net_UnbanHost(P[1]) then
5762 begin
5763 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5764 g_Net_SaveBanList();
5765 end else
5766 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5767 end else
5768 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5769 end
5770 else if cmd = 'clientlist' then
5771 begin
5772 if g_Game_IsServer and g_Game_IsNet then
5773 begin
5774 b := 0;
5775 if NetClients <> nil then
5776 for a := Low(NetClients) to High(NetClients) do
5777 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5778 begin
5779 plr := g_Player_Get(NetClients[a].Player);
5780 if plr = nil then continue;
5781 Inc(b);
5782 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5783 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5784 end;
5785 if b = 0 then
5786 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5787 end else
5788 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5789 end
5790 else if cmd = 'connect' then
5791 begin
5792 if (NetMode = NET_NONE) then
5793 begin
5794 if Length(P) < 2 then
5795 begin
5796 g_Console_Add('connect <IP> [port] [password]');
5797 Exit;
5798 end;
5799 if P[1] = '' then
5800 begin
5801 g_Console_Add('connect <IP> [port] [password]');
5802 Exit;
5803 end;
5805 if Length(P) > 2 then
5806 prt := StrToIntDef(P[2], 25666)
5807 else
5808 prt := 25666;
5810 if Length(P) > 3 then
5811 pw := P[3]
5812 else
5813 pw := '';
5815 g_Game_StartClient(P[1], prt, pw);
5816 end;
5817 end
5818 else if cmd = 'disconnect' then
5819 begin
5820 if (NetMode = NET_CLIENT) then
5821 g_Net_Disconnect();
5822 end
5823 else if cmd = 'reconnect' then
5824 begin
5825 if (NetMode = NET_SERVER) then
5826 Exit;
5828 if (NetMode = NET_CLIENT) then
5829 begin
5830 g_Net_Disconnect();
5831 gExit := EXIT_SIMPLE;
5832 EndGame;
5833 end;
5835 //TODO: Use last successful password to reconnect, instead of ''
5836 g_Game_StartClient(NetClientIP, NetClientPort, '');
5837 end
5838 else if (cmd = 'addbot') or
5839 (cmd = 'bot_add') then
5840 begin
5841 if Length(P) > 1 then
5842 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5843 else
5844 g_Bot_Add(TEAM_NONE, 2);
5845 end
5846 else if cmd = 'bot_addlist' then
5847 begin
5848 if Length(P) > 1 then
5849 if Length(P) = 2 then
5850 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5851 else
5852 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5853 end
5854 else if cmd = 'bot_removeall' then
5855 g_Bot_RemoveAll()
5856 else if cmd = 'chat' then
5857 begin
5858 if g_Game_IsNet then
5859 begin
5860 if Length(P) > 1 then
5861 begin
5862 for a := 1 to High(P) do
5863 chstr := chstr + P[a] + ' ';
5865 if Length(chstr) > 200 then SetLength(chstr, 200);
5867 if Length(chstr) < 1 then
5868 begin
5869 g_Console_Add('chat <text>');
5870 Exit;
5871 end;
5873 chstr := b_Text_Format(chstr);
5874 if g_Game_IsClient then
5875 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5876 else
5877 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5878 end
5879 else
5880 g_Console_Add('chat <text>');
5881 end else
5882 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5883 end
5884 else if cmd = 'teamchat' then
5885 begin
5886 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5887 begin
5888 if Length(P) > 1 then
5889 begin
5890 for a := 1 to High(P) do
5891 chstr := chstr + P[a] + ' ';
5893 if Length(chstr) > 200 then SetLength(chstr, 200);
5895 if Length(chstr) < 1 then
5896 begin
5897 g_Console_Add('teamchat <text>');
5898 Exit;
5899 end;
5901 chstr := b_Text_Format(chstr);
5902 if g_Game_IsClient then
5903 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5904 else
5905 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5906 gPlayer1Settings.Team);
5907 end
5908 else
5909 g_Console_Add('teamchat <text>');
5910 end else
5911 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5912 end
5913 else if cmd = 'game' then
5914 begin
5915 if gGameSettings.GameType <> GT_NONE then
5916 begin
5917 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5918 Exit;
5919 end;
5920 if Length(P) = 1 then
5921 begin
5922 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5923 Exit;
5924 end;
5925 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5926 P[1] := addWadExtension(P[1]);
5927 if FileExists(MapsDir + P[1]) then
5928 begin
5929 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5930 if Length(P) < 3 then
5931 begin
5932 SetLength(P, 3);
5933 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5934 end;
5936 s := P[1] + ':\' + UpperCase(P[2]);
5938 if g_Map_Exist(MapsDir + s) then
5939 begin
5940 // Çàïóñêàåì ñâîþ èãðó
5941 g_Game_Free();
5942 with gGameSettings do
5943 begin
5944 GameMode := g_Game_TextToMode(gcGameMode);
5945 if gSwitchGameMode <> GM_NONE then
5946 GameMode := gSwitchGameMode;
5947 if GameMode = GM_NONE then GameMode := GM_DM;
5948 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5949 b := 1;
5950 if Length(P) >= 4 then
5951 b := StrToIntDef(P[3], 1);
5952 g_Game_StartCustom(s, GameMode, TimeLimit,
5953 GoalLimit, MaxLives, Options, b);
5954 end;
5955 end
5956 else
5957 if P[2] = '' then
5958 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5959 else
5960 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5961 end else
5962 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5963 end
5964 else if cmd = 'host' then
5965 begin
5966 if gGameSettings.GameType <> GT_NONE then
5967 begin
5968 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5969 Exit;
5970 end;
5971 if Length(P) < 4 then
5972 begin
5973 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5974 Exit;
5975 end;
5976 if not StrToIp(P[1], listen) then
5977 Exit;
5978 prt := StrToIntDef(P[2], 25666);
5980 P[3] := addWadExtension(P[3]);
5981 if FileExists(MapsDir + P[3]) then
5982 begin
5983 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5984 if Length(P) < 5 then
5985 begin
5986 SetLength(P, 5);
5987 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5988 end;
5990 s := P[3] + ':\' + UpperCase(P[4]);
5992 if g_Map_Exist(MapsDir + s) then
5993 begin
5994 // Çàïóñêàåì ñâîþ èãðó
5995 g_Game_Free();
5996 with gGameSettings do
5997 begin
5998 GameMode := g_Game_TextToMode(gcGameMode);
5999 if gSwitchGameMode <> GM_NONE then
6000 GameMode := gSwitchGameMode;
6001 if GameMode = GM_NONE then GameMode := GM_DM;
6002 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6003 b := 0;
6004 if Length(P) >= 6 then
6005 b := StrToIntDef(P[5], 0);
6006 g_Game_StartServer(s, GameMode, TimeLimit,
6007 GoalLimit, MaxLives, Options, b, listen, prt);
6008 end;
6009 end
6010 else
6011 if P[4] = '' then
6012 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6013 else
6014 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
6015 end else
6016 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6017 end
6018 else if cmd = 'map' then
6019 begin
6020 if Length(P) = 1 then
6021 begin
6022 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6023 begin
6024 g_Console_Add(cmd + ' <MAP>');
6025 g_Console_Add(cmd + ' <WAD> [MAP]');
6026 end else
6027 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6028 end else
6029 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6030 begin
6031 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6032 if Length(P) < 3 then
6033 begin
6034 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6035 s := UpperCase(P[1]);
6036 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6037 begin // Êàðòà íàøëàñü
6038 gExitByTrigger := False;
6039 if gGameOn then
6040 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6041 gNextMap := s;
6042 gExit := EXIT_ENDLEVELCUSTOM;
6043 end
6044 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6045 g_Game_ChangeMap(s);
6046 end else
6047 begin
6048 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6049 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6050 P[1] := addWadExtension(P[1]);
6051 if FileExists(MapsDir + P[1]) then
6052 begin
6053 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6054 SetLength(P, 3);
6055 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6057 s := P[1] + ':\' + P[2];
6059 if g_Map_Exist(MapsDir + s) then
6060 begin
6061 gExitByTrigger := False;
6062 if gGameOn then
6063 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6064 gNextMap := s;
6065 gExit := EXIT_ENDLEVELCUSTOM;
6066 end
6067 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6068 g_Game_ChangeMap(s);
6069 end else
6070 if P[2] = '' then
6071 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6072 else
6073 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6074 end else
6075 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6076 end;
6077 end else
6078 begin
6079 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6080 P[1] := addWadExtension(P[1]);
6081 if FileExists(MapsDir + P[1]) then
6082 begin
6083 // Íàøëè WAD ôàéë
6084 P[2] := UpperCase(P[2]);
6085 s := P[1] + ':\' + P[2];
6087 if g_Map_Exist(MapsDir + s) then
6088 begin // Íàøëè êàðòó
6089 gExitByTrigger := False;
6090 if gGameOn then
6091 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6092 gNextMap := s;
6093 gExit := EXIT_ENDLEVELCUSTOM;
6094 end
6095 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6096 g_Game_ChangeMap(s);
6097 end else
6098 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6099 end else
6100 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6101 end;
6102 end else
6103 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6104 end
6105 else if cmd = 'nextmap' then
6106 begin
6107 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6108 g_Console_Add(_lc[I_MSG_NOT_GAME])
6109 else begin
6110 nm := True;
6111 if Length(P) = 1 then
6112 begin
6113 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6114 begin
6115 g_Console_Add(cmd + ' <MAP>');
6116 g_Console_Add(cmd + ' <WAD> [MAP]');
6117 end else begin
6118 nm := False;
6119 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6120 end;
6121 end else
6122 begin
6123 nm := False;
6124 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6125 begin
6126 if Length(P) < 3 then
6127 begin
6128 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6129 s := UpperCase(P[1]);
6130 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6131 begin // Êàðòà íàøëàñü
6132 gExitByTrigger := False;
6133 gNextMap := s;
6134 nm := True;
6135 end else
6136 begin
6137 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6138 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6139 P[1] := addWadExtension(P[1]);
6140 if FileExists(MapsDir + P[1]) then
6141 begin
6142 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6143 SetLength(P, 3);
6144 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6146 s := P[1] + ':\' + P[2];
6148 if g_Map_Exist(MapsDir + s) then
6149 begin // Óñòàíàâëèâàåì êàðòó
6150 gExitByTrigger := False;
6151 gNextMap := s;
6152 nm := True;
6153 end else
6154 if P[2] = '' then
6155 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6156 else
6157 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6158 end else
6159 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6160 end;
6161 end else
6162 begin
6163 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6164 P[1] := addWadExtension(P[1]);
6165 if FileExists(MapsDir + P[1]) then
6166 begin
6167 // Íàøëè WAD ôàéë
6168 P[2] := UpperCase(P[2]);
6169 s := P[1] + ':\' + P[2];
6171 if g_Map_Exist(MapsDir + s) then
6172 begin // Íàøëè êàðòó
6173 gExitByTrigger := False;
6174 gNextMap := s;
6175 nm := True;
6176 end else
6177 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6178 end else
6179 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6180 end;
6181 end else
6182 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6183 end;
6184 if nm then
6185 if gNextMap = '' then
6186 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6187 else
6188 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6189 end;
6190 end
6191 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6192 begin
6193 if not gGameOn then
6194 g_Console_Add(_lc[I_MSG_NOT_GAME])
6195 else
6196 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6197 begin
6198 gExitByTrigger := False;
6199 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6200 if (gNextMap = '') and (gTriggers <> nil) then
6201 for a := 0 to High(gTriggers) do
6202 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6203 begin
6204 gExitByTrigger := True;
6205 //gNextMap := gTriggers[a].Data.MapName;
6206 gNextMap := gTriggers[a].tgcMap;
6207 Break;
6208 end;
6209 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6210 if gNextMap = '' then
6211 gNextMap := g_Game_GetNextMap();
6212 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6213 if not isWadPath(gNextMap) then
6214 s := gGameSettings.WAD + ':\' + gNextMap
6215 else
6216 s := gNextMap;
6217 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6218 if g_Map_Exist(MapsDir + s) then
6219 gExit := EXIT_ENDLEVELCUSTOM
6220 else
6221 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6222 end else
6223 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6224 end
6225 else if (cmd = 'event') then
6226 begin
6227 if (Length(P) <= 1) then
6228 begin
6229 for a := 0 to High(gEvents) do
6230 if gEvents[a].Command = '' then
6231 g_Console_Add(gEvents[a].Name + ' <none>')
6232 else
6233 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6234 Exit;
6235 end;
6236 if (Length(P) = 2) then
6237 begin
6238 for a := 0 to High(gEvents) do
6239 if gEvents[a].Name = P[1] then
6240 if gEvents[a].Command = '' then
6241 g_Console_Add(gEvents[a].Name + ' <none>')
6242 else
6243 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6244 Exit;
6245 end;
6246 for a := 0 to High(gEvents) do
6247 if gEvents[a].Name = P[1] then
6248 begin
6249 gEvents[a].Command := '';
6250 for b := 2 to High(P) do
6251 if Pos(' ', P[b]) = 0 then
6252 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6253 else
6254 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6255 gEvents[a].Command := Trim(gEvents[a].Command);
6256 Exit;
6257 end;
6258 end
6259 else if cmd = 'suicide' then
6260 begin
6261 if gGameOn then
6262 begin
6263 if g_Game_IsClient then
6264 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6265 else
6266 begin
6267 if gPlayer1 <> nil then
6268 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6269 if gPlayer2 <> nil then
6270 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6271 end;
6272 end;
6273 end
6274 // Êîìàíäû Ñâîåé èãðû:
6275 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6276 begin
6277 if cmd = 'bot_addred' then
6278 begin
6279 if Length(P) > 1 then
6280 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6281 else
6282 g_Bot_Add(TEAM_RED, 2);
6283 end
6284 else if cmd = 'bot_addblue' then
6285 begin
6286 if Length(P) > 1 then
6287 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6288 else
6289 g_Bot_Add(TEAM_BLUE, 2);
6290 end
6291 else if cmd = 'spectate' then
6292 begin
6293 if not gGameOn then
6294 Exit;
6295 g_Game_Spectate();
6296 end
6297 else if cmd = 'say' then
6298 begin
6299 if g_Game_IsServer and g_Game_IsNet then
6300 begin
6301 if Length(P) > 1 then
6302 begin
6303 chstr := '';
6304 for a := 1 to High(P) do
6305 chstr := chstr + P[a] + ' ';
6307 if Length(chstr) > 200 then SetLength(chstr, 200);
6309 if Length(chstr) < 1 then
6310 begin
6311 g_Console_Add('say <text>');
6312 Exit;
6313 end;
6315 chstr := b_Text_Format(chstr);
6316 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6317 end
6318 else g_Console_Add('say <text>');
6319 end else
6320 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6321 end
6322 else if cmd = 'tell' then
6323 begin
6324 if g_Game_IsServer and g_Game_IsNet then
6325 begin
6326 if (Length(P) > 2) and (P[1] <> '') then
6327 begin
6328 chstr := '';
6329 for a := 2 to High(P) do
6330 chstr := chstr + P[a] + ' ';
6332 if Length(chstr) > 200 then SetLength(chstr, 200);
6334 if Length(chstr) < 1 then
6335 begin
6336 g_Console_Add('tell <playername> <text>');
6337 Exit;
6338 end;
6340 pl := g_Net_Client_ByName(P[1]);
6341 if pl <> nil then
6342 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6343 else
6344 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6345 end
6346 else g_Console_Add('tell <playername> <text>');
6347 end else
6348 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6349 end
6350 else if (cmd = 'overtime') and not g_Game_IsClient then
6351 begin
6352 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6353 Exit;
6354 // Äîïîëíèòåëüíîå âðåìÿ:
6355 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6357 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6358 [gGameSettings.TimeLimit div 3600,
6359 (gGameSettings.TimeLimit div 60) mod 60,
6360 gGameSettings.TimeLimit mod 60]));
6361 if g_Game_IsNet then MH_SEND_GameSettings;
6362 end
6363 else if (cmd = 'rcon_password') and g_Game_IsClient then
6364 begin
6365 if (Length(P) <= 1) then
6366 g_Console_Add('rcon_password <password>')
6367 else
6368 MC_SEND_RCONPassword(P[1]);
6369 end
6370 else if cmd = 'rcon' then
6371 begin
6372 if g_Game_IsClient then
6373 begin
6374 if Length(P) > 1 then
6375 begin
6376 chstr := '';
6377 for a := 1 to High(P) do
6378 chstr := chstr + P[a] + ' ';
6380 if Length(chstr) > 200 then SetLength(chstr, 200);
6382 if Length(chstr) < 1 then
6383 begin
6384 g_Console_Add('rcon <command>');
6385 Exit;
6386 end;
6388 MC_SEND_RCONCommand(chstr);
6389 end
6390 else g_Console_Add('rcon <command>');
6391 end;
6392 end
6393 else if cmd = 'ready' then
6394 begin
6395 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6396 gLMSRespawnTime := gTime + 100;
6397 end
6398 else if (cmd = 'callvote') and g_Game_IsNet then
6399 begin
6400 if Length(P) > 1 then
6401 begin
6402 chstr := '';
6403 for a := 1 to High(P) do begin
6404 if a > 1 then chstr := chstr + ' ';
6405 chstr := chstr + P[a];
6406 end;
6408 if Length(chstr) > 200 then SetLength(chstr, 200);
6410 if Length(chstr) < 1 then
6411 begin
6412 g_Console_Add('callvote <command>');
6413 Exit;
6414 end;
6416 if g_Game_IsClient then
6417 MC_SEND_Vote(True, chstr)
6418 else
6419 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6420 g_Console_Process('vote', True);
6421 end
6422 else
6423 g_Console_Add('callvote <command>');
6424 end
6425 else if (cmd = 'vote') and g_Game_IsNet then
6426 begin
6427 if g_Game_IsClient then
6428 MC_SEND_Vote(False)
6429 else if gVoteInProgress then
6430 begin
6431 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6432 a := Floor((NetClientCount+1)/2.0) + 1
6433 else
6434 a := Floor(NetClientCount/2.0) + 1;
6435 if gVoted then
6436 begin
6437 Dec(gVoteCount);
6438 gVoted := False;
6439 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6440 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6441 end
6442 else
6443 begin
6444 Inc(gVoteCount);
6445 gVoted := True;
6446 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6447 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6448 g_Game_CheckVote;
6449 end;
6450 end;
6451 end
6452 end;
6453 end;
6455 procedure g_TakeScreenShot();
6456 var
6457 a: Word;
6458 FileName: string;
6459 ssdir, t: string;
6460 st: TStream;
6461 ok: Boolean;
6462 begin
6463 if e_NoGraphics then Exit;
6464 ssdir := GameDir+'/screenshots';
6465 if not findFileCI(ssdir, true) then
6466 begin
6467 // try to create dir
6468 try
6469 CreateDir(ssdir);
6470 except
6471 end;
6472 if not findFileCI(ssdir, true) then exit; // alas
6473 end;
6474 try
6475 for a := 1 to High(Word) do
6476 begin
6477 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6478 t := FileName;
6479 if findFileCI(t, true) then continue;
6480 if not findFileCI(FileName) then
6481 begin
6482 ok := false;
6483 st := createDiskFile(FileName);
6484 try
6485 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6486 ok := true;
6487 finally
6488 st.Free();
6489 end;
6490 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6491 break;
6492 end;
6493 end;
6494 except
6495 end;
6496 end;
6498 procedure g_Game_InGameMenu(Show: Boolean);
6499 begin
6500 if (g_ActiveWindow = nil) and Show then
6501 begin
6502 if gGameSettings.GameType = GT_SINGLE then
6503 g_GUI_ShowWindow('GameSingleMenu')
6504 else
6505 begin
6506 if g_Game_IsClient then
6507 g_GUI_ShowWindow('GameClientMenu')
6508 else
6509 if g_Game_IsNet then
6510 g_GUI_ShowWindow('GameServerMenu')
6511 else
6512 g_GUI_ShowWindow('GameCustomMenu');
6513 end;
6514 g_Sound_PlayEx('MENU_OPEN');
6516 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6517 if (not g_Game_IsNet) then
6518 g_Game_Pause(True);
6519 end
6520 else
6521 if (g_ActiveWindow <> nil) and (not Show) then
6522 begin
6523 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6524 if (not g_Game_IsNet) then
6525 g_Game_Pause(False);
6526 end;
6527 end;
6529 procedure g_Game_Pause(Enable: Boolean);
6530 begin
6531 if not gGameOn then
6532 Exit;
6534 if gPause = Enable then
6535 Exit;
6537 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6538 Exit;
6540 gPause := Enable;
6541 g_Game_PauseAllSounds(Enable);
6542 end;
6544 procedure g_Game_PauseAllSounds(Enable: Boolean);
6545 var
6546 i: Integer;
6547 begin
6548 // Òðèããåðû:
6549 if gTriggers <> nil then
6550 for i := 0 to High(gTriggers) do
6551 with gTriggers[i] do
6552 if (TriggerType = TRIGGER_SOUND) and
6553 (Sound <> nil) and
6554 Sound.IsPlaying() then
6555 begin
6556 Sound.Pause(Enable);
6557 end;
6559 // Çâóêè èãðîêîâ:
6560 if gPlayers <> nil then
6561 for i := 0 to High(gPlayers) do
6562 if gPlayers[i] <> nil then
6563 gPlayers[i].PauseSounds(Enable);
6565 // Ìóçûêà:
6566 if gMusic <> nil then
6567 gMusic.Pause(Enable);
6568 end;
6570 procedure g_Game_StopAllSounds(all: Boolean);
6571 var
6572 i: Integer;
6573 begin
6574 if gTriggers <> nil then
6575 for i := 0 to High(gTriggers) do
6576 with gTriggers[i] do
6577 if (TriggerType = TRIGGER_SOUND) and
6578 (Sound <> nil) then
6579 Sound.Stop();
6581 if gMusic <> nil then
6582 gMusic.Stop();
6584 if all then
6585 e_StopChannels();
6586 end;
6588 procedure g_Game_UpdateTriggerSounds();
6589 var
6590 i: Integer;
6591 begin
6592 if gTriggers <> nil then
6593 for i := 0 to High(gTriggers) do
6594 with gTriggers[i] do
6595 if (TriggerType = TRIGGER_SOUND) and
6596 (Sound <> nil) and
6597 (tgcLocal) and
6598 Sound.IsPlaying() then
6599 begin
6600 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6601 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6602 begin
6603 Sound.SetPan(0.5 - tgcPan/255.0);
6604 Sound.SetVolume(tgcVolume/255.0);
6605 end
6606 else
6607 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
6608 end;
6609 end;
6611 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6612 begin
6613 Result := False;
6614 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6615 begin
6616 Result := True;
6617 Exit;
6618 end;
6619 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6620 begin
6621 Result := True;
6622 Exit;
6623 end;
6624 if gSpectMode <> SPECT_PLAYERS then
6625 Exit;
6626 if gSpectPID1 = UID then
6627 begin
6628 Result := True;
6629 Exit;
6630 end;
6631 if gSpectViewTwo and (gSpectPID2 = UID) then
6632 begin
6633 Result := True;
6634 Exit;
6635 end;
6636 end;
6638 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6639 var
6640 Pl: TPlayer;
6641 begin
6642 Result := False;
6643 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6644 begin
6645 Result := True;
6646 Exit;
6647 end;
6648 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6649 begin
6650 Result := True;
6651 Exit;
6652 end;
6653 if gSpectMode <> SPECT_PLAYERS then
6654 Exit;
6655 Pl := g_Player_Get(gSpectPID1);
6656 if (Pl <> nil) and (Pl.Team = Team) then
6657 begin
6658 Result := True;
6659 Exit;
6660 end;
6661 if gSpectViewTwo then
6662 begin
6663 Pl := g_Player_Get(gSpectPID2);
6664 if (Pl <> nil) and (Pl.Team = Team) then
6665 begin
6666 Result := True;
6667 Exit;
6668 end;
6669 end;
6670 end;
6672 procedure g_Game_Message(Msg: string; Time: Word);
6673 begin
6674 MessageText := b_Text_Format(Msg);
6675 MessageTime := Time;
6676 end;
6678 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6679 var
6680 a: Integer;
6681 begin
6682 case gAnnouncer of
6683 ANNOUNCE_NONE:
6684 Exit;
6685 ANNOUNCE_ME,
6686 ANNOUNCE_MEPLUS:
6687 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6688 Exit;
6689 end;
6690 for a := 0 to 3 do
6691 if goodsnd[a].IsPlaying() then
6692 Exit;
6694 goodsnd[Random(4)].Play();
6695 end;
6697 procedure g_Game_Announce_KillCombo(Param: Integer);
6698 var
6699 UID: Word;
6700 c, n: Byte;
6701 Pl: TPlayer;
6702 Name: String;
6703 begin
6704 UID := Param and $FFFF;
6705 c := Param shr 16;
6706 if c < 2 then
6707 Exit;
6709 Pl := g_Player_Get(UID);
6710 if Pl = nil then
6711 Name := '?'
6712 else
6713 Name := Pl.Name;
6715 case c of
6716 2: begin
6717 n := 0;
6718 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6719 end;
6720 3: begin
6721 n := 1;
6722 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6723 end;
6724 4: begin
6725 n := 2;
6726 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6727 end;
6728 else begin
6729 n := 3;
6730 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6731 end;
6732 end;
6734 case gAnnouncer of
6735 ANNOUNCE_NONE:
6736 Exit;
6737 ANNOUNCE_ME:
6738 if not g_Game_IsWatchedPlayer(UID) then
6739 Exit;
6740 ANNOUNCE_MEPLUS:
6741 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6742 Exit;
6743 end;
6745 if killsnd[n].IsPlaying() then
6746 killsnd[n].Stop();
6747 killsnd[n].Play();
6748 end;
6750 procedure g_Game_StartVote(Command, Initiator: string);
6751 var
6752 Need: Integer;
6753 begin
6754 if not gVotesEnabled then Exit;
6755 if gGameSettings.GameType <> GT_SERVER then Exit;
6756 if gVoteInProgress or gVotePassed then
6757 begin
6758 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6759 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6760 Exit;
6761 end;
6762 gVoteInProgress := True;
6763 gVotePassed := False;
6764 gVoteTimer := gTime + gVoteTimeout * 1000;
6765 gVoteCount := 0;
6766 gVoted := False;
6767 gVoteCommand := Command;
6769 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6770 Need := Floor((NetClientCount+1)/2.0)+1
6771 else
6772 Need := Floor(NetClientCount/2.0)+1;
6773 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6774 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6775 end;
6777 procedure g_Game_CheckVote;
6778 var
6779 I, Need: Integer;
6780 begin
6781 if gGameSettings.GameType <> GT_SERVER then Exit;
6782 if not gVoteInProgress then Exit;
6784 if (gTime >= gVoteTimer) then
6785 begin
6786 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6787 Need := Floor((NetClientCount+1)/2.0) + 1
6788 else
6789 Need := Floor(NetClientCount/2.0) + 1;
6790 if gVoteCount >= Need then
6791 begin
6792 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6793 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6794 gVotePassed := True;
6795 gVoteCmdTimer := gTime + 5000;
6796 end
6797 else
6798 begin
6799 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6800 MH_SEND_VoteEvent(NET_VE_FAILED);
6801 end;
6802 if NetClients <> nil then
6803 for I := Low(NetClients) to High(NetClients) do
6804 if NetClients[i].Used then
6805 NetClients[i].Voted := False;
6806 gVoteInProgress := False;
6807 gVoted := False;
6808 gVoteCount := 0;
6809 end
6810 else
6811 begin
6812 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6813 Need := Floor((NetClientCount+1)/2.0) + 1
6814 else
6815 Need := Floor(NetClientCount/2.0) + 1;
6816 if gVoteCount >= Need then
6817 begin
6818 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6819 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6820 gVoteInProgress := False;
6821 gVotePassed := True;
6822 gVoteCmdTimer := gTime + 5000;
6823 gVoted := False;
6824 gVoteCount := 0;
6825 if NetClients <> nil then
6826 for I := Low(NetClients) to High(NetClients) do
6827 if NetClients[i].Used then
6828 NetClients[i].Voted := False;
6829 end;
6830 end;
6831 end;
6833 procedure g_Game_LoadMapList(FileName: string);
6834 var
6835 ListFile: TextFile;
6836 s: string;
6837 begin
6838 MapList := nil;
6839 MapIndex := -1;
6841 if not FileExists(FileName) then Exit;
6843 AssignFile(ListFile, FileName);
6844 Reset(ListFile);
6845 while not EOF(ListFile) do
6846 begin
6847 ReadLn(ListFile, s);
6849 s := Trim(s);
6850 if s = '' then Continue;
6852 SetLength(MapList, Length(MapList)+1);
6853 MapList[High(MapList)] := s;
6854 end;
6855 CloseFile(ListFile);
6856 end;
6858 procedure g_Game_SetDebugMode();
6859 begin
6860 gDebugMode := True;
6861 // ×èòû (äàæå â ñâîåé èãðå):
6862 gCheats := True;
6863 end;
6865 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6866 var
6867 i: Word;
6868 begin
6869 if Length(LoadingStat.Msgs) = 0 then
6870 Exit;
6872 with LoadingStat do
6873 begin
6874 if not reWrite then
6875 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6876 if NextMsg = Length(Msgs) then
6877 begin // scroll
6878 for i := 0 to High(Msgs)-1 do
6879 Msgs[i] := Msgs[i+1];
6880 end
6881 else
6882 Inc(NextMsg);
6883 end else
6884 if NextMsg = 0 then
6885 Inc(NextMsg);
6887 Msgs[NextMsg-1] := Text;
6888 CurValue := 0;
6889 MaxValue := Max;
6890 ShowCount := 0;
6891 end;
6893 g_ActiveWindow := nil;
6895 ProcessLoading(true);
6896 end;
6898 procedure g_Game_StepLoading();
6899 begin
6900 with LoadingStat do
6901 begin
6902 Inc(CurValue);
6903 Inc(ShowCount);
6904 if (ShowCount > LOADING_SHOW_STEP) then
6905 begin
6906 ShowCount := 0;
6907 ProcessLoading();
6908 end;
6909 end;
6910 end;
6912 procedure g_Game_ClearLoading();
6913 var
6914 len: Word;
6915 begin
6916 with LoadingStat do
6917 begin
6918 CurValue := 0;
6919 MaxValue := 0;
6920 ShowCount := 0;
6921 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6922 if len < 1 then len := 1;
6923 SetLength(Msgs, len);
6924 for len := Low(Msgs) to High(Msgs) do
6925 Msgs[len] := '';
6926 NextMsg := 0;
6927 end;
6928 end;
6930 procedure Parse_Params(var pars: TParamStrValues);
6931 var
6932 i: Integer;
6933 s: String;
6934 begin
6935 SetLength(pars, 0);
6936 i := 1;
6937 while i <= ParamCount do
6938 begin
6939 s := ParamStr(i);
6940 if (s[1] = '-') and (Length(s) > 1) then
6941 begin
6942 if (s[2] = '-') and (Length(s) > 2) then
6943 begin // Îäèíî÷íûé ïàðàìåòð
6944 SetLength(pars, Length(pars) + 1);
6945 with pars[High(pars)] do
6946 begin
6947 Name := LowerCase(s);
6948 Value := '+';
6949 end;
6950 end
6951 else
6952 if (i < ParamCount) then
6953 begin // Ïàðàìåòð ñî çíà÷åíèåì
6954 Inc(i);
6955 SetLength(pars, Length(pars) + 1);
6956 with pars[High(pars)] do
6957 begin
6958 Name := LowerCase(s);
6959 Value := LowerCase(ParamStr(i));
6960 end;
6961 end;
6962 end;
6964 Inc(i);
6965 end;
6966 end;
6968 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6969 var
6970 i: Integer;
6971 begin
6972 Result := '';
6973 for i := 0 to High(pars) do
6974 if pars[i].Name = aName then
6975 begin
6976 Result := pars[i].Value;
6977 Break;
6978 end;
6979 end;
6981 procedure g_Game_Process_Params();
6982 var
6983 pars: TParamStrValues;
6984 map: String;
6985 GMode, n: Byte;
6986 LimT, LimS: Integer;
6987 Opt: LongWord;
6988 Lives: Integer;
6989 s: String;
6990 Port: Integer;
6991 ip: String;
6992 F: TextFile;
6993 begin
6994 Parse_Params(pars);
6996 // Debug mode:
6997 s := Find_Param_Value(pars, '--debug');
6998 if (s <> '') then
6999 begin
7000 g_Game_SetDebugMode();
7001 s := Find_Param_Value(pars, '--netdump');
7002 if (s <> '') then
7003 NetDump := True;
7004 end;
7006 // Connect when game loads
7007 ip := Find_Param_Value(pars, '-connect');
7009 if ip <> '' then
7010 begin
7011 s := Find_Param_Value(pars, '-port');
7012 if (s = '') or not TryStrToInt(s, Port) then
7013 Port := 25666;
7015 s := Find_Param_Value(pars, '-pw');
7017 g_Game_StartClient(ip, Port, s);
7018 Exit;
7019 end;
7021 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7022 if (s <> '') then
7023 begin
7024 gDefaultMegawadStart := s;
7025 end;
7027 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7028 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7029 begin
7030 gDefaultMegawadStart := DF_Default_Megawad_Start;
7031 end;
7033 // Start map when game loads:
7034 map := LowerCase(Find_Param_Value(pars, '-map'));
7035 if isWadPath(map) then
7036 begin
7037 // Game mode:
7038 s := Find_Param_Value(pars, '-gm');
7039 GMode := g_Game_TextToMode(s);
7040 if GMode = GM_NONE then GMode := GM_DM;
7041 if GMode = GM_SINGLE then GMode := GM_COOP;
7043 // Time limit:
7044 s := Find_Param_Value(pars, '-limt');
7045 if (s = '') or (not TryStrToInt(s, LimT)) then
7046 LimT := 0;
7047 if LimT < 0 then
7048 LimT := 0;
7050 // Goal limit:
7051 s := Find_Param_Value(pars, '-lims');
7052 if (s = '') or (not TryStrToInt(s, LimS)) then
7053 LimS := 0;
7054 if LimS < 0 then
7055 LimS := 0;
7057 // Lives limit:
7058 s := Find_Param_Value(pars, '-lives');
7059 if (s = '') or (not TryStrToInt(s, Lives)) then
7060 Lives := 0;
7061 if Lives < 0 then
7062 Lives := 0;
7064 // Options:
7065 s := Find_Param_Value(pars, '-opt');
7066 if (s = '') then
7067 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7068 else
7069 Opt := StrToIntDef(s, 0);
7070 if Opt = 0 then
7071 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7073 // Close after map:
7074 s := Find_Param_Value(pars, '--close');
7075 if (s <> '') then
7076 gMapOnce := True;
7078 // Delete test map after play:
7079 s := Find_Param_Value(pars, '--testdelete');
7080 if (s <> '') then
7081 begin
7082 gMapToDelete := MapsDir + map;
7083 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
7084 Halt(1);
7085 end;
7087 // Delete temporary WAD after play:
7088 s := Find_Param_Value(pars, '--tempdelete');
7089 if (s <> '') then
7090 begin
7091 gMapToDelete := MapsDir + map;
7092 gTempDelete := True;
7093 end;
7095 // Number of players:
7096 s := Find_Param_Value(pars, '-pl');
7097 if (s = '') then
7098 n := 1
7099 else
7100 n := StrToIntDef(s, 1);
7102 // Start:
7103 s := Find_Param_Value(pars, '-port');
7104 if (s = '') or not TryStrToInt(s, Port) then
7105 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7106 else
7107 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7108 end;
7110 // Execute script when game loads:
7111 s := Find_Param_Value(pars, '-exec');
7112 if s <> '' then
7113 begin
7114 if not isWadPath(s) then
7115 s := GameDir + '/' + s;
7117 {$I-}
7118 AssignFile(F, s);
7119 Reset(F);
7120 if IOResult <> 0 then
7121 begin
7122 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7123 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7124 CloseFile(F);
7125 Exit;
7126 end;
7127 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
7128 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7130 while not EOF(F) do
7131 begin
7132 ReadLn(F, s);
7133 if IOResult <> 0 then
7134 begin
7135 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7136 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7137 CloseFile(F);
7138 Exit;
7139 end;
7140 if Pos('#', s) <> 1 then // script comment
7141 g_Console_Process(s, True);
7142 end;
7144 CloseFile(F);
7145 {$I+}
7146 end;
7148 SetLength(pars, 0);
7149 end;
7151 begin
7152 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7153 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7154 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7155 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7157 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7158 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7159 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7160 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7162 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7163 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7165 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7166 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7168 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7170 conRegVar('dbg_scale', @g_dbg_scale, 0.01, 5.0, 'experimental deBUG scale mode', '', true);
7171 end.