DEADSOFTWARE

render monster drop after monsters, so monster corpses will not obscure ammo clips...
[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 drawOther('itemdrop', @g_Items_DrawDrop);
2943 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
2944 drawOther('gfx', @g_GFX_Draw);
2945 drawOther('flags', @g_Map_DrawFlags);
2946 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
2947 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
2948 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
2949 drawOther('dynlights', @renderDynLightsInternal);
2951 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
2952 begin
2953 renderAmbientQuad(hasAmbient, ambColor);
2954 end;
2956 doAmbient := true;
2957 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
2960 if g_debug_HealthBar then
2961 begin
2962 g_Monsters_DrawHealth();
2963 g_Player_DrawHealth();
2964 end;
2966 profileFrameDraw.mainEnd(); // map rendering
2967 end;
2970 procedure DrawMapView(x, y, w, h: Integer);
2972 var
2973 bx, by: Integer;
2974 begin
2975 glPushMatrix();
2977 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2978 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2980 sX := x;
2981 sY := y;
2982 sWidth := w;
2983 sHeight := h;
2985 fixViewportForScale();
2986 renderMapInternal(-bx, -by, true);
2988 glPopMatrix();
2989 end;
2992 procedure DrawPlayer(p: TPlayer);
2993 var
2994 px, py, a, b, c, d: Integer;
2995 //R: TRect;
2996 begin
2997 if (p = nil) or (p.FDummy) then
2998 begin
2999 glPushMatrix();
3000 g_Map_DrawBack(0, 0);
3001 glPopMatrix();
3002 Exit;
3003 end;
3005 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3006 profileFrameDraw.mainBegin(g_profile_frame_draw);
3008 gPlayerDrawn := p;
3010 glPushMatrix();
3012 px := p.GameX + PLAYER_RECT_CX;
3013 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
3015 if (g_dbg_scale = 1.0) then
3016 begin
3017 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3018 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3020 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3021 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3023 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3024 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3025 begin
3026 // hcenter
3027 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3028 end;
3030 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3031 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3032 begin
3033 // vcenter
3034 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3035 end;
3036 end
3037 else
3038 begin
3039 // scaled, ignore level bounds
3040 a := -px+(gPlayerScreenSize.X div 2);
3041 b := -py+(gPlayerScreenSize.Y div 2);
3042 end;
3044 if p.IncCam <> 0 then
3045 begin
3046 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
3047 begin
3048 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
3049 begin
3050 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
3051 end;
3052 end;
3054 if py < gPlayerScreenSize.Y div 2 then
3055 begin
3056 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
3057 begin
3058 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
3059 end;
3060 end;
3062 if p.IncCam < 0 then
3063 begin
3064 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
3065 end;
3067 if p.IncCam > 0 then
3068 begin
3069 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
3070 end;
3071 end;
3073 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
3074 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
3075 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
3077 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
3078 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
3079 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
3081 sX := -a;
3082 sY := -(b+p.IncCam);
3083 sWidth := gPlayerScreenSize.X;
3084 sHeight := gPlayerScreenSize.Y;
3086 //glTranslatef(a, b+p.IncCam, 0);
3088 if (p = gPlayer1) then g_Holmes_plrViewSize(sWidth, sHeight);
3090 fixViewportForScale();
3091 p.viewPortX := sX;
3092 p.viewPortY := sY;
3093 p.viewPortW := sWidth;
3094 p.viewPortH := sHeight;
3096 if (p = gPlayer1) then g_Holmes_plrViewPos(sX, sY);
3098 renderMapInternal(-c, -d, true);
3100 if p.FSpectator then
3101 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3102 p.GameY + PLAYER_RECT_CY - 4,
3103 'X', gStdFont, 255, 255, 255, 1, True);
3105 for a := 0 to High(gCollideMap) do
3106 for b := 0 to High(gCollideMap[a]) do
3107 begin
3108 d := 0;
3109 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3110 d := d + 1;
3111 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3112 d := d + 2;
3114 case d of
3115 1: e_DrawPoint(1, b, a, 200, 200, 200);
3116 2: e_DrawPoint(1, b, a, 64, 64, 255);
3117 3: e_DrawPoint(1, b, a, 255, 0, 255);
3118 end;
3119 end;
3122 glPopMatrix();
3124 p.DrawPain();
3125 p.DrawPickup();
3126 p.DrawRulez();
3127 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3128 if g_Debug_Player then
3129 g_Player_DrawDebug(p);
3130 p.DrawGUI();
3131 end;
3133 procedure drawProfilers ();
3134 var
3135 px: Integer = -1;
3136 py: Integer = -1;
3137 begin
3138 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
3139 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3140 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3141 end;
3143 procedure g_Game_Draw();
3144 var
3145 ID: DWORD;
3146 w, h: Word;
3147 ww, hh: Byte;
3148 Time: Int64;
3149 back: string;
3150 plView1, plView2: TPlayer;
3151 Split: Boolean;
3152 begin
3153 if gExit = EXIT_QUIT then Exit;
3155 Time := GetTimer() {div 1000};
3156 FPSCounter := FPSCounter+1;
3157 if Time - FPSTime >= 1000 then
3158 begin
3159 FPS := FPSCounter;
3160 FPSCounter := 0;
3161 FPSTime := Time;
3162 end;
3164 if gGameOn or (gState = STATE_FOLD) then
3165 begin
3166 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3167 begin
3168 gSpectMode := SPECT_NONE;
3169 if not gRevertPlayers then
3170 begin
3171 plView1 := gPlayer1;
3172 plView2 := gPlayer2;
3173 end
3174 else
3175 begin
3176 plView1 := gPlayer2;
3177 plView2 := gPlayer1;
3178 end;
3179 end
3180 else
3181 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3182 begin
3183 gSpectMode := SPECT_NONE;
3184 if gPlayer2 = nil then
3185 plView1 := gPlayer1
3186 else
3187 plView1 := gPlayer2;
3188 plView2 := nil;
3189 end
3190 else
3191 begin
3192 plView1 := nil;
3193 plView2 := nil;
3194 end;
3196 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3197 gSpectMode := SPECT_STATS;
3199 if gSpectMode = SPECT_PLAYERS then
3200 if gPlayers <> nil then
3201 begin
3202 plView1 := GetActivePlayer_ByID(gSpectPID1);
3203 if plView1 = nil then
3204 begin
3205 gSpectPID1 := GetActivePlayerID_Next();
3206 plView1 := GetActivePlayer_ByID(gSpectPID1);
3207 end;
3208 if gSpectViewTwo then
3209 begin
3210 plView2 := GetActivePlayer_ByID(gSpectPID2);
3211 if plView2 = nil then
3212 begin
3213 gSpectPID2 := GetActivePlayerID_Next();
3214 plView2 := GetActivePlayer_ByID(gSpectPID2);
3215 end;
3216 end;
3217 end;
3219 if gSpectMode = SPECT_MAPVIEW then
3220 begin
3221 // Ðåæèì ïðîñìîòðà êàðòû
3222 Split := False;
3223 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3224 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3225 gHearPoint1.Active := True;
3226 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3227 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3228 gHearPoint2.Active := False;
3229 end
3230 else
3231 begin
3232 Split := (plView1 <> nil) and (plView2 <> nil);
3234 // Òî÷êè ñëóõà èãðîêîâ
3235 if plView1 <> nil then
3236 begin
3237 gHearPoint1.Active := True;
3238 gHearPoint1.Coords.X := plView1.GameX;
3239 gHearPoint1.Coords.Y := plView1.GameY;
3240 end else
3241 gHearPoint1.Active := False;
3242 if plView2 <> nil then
3243 begin
3244 gHearPoint2.Active := True;
3245 gHearPoint2.Coords.X := plView2.GameX;
3246 gHearPoint2.Coords.Y := plView2.GameY;
3247 end else
3248 gHearPoint2.Active := False;
3250 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3251 gPlayerScreenSize.X := gScreenWidth-196;
3252 if Split then
3253 begin
3254 gPlayerScreenSize.Y := gScreenHeight div 2;
3255 if gScreenHeight mod 2 = 0 then
3256 Dec(gPlayerScreenSize.Y);
3257 end
3258 else
3259 gPlayerScreenSize.Y := gScreenHeight;
3261 if Split then
3262 if gScreenHeight mod 2 = 0 then
3263 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3264 else
3265 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3267 DrawPlayer(plView1);
3268 gPlayer1ScreenCoord.X := sX;
3269 gPlayer1ScreenCoord.Y := sY;
3271 if Split then
3272 begin
3273 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3275 DrawPlayer(plView2);
3276 gPlayer2ScreenCoord.X := sX;
3277 gPlayer2ScreenCoord.Y := sY;
3278 end;
3280 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3282 if Split then
3283 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3284 end;
3286 if MessageText <> '' then
3287 begin
3288 w := 0;
3289 h := 0;
3290 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3291 if Split then
3292 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3293 (gScreenHeight div 2)-(h div 2), MessageText)
3294 else
3295 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3296 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3297 end;
3299 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3301 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3302 begin
3303 // Draw spectator GUI
3304 ww := 0;
3305 hh := 0;
3306 e_TextureFontGetSize(gStdFont, ww, hh);
3307 case gSpectMode of
3308 SPECT_STATS:
3309 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3310 SPECT_MAPVIEW:
3311 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3312 SPECT_PLAYERS:
3313 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3314 end;
3315 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3316 if gSpectMode = SPECT_MAPVIEW then
3317 begin
3318 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3319 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3320 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3321 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3322 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3323 end;
3324 if gSpectMode = SPECT_PLAYERS then
3325 begin
3326 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3327 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3328 if gSpectViewTwo then
3329 begin
3330 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3331 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3332 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3333 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3334 end
3335 else
3336 begin
3337 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3338 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3339 end;
3340 end;
3341 end;
3342 end;
3344 if gPause and gGameOn and (g_ActiveWindow = nil) then
3345 begin
3346 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3347 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3349 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3350 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3351 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3352 end;
3354 if not gGameOn then
3355 begin
3356 if (gState = STATE_MENU) then
3357 begin
3358 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3359 begin
3360 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3361 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3362 end;
3363 // F3 at menu will show game loading dialog
3364 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3365 if (g_ActiveWindow <> nil) then
3366 begin
3367 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3368 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3369 end
3370 else
3371 begin
3372 // F3 at titlepic will show game loading dialog
3373 if e_KeyPressed(IK_F3) then
3374 begin
3375 g_Menu_Show_LoadMenu(true);
3376 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3377 end;
3378 end;
3379 end;
3381 if gState = STATE_FOLD then
3382 begin
3383 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3384 end;
3386 if gState = STATE_INTERCUSTOM then
3387 begin
3388 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3389 begin
3390 back := 'TEXTURE_endpic';
3391 if not g_Texture_Get(back, ID) then
3392 back := _lc[I_TEXTURE_ENDPIC];
3393 end
3394 else
3395 back := 'INTER';
3397 if g_Texture_Get(back, ID) then
3398 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3399 else
3400 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3402 DrawCustomStat();
3404 if g_ActiveWindow <> nil then
3405 begin
3406 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3407 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3408 end;
3409 end;
3411 if gState = STATE_INTERSINGLE then
3412 begin
3413 if EndingGameCounter > 0 then
3414 begin
3415 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3416 end
3417 else
3418 begin
3419 back := 'INTER';
3421 if g_Texture_Get(back, ID) then
3422 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3423 else
3424 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3426 DrawSingleStat();
3428 if g_ActiveWindow <> nil then
3429 begin
3430 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3431 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3432 end;
3433 end;
3434 end;
3436 if gState = STATE_ENDPIC then
3437 begin
3438 ID := DWORD(-1);
3439 if not g_Texture_Get('TEXTURE_endpic', ID) then
3440 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3442 if ID <> DWORD(-1) then
3443 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3444 else
3445 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3447 if g_ActiveWindow <> nil then
3448 begin
3449 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3450 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3451 end;
3452 end;
3454 if gState = STATE_SLIST then
3455 begin
3456 if g_Texture_Get('MENU_BACKGROUND', ID) then
3457 begin
3458 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3459 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3460 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3461 end;
3462 g_Serverlist_Draw(slCurrent);
3463 end;
3464 end;
3466 if g_ActiveWindow <> nil then
3467 begin
3468 if gGameOn then
3469 begin
3470 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3471 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3472 end;
3473 g_ActiveWindow.Draw();
3474 end;
3476 // draw inspector
3477 if (g_holmes_enabled) then g_Holmes_Draw();
3479 g_Console_Draw();
3481 if g_debug_Sounds and gGameOn then
3482 begin
3483 for w := 0 to High(e_SoundsArray) do
3484 for h := 0 to e_SoundsArray[w].nRefs do
3485 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3486 end;
3488 if gShowFPS then
3489 begin
3490 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3491 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3492 end;
3494 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3495 drawTime(gScreenWidth-72, gScreenHeight-16);
3497 if gGameOn then drawProfilers();
3499 g_Holmes_DrawUI();
3500 end;
3502 procedure g_Game_Quit();
3503 begin
3504 g_Game_StopAllSounds(True);
3505 gMusic.Free();
3506 g_Game_SaveOptions();
3507 g_Game_FreeData();
3508 g_PlayerModel_FreeData();
3509 g_Texture_DeleteAll();
3510 g_Frames_DeleteAll();
3511 g_Menu_Free();
3513 if NetInitDone then g_Net_Free;
3515 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3516 if gMapToDelete <> '' then
3517 g_Game_DeleteTestMap();
3519 gExit := EXIT_QUIT;
3520 PushExitEvent();
3521 end;
3523 procedure g_FatalError(Text: String);
3524 begin
3525 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3526 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3528 gExit := EXIT_SIMPLE;
3529 end;
3531 procedure g_SimpleError(Text: String);
3532 begin
3533 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3534 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3535 end;
3537 procedure g_Game_SetupScreenSize();
3538 const
3539 RES_FACTOR = 4.0 / 3.0;
3540 var
3541 s: Single;
3542 rf: Single;
3543 bw, bh: Word;
3544 begin
3545 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3546 gPlayerScreenSize.X := gScreenWidth-196;
3547 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3548 gPlayerScreenSize.Y := gScreenHeight div 2
3549 else
3550 gPlayerScreenSize.Y := gScreenHeight;
3552 // Ðàçìåð çàäíåãî ïëàíà:
3553 if BackID <> DWORD(-1) then
3554 begin
3555 s := SKY_STRETCH;
3556 if (gScreenWidth*s > gMapInfo.Width) or
3557 (gScreenHeight*s > gMapInfo.Height) then
3558 begin
3559 gBackSize.X := gScreenWidth;
3560 gBackSize.Y := gScreenHeight;
3561 end
3562 else
3563 begin
3564 e_GetTextureSize(BackID, @bw, @bh);
3565 rf := Single(bw) / Single(bh);
3566 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3567 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3568 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3569 if (s < 1.0) then s := 1.0;
3570 gBackSize.X := Round(bw*s);
3571 gBackSize.Y := Round(bh*s);
3572 end;
3573 end;
3574 end;
3576 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3577 begin
3578 g_Window_SetSize(newWidth, newHeight, nowFull);
3579 end;
3581 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3582 begin
3583 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3584 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3585 Exit;
3586 if gPlayer1 = nil then
3587 begin
3588 if g_Game_IsClient then
3589 begin
3590 if NetPlrUID1 > -1 then
3591 begin
3592 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3593 gPlayer1 := g_Player_Get(NetPlrUID1);
3594 end;
3595 Exit;
3596 end;
3598 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3599 Team := gPlayer1Settings.Team;
3601 // Ñîçäàíèå ïåðâîãî èãðîêà:
3602 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3603 gPlayer1Settings.Color,
3604 Team, False));
3605 if gPlayer1 = nil then
3606 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3607 else
3608 begin
3609 gPlayer1.Name := gPlayer1Settings.Name;
3610 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3611 if g_Game_IsServer and g_Game_IsNet then
3612 MH_SEND_PlayerCreate(gPlayer1.UID);
3613 gPlayer1.Respawn(False, True);
3615 if g_Game_IsNet and NetUseMaster then
3616 g_Net_Slist_Update;
3617 end;
3619 Exit;
3620 end;
3621 if gPlayer2 = nil then
3622 begin
3623 if g_Game_IsClient then
3624 begin
3625 if NetPlrUID2 > -1 then
3626 gPlayer2 := g_Player_Get(NetPlrUID2);
3627 Exit;
3628 end;
3630 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3631 Team := gPlayer2Settings.Team;
3633 // Ñîçäàíèå âòîðîãî èãðîêà:
3634 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3635 gPlayer2Settings.Color,
3636 Team, False));
3637 if gPlayer2 = nil then
3638 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3639 else
3640 begin
3641 gPlayer2.Name := gPlayer2Settings.Name;
3642 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3643 if g_Game_IsServer and g_Game_IsNet then
3644 MH_SEND_PlayerCreate(gPlayer2.UID);
3645 gPlayer2.Respawn(False, True);
3647 if g_Game_IsNet and NetUseMaster then
3648 g_Net_Slist_Update;
3649 end;
3651 Exit;
3652 end;
3653 end;
3655 procedure g_Game_RemovePlayer();
3656 var
3657 Pl: TPlayer;
3658 begin
3659 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3660 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3661 Exit;
3662 Pl := gPlayer2;
3663 if Pl <> nil then
3664 begin
3665 if g_Game_IsServer then
3666 begin
3667 Pl.Lives := 0;
3668 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3669 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3670 g_Player_Remove(Pl.UID);
3672 if g_Game_IsNet and NetUseMaster then
3673 g_Net_Slist_Update;
3674 end else
3675 gPlayer2 := nil;
3676 Exit;
3677 end;
3678 Pl := gPlayer1;
3679 if Pl <> nil then
3680 begin
3681 if g_Game_IsServer then
3682 begin
3683 Pl.Lives := 0;
3684 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3685 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3686 g_Player_Remove(Pl.UID);
3688 if g_Game_IsNet and NetUseMaster then
3689 g_Net_Slist_Update;
3690 end else
3691 begin
3692 gPlayer1 := nil;
3693 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3694 end;
3695 Exit;
3696 end;
3697 end;
3699 procedure g_Game_Spectate();
3700 begin
3701 g_Game_RemovePlayer();
3702 if gPlayer1 <> nil then
3703 g_Game_RemovePlayer();
3704 end;
3706 procedure g_Game_SpectateCenterView();
3707 begin
3708 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3709 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3710 end;
3712 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3713 var
3714 i, nPl: Integer;
3715 begin
3716 g_Game_Free();
3718 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3720 g_Game_ClearLoading();
3722 // Íàñòðîéêè èãðû:
3723 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3724 gAimLine := False;
3725 gShowMap := False;
3726 gGameSettings.GameType := GT_SINGLE;
3727 gGameSettings.MaxLives := 0;
3728 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3729 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3730 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3731 gSwitchGameMode := GM_SINGLE;
3733 g_Game_ExecuteEvent('ongamestart');
3735 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3736 g_Game_SetupScreenSize();
3738 // Ñîçäàíèå ïåðâîãî èãðîêà:
3739 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3740 gPlayer1Settings.Color,
3741 gPlayer1Settings.Team, False));
3742 if gPlayer1 = nil then
3743 begin
3744 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3745 Exit;
3746 end;
3748 gPlayer1.Name := gPlayer1Settings.Name;
3749 nPl := 1;
3751 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3752 if TwoPlayers then
3753 begin
3754 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3755 gPlayer2Settings.Color,
3756 gPlayer2Settings.Team, False));
3757 if gPlayer2 = nil then
3758 begin
3759 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3760 Exit;
3761 end;
3763 gPlayer2.Name := gPlayer2Settings.Name;
3764 Inc(nPl);
3765 end;
3767 // Çàãðóçêà è çàïóñê êàðòû:
3768 if not g_Game_StartMap(MAP, True) then
3769 begin
3770 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3771 Exit;
3772 end;
3774 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3775 g_Player_Init();
3777 // Ñîçäàåì áîòîâ:
3778 for i := nPl+1 to nPlayers do
3779 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3780 end;
3782 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3783 TimeLimit, GoalLimit: Word;
3784 MaxLives: Byte;
3785 Options: LongWord; nPlayers: Byte);
3786 var
3787 i, nPl: Integer;
3788 begin
3789 g_Game_Free();
3791 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3793 g_Game_ClearLoading();
3795 // Íàñòðîéêè èãðû:
3796 gGameSettings.GameType := GT_CUSTOM;
3797 gGameSettings.GameMode := GameMode;
3798 gSwitchGameMode := GameMode;
3799 gGameSettings.TimeLimit := TimeLimit;
3800 gGameSettings.GoalLimit := GoalLimit;
3801 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3802 gGameSettings.Options := Options;
3804 gCoopTotalMonstersKilled := 0;
3805 gCoopTotalSecretsFound := 0;
3806 gCoopTotalMonsters := 0;
3807 gCoopTotalSecrets := 0;
3808 gAimLine := False;
3809 gShowMap := False;
3811 g_Game_ExecuteEvent('ongamestart');
3813 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3814 g_Game_SetupScreenSize();
3816 // Ðåæèì íàáëþäàòåëÿ:
3817 if nPlayers = 0 then
3818 begin
3819 gPlayer1 := nil;
3820 gPlayer2 := nil;
3821 end;
3823 nPl := 0;
3824 if nPlayers >= 1 then
3825 begin
3826 // Ñîçäàíèå ïåðâîãî èãðîêà:
3827 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3828 gPlayer1Settings.Color,
3829 gPlayer1Settings.Team, False));
3830 if gPlayer1 = nil then
3831 begin
3832 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3833 Exit;
3834 end;
3836 gPlayer1.Name := gPlayer1Settings.Name;
3837 Inc(nPl);
3838 end;
3840 if nPlayers >= 2 then
3841 begin
3842 // Ñîçäàíèå âòîðîãî èãðîêà:
3843 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3844 gPlayer2Settings.Color,
3845 gPlayer2Settings.Team, False));
3846 if gPlayer2 = nil then
3847 begin
3848 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3849 Exit;
3850 end;
3852 gPlayer2.Name := gPlayer2Settings.Name;
3853 Inc(nPl);
3854 end;
3856 // Çàãðóçêà è çàïóñê êàðòû:
3857 if not g_Game_StartMap(Map, True) then
3858 begin
3859 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3860 Exit;
3861 end;
3863 // Íåò òî÷åê ïîÿâëåíèÿ:
3864 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3865 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3866 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3867 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3868 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3869 begin
3870 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3871 Exit;
3872 end;
3874 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3875 g_Player_Init();
3877 // Ñîçäàåì áîòîâ:
3878 for i := nPl+1 to nPlayers do
3879 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3880 end;
3882 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3883 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3884 Options: LongWord; nPlayers: Byte;
3885 IPAddr: LongWord; Port: Word);
3886 begin
3887 g_Game_Free();
3889 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3891 g_Game_ClearLoading();
3893 // Íàñòðîéêè èãðû:
3894 gGameSettings.GameType := GT_SERVER;
3895 gGameSettings.GameMode := GameMode;
3896 gSwitchGameMode := GameMode;
3897 gGameSettings.TimeLimit := TimeLimit;
3898 gGameSettings.GoalLimit := GoalLimit;
3899 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3900 gGameSettings.Options := Options;
3902 gCoopTotalMonstersKilled := 0;
3903 gCoopTotalSecretsFound := 0;
3904 gCoopTotalMonsters := 0;
3905 gCoopTotalSecrets := 0;
3906 gAimLine := False;
3907 gShowMap := False;
3909 g_Game_ExecuteEvent('ongamestart');
3911 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3912 g_Game_SetupScreenSize();
3914 // Ðåæèì íàáëþäàòåëÿ:
3915 if nPlayers = 0 then
3916 begin
3917 gPlayer1 := nil;
3918 gPlayer2 := nil;
3919 end;
3921 if nPlayers >= 1 then
3922 begin
3923 // Ñîçäàíèå ïåðâîãî èãðîêà:
3924 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3925 gPlayer1Settings.Color,
3926 gPlayer1Settings.Team, False));
3927 if gPlayer1 = nil then
3928 begin
3929 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3930 Exit;
3931 end;
3933 gPlayer1.Name := gPlayer1Settings.Name;
3934 end;
3936 if nPlayers >= 2 then
3937 begin
3938 // Ñîçäàíèå âòîðîãî èãðîêà:
3939 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3940 gPlayer2Settings.Color,
3941 gPlayer2Settings.Team, False));
3942 if gPlayer2 = nil then
3943 begin
3944 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3945 Exit;
3946 end;
3948 gPlayer2.Name := gPlayer2Settings.Name;
3949 end;
3951 // Ñòàðòóåì ñåðâåð
3952 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3953 begin
3954 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3955 Exit;
3956 end;
3958 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3960 // Çàãðóçêà è çàïóñê êàðòû:
3961 if not g_Game_StartMap(Map, True) then
3962 begin
3963 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3964 Exit;
3965 end;
3967 // Íåò òî÷åê ïîÿâëåíèÿ:
3968 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3969 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3970 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3971 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3972 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3973 begin
3974 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3975 Exit;
3976 end;
3978 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3979 g_Player_Init();
3981 NetState := NET_STATE_GAME;
3982 end;
3984 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3985 var
3986 Map: String;
3987 WadName: string;
3988 Ptr: Pointer;
3989 T: Cardinal;
3990 MID: Byte;
3991 State: Byte;
3992 OuterLoop: Boolean;
3993 newResPath: string;
3994 InMsg: TMsg;
3995 begin
3996 g_Game_Free();
3998 State := 0;
3999 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
4000 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
4002 g_Game_ClearLoading();
4004 // Íàñòðîéêè èãðû:
4005 gGameSettings.GameType := GT_CLIENT;
4007 gCoopTotalMonstersKilled := 0;
4008 gCoopTotalSecretsFound := 0;
4009 gCoopTotalMonsters := 0;
4010 gCoopTotalSecrets := 0;
4011 gAimLine := False;
4012 gShowMap := False;
4014 g_Game_ExecuteEvent('ongamestart');
4016 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4017 g_Game_SetupScreenSize();
4019 NetState := NET_STATE_AUTH;
4021 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4022 // Ñòàðòóåì êëèåíò
4023 if not g_Net_Connect(Addr, Port) then
4024 begin
4025 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4026 NetState := NET_STATE_NONE;
4027 Exit;
4028 end;
4030 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4031 MC_SEND_Info(PW);
4032 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4034 OuterLoop := True;
4035 while OuterLoop do
4036 begin
4037 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4038 begin
4039 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4040 begin
4041 Ptr := NetEvent.packet^.data;
4042 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4043 continue;
4045 MID := InMsg.ReadByte();
4047 if (MID = NET_MSG_INFO) and (State = 0) then
4048 begin
4049 NetMyID := InMsg.ReadByte();
4050 NetPlrUID1 := InMsg.ReadWord();
4052 WadName := InMsg.ReadString();
4053 Map := InMsg.ReadString();
4055 gWADHash := InMsg.ReadMD5();
4057 gGameSettings.GameMode := InMsg.ReadByte();
4058 gSwitchGameMode := gGameSettings.GameMode;
4059 gGameSettings.GoalLimit := InMsg.ReadWord();
4060 gGameSettings.TimeLimit := InMsg.ReadWord();
4061 gGameSettings.MaxLives := InMsg.ReadByte();
4062 gGameSettings.Options := InMsg.ReadLongWord();
4063 T := InMsg.ReadLongWord();
4065 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4066 if newResPath = '' then
4067 begin
4068 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4069 newResPath := g_Res_DownloadWAD(WadName);
4070 if newResPath = '' then
4071 begin
4072 g_FatalError(_lc[I_NET_ERR_HASH]);
4073 enet_packet_destroy(NetEvent.packet);
4074 NetState := NET_STATE_NONE;
4075 Exit;
4076 end;
4077 end;
4078 newResPath := ExtractRelativePath(MapsDir, newResPath);
4080 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4081 gPlayer1Settings.Color,
4082 gPlayer1Settings.Team, False));
4084 if gPlayer1 = nil then
4085 begin
4086 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4088 enet_packet_destroy(NetEvent.packet);
4089 NetState := NET_STATE_NONE;
4090 Exit;
4091 end;
4093 gPlayer1.Name := gPlayer1Settings.Name;
4094 gPlayer1.UID := NetPlrUID1;
4095 gPlayer1.Reset(True);
4097 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4098 begin
4099 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4101 enet_packet_destroy(NetEvent.packet);
4102 NetState := NET_STATE_NONE;
4103 Exit;
4104 end;
4106 gTime := T;
4108 State := 1;
4109 OuterLoop := False;
4110 enet_packet_destroy(NetEvent.packet);
4111 break;
4112 end
4113 else
4114 enet_packet_destroy(NetEvent.packet);
4115 end
4116 else
4117 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4118 begin
4119 State := 0;
4120 if (NetEvent.data <= NET_DISC_MAX) then
4121 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4122 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4123 OuterLoop := False;
4124 Break;
4125 end;
4126 end;
4128 ProcessLoading(true);
4130 e_PollInput();
4132 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
4133 begin
4134 State := 0;
4135 break;
4136 end;
4137 end;
4139 if State <> 1 then
4140 begin
4141 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4142 NetState := NET_STATE_NONE;
4143 Exit;
4144 end;
4146 gLMSRespawn := LMS_RESPAWN_NONE;
4147 gLMSRespawnTime := 0;
4149 g_Player_Init();
4150 NetState := NET_STATE_GAME;
4151 MC_SEND_FullStateRequest;
4152 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
4153 end;
4155 procedure g_Game_SaveOptions();
4156 begin
4157 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4158 end;
4160 procedure g_Game_ChangeMap(const MapPath: String);
4161 var
4162 Force: Boolean;
4163 begin
4164 g_Game_ClearLoading();
4166 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4167 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4168 if gExitByTrigger then
4169 begin
4170 Force := False;
4171 gExitByTrigger := False;
4172 end;
4173 if not g_Game_StartMap(MapPath, Force) then
4174 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4175 end;
4177 procedure g_Game_Restart();
4178 var
4179 Map: string;
4180 begin
4181 if g_Game_IsClient then
4182 Exit;
4183 map := g_ExtractFileName(gMapInfo.Map);
4185 MessageTime := 0;
4186 gGameOn := False;
4187 g_Game_ClearLoading();
4188 g_Game_StartMap(Map, True, gCurrentMapFileName);
4189 end;
4191 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4192 var
4193 NewWAD, ResName: String;
4194 I: Integer;
4195 begin
4196 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4197 g_Player_RemoveAllCorpses();
4199 if (not g_Game_IsClient) and
4200 (gSwitchGameMode <> gGameSettings.GameMode) and
4201 (gGameSettings.GameMode <> GM_SINGLE) then
4202 begin
4203 if gSwitchGameMode = GM_CTF then
4204 gGameSettings.MaxLives := 0;
4205 gGameSettings.GameMode := gSwitchGameMode;
4206 Force := True;
4207 end else
4208 gSwitchGameMode := gGameSettings.GameMode;
4210 g_Player_ResetTeams();
4212 if isWadPath(Map) then
4213 begin
4214 NewWAD := g_ExtractWadName(Map);
4215 ResName := g_ExtractFileName(Map);
4216 if g_Game_IsServer then
4217 begin
4218 gWADHash := MD5File(MapsDir + NewWAD);
4219 g_Game_LoadWAD(NewWAD);
4220 end else
4221 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4222 g_Game_ClientWAD(NewWAD, gWADHash);
4223 end else
4224 ResName := Map;
4226 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4227 if Result then
4228 begin
4229 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4231 gState := STATE_NONE;
4232 g_ActiveWindow := nil;
4233 gGameOn := True;
4235 DisableCheats();
4236 ResetTimer();
4238 if gGameSettings.GameMode = GM_CTF then
4239 begin
4240 g_Map_ResetFlag(FLAG_RED);
4241 g_Map_ResetFlag(FLAG_BLUE);
4242 // CTF, à ôëàãîâ íåò:
4243 if not g_Map_HaveFlagPoints() then
4244 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4245 end;
4246 end
4247 else
4248 begin
4249 gState := STATE_MENU;
4250 gGameOn := False;
4251 end;
4253 gExit := 0;
4254 gPause := False;
4255 gTime := 0;
4256 NetTimeToUpdate := 1;
4257 NetTimeToReliable := 0;
4258 NetTimeToMaster := NetMasterRate;
4259 gLMSRespawn := LMS_RESPAWN_NONE;
4260 gLMSRespawnTime := 0;
4261 gMissionFailed := False;
4262 gNextMap := '';
4264 gCoopMonstersKilled := 0;
4265 gCoopSecretsFound := 0;
4267 gVoteInProgress := False;
4268 gVotePassed := False;
4269 gVoteCount := 0;
4270 gVoted := False;
4272 gStatsOff := False;
4274 if not gGameOn then Exit;
4276 g_Game_SpectateCenterView();
4278 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4279 begin
4280 gLMSRespawn := LMS_RESPAWN_WARMUP;
4281 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4282 gLMSSoftSpawn := True;
4283 if NetMode = NET_SERVER then
4284 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4285 else
4286 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4287 end;
4289 if NetMode = NET_SERVER then
4290 begin
4291 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4293 // Ìàñòåðñåðâåð
4294 if NetUseMaster then
4295 begin
4296 if (NetMHost = nil) or (NetMPeer = nil) then
4297 if not g_Net_Slist_Connect then
4298 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4300 g_Net_Slist_Update;
4301 end;
4303 if NetClients <> nil then
4304 for I := 0 to High(NetClients) do
4305 if NetClients[I].Used then
4306 begin
4307 NetClients[I].Voted := False;
4308 if NetClients[I].RequestedFullUpdate then
4309 begin
4310 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4311 NetClients[I].RequestedFullUpdate := False;
4312 end;
4313 end;
4315 g_Net_UnbanNonPermHosts();
4316 end;
4318 if gLastMap then
4319 begin
4320 gCoopTotalMonstersKilled := 0;
4321 gCoopTotalSecretsFound := 0;
4322 gCoopTotalMonsters := 0;
4323 gCoopTotalSecrets := 0;
4324 gLastMap := False;
4325 end;
4327 g_Game_ExecuteEvent('onmapstart');
4328 end;
4330 procedure SetFirstLevel();
4331 begin
4332 gNextMap := '';
4334 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4335 if MapList = nil then
4336 Exit;
4338 SortSArray(MapList);
4339 gNextMap := MapList[Low(MapList)];
4341 MapList := nil;
4342 end;
4344 procedure g_Game_ExitLevel(const Map: AnsiString);
4345 begin
4346 gNextMap := Map;
4348 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4349 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4350 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4351 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4353 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4354 if gGameSettings.GameType = GT_SINGLE then
4355 gExit := EXIT_ENDLEVELSINGLE
4356 else // Âûøëè â âûõîä â Ñâîåé èãðå
4357 begin
4358 gExit := EXIT_ENDLEVELCUSTOM;
4359 if gGameSettings.GameMode = GM_COOP then
4360 g_Player_RememberAll;
4362 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4363 begin
4364 gLastMap := True;
4365 if gGameSettings.GameMode = GM_COOP then
4366 gStatsOff := True;
4368 gStatsPressed := True;
4369 gNextMap := 'MAP01';
4371 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4372 g_Game_NextLevel;
4374 if g_Game_IsNet then
4375 begin
4376 MH_SEND_GameStats();
4377 MH_SEND_CoopStats();
4378 end;
4379 end;
4380 end;
4381 end;
4383 procedure g_Game_RestartLevel();
4384 var
4385 Map: string;
4386 begin
4387 if gGameSettings.GameMode = GM_SINGLE then
4388 begin
4389 g_Game_Restart();
4390 Exit;
4391 end;
4392 gExit := EXIT_ENDLEVELCUSTOM;
4393 Map := g_ExtractFileName(gMapInfo.Map);
4394 gNextMap := Map;
4395 end;
4397 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4398 var
4399 gWAD: String;
4400 begin
4401 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4402 Exit;
4403 if not g_Game_IsClient then
4404 Exit;
4405 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4406 if gWAD = '' then
4407 begin
4408 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4409 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4410 if gWAD = '' then
4411 begin
4412 g_Game_Free();
4413 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4414 Exit;
4415 end;
4416 end;
4417 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4418 g_Game_LoadWAD(NewWAD);
4419 end;
4421 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4422 var
4423 i, n, nb, nr: Integer;
4425 function monRespawn (mon: TMonster): Boolean;
4426 begin
4427 result := false; // don't stop
4428 if not mon.FNoRespawn then mon.Respawn();
4429 end;
4431 begin
4432 if not g_Game_IsServer then Exit;
4433 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4434 gLMSRespawn := LMS_RESPAWN_NONE;
4435 gLMSRespawnTime := 0;
4436 MessageTime := 0;
4438 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4439 begin
4440 gMissionFailed := True;
4441 g_Game_RestartLevel;
4442 Exit;
4443 end;
4445 n := 0; nb := 0; nr := 0;
4446 for i := Low(gPlayers) to High(gPlayers) do
4447 if (gPlayers[i] <> nil) and
4448 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4449 (gPlayers[i] is TBot)) then
4450 begin
4451 Inc(n);
4452 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4453 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4454 end;
4456 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4457 begin
4458 // wait a second until the fuckers finally decide to join
4459 gLMSRespawn := LMS_RESPAWN_WARMUP;
4460 gLMSRespawnTime := gTime + 1000;
4461 gLMSSoftSpawn := NoMapRestart;
4462 Exit;
4463 end;
4465 g_Player_RemoveAllCorpses;
4466 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4467 if g_Game_IsNet then
4468 MH_SEND_GameEvent(NET_EV_LMS_START);
4470 for i := Low(gPlayers) to High(gPlayers) do
4471 begin
4472 if gPlayers[i] = nil then continue;
4473 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4474 // don't touch normal spectators
4475 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4476 begin
4477 gPlayers[i].FNoRespawn := True;
4478 gPlayers[i].Lives := 0;
4479 if g_Game_IsNet then
4480 MH_SEND_PlayerStats(gPlayers[I].UID);
4481 continue;
4482 end;
4483 gPlayers[i].FNoRespawn := False;
4484 gPlayers[i].Lives := gGameSettings.MaxLives;
4485 gPlayers[i].Respawn(False, True);
4486 if gGameSettings.GameMode = GM_COOP then
4487 begin
4488 gPlayers[i].Frags := 0;
4489 gPlayers[i].RecallState;
4490 end;
4491 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4492 gPlayer1 := g_Player_Get(gLMSPID1);
4493 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4494 gPlayer2 := g_Player_Get(gLMSPID2);
4495 end;
4497 g_Items_RestartRound();
4500 g_Mons_ForEach(monRespawn);
4502 gLMSSoftSpawn := False;
4503 end;
4505 function g_Game_GetFirstMap(WAD: String): String;
4506 begin
4507 Result := '';
4509 MapList := g_Map_GetMapsList(WAD);
4510 if MapList = nil then
4511 Exit;
4513 SortSArray(MapList);
4514 Result := MapList[Low(MapList)];
4516 if not g_Map_Exist(WAD + ':\' + Result) then
4517 Result := '';
4519 MapList := nil;
4520 end;
4522 function g_Game_GetNextMap(): String;
4523 var
4524 I: Integer;
4525 Map: string;
4526 begin
4527 Result := '';
4529 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4530 if MapList = nil then
4531 Exit;
4533 Map := g_ExtractFileName(gMapInfo.Map);
4535 SortSArray(MapList);
4536 MapIndex := -255;
4537 for I := Low(MapList) to High(MapList) do
4538 if Map = MapList[I] then
4539 begin
4540 MapIndex := I;
4541 Break;
4542 end;
4544 if MapIndex <> -255 then
4545 begin
4546 if MapIndex = High(MapList) then
4547 Result := MapList[Low(MapList)]
4548 else
4549 Result := MapList[MapIndex + 1];
4551 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4552 end;
4554 MapList := nil;
4555 end;
4557 procedure g_Game_NextLevel();
4558 begin
4559 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4560 gExit := EXIT_ENDLEVELCUSTOM
4561 else
4562 begin
4563 gExit := EXIT_ENDLEVELSINGLE;
4564 Exit;
4565 end;
4567 if gNextMap <> '' then Exit;
4568 gNextMap := g_Game_GetNextMap();
4569 end;
4571 function g_Game_IsTestMap(): Boolean;
4572 begin
4573 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4574 end;
4576 procedure g_Game_DeleteTestMap();
4577 var
4578 a: Integer;
4579 //MapName: AnsiString;
4580 WadName: string;
4582 WAD: TWADFile;
4583 MapList: SArray;
4584 time: Integer;
4586 begin
4587 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
4588 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
4589 if (a = 0) then exit;
4591 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
4592 WadName := Copy(gMapToDelete, 1, a+3);
4593 Delete(gMapToDelete, 1, a+5);
4594 gMapToDelete := UpperCase(gMapToDelete);
4595 //MapName := '';
4596 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4599 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4600 if MapName <> TEST_MAP_NAME then
4601 Exit;
4603 if not gTempDelete then
4604 begin
4605 time := g_GetFileTime(WadName);
4606 WAD := TWADFile.Create();
4608 // ×èòàåì Wad-ôàéë:
4609 if not WAD.ReadFile(WadName) then
4610 begin // Íåò òàêîãî WAD-ôàéëà
4611 WAD.Free();
4612 Exit;
4613 end;
4615 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4616 WAD.CreateImage();
4617 MapList := WAD.GetResourcesList('');
4619 if MapList <> nil then
4620 for a := 0 to High(MapList) do
4621 if MapList[a] = MapName then
4622 begin
4623 // Óäàëÿåì è ñîõðàíÿåì:
4624 WAD.RemoveResource('', MapName);
4625 WAD.SaveTo(WadName);
4626 Break;
4627 end;
4629 WAD.Free();
4630 g_SetFileTime(WadName, time);
4631 end else
4633 if gTempDelete then DeleteFile(WadName);
4634 end;
4636 procedure GameCVars(P: SArray);
4637 var
4638 a, b: Integer;
4639 stat: TPlayerStatArray;
4640 cmd, s: string;
4641 config: TConfig;
4642 begin
4643 stat := nil;
4644 cmd := LowerCase(P[0]);
4645 if cmd = 'r_showfps' then
4646 begin
4647 if (Length(P) > 1) and
4648 ((P[1] = '1') or (P[1] = '0')) then
4649 gShowFPS := (P[1][1] = '1');
4651 if gShowFPS then
4652 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4653 else
4654 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4655 end
4656 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4657 begin
4658 with gGameSettings do
4659 begin
4660 if (Length(P) > 1) and
4661 ((P[1] = '1') or (P[1] = '0')) then
4662 begin
4663 if (P[1][1] = '1') then
4664 Options := Options or GAME_OPTION_TEAMDAMAGE
4665 else
4666 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4667 end;
4669 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4670 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4671 else
4672 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4674 if g_Game_IsNet then MH_SEND_GameSettings;
4675 end;
4676 end
4677 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4678 begin
4679 with gGameSettings do
4680 begin
4681 if (Length(P) > 1) and
4682 ((P[1] = '1') or (P[1] = '0')) then
4683 begin
4684 if (P[1][1] = '1') then
4685 Options := Options or GAME_OPTION_WEAPONSTAY
4686 else
4687 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4688 end;
4690 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4691 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4692 else
4693 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4695 if g_Game_IsNet then MH_SEND_GameSettings;
4696 end;
4697 end
4698 else if cmd = 'g_gamemode' then
4699 begin
4700 a := g_Game_TextToMode(P[1]);
4701 if a = GM_SINGLE then a := GM_COOP;
4702 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4703 begin
4704 gSwitchGameMode := a;
4705 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4706 (gState = STATE_INTERSINGLE) then
4707 gSwitchGameMode := GM_SINGLE;
4708 if not gGameOn then
4709 gGameSettings.GameMode := gSwitchGameMode;
4710 end;
4711 if gSwitchGameMode = gGameSettings.GameMode then
4712 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4713 [g_Game_ModeToText(gGameSettings.GameMode)]))
4714 else
4715 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4716 [g_Game_ModeToText(gGameSettings.GameMode),
4717 g_Game_ModeToText(gSwitchGameMode)]));
4718 end
4719 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4720 begin
4721 with gGameSettings do
4722 begin
4723 if (Length(P) > 1) and
4724 ((P[1] = '1') or (P[1] = '0')) then
4725 begin
4726 if (P[1][1] = '1') then
4727 Options := Options or GAME_OPTION_ALLOWEXIT
4728 else
4729 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4730 end;
4732 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4733 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4734 else
4735 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4736 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4738 if g_Game_IsNet then MH_SEND_GameSettings;
4739 end;
4740 end
4741 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4742 begin
4743 with gGameSettings do
4744 begin
4745 if (Length(P) > 1) and
4746 ((P[1] = '1') or (P[1] = '0')) then
4747 begin
4748 if (P[1][1] = '1') then
4749 Options := Options or GAME_OPTION_MONSTERS
4750 else
4751 Options := Options and (not GAME_OPTION_MONSTERS);
4752 end;
4754 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4755 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4756 else
4757 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4758 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4760 if g_Game_IsNet then MH_SEND_GameSettings;
4761 end;
4762 end
4763 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4764 begin
4765 with gGameSettings do
4766 begin
4767 if (Length(P) > 1) and
4768 ((P[1] = '1') or (P[1] = '0')) then
4769 begin
4770 if (P[1][1] = '1') then
4771 Options := Options or GAME_OPTION_BOTVSPLAYER
4772 else
4773 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4774 end;
4776 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4777 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4778 else
4779 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4781 if g_Game_IsNet then MH_SEND_GameSettings;
4782 end;
4783 end
4784 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4785 begin
4786 with gGameSettings do
4787 begin
4788 if (Length(P) > 1) and
4789 ((P[1] = '1') or (P[1] = '0')) then
4790 begin
4791 if (P[1][1] = '1') then
4792 Options := Options or GAME_OPTION_BOTVSMONSTER
4793 else
4794 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4795 end;
4797 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4798 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4799 else
4800 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4802 if g_Game_IsNet then MH_SEND_GameSettings;
4803 end;
4804 end
4805 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4806 begin
4807 if Length(P) > 1 then
4808 begin
4809 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4810 gGameSettings.WarmupTime := 30
4811 else
4812 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4813 end;
4815 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4816 [gGameSettings.WarmupTime]));
4817 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4818 end
4819 else if cmd = 'net_interp' then
4820 begin
4821 if (Length(P) > 1) then
4822 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4824 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4825 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4826 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4827 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4828 config.Free();
4829 end
4830 else if cmd = 'net_forceplayerupdate' then
4831 begin
4832 if (Length(P) > 1) and
4833 ((P[1] = '1') or (P[1] = '0')) then
4834 NetForcePlayerUpdate := (P[1][1] = '1');
4836 if NetForcePlayerUpdate then
4837 g_Console_Add('net_forceplayerupdate = 1')
4838 else
4839 g_Console_Add('net_forceplayerupdate = 0');
4840 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4841 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4842 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4843 config.Free();
4844 end
4845 else if cmd = 'net_predictself' then
4846 begin
4847 if (Length(P) > 1) and
4848 ((P[1] = '1') or (P[1] = '0')) then
4849 NetPredictSelf := (P[1][1] = '1');
4851 if NetPredictSelf then
4852 g_Console_Add('net_predictself = 1')
4853 else
4854 g_Console_Add('net_predictself = 0');
4855 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4856 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4857 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4858 config.Free();
4859 end
4860 else if cmd = 'sv_name' then
4861 begin
4862 if (Length(P) > 1) and (Length(P[1]) > 0) then
4863 begin
4864 NetServerName := P[1];
4865 if Length(NetServerName) > 64 then
4866 SetLength(NetServerName, 64);
4867 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4868 g_Net_Slist_Update;
4869 end;
4871 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4872 end
4873 else if cmd = 'sv_passwd' then
4874 begin
4875 if (Length(P) > 1) and (Length(P[1]) > 0) then
4876 begin
4877 NetPassword := P[1];
4878 if Length(NetPassword) > 24 then
4879 SetLength(NetPassword, 24);
4880 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4881 g_Net_Slist_Update;
4882 end;
4884 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4885 end
4886 else if cmd = 'sv_maxplrs' then
4887 begin
4888 if (Length(P) > 1) then
4889 begin
4890 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4891 if g_Game_IsServer and g_Game_IsNet then
4892 begin
4893 b := 0;
4894 for a := 0 to High(NetClients) do
4895 if NetClients[a].Used then
4896 begin
4897 Inc(b);
4898 if b > NetMaxClients then
4899 begin
4900 s := g_Player_Get(NetClients[a].Player).Name;
4901 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4902 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4903 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4904 end;
4905 end;
4906 if NetUseMaster then
4907 g_Net_Slist_Update;
4908 end;
4909 end;
4911 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4912 end
4913 else if cmd = 'sv_public' then
4914 begin
4915 if (Length(P) > 1) then
4916 begin
4917 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4918 if g_Game_IsServer and g_Game_IsNet then
4919 if NetUseMaster then
4920 begin
4921 if NetMPeer = nil then
4922 if not g_Net_Slist_Connect() then
4923 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4924 g_Net_Slist_Update();
4925 end
4926 else
4927 if NetMPeer <> nil then
4928 g_Net_Slist_Disconnect();
4929 end;
4931 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4932 end
4933 else if cmd = 'sv_intertime' then
4934 begin
4935 if (Length(P) > 1) then
4936 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4938 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4939 end
4940 else if cmd = 'p1_name' then
4941 begin
4942 if (Length(P) > 1) and gGameOn then
4943 begin
4944 if g_Game_IsClient then
4945 begin
4946 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4947 MC_SEND_PlayerSettings;
4948 end
4949 else
4950 if gPlayer1 <> nil then
4951 begin
4952 gPlayer1.Name := b_Text_Unformat(P[1]);
4953 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4954 end
4955 else
4956 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4957 end;
4958 end
4959 else if cmd = 'p2_name' then
4960 begin
4961 if (Length(P) > 1) and gGameOn then
4962 begin
4963 if g_Game_IsClient then
4964 begin
4965 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4966 MC_SEND_PlayerSettings;
4967 end
4968 else
4969 if gPlayer2 <> nil then
4970 begin
4971 gPlayer2.Name := b_Text_Unformat(P[1]);
4972 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4973 end
4974 else
4975 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4976 end;
4977 end
4978 else if cmd = 'p1_color' then
4979 begin
4980 if Length(P) > 3 then
4981 if g_Game_IsClient then
4982 begin
4983 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4984 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4985 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4986 MC_SEND_PlayerSettings;
4987 end
4988 else
4989 if gPlayer1 <> nil then
4990 begin
4991 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4992 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4993 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4994 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4995 end
4996 else
4997 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4998 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4999 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5000 end
5001 else if (cmd = 'p2_color') and not g_Game_IsNet then
5002 begin
5003 if Length(P) > 3 then
5004 if g_Game_IsClient then
5005 begin
5006 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5007 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5008 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5009 MC_SEND_PlayerSettings;
5010 end
5011 else
5012 if gPlayer2 <> nil then
5013 begin
5014 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5015 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5016 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5017 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5018 end
5019 else
5020 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5021 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5022 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5023 end
5024 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5025 begin
5026 if cmd = 'r_showtime' then
5027 begin
5028 if (Length(P) > 1) and
5029 ((P[1] = '1') or (P[1] = '0')) then
5030 gShowTime := (P[1][1] = '1');
5032 if gShowTime then
5033 g_Console_Add(_lc[I_MSG_TIME_ON])
5034 else
5035 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5036 end
5037 else if cmd = 'r_showscore' then
5038 begin
5039 if (Length(P) > 1) and
5040 ((P[1] = '1') or (P[1] = '0')) then
5041 gShowGoals := (P[1][1] = '1');
5043 if gShowGoals then
5044 g_Console_Add(_lc[I_MSG_SCORE_ON])
5045 else
5046 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5047 end
5048 else if cmd = 'r_showstat' then
5049 begin
5050 if (Length(P) > 1) and
5051 ((P[1] = '1') or (P[1] = '0')) then
5052 gShowStat := (P[1][1] = '1');
5054 if gShowStat then
5055 g_Console_Add(_lc[I_MSG_STATS_ON])
5056 else
5057 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5058 end
5059 else if cmd = 'r_showkillmsg' then
5060 begin
5061 if (Length(P) > 1) and
5062 ((P[1] = '1') or (P[1] = '0')) then
5063 gShowKillMsg := (P[1][1] = '1');
5065 if gShowKillMsg then
5066 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5067 else
5068 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5069 end
5070 else if cmd = 'r_showlives' then
5071 begin
5072 if (Length(P) > 1) and
5073 ((P[1] = '1') or (P[1] = '0')) then
5074 gShowLives := (P[1][1] = '1');
5076 if gShowLives then
5077 g_Console_Add(_lc[I_MSG_LIVES_ON])
5078 else
5079 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5080 end
5081 else if cmd = 'r_showspect' then
5082 begin
5083 if (Length(P) > 1) and
5084 ((P[1] = '1') or (P[1] = '0')) then
5085 gSpectHUD := (P[1][1] = '1');
5087 if gSpectHUD then
5088 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5089 else
5090 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5091 end
5092 else if cmd = 'r_showping' then
5093 begin
5094 if (Length(P) > 1) and
5095 ((P[1] = '1') or (P[1] = '0')) then
5096 gShowPing := (P[1][1] = '1');
5098 if gShowPing then
5099 g_Console_Add(_lc[I_MSG_PING_ON])
5100 else
5101 g_Console_Add(_lc[I_MSG_PING_OFF]);
5102 end
5103 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5104 begin
5105 if Length(P) > 1 then
5106 begin
5107 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5108 gGameSettings.GoalLimit := 0
5109 else
5110 begin
5111 b := 0;
5113 if gGameSettings.GameMode = GM_DM then
5114 begin // DM
5115 stat := g_Player_GetStats();
5116 if stat <> nil then
5117 for a := 0 to High(stat) do
5118 if stat[a].Frags > b then
5119 b := stat[a].Frags;
5120 end
5121 else // TDM/CTF
5122 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5124 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5125 end;
5127 if g_Game_IsNet then MH_SEND_GameSettings;
5128 end;
5130 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5131 end
5132 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5133 begin
5134 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5135 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5137 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5138 [gGameSettings.TimeLimit div 3600,
5139 (gGameSettings.TimeLimit div 60) mod 60,
5140 gGameSettings.TimeLimit mod 60]));
5141 if g_Game_IsNet then MH_SEND_GameSettings;
5142 end
5143 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5144 begin
5145 if Length(P) > 1 then
5146 begin
5147 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5148 gGameSettings.MaxLives := 0
5149 else
5150 begin
5151 b := 0;
5152 stat := g_Player_GetStats();
5153 if stat <> nil then
5154 for a := 0 to High(stat) do
5155 if stat[a].Lives > b then
5156 b := stat[a].Lives;
5157 gGameSettings.MaxLives :=
5158 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5159 end;
5160 end;
5162 g_Console_Add(Format(_lc[I_MSG_LIVES],
5163 [gGameSettings.MaxLives]));
5164 if g_Game_IsNet then MH_SEND_GameSettings;
5165 end;
5166 end;
5167 end;
5169 procedure PrintHeapStats();
5170 var
5171 hs: TFPCHeapStatus;
5172 begin
5173 hs := GetFPCHeapStatus();
5174 e_LogWriteLn ('v===== heap status =====v');
5175 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
5176 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
5177 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
5178 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
5179 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
5180 e_LogWriteLn ('^=======================^');
5181 end;
5183 procedure DebugCommands(P: SArray);
5184 var
5185 a, b: Integer;
5186 cmd: string;
5187 //pt: TDFPoint;
5188 mon: TMonster;
5189 begin
5190 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5191 if gDebugMode then
5192 begin
5193 cmd := LowerCase(P[0]);
5194 if cmd = 'd_window' then
5195 begin
5196 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5197 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5198 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5199 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5200 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5201 end
5202 else if cmd = 'd_sounds' then
5203 begin
5204 if (Length(P) > 1) and
5205 ((P[1] = '1') or (P[1] = '0')) then
5206 g_Debug_Sounds := (P[1][1] = '1');
5208 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5209 end
5210 else if cmd = 'd_frames' then
5211 begin
5212 if (Length(P) > 1) and
5213 ((P[1] = '1') or (P[1] = '0')) then
5214 g_Debug_Frames := (P[1][1] = '1');
5216 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5217 end
5218 else if cmd = 'd_winmsg' then
5219 begin
5220 if (Length(P) > 1) and
5221 ((P[1] = '1') or (P[1] = '0')) then
5222 g_Debug_WinMsgs := (P[1][1] = '1');
5224 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5225 end
5226 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5227 begin
5228 if (Length(P) > 1) and
5229 ((P[1] = '1') or (P[1] = '0')) then
5230 g_Debug_MonsterOff := (P[1][1] = '1');
5232 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5233 end
5234 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5235 begin
5236 if Length(P) > 1 then
5237 case P[1][1] of
5238 '0': g_debug_BotAIOff := 0;
5239 '1': g_debug_BotAIOff := 1;
5240 '2': g_debug_BotAIOff := 2;
5241 '3': g_debug_BotAIOff := 3;
5242 end;
5244 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5245 end
5246 else if cmd = 'd_monster' then
5247 begin
5248 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5249 if Length(P) < 2 then
5250 begin
5251 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5252 g_Console_Add('ID | Name');
5253 for b := MONSTER_DEMON to MONSTER_MAN do
5254 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5255 end else
5256 begin
5257 a := StrToIntDef(P[1], 0);
5258 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5259 a := g_Mons_TypeIdByName(P[1]);
5261 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5262 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5263 else
5264 begin
5265 with gPlayer1.Obj do
5266 begin
5267 mon := g_Monsters_Create(a,
5268 X + Rect.X + (Rect.Width div 2),
5269 Y + Rect.Y + Rect.Height,
5270 gPlayer1.Direction, True);
5271 end;
5272 if (Length(P) > 2) and (mon <> nil) then
5273 mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5274 end;
5275 end;
5276 end
5277 else if (cmd = 'd_health') then
5278 begin
5279 if (Length(P) > 1) and
5280 ((P[1] = '1') or (P[1] = '0')) then
5281 g_debug_HealthBar := (P[1][1] = '1');
5283 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5284 end
5285 else if (cmd = 'd_player') then
5286 begin
5287 if (Length(P) > 1) and
5288 ((P[1] = '1') or (P[1] = '0')) then
5289 g_debug_Player := (P[1][1] = '1');
5291 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5292 end
5293 else if (cmd = 'd_joy') then
5294 begin
5295 for a := 1 to 8 do
5296 g_Console_Add(e_JoystickStateToString(a));
5297 end
5298 else if (cmd = 'd_mem') then
5299 begin
5300 PrintHeapStats();
5301 end;
5302 end
5303 else
5304 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5305 end;
5308 procedure GameCheats(P: SArray);
5309 var
5310 cmd: string;
5311 f, a: Integer;
5312 plr: TPlayer;
5313 begin
5314 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5315 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5316 begin
5317 g_Console_Add('not available');
5318 exit;
5319 end;
5320 plr := gPlayer1;
5321 if plr = nil then
5322 begin
5323 g_Console_Add('where is the player?!');
5324 exit;
5325 end;
5326 cmd := LowerCase(P[0]);
5327 // god
5328 if cmd = 'god' then
5329 begin
5330 plr.GodMode := not plr.GodMode;
5331 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5332 exit;
5333 end;
5334 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5335 if cmd = 'give' then
5336 begin
5337 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5338 for f := 1 to High(P) do
5339 begin
5340 cmd := LowerCase(P[f]);
5341 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5342 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5343 if cmd = 'exit' then
5344 begin
5345 if gTriggers <> nil then
5346 begin
5347 for a := 0 to High(gTriggers) do
5348 begin
5349 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5350 begin
5351 g_Console_Add('player left the map');
5352 gExitByTrigger := True;
5353 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5354 g_Game_ExitLevel(gTriggers[a].tgcMap);
5355 break;
5356 end;
5357 end;
5358 end;
5359 continue;
5360 end;
5362 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5363 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5364 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5365 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5366 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5368 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5369 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5371 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5372 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;
5374 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5375 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5377 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5378 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5380 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5381 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5383 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5384 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5385 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5387 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5388 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5389 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5390 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;
5391 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5392 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5394 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;
5395 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;
5396 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;
5397 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;
5398 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;
5399 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;
5401 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5402 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;
5404 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;
5405 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;
5407 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5409 if cmd = 'ammo' then
5410 begin
5411 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5412 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5413 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5414 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5415 plr.GiveItem(ITEM_AMMO_FUELCAN);
5416 g_Console_Add('player got some ammo');
5417 continue;
5418 end;
5420 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5421 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5423 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5424 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5426 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5427 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5429 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5430 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5432 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5434 if cmd = 'weapons' then
5435 begin
5436 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5437 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5438 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5439 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5440 plr.GiveItem(ITEM_WEAPON_PLASMA);
5441 plr.GiveItem(ITEM_WEAPON_BFG);
5442 g_Console_Add('player got weapons');
5443 continue;
5444 end;
5446 if cmd = 'keys' then
5447 begin
5448 plr.GiveItem(ITEM_KEY_RED);
5449 plr.GiveItem(ITEM_KEY_GREEN);
5450 plr.GiveItem(ITEM_KEY_BLUE);
5451 g_Console_Add('player got all keys');
5452 continue;
5453 end;
5455 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5456 end;
5457 exit;
5458 end;
5459 // open
5460 if cmd = 'open' then
5461 begin
5462 g_Console_Add('player activated sesame');
5463 g_Triggers_OpenAll();
5464 exit;
5465 end;
5466 // fly
5467 if cmd = 'fly' then
5468 begin
5469 gFly := not gFly;
5470 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5471 exit;
5472 end;
5473 // noclip
5474 if cmd = 'noclip' then
5475 begin
5476 plr.SwitchNoClip;
5477 g_Console_Add('wall hardeness adjusted');
5478 exit;
5479 end;
5480 // notarget
5481 if cmd = 'notarget' then
5482 begin
5483 plr.NoTarget := not plr.NoTarget;
5484 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5485 exit;
5486 end;
5487 // noreload
5488 if cmd = 'noreload' then
5489 begin
5490 plr.NoReload := not plr.NoReload;
5491 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5492 exit;
5493 end;
5494 // speedy
5495 if cmd = 'speedy' then
5496 begin
5497 MAX_RUNVEL := 32-MAX_RUNVEL;
5498 g_Console_Add('speed adjusted');
5499 exit;
5500 end;
5501 // jumpy
5502 if cmd = 'jumpy' then
5503 begin
5504 VEL_JUMP := 30-VEL_JUMP;
5505 g_Console_Add('jump height adjusted');
5506 exit;
5507 end;
5508 // automap
5509 if cmd = 'automap' then
5510 begin
5511 gShowMap := not gShowMap;
5512 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5513 exit;
5514 end;
5515 // aimline
5516 if cmd = 'aimline' then
5517 begin
5518 gAimLine := not gAimLine;
5519 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5520 exit;
5521 end;
5522 end;
5524 procedure GameCommands(P: SArray);
5525 var
5526 a, b: Integer;
5527 s, pw: String;
5528 chstr: string;
5529 cmd: string;
5530 pl: pTNetClient = nil;
5531 plr: TPlayer;
5532 prt: Word;
5533 nm: Boolean;
5534 listen: LongWord;
5535 begin
5536 // Îáùèå êîìàíäû:
5537 cmd := LowerCase(P[0]);
5538 chstr := '';
5539 if (cmd = 'quit') or
5540 (cmd = 'exit') then
5541 begin
5542 g_Game_Free();
5543 g_Game_Quit();
5544 Exit;
5545 end
5546 else if cmd = 'pause' then
5547 begin
5548 if (g_ActiveWindow = nil) then
5549 g_Game_Pause(not gPause);
5550 end
5551 else if cmd = 'endgame' then
5552 gExit := EXIT_SIMPLE
5553 else if cmd = 'restart' then
5554 begin
5555 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5556 begin
5557 if g_Game_IsClient then
5558 begin
5559 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5560 Exit;
5561 end;
5562 g_Game_Restart();
5563 end else
5564 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5565 end
5566 else if cmd = 'kick' then
5567 begin
5568 if g_Game_IsServer then
5569 begin
5570 if Length(P) < 2 then
5571 begin
5572 g_Console_Add('kick <name>');
5573 Exit;
5574 end;
5575 if P[1] = '' then
5576 begin
5577 g_Console_Add('kick <name>');
5578 Exit;
5579 end;
5581 if g_Game_IsNet then
5582 pl := g_Net_Client_ByName(P[1]);
5583 if (pl <> nil) then
5584 begin
5585 s := g_Net_ClientName_ByID(pl^.ID);
5586 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5587 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5588 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5589 if NetUseMaster then
5590 g_Net_Slist_Update;
5591 end else if gPlayers <> nil then
5592 for a := Low(gPlayers) to High(gPlayers) do
5593 if gPlayers[a] <> nil then
5594 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5595 begin
5596 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5597 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5598 continue;
5599 gPlayers[a].Lives := 0;
5600 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5601 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5602 g_Player_Remove(gPlayers[a].UID);
5603 if NetUseMaster then
5604 g_Net_Slist_Update;
5605 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5606 g_Bot_MixNames();
5607 end;
5608 end else
5609 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5610 end
5611 else if cmd = 'kick_id' then
5612 begin
5613 if g_Game_IsServer and g_Game_IsNet then
5614 begin
5615 if Length(P) < 2 then
5616 begin
5617 g_Console_Add('kick_id <client ID>');
5618 Exit;
5619 end;
5620 if P[1] = '' then
5621 begin
5622 g_Console_Add('kick_id <client ID>');
5623 Exit;
5624 end;
5626 a := StrToIntDef(P[1], 0);
5627 if (NetClients <> nil) and (a <= High(NetClients)) then
5628 begin
5629 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5630 begin
5631 s := g_Net_ClientName_ByID(NetClients[a].ID);
5632 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5633 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5634 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5635 if NetUseMaster then
5636 g_Net_Slist_Update;
5637 end;
5638 end;
5639 end else
5640 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5641 end
5642 else if cmd = 'ban' then
5643 begin
5644 if g_Game_IsServer and g_Game_IsNet then
5645 begin
5646 if Length(P) < 2 then
5647 begin
5648 g_Console_Add('ban <name>');
5649 Exit;
5650 end;
5651 if P[1] = '' then
5652 begin
5653 g_Console_Add('ban <name>');
5654 Exit;
5655 end;
5657 pl := g_Net_Client_ByName(P[1]);
5658 if (pl <> nil) then
5659 begin
5660 s := g_Net_ClientName_ByID(pl^.ID);
5661 g_Net_BanHost(pl^.Peer^.address.host, False);
5662 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5663 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5664 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5665 if NetUseMaster then
5666 g_Net_Slist_Update;
5667 end else
5668 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5669 end else
5670 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5671 end
5672 else if cmd = 'ban_id' then
5673 begin
5674 if g_Game_IsServer and g_Game_IsNet then
5675 begin
5676 if Length(P) < 2 then
5677 begin
5678 g_Console_Add('ban_id <client ID>');
5679 Exit;
5680 end;
5681 if P[1] = '' then
5682 begin
5683 g_Console_Add('ban_id <client ID>');
5684 Exit;
5685 end;
5687 a := StrToIntDef(P[1], 0);
5688 if (NetClients <> nil) and (a <= High(NetClients)) then
5689 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5690 begin
5691 s := g_Net_ClientName_ByID(NetClients[a].ID);
5692 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5693 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5694 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5695 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5696 if NetUseMaster then
5697 g_Net_Slist_Update;
5698 end;
5699 end else
5700 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5701 end
5702 else if cmd = 'permban' then
5703 begin
5704 if g_Game_IsServer and g_Game_IsNet then
5705 begin
5706 if Length(P) < 2 then
5707 begin
5708 g_Console_Add('permban <name>');
5709 Exit;
5710 end;
5711 if P[1] = '' then
5712 begin
5713 g_Console_Add('permban <name>');
5714 Exit;
5715 end;
5717 pl := g_Net_Client_ByName(P[1]);
5718 if (pl <> nil) then
5719 begin
5720 s := g_Net_ClientName_ByID(pl^.ID);
5721 g_Net_BanHost(pl^.Peer^.address.host);
5722 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5723 g_Net_SaveBanList();
5724 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5725 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5726 if NetUseMaster then
5727 g_Net_Slist_Update;
5728 end else
5729 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5730 end else
5731 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5732 end
5733 else if cmd = 'permban_id' then
5734 begin
5735 if g_Game_IsServer and g_Game_IsNet then
5736 begin
5737 if Length(P) < 2 then
5738 begin
5739 g_Console_Add('permban_id <client ID>');
5740 Exit;
5741 end;
5742 if P[1] = '' then
5743 begin
5744 g_Console_Add('permban_id <client ID>');
5745 Exit;
5746 end;
5748 a := StrToIntDef(P[1], 0);
5749 if (NetClients <> nil) and (a <= High(NetClients)) then
5750 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5751 begin
5752 s := g_Net_ClientName_ByID(NetClients[a].ID);
5753 g_Net_BanHost(NetClients[a].Peer^.address.host);
5754 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5755 g_Net_SaveBanList();
5756 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5757 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5758 if NetUseMaster then
5759 g_Net_Slist_Update;
5760 end;
5761 end else
5762 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5763 end
5764 else if cmd = 'unban' then
5765 begin
5766 if g_Game_IsServer and g_Game_IsNet then
5767 begin
5768 if Length(P) < 2 then
5769 begin
5770 g_Console_Add('unban <IP Address>');
5771 Exit;
5772 end;
5773 if P[1] = '' then
5774 begin
5775 g_Console_Add('unban <IP Address>');
5776 Exit;
5777 end;
5779 if g_Net_UnbanHost(P[1]) then
5780 begin
5781 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5782 g_Net_SaveBanList();
5783 end else
5784 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5785 end else
5786 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5787 end
5788 else if cmd = 'clientlist' then
5789 begin
5790 if g_Game_IsServer and g_Game_IsNet then
5791 begin
5792 b := 0;
5793 if NetClients <> nil then
5794 for a := Low(NetClients) to High(NetClients) do
5795 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5796 begin
5797 plr := g_Player_Get(NetClients[a].Player);
5798 if plr = nil then continue;
5799 Inc(b);
5800 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5801 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5802 end;
5803 if b = 0 then
5804 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5805 end else
5806 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5807 end
5808 else if cmd = 'connect' then
5809 begin
5810 if (NetMode = NET_NONE) then
5811 begin
5812 if Length(P) < 2 then
5813 begin
5814 g_Console_Add('connect <IP> [port] [password]');
5815 Exit;
5816 end;
5817 if P[1] = '' then
5818 begin
5819 g_Console_Add('connect <IP> [port] [password]');
5820 Exit;
5821 end;
5823 if Length(P) > 2 then
5824 prt := StrToIntDef(P[2], 25666)
5825 else
5826 prt := 25666;
5828 if Length(P) > 3 then
5829 pw := P[3]
5830 else
5831 pw := '';
5833 g_Game_StartClient(P[1], prt, pw);
5834 end;
5835 end
5836 else if cmd = 'disconnect' then
5837 begin
5838 if (NetMode = NET_CLIENT) then
5839 g_Net_Disconnect();
5840 end
5841 else if cmd = 'reconnect' then
5842 begin
5843 if (NetMode = NET_SERVER) then
5844 Exit;
5846 if (NetMode = NET_CLIENT) then
5847 begin
5848 g_Net_Disconnect();
5849 gExit := EXIT_SIMPLE;
5850 EndGame;
5851 end;
5853 //TODO: Use last successful password to reconnect, instead of ''
5854 g_Game_StartClient(NetClientIP, NetClientPort, '');
5855 end
5856 else if (cmd = 'addbot') or
5857 (cmd = 'bot_add') then
5858 begin
5859 if Length(P) > 1 then
5860 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5861 else
5862 g_Bot_Add(TEAM_NONE, 2);
5863 end
5864 else if cmd = 'bot_addlist' then
5865 begin
5866 if Length(P) > 1 then
5867 if Length(P) = 2 then
5868 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5869 else
5870 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5871 end
5872 else if cmd = 'bot_removeall' then
5873 g_Bot_RemoveAll()
5874 else if cmd = 'chat' then
5875 begin
5876 if g_Game_IsNet then
5877 begin
5878 if Length(P) > 1 then
5879 begin
5880 for a := 1 to High(P) do
5881 chstr := chstr + P[a] + ' ';
5883 if Length(chstr) > 200 then SetLength(chstr, 200);
5885 if Length(chstr) < 1 then
5886 begin
5887 g_Console_Add('chat <text>');
5888 Exit;
5889 end;
5891 chstr := b_Text_Format(chstr);
5892 if g_Game_IsClient then
5893 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5894 else
5895 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5896 end
5897 else
5898 g_Console_Add('chat <text>');
5899 end else
5900 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5901 end
5902 else if cmd = 'teamchat' then
5903 begin
5904 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5905 begin
5906 if Length(P) > 1 then
5907 begin
5908 for a := 1 to High(P) do
5909 chstr := chstr + P[a] + ' ';
5911 if Length(chstr) > 200 then SetLength(chstr, 200);
5913 if Length(chstr) < 1 then
5914 begin
5915 g_Console_Add('teamchat <text>');
5916 Exit;
5917 end;
5919 chstr := b_Text_Format(chstr);
5920 if g_Game_IsClient then
5921 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5922 else
5923 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5924 gPlayer1Settings.Team);
5925 end
5926 else
5927 g_Console_Add('teamchat <text>');
5928 end else
5929 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5930 end
5931 else if cmd = 'game' then
5932 begin
5933 if gGameSettings.GameType <> GT_NONE then
5934 begin
5935 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5936 Exit;
5937 end;
5938 if Length(P) = 1 then
5939 begin
5940 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5941 Exit;
5942 end;
5943 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5944 P[1] := addWadExtension(P[1]);
5945 if FileExists(MapsDir + P[1]) then
5946 begin
5947 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5948 if Length(P) < 3 then
5949 begin
5950 SetLength(P, 3);
5951 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5952 end;
5954 s := P[1] + ':\' + UpperCase(P[2]);
5956 if g_Map_Exist(MapsDir + s) then
5957 begin
5958 // Çàïóñêàåì ñâîþ èãðó
5959 g_Game_Free();
5960 with gGameSettings do
5961 begin
5962 GameMode := g_Game_TextToMode(gcGameMode);
5963 if gSwitchGameMode <> GM_NONE then
5964 GameMode := gSwitchGameMode;
5965 if GameMode = GM_NONE then GameMode := GM_DM;
5966 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5967 b := 1;
5968 if Length(P) >= 4 then
5969 b := StrToIntDef(P[3], 1);
5970 g_Game_StartCustom(s, GameMode, TimeLimit,
5971 GoalLimit, MaxLives, Options, b);
5972 end;
5973 end
5974 else
5975 if P[2] = '' then
5976 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5977 else
5978 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5979 end else
5980 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5981 end
5982 else if cmd = 'host' then
5983 begin
5984 if gGameSettings.GameType <> GT_NONE then
5985 begin
5986 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5987 Exit;
5988 end;
5989 if Length(P) < 4 then
5990 begin
5991 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5992 Exit;
5993 end;
5994 if not StrToIp(P[1], listen) then
5995 Exit;
5996 prt := StrToIntDef(P[2], 25666);
5998 P[3] := addWadExtension(P[3]);
5999 if FileExists(MapsDir + P[3]) then
6000 begin
6001 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6002 if Length(P) < 5 then
6003 begin
6004 SetLength(P, 5);
6005 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
6006 end;
6008 s := P[3] + ':\' + UpperCase(P[4]);
6010 if g_Map_Exist(MapsDir + s) then
6011 begin
6012 // Çàïóñêàåì ñâîþ èãðó
6013 g_Game_Free();
6014 with gGameSettings do
6015 begin
6016 GameMode := g_Game_TextToMode(gcGameMode);
6017 if gSwitchGameMode <> GM_NONE then
6018 GameMode := gSwitchGameMode;
6019 if GameMode = GM_NONE then GameMode := GM_DM;
6020 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6021 b := 0;
6022 if Length(P) >= 6 then
6023 b := StrToIntDef(P[5], 0);
6024 g_Game_StartServer(s, GameMode, TimeLimit,
6025 GoalLimit, MaxLives, Options, b, listen, prt);
6026 end;
6027 end
6028 else
6029 if P[4] = '' then
6030 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6031 else
6032 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
6033 end else
6034 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6035 end
6036 else if cmd = 'map' then
6037 begin
6038 if Length(P) = 1 then
6039 begin
6040 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6041 begin
6042 g_Console_Add(cmd + ' <MAP>');
6043 g_Console_Add(cmd + ' <WAD> [MAP]');
6044 end else
6045 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6046 end else
6047 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6048 begin
6049 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6050 if Length(P) < 3 then
6051 begin
6052 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6053 s := UpperCase(P[1]);
6054 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6055 begin // Êàðòà íàøëàñü
6056 gExitByTrigger := False;
6057 if gGameOn then
6058 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6059 gNextMap := s;
6060 gExit := EXIT_ENDLEVELCUSTOM;
6061 end
6062 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6063 g_Game_ChangeMap(s);
6064 end else
6065 begin
6066 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6067 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6068 P[1] := addWadExtension(P[1]);
6069 if FileExists(MapsDir + P[1]) then
6070 begin
6071 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6072 SetLength(P, 3);
6073 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6075 s := P[1] + ':\' + P[2];
6077 if g_Map_Exist(MapsDir + s) then
6078 begin
6079 gExitByTrigger := False;
6080 if gGameOn then
6081 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6082 gNextMap := s;
6083 gExit := EXIT_ENDLEVELCUSTOM;
6084 end
6085 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6086 g_Game_ChangeMap(s);
6087 end else
6088 if P[2] = '' then
6089 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6090 else
6091 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6092 end else
6093 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6094 end;
6095 end else
6096 begin
6097 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6098 P[1] := addWadExtension(P[1]);
6099 if FileExists(MapsDir + P[1]) then
6100 begin
6101 // Íàøëè WAD ôàéë
6102 P[2] := UpperCase(P[2]);
6103 s := P[1] + ':\' + P[2];
6105 if g_Map_Exist(MapsDir + s) then
6106 begin // Íàøëè êàðòó
6107 gExitByTrigger := False;
6108 if gGameOn then
6109 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6110 gNextMap := s;
6111 gExit := EXIT_ENDLEVELCUSTOM;
6112 end
6113 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6114 g_Game_ChangeMap(s);
6115 end else
6116 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6117 end else
6118 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6119 end;
6120 end else
6121 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6122 end
6123 else if cmd = 'nextmap' then
6124 begin
6125 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6126 g_Console_Add(_lc[I_MSG_NOT_GAME])
6127 else begin
6128 nm := True;
6129 if Length(P) = 1 then
6130 begin
6131 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6132 begin
6133 g_Console_Add(cmd + ' <MAP>');
6134 g_Console_Add(cmd + ' <WAD> [MAP]');
6135 end else begin
6136 nm := False;
6137 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6138 end;
6139 end else
6140 begin
6141 nm := False;
6142 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6143 begin
6144 if Length(P) < 3 then
6145 begin
6146 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6147 s := UpperCase(P[1]);
6148 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6149 begin // Êàðòà íàøëàñü
6150 gExitByTrigger := False;
6151 gNextMap := s;
6152 nm := True;
6153 end else
6154 begin
6155 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6156 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6157 P[1] := addWadExtension(P[1]);
6158 if FileExists(MapsDir + P[1]) then
6159 begin
6160 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6161 SetLength(P, 3);
6162 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6164 s := P[1] + ':\' + P[2];
6166 if g_Map_Exist(MapsDir + s) then
6167 begin // Óñòàíàâëèâàåì êàðòó
6168 gExitByTrigger := False;
6169 gNextMap := s;
6170 nm := True;
6171 end else
6172 if P[2] = '' then
6173 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6174 else
6175 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6176 end else
6177 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6178 end;
6179 end else
6180 begin
6181 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6182 P[1] := addWadExtension(P[1]);
6183 if FileExists(MapsDir + P[1]) then
6184 begin
6185 // Íàøëè WAD ôàéë
6186 P[2] := UpperCase(P[2]);
6187 s := P[1] + ':\' + P[2];
6189 if g_Map_Exist(MapsDir + s) then
6190 begin // Íàøëè êàðòó
6191 gExitByTrigger := False;
6192 gNextMap := s;
6193 nm := True;
6194 end else
6195 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6196 end else
6197 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6198 end;
6199 end else
6200 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6201 end;
6202 if nm then
6203 if gNextMap = '' then
6204 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6205 else
6206 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6207 end;
6208 end
6209 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6210 begin
6211 if not gGameOn then
6212 g_Console_Add(_lc[I_MSG_NOT_GAME])
6213 else
6214 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6215 begin
6216 gExitByTrigger := False;
6217 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6218 if (gNextMap = '') and (gTriggers <> nil) then
6219 for a := 0 to High(gTriggers) do
6220 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6221 begin
6222 gExitByTrigger := True;
6223 //gNextMap := gTriggers[a].Data.MapName;
6224 gNextMap := gTriggers[a].tgcMap;
6225 Break;
6226 end;
6227 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6228 if gNextMap = '' then
6229 gNextMap := g_Game_GetNextMap();
6230 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6231 if not isWadPath(gNextMap) then
6232 s := gGameSettings.WAD + ':\' + gNextMap
6233 else
6234 s := gNextMap;
6235 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6236 if g_Map_Exist(MapsDir + s) then
6237 gExit := EXIT_ENDLEVELCUSTOM
6238 else
6239 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6240 end else
6241 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6242 end
6243 else if (cmd = 'event') then
6244 begin
6245 if (Length(P) <= 1) then
6246 begin
6247 for a := 0 to High(gEvents) do
6248 if gEvents[a].Command = '' then
6249 g_Console_Add(gEvents[a].Name + ' <none>')
6250 else
6251 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6252 Exit;
6253 end;
6254 if (Length(P) = 2) then
6255 begin
6256 for a := 0 to High(gEvents) do
6257 if gEvents[a].Name = P[1] then
6258 if gEvents[a].Command = '' then
6259 g_Console_Add(gEvents[a].Name + ' <none>')
6260 else
6261 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6262 Exit;
6263 end;
6264 for a := 0 to High(gEvents) do
6265 if gEvents[a].Name = P[1] then
6266 begin
6267 gEvents[a].Command := '';
6268 for b := 2 to High(P) do
6269 if Pos(' ', P[b]) = 0 then
6270 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6271 else
6272 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6273 gEvents[a].Command := Trim(gEvents[a].Command);
6274 Exit;
6275 end;
6276 end
6277 else if cmd = 'suicide' then
6278 begin
6279 if gGameOn then
6280 begin
6281 if g_Game_IsClient then
6282 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6283 else
6284 begin
6285 if gPlayer1 <> nil then
6286 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6287 if gPlayer2 <> nil then
6288 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6289 end;
6290 end;
6291 end
6292 // Êîìàíäû Ñâîåé èãðû:
6293 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6294 begin
6295 if cmd = 'bot_addred' then
6296 begin
6297 if Length(P) > 1 then
6298 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6299 else
6300 g_Bot_Add(TEAM_RED, 2);
6301 end
6302 else if cmd = 'bot_addblue' then
6303 begin
6304 if Length(P) > 1 then
6305 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6306 else
6307 g_Bot_Add(TEAM_BLUE, 2);
6308 end
6309 else if cmd = 'spectate' then
6310 begin
6311 if not gGameOn then
6312 Exit;
6313 g_Game_Spectate();
6314 end
6315 else if cmd = 'say' then
6316 begin
6317 if g_Game_IsServer and g_Game_IsNet then
6318 begin
6319 if Length(P) > 1 then
6320 begin
6321 chstr := '';
6322 for a := 1 to High(P) do
6323 chstr := chstr + P[a] + ' ';
6325 if Length(chstr) > 200 then SetLength(chstr, 200);
6327 if Length(chstr) < 1 then
6328 begin
6329 g_Console_Add('say <text>');
6330 Exit;
6331 end;
6333 chstr := b_Text_Format(chstr);
6334 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6335 end
6336 else g_Console_Add('say <text>');
6337 end else
6338 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6339 end
6340 else if cmd = 'tell' then
6341 begin
6342 if g_Game_IsServer and g_Game_IsNet then
6343 begin
6344 if (Length(P) > 2) and (P[1] <> '') then
6345 begin
6346 chstr := '';
6347 for a := 2 to High(P) do
6348 chstr := chstr + P[a] + ' ';
6350 if Length(chstr) > 200 then SetLength(chstr, 200);
6352 if Length(chstr) < 1 then
6353 begin
6354 g_Console_Add('tell <playername> <text>');
6355 Exit;
6356 end;
6358 pl := g_Net_Client_ByName(P[1]);
6359 if pl <> nil then
6360 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6361 else
6362 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6363 end
6364 else g_Console_Add('tell <playername> <text>');
6365 end else
6366 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6367 end
6368 else if (cmd = 'overtime') and not g_Game_IsClient then
6369 begin
6370 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6371 Exit;
6372 // Äîïîëíèòåëüíîå âðåìÿ:
6373 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6375 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6376 [gGameSettings.TimeLimit div 3600,
6377 (gGameSettings.TimeLimit div 60) mod 60,
6378 gGameSettings.TimeLimit mod 60]));
6379 if g_Game_IsNet then MH_SEND_GameSettings;
6380 end
6381 else if (cmd = 'rcon_password') and g_Game_IsClient then
6382 begin
6383 if (Length(P) <= 1) then
6384 g_Console_Add('rcon_password <password>')
6385 else
6386 MC_SEND_RCONPassword(P[1]);
6387 end
6388 else if cmd = 'rcon' then
6389 begin
6390 if g_Game_IsClient then
6391 begin
6392 if Length(P) > 1 then
6393 begin
6394 chstr := '';
6395 for a := 1 to High(P) do
6396 chstr := chstr + P[a] + ' ';
6398 if Length(chstr) > 200 then SetLength(chstr, 200);
6400 if Length(chstr) < 1 then
6401 begin
6402 g_Console_Add('rcon <command>');
6403 Exit;
6404 end;
6406 MC_SEND_RCONCommand(chstr);
6407 end
6408 else g_Console_Add('rcon <command>');
6409 end;
6410 end
6411 else if cmd = 'ready' then
6412 begin
6413 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6414 gLMSRespawnTime := gTime + 100;
6415 end
6416 else if (cmd = 'callvote') and g_Game_IsNet then
6417 begin
6418 if Length(P) > 1 then
6419 begin
6420 chstr := '';
6421 for a := 1 to High(P) do begin
6422 if a > 1 then chstr := chstr + ' ';
6423 chstr := chstr + P[a];
6424 end;
6426 if Length(chstr) > 200 then SetLength(chstr, 200);
6428 if Length(chstr) < 1 then
6429 begin
6430 g_Console_Add('callvote <command>');
6431 Exit;
6432 end;
6434 if g_Game_IsClient then
6435 MC_SEND_Vote(True, chstr)
6436 else
6437 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6438 g_Console_Process('vote', True);
6439 end
6440 else
6441 g_Console_Add('callvote <command>');
6442 end
6443 else if (cmd = 'vote') and g_Game_IsNet then
6444 begin
6445 if g_Game_IsClient then
6446 MC_SEND_Vote(False)
6447 else if gVoteInProgress then
6448 begin
6449 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6450 a := Floor((NetClientCount+1)/2.0) + 1
6451 else
6452 a := Floor(NetClientCount/2.0) + 1;
6453 if gVoted then
6454 begin
6455 Dec(gVoteCount);
6456 gVoted := False;
6457 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6458 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6459 end
6460 else
6461 begin
6462 Inc(gVoteCount);
6463 gVoted := True;
6464 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6465 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6466 g_Game_CheckVote;
6467 end;
6468 end;
6469 end
6470 end;
6471 end;
6473 procedure g_TakeScreenShot();
6474 var
6475 a: Word;
6476 FileName: string;
6477 ssdir, t: string;
6478 st: TStream;
6479 ok: Boolean;
6480 begin
6481 if e_NoGraphics then Exit;
6482 ssdir := GameDir+'/screenshots';
6483 if not findFileCI(ssdir, true) then
6484 begin
6485 // try to create dir
6486 try
6487 CreateDir(ssdir);
6488 except
6489 end;
6490 if not findFileCI(ssdir, true) then exit; // alas
6491 end;
6492 try
6493 for a := 1 to High(Word) do
6494 begin
6495 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6496 t := FileName;
6497 if findFileCI(t, true) then continue;
6498 if not findFileCI(FileName) then
6499 begin
6500 ok := false;
6501 st := createDiskFile(FileName);
6502 try
6503 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6504 ok := true;
6505 finally
6506 st.Free();
6507 end;
6508 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6509 break;
6510 end;
6511 end;
6512 except
6513 end;
6514 end;
6516 procedure g_Game_InGameMenu(Show: Boolean);
6517 begin
6518 if (g_ActiveWindow = nil) and Show then
6519 begin
6520 if gGameSettings.GameType = GT_SINGLE then
6521 g_GUI_ShowWindow('GameSingleMenu')
6522 else
6523 begin
6524 if g_Game_IsClient then
6525 g_GUI_ShowWindow('GameClientMenu')
6526 else
6527 if g_Game_IsNet then
6528 g_GUI_ShowWindow('GameServerMenu')
6529 else
6530 g_GUI_ShowWindow('GameCustomMenu');
6531 end;
6532 g_Sound_PlayEx('MENU_OPEN');
6534 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6535 if (not g_Game_IsNet) then
6536 g_Game_Pause(True);
6537 end
6538 else
6539 if (g_ActiveWindow <> nil) and (not Show) then
6540 begin
6541 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6542 if (not g_Game_IsNet) then
6543 g_Game_Pause(False);
6544 end;
6545 end;
6547 procedure g_Game_Pause(Enable: Boolean);
6548 begin
6549 if not gGameOn then
6550 Exit;
6552 if gPause = Enable then
6553 Exit;
6555 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6556 Exit;
6558 gPause := Enable;
6559 g_Game_PauseAllSounds(Enable);
6560 end;
6562 procedure g_Game_PauseAllSounds(Enable: Boolean);
6563 var
6564 i: Integer;
6565 begin
6566 // Òðèããåðû:
6567 if gTriggers <> nil then
6568 for i := 0 to High(gTriggers) do
6569 with gTriggers[i] do
6570 if (TriggerType = TRIGGER_SOUND) and
6571 (Sound <> nil) and
6572 Sound.IsPlaying() then
6573 begin
6574 Sound.Pause(Enable);
6575 end;
6577 // Çâóêè èãðîêîâ:
6578 if gPlayers <> nil then
6579 for i := 0 to High(gPlayers) do
6580 if gPlayers[i] <> nil then
6581 gPlayers[i].PauseSounds(Enable);
6583 // Ìóçûêà:
6584 if gMusic <> nil then
6585 gMusic.Pause(Enable);
6586 end;
6588 procedure g_Game_StopAllSounds(all: Boolean);
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) then
6597 Sound.Stop();
6599 if gMusic <> nil then
6600 gMusic.Stop();
6602 if all then
6603 e_StopChannels();
6604 end;
6606 procedure g_Game_UpdateTriggerSounds();
6607 var
6608 i: Integer;
6609 begin
6610 if gTriggers <> nil then
6611 for i := 0 to High(gTriggers) do
6612 with gTriggers[i] do
6613 if (TriggerType = TRIGGER_SOUND) and
6614 (Sound <> nil) and
6615 (tgcLocal) and
6616 Sound.IsPlaying() then
6617 begin
6618 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6619 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6620 begin
6621 Sound.SetPan(0.5 - tgcPan/255.0);
6622 Sound.SetVolume(tgcVolume/255.0);
6623 end
6624 else
6625 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
6626 end;
6627 end;
6629 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6630 begin
6631 Result := False;
6632 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6633 begin
6634 Result := True;
6635 Exit;
6636 end;
6637 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6638 begin
6639 Result := True;
6640 Exit;
6641 end;
6642 if gSpectMode <> SPECT_PLAYERS then
6643 Exit;
6644 if gSpectPID1 = UID then
6645 begin
6646 Result := True;
6647 Exit;
6648 end;
6649 if gSpectViewTwo and (gSpectPID2 = UID) then
6650 begin
6651 Result := True;
6652 Exit;
6653 end;
6654 end;
6656 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6657 var
6658 Pl: TPlayer;
6659 begin
6660 Result := False;
6661 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6662 begin
6663 Result := True;
6664 Exit;
6665 end;
6666 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6667 begin
6668 Result := True;
6669 Exit;
6670 end;
6671 if gSpectMode <> SPECT_PLAYERS then
6672 Exit;
6673 Pl := g_Player_Get(gSpectPID1);
6674 if (Pl <> nil) and (Pl.Team = Team) then
6675 begin
6676 Result := True;
6677 Exit;
6678 end;
6679 if gSpectViewTwo then
6680 begin
6681 Pl := g_Player_Get(gSpectPID2);
6682 if (Pl <> nil) and (Pl.Team = Team) then
6683 begin
6684 Result := True;
6685 Exit;
6686 end;
6687 end;
6688 end;
6690 procedure g_Game_Message(Msg: string; Time: Word);
6691 begin
6692 MessageText := b_Text_Format(Msg);
6693 MessageTime := Time;
6694 end;
6696 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6697 var
6698 a: Integer;
6699 begin
6700 case gAnnouncer of
6701 ANNOUNCE_NONE:
6702 Exit;
6703 ANNOUNCE_ME,
6704 ANNOUNCE_MEPLUS:
6705 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6706 Exit;
6707 end;
6708 for a := 0 to 3 do
6709 if goodsnd[a].IsPlaying() then
6710 Exit;
6712 goodsnd[Random(4)].Play();
6713 end;
6715 procedure g_Game_Announce_KillCombo(Param: Integer);
6716 var
6717 UID: Word;
6718 c, n: Byte;
6719 Pl: TPlayer;
6720 Name: String;
6721 begin
6722 UID := Param and $FFFF;
6723 c := Param shr 16;
6724 if c < 2 then
6725 Exit;
6727 Pl := g_Player_Get(UID);
6728 if Pl = nil then
6729 Name := '?'
6730 else
6731 Name := Pl.Name;
6733 case c of
6734 2: begin
6735 n := 0;
6736 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6737 end;
6738 3: begin
6739 n := 1;
6740 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6741 end;
6742 4: begin
6743 n := 2;
6744 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6745 end;
6746 else begin
6747 n := 3;
6748 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6749 end;
6750 end;
6752 case gAnnouncer of
6753 ANNOUNCE_NONE:
6754 Exit;
6755 ANNOUNCE_ME:
6756 if not g_Game_IsWatchedPlayer(UID) then
6757 Exit;
6758 ANNOUNCE_MEPLUS:
6759 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6760 Exit;
6761 end;
6763 if killsnd[n].IsPlaying() then
6764 killsnd[n].Stop();
6765 killsnd[n].Play();
6766 end;
6768 procedure g_Game_StartVote(Command, Initiator: string);
6769 var
6770 Need: Integer;
6771 begin
6772 if not gVotesEnabled then Exit;
6773 if gGameSettings.GameType <> GT_SERVER then Exit;
6774 if gVoteInProgress or gVotePassed then
6775 begin
6776 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6777 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6778 Exit;
6779 end;
6780 gVoteInProgress := True;
6781 gVotePassed := False;
6782 gVoteTimer := gTime + gVoteTimeout * 1000;
6783 gVoteCount := 0;
6784 gVoted := False;
6785 gVoteCommand := Command;
6787 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6788 Need := Floor((NetClientCount+1)/2.0)+1
6789 else
6790 Need := Floor(NetClientCount/2.0)+1;
6791 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6792 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6793 end;
6795 procedure g_Game_CheckVote;
6796 var
6797 I, Need: Integer;
6798 begin
6799 if gGameSettings.GameType <> GT_SERVER then Exit;
6800 if not gVoteInProgress then Exit;
6802 if (gTime >= gVoteTimer) then
6803 begin
6804 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6805 Need := Floor((NetClientCount+1)/2.0) + 1
6806 else
6807 Need := Floor(NetClientCount/2.0) + 1;
6808 if gVoteCount >= Need then
6809 begin
6810 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6811 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6812 gVotePassed := True;
6813 gVoteCmdTimer := gTime + 5000;
6814 end
6815 else
6816 begin
6817 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6818 MH_SEND_VoteEvent(NET_VE_FAILED);
6819 end;
6820 if NetClients <> nil then
6821 for I := Low(NetClients) to High(NetClients) do
6822 if NetClients[i].Used then
6823 NetClients[i].Voted := False;
6824 gVoteInProgress := False;
6825 gVoted := False;
6826 gVoteCount := 0;
6827 end
6828 else
6829 begin
6830 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6831 Need := Floor((NetClientCount+1)/2.0) + 1
6832 else
6833 Need := Floor(NetClientCount/2.0) + 1;
6834 if gVoteCount >= Need then
6835 begin
6836 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6837 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6838 gVoteInProgress := False;
6839 gVotePassed := True;
6840 gVoteCmdTimer := gTime + 5000;
6841 gVoted := False;
6842 gVoteCount := 0;
6843 if NetClients <> nil then
6844 for I := Low(NetClients) to High(NetClients) do
6845 if NetClients[i].Used then
6846 NetClients[i].Voted := False;
6847 end;
6848 end;
6849 end;
6851 procedure g_Game_LoadMapList(FileName: string);
6852 var
6853 ListFile: TextFile;
6854 s: string;
6855 begin
6856 MapList := nil;
6857 MapIndex := -1;
6859 if not FileExists(FileName) then Exit;
6861 AssignFile(ListFile, FileName);
6862 Reset(ListFile);
6863 while not EOF(ListFile) do
6864 begin
6865 ReadLn(ListFile, s);
6867 s := Trim(s);
6868 if s = '' then Continue;
6870 SetLength(MapList, Length(MapList)+1);
6871 MapList[High(MapList)] := s;
6872 end;
6873 CloseFile(ListFile);
6874 end;
6876 procedure g_Game_SetDebugMode();
6877 begin
6878 gDebugMode := True;
6879 // ×èòû (äàæå â ñâîåé èãðå):
6880 gCheats := True;
6881 end;
6883 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6884 var
6885 i: Word;
6886 begin
6887 if Length(LoadingStat.Msgs) = 0 then
6888 Exit;
6890 with LoadingStat do
6891 begin
6892 if not reWrite then
6893 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6894 if NextMsg = Length(Msgs) then
6895 begin // scroll
6896 for i := 0 to High(Msgs)-1 do
6897 Msgs[i] := Msgs[i+1];
6898 end
6899 else
6900 Inc(NextMsg);
6901 end else
6902 if NextMsg = 0 then
6903 Inc(NextMsg);
6905 Msgs[NextMsg-1] := Text;
6906 CurValue := 0;
6907 MaxValue := Max;
6908 ShowCount := 0;
6909 end;
6911 g_ActiveWindow := nil;
6913 ProcessLoading(true);
6914 end;
6916 procedure g_Game_StepLoading();
6917 begin
6918 with LoadingStat do
6919 begin
6920 Inc(CurValue);
6921 Inc(ShowCount);
6922 if (ShowCount > LOADING_SHOW_STEP) then
6923 begin
6924 ShowCount := 0;
6925 ProcessLoading();
6926 end;
6927 end;
6928 end;
6930 procedure g_Game_ClearLoading();
6931 var
6932 len: Word;
6933 begin
6934 with LoadingStat do
6935 begin
6936 CurValue := 0;
6937 MaxValue := 0;
6938 ShowCount := 0;
6939 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6940 if len < 1 then len := 1;
6941 SetLength(Msgs, len);
6942 for len := Low(Msgs) to High(Msgs) do
6943 Msgs[len] := '';
6944 NextMsg := 0;
6945 end;
6946 end;
6948 procedure Parse_Params(var pars: TParamStrValues);
6949 var
6950 i: Integer;
6951 s: String;
6952 begin
6953 SetLength(pars, 0);
6954 i := 1;
6955 while i <= ParamCount do
6956 begin
6957 s := ParamStr(i);
6958 if (s[1] = '-') and (Length(s) > 1) then
6959 begin
6960 if (s[2] = '-') and (Length(s) > 2) then
6961 begin // Îäèíî÷íûé ïàðàìåòð
6962 SetLength(pars, Length(pars) + 1);
6963 with pars[High(pars)] do
6964 begin
6965 Name := LowerCase(s);
6966 Value := '+';
6967 end;
6968 end
6969 else
6970 if (i < ParamCount) then
6971 begin // Ïàðàìåòð ñî çíà÷åíèåì
6972 Inc(i);
6973 SetLength(pars, Length(pars) + 1);
6974 with pars[High(pars)] do
6975 begin
6976 Name := LowerCase(s);
6977 Value := LowerCase(ParamStr(i));
6978 end;
6979 end;
6980 end;
6982 Inc(i);
6983 end;
6984 end;
6986 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6987 var
6988 i: Integer;
6989 begin
6990 Result := '';
6991 for i := 0 to High(pars) do
6992 if pars[i].Name = aName then
6993 begin
6994 Result := pars[i].Value;
6995 Break;
6996 end;
6997 end;
6999 procedure g_Game_Process_Params();
7000 var
7001 pars: TParamStrValues;
7002 map: String;
7003 GMode, n: Byte;
7004 LimT, LimS: Integer;
7005 Opt: LongWord;
7006 Lives: Integer;
7007 s: String;
7008 Port: Integer;
7009 ip: String;
7010 F: TextFile;
7011 begin
7012 Parse_Params(pars);
7014 // Debug mode:
7015 s := Find_Param_Value(pars, '--debug');
7016 if (s <> '') then
7017 begin
7018 g_Game_SetDebugMode();
7019 s := Find_Param_Value(pars, '--netdump');
7020 if (s <> '') then
7021 NetDump := True;
7022 end;
7024 // Connect when game loads
7025 ip := Find_Param_Value(pars, '-connect');
7027 if ip <> '' then
7028 begin
7029 s := Find_Param_Value(pars, '-port');
7030 if (s = '') or not TryStrToInt(s, Port) then
7031 Port := 25666;
7033 s := Find_Param_Value(pars, '-pw');
7035 g_Game_StartClient(ip, Port, s);
7036 Exit;
7037 end;
7039 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7040 if (s <> '') then
7041 begin
7042 gDefaultMegawadStart := s;
7043 end;
7045 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7046 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7047 begin
7048 gDefaultMegawadStart := DF_Default_Megawad_Start;
7049 end;
7051 // Start map when game loads:
7052 map := LowerCase(Find_Param_Value(pars, '-map'));
7053 if isWadPath(map) then
7054 begin
7055 // Game mode:
7056 s := Find_Param_Value(pars, '-gm');
7057 GMode := g_Game_TextToMode(s);
7058 if GMode = GM_NONE then GMode := GM_DM;
7059 if GMode = GM_SINGLE then GMode := GM_COOP;
7061 // Time limit:
7062 s := Find_Param_Value(pars, '-limt');
7063 if (s = '') or (not TryStrToInt(s, LimT)) then
7064 LimT := 0;
7065 if LimT < 0 then
7066 LimT := 0;
7068 // Goal limit:
7069 s := Find_Param_Value(pars, '-lims');
7070 if (s = '') or (not TryStrToInt(s, LimS)) then
7071 LimS := 0;
7072 if LimS < 0 then
7073 LimS := 0;
7075 // Lives limit:
7076 s := Find_Param_Value(pars, '-lives');
7077 if (s = '') or (not TryStrToInt(s, Lives)) then
7078 Lives := 0;
7079 if Lives < 0 then
7080 Lives := 0;
7082 // Options:
7083 s := Find_Param_Value(pars, '-opt');
7084 if (s = '') then
7085 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7086 else
7087 Opt := StrToIntDef(s, 0);
7088 if Opt = 0 then
7089 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7091 // Close after map:
7092 s := Find_Param_Value(pars, '--close');
7093 if (s <> '') then
7094 gMapOnce := True;
7096 // Delete test map after play:
7097 s := Find_Param_Value(pars, '--testdelete');
7098 if (s <> '') then
7099 begin
7100 gMapToDelete := MapsDir + map;
7101 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
7102 Halt(1);
7103 end;
7105 // Delete temporary WAD after play:
7106 s := Find_Param_Value(pars, '--tempdelete');
7107 if (s <> '') then
7108 begin
7109 gMapToDelete := MapsDir + map;
7110 gTempDelete := True;
7111 end;
7113 // Number of players:
7114 s := Find_Param_Value(pars, '-pl');
7115 if (s = '') then
7116 n := 1
7117 else
7118 n := StrToIntDef(s, 1);
7120 // Start:
7121 s := Find_Param_Value(pars, '-port');
7122 if (s = '') or not TryStrToInt(s, Port) then
7123 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7124 else
7125 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7126 end;
7128 // Execute script when game loads:
7129 s := Find_Param_Value(pars, '-exec');
7130 if s <> '' then
7131 begin
7132 if not isWadPath(s) then
7133 s := GameDir + '/' + s;
7135 {$I-}
7136 AssignFile(F, s);
7137 Reset(F);
7138 if IOResult <> 0 then
7139 begin
7140 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7141 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7142 CloseFile(F);
7143 Exit;
7144 end;
7145 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
7146 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7148 while not EOF(F) do
7149 begin
7150 ReadLn(F, s);
7151 if IOResult <> 0 then
7152 begin
7153 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7154 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7155 CloseFile(F);
7156 Exit;
7157 end;
7158 if Pos('#', s) <> 1 then // script comment
7159 g_Console_Process(s, True);
7160 end;
7162 CloseFile(F);
7163 {$I+}
7164 end;
7166 SetLength(pars, 0);
7167 end;
7169 begin
7170 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7171 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7172 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7173 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7175 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7176 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7177 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7178 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7180 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7181 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7183 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7184 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7186 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7188 conRegVar('dbg_scale', @g_dbg_scale, 0.01, 100.0, 'experimental deBUG scale mode', '', false);
7189 end.