DEADSOFTWARE

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