DEADSOFTWARE

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