DEADSOFTWARE

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