DEADSOFTWARE

Map: Add test map override feature
[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 e_PollInput();
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_RB', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
2058 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
2059 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
2060 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
2061 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
2062 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD', 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 e_PollInput();
2186 if e_KeyPressed(IK_TAB) then
2187 begin
2188 if not gStatsPressed then
2189 begin
2190 gStatsOff := not gStatsOff;
2191 gStatsPressed := True;
2192 end;
2193 end
2194 else
2195 gStatsPressed := False;
2197 if gStatsOff then
2198 begin
2199 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2200 w := (Length(s1) * ww2) div 2;
2201 x := gScreenWidth div 2 - w;
2202 y := 8;
2203 e_TextureFontPrint(x, y, s1, gStdFont);
2204 Exit;
2205 end;
2207 if (gGameSettings.GameMode = GM_COOP) then
2208 begin
2209 if gMissionFailed then
2210 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2211 else
2212 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2213 end
2214 else
2215 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2217 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2218 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2220 if g_Game_IsNet then
2221 begin
2222 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2223 if not gChatShow then
2224 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2225 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2226 end;
2228 if g_Game_IsClient then
2229 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2230 else
2231 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2232 if not gChatShow then
2233 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2234 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2236 x := 32;
2237 y := 16+hh1+16;
2239 w := gScreenWidth-x*2;
2241 w2 := (w-16) div 6;
2242 w3 := w2;
2243 w1 := w-16-w2-w3;
2245 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2246 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2248 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2250 case CustomStat.GameMode of
2251 GM_DM:
2252 begin
2253 if gGameSettings.MaxLives = 0 then
2254 s1 := _lc[I_GAME_DM]
2255 else
2256 s1 := _lc[I_GAME_LMS];
2257 end;
2258 GM_TDM:
2259 begin
2260 if gGameSettings.MaxLives = 0 then
2261 s1 := _lc[I_GAME_TDM]
2262 else
2263 s1 := _lc[I_GAME_TLMS];
2264 end;
2265 GM_CTF: s1 := _lc[I_GAME_CTF];
2266 GM_COOP:
2267 begin
2268 if gGameSettings.MaxLives = 0 then
2269 s1 := _lc[I_GAME_COOP]
2270 else
2271 s1 := _lc[I_GAME_SURV];
2272 end;
2273 else s1 := '';
2274 end;
2276 _y := y+16;
2277 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2278 _y := _y+8;
2280 _y := _y+16;
2281 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2282 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2284 _y := _y+16;
2285 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2286 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2287 (CustomStat.GameTime div 1000 div 60) mod 60,
2288 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2290 pc := Length(CustomStat.PlayerStat);
2291 if pc = 0 then Exit;
2293 if CustomStat.GameMode = GM_COOP then
2294 begin
2295 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2296 _y := _y+32;
2297 s2 := _lc[I_GAME_MONSTERS];
2298 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2299 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2300 _y := _y+16;
2301 s2 := _lc[I_GAME_SECRETS];
2302 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2303 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2304 if gLastMap then
2305 begin
2306 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2307 _y := _y-16;
2308 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2309 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2310 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2311 _y := _y+16;
2312 s2 := _lc[I_GAME_SECRETS_TOTAL];
2313 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2314 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2315 end;
2316 end;
2318 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2319 begin
2320 _y := _y+16+16;
2322 with CustomStat do
2323 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2324 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2325 else s1 := _lc[I_GAME_WIN_DRAW];
2327 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2328 _y := _y+40;
2330 for t := TEAM_RED to TEAM_BLUE do
2331 begin
2332 if t = TEAM_RED then
2333 begin
2334 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2335 gStdFont, 255, 0, 0, 1);
2336 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2337 gStdFont, 255, 0, 0, 1);
2338 r := 255;
2339 g := 0;
2340 b := 0;
2341 end
2342 else
2343 begin
2344 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2345 gStdFont, 0, 0, 255, 1);
2346 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2347 gStdFont, 0, 0, 255, 1);
2348 r := 0;
2349 g := 0;
2350 b := 255;
2351 end;
2353 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2354 _y := _y+24;
2356 for p := 0 to High(CustomStat.PlayerStat) do
2357 if CustomStat.PlayerStat[p].Team = t then
2358 with CustomStat.PlayerStat[p] do
2359 begin
2360 if Spectator then
2361 begin
2362 rr := r div 2;
2363 gg := g div 2;
2364 bb := b div 2;
2365 end
2366 else
2367 begin
2368 rr := r;
2369 gg := g;
2370 bb := b;
2371 end;
2372 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2373 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2374 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2375 _y := _y+24;
2376 end;
2378 _y := _y+16+16;
2379 end;
2380 end
2381 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2382 begin
2383 _y := _y+40;
2384 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2385 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2386 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2388 _y := _y+24;
2389 for p := 0 to High(CustomStat.PlayerStat) do
2390 with CustomStat.PlayerStat[p] do
2391 begin
2392 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2394 if Spectator then
2395 r := 127
2396 else
2397 r := 255;
2399 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2400 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2401 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2402 _y := _y+24;
2403 end;
2404 end;
2405 end;
2407 procedure DrawSingleStat();
2408 var
2409 tm, key_x, val_x, y: Integer;
2410 w1, w2, h: Word;
2411 s1, s2: String;
2413 procedure player_stat(n: Integer);
2414 var
2415 kpm: Real;
2417 begin
2418 // "Kills: # / #":
2419 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2420 s2 := Format(' %d', [gTotalMonsters]);
2422 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2423 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2424 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2425 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2426 s1 := s1 + '/';
2427 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2428 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2430 // "Kills-per-minute: ##.#":
2431 s1 := _lc[I_MENU_INTER_KPM];
2432 if tm > 0 then
2433 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2434 else
2435 kpm := SingleStat.PlayerStat[n].Kills;
2436 s2 := Format(' %.1f', [kpm]);
2438 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2439 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2441 // "Secrets found: # / #":
2442 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2443 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2445 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2446 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2447 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2448 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2449 s1 := s1 + '/';
2450 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2451 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2452 end;
2454 begin
2455 // "Level Complete":
2456 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2457 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2459 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2460 s1 := _lc[I_MENU_INTER_KPM];
2461 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2462 Inc(w1, 16);
2463 s1 := ' 9999.9';
2464 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2466 key_x := (gScreenWidth-w1-w2) div 2;
2467 val_x := key_x + w1;
2469 // "Time: #:##:##":
2470 tm := SingleStat.GameTime div 1000;
2471 s1 := _lc[I_MENU_INTER_TIME];
2472 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2474 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2475 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2477 if SingleStat.TwoPlayers then
2478 begin
2479 // "Player 1":
2480 s1 := _lc[I_MENU_PLAYER_1];
2481 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2482 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2484 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2485 y := 176;
2486 player_stat(0);
2488 // "Player 2":
2489 s1 := _lc[I_MENU_PLAYER_2];
2490 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2491 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2493 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2494 y := 336;
2495 player_stat(1);
2496 end
2497 else
2498 begin
2499 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2500 y := 128;
2501 player_stat(0);
2502 end;
2503 end;
2505 procedure DrawLoadingStat();
2506 procedure drawRect (x, y, w, h: Integer);
2507 begin
2508 if (w < 1) or (h < 1) then exit;
2509 glBegin(GL_QUADS);
2510 glVertex2f(x+0.375, y+0.375);
2511 glVertex2f(x+w+0.375, y+0.375);
2512 glVertex2f(x+w+0.375, y+h+0.375);
2513 glVertex2f(x+0.375, y+h+0.375);
2514 glEnd();
2515 end;
2517 procedure drawPBar (cur, total: Integer);
2518 var
2519 rectW, rectH: Integer;
2520 x0, y0: Integer;
2521 wdt: Integer;
2522 begin
2523 if (total < 1) then exit;
2524 if (cur < 1) then exit; // don't blink
2525 if (cur >= total) then exit; // don't blink
2526 //if (cur < 0) then cur := 0;
2527 //if (cur > total) then cur := total;
2529 rectW := gScreenWidth-64;
2530 rectH := 16;
2532 x0 := (gScreenWidth-rectW) div 2;
2533 y0 := gScreenHeight-rectH-64;
2534 if (y0 < 2) then y0 := 2;
2536 glDisable(GL_BLEND);
2537 glDisable(GL_TEXTURE_2D);
2539 //glClearColor(0, 0, 0, 0);
2540 //glClear(GL_COLOR_BUFFER_BIT);
2542 glColor4ub(127, 127, 127, 255);
2543 drawRect(x0-2, y0-2, rectW+4, rectH+4);
2545 glColor4ub(0, 0, 0, 255);
2546 drawRect(x0-1, y0-1, rectW+2, rectH+2);
2548 glColor4ub(127, 127, 127, 255);
2549 wdt := rectW*cur div total;
2550 if (wdt > rectW) then wdt := rectW;
2551 drawRect(x0, y0, wdt, rectH);
2552 end;
2554 var
2555 ww, hh: Word;
2556 xx, yy, i: Integer;
2557 s: String;
2558 begin
2559 if (Length(LoadingStat.Msgs) = 0) then exit;
2561 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2562 yy := (gScreenHeight div 3);
2563 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2564 xx := (gScreenWidth div 3);
2566 with LoadingStat do
2567 begin
2568 for i := 0 to NextMsg-1 do
2569 begin
2570 if (i = (NextMsg-1)) and (MaxValue > 0) then
2571 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2572 else
2573 s := Msgs[i];
2575 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2576 yy := yy + LOADING_INTERLINE;
2577 drawPBar(CurValue, MaxValue);
2578 end;
2579 end;
2580 end;
2582 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2583 var
2584 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2586 function monDraw (mon: TMonster): Boolean;
2587 begin
2588 result := false; // don't stop
2589 with mon do
2590 begin
2591 if alive then
2592 begin
2593 // Ëåâûé âåðõíèé óãîë
2594 aX := Obj.X div ScaleSz + 1;
2595 aY := Obj.Y div ScaleSz + 1;
2596 // Ðàçìåðû
2597 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2598 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2599 // Ïðàâûé íèæíèé óãîë
2600 aX2 := aX + aX2 - 1;
2601 aY2 := aY + aY2 - 1;
2602 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2603 end;
2604 end;
2605 end;
2607 begin
2608 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2609 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2610 begin
2611 Scale := 1;
2612 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2613 ScaleSz := 16 div Scale;
2614 // Ðàçìåðû ìèíè-êàðòû:
2615 aX := max(gMapInfo.Width div ScaleSz, 1);
2616 aY := max(gMapInfo.Height div ScaleSz, 1);
2617 // Ðàìêà êàðòû:
2618 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2620 if gWalls <> nil then
2621 begin
2622 // Ðèñóåì ñòåíû:
2623 for a := 0 to High(gWalls) do
2624 with gWalls[a] do
2625 if PanelType <> 0 then
2626 begin
2627 // Ëåâûé âåðõíèé óãîë:
2628 aX := X div ScaleSz;
2629 aY := Y div ScaleSz;
2630 // Ðàçìåðû:
2631 aX2 := max(Width div ScaleSz, 1);
2632 aY2 := max(Height div ScaleSz, 1);
2633 // Ïðàâûé íèæíèé óãîë:
2634 aX2 := aX + aX2 - 1;
2635 aY2 := aY + aY2 - 1;
2637 case PanelType of
2638 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2639 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2640 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2641 end;
2642 end;
2643 end;
2644 if gSteps <> nil then
2645 begin
2646 // Ðèñóåì ñòóïåíè:
2647 for a := 0 to High(gSteps) do
2648 with gSteps[a] do
2649 if PanelType <> 0 then
2650 begin
2651 // Ëåâûé âåðõíèé óãîë:
2652 aX := X div ScaleSz;
2653 aY := Y div ScaleSz;
2654 // Ðàçìåðû:
2655 aX2 := max(Width div ScaleSz, 1);
2656 aY2 := max(Height div ScaleSz, 1);
2657 // Ïðàâûé íèæíèé óãîë:
2658 aX2 := aX + aX2 - 1;
2659 aY2 := aY + aY2 - 1;
2661 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2662 end;
2663 end;
2664 if gLifts <> nil then
2665 begin
2666 // Ðèñóåì ëèôòû:
2667 for a := 0 to High(gLifts) do
2668 with gLifts[a] do
2669 if PanelType <> 0 then
2670 begin
2671 // Ëåâûé âåðõíèé óãîë:
2672 aX := X div ScaleSz;
2673 aY := Y div ScaleSz;
2674 // Ðàçìåðû:
2675 aX2 := max(Width div ScaleSz, 1);
2676 aY2 := max(Height div ScaleSz, 1);
2677 // Ïðàâûé íèæíèé óãîë:
2678 aX2 := aX + aX2 - 1;
2679 aY2 := aY + aY2 - 1;
2681 case LiftType of
2682 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2683 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2684 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2685 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2686 end;
2687 end;
2688 end;
2689 if gWater <> nil then
2690 begin
2691 // Ðèñóåì âîäó:
2692 for a := 0 to High(gWater) do
2693 with gWater[a] do
2694 if PanelType <> 0 then
2695 begin
2696 // Ëåâûé âåðõíèé óãîë:
2697 aX := X div ScaleSz;
2698 aY := Y div ScaleSz;
2699 // Ðàçìåðû:
2700 aX2 := max(Width div ScaleSz, 1);
2701 aY2 := max(Height div ScaleSz, 1);
2702 // Ïðàâûé íèæíèé óãîë:
2703 aX2 := aX + aX2 - 1;
2704 aY2 := aY + aY2 - 1;
2706 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2707 end;
2708 end;
2709 if gAcid1 <> nil then
2710 begin
2711 // Ðèñóåì êèñëîòó 1:
2712 for a := 0 to High(gAcid1) do
2713 with gAcid1[a] do
2714 if PanelType <> 0 then
2715 begin
2716 // Ëåâûé âåðõíèé óãîë:
2717 aX := X div ScaleSz;
2718 aY := Y div ScaleSz;
2719 // Ðàçìåðû:
2720 aX2 := max(Width div ScaleSz, 1);
2721 aY2 := max(Height div ScaleSz, 1);
2722 // Ïðàâûé íèæíèé óãîë:
2723 aX2 := aX + aX2 - 1;
2724 aY2 := aY + aY2 - 1;
2726 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2727 end;
2728 end;
2729 if gAcid2 <> nil then
2730 begin
2731 // Ðèñóåì êèñëîòó 2:
2732 for a := 0 to High(gAcid2) do
2733 with gAcid2[a] do
2734 if PanelType <> 0 then
2735 begin
2736 // Ëåâûé âåðõíèé óãîë:
2737 aX := X div ScaleSz;
2738 aY := Y div ScaleSz;
2739 // Ðàçìåðû:
2740 aX2 := max(Width div ScaleSz, 1);
2741 aY2 := max(Height div ScaleSz, 1);
2742 // Ïðàâûé íèæíèé óãîë:
2743 aX2 := aX + aX2 - 1;
2744 aY2 := aY + aY2 - 1;
2746 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2747 end;
2748 end;
2749 if gPlayers <> nil then
2750 begin
2751 // Ðèñóåì èãðîêîâ:
2752 for a := 0 to High(gPlayers) do
2753 if gPlayers[a] <> nil then with gPlayers[a] do
2754 if alive then begin
2755 // Ëåâûé âåðõíèé óãîë:
2756 aX := Obj.X div ScaleSz + 1;
2757 aY := Obj.Y div ScaleSz + 1;
2758 // Ðàçìåðû:
2759 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2760 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2761 // Ïðàâûé íèæíèé óãîë:
2762 aX2 := aX + aX2 - 1;
2763 aY2 := aY + aY2 - 1;
2765 if gPlayers[a] = p then
2766 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2767 else
2768 case Team of
2769 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2770 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2771 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2772 end;
2773 end;
2774 end;
2775 // Ðèñóåì ìîíñòðîâ
2776 g_Mons_ForEach(monDraw);
2777 end;
2778 end;
2781 procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor);
2782 begin
2783 if not hasAmbient then exit;
2784 e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
2785 end;
2788 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2789 //FIXME: broken for splitscreen mode
2790 procedure renderDynLightsInternal ();
2791 var
2792 //hasAmbient: Boolean;
2793 //ambColor: TDFColor;
2794 lln: Integer;
2795 lx, ly, lrad: Integer;
2796 scxywh: array[0..3] of GLint;
2797 wassc: Boolean;
2798 begin
2799 if e_NoGraphics then exit;
2801 //TODO: lights should be in separate grid, i think
2802 // but on the other side: grid may be slower for dynlights, as their lifetime is short
2803 if (not gwin_k8_enable_light_experiments) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit;
2805 // rendering mode
2806 //ambColor := gCurrentMap['light_ambient'].rgba;
2807 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
2809 { // this will multiply incoming color to alpha from framebuffer
2810 glEnable(GL_BLEND);
2811 glBlendFunc(GL_DST_ALPHA, GL_ONE);
2814 (*
2815 * light rendering: (INVALID!)
2816 * glStencilFunc(GL_EQUAL, 0, $ff);
2817 * for each light:
2818 * glClear(GL_STENCIL_BUFFER_BIT);
2819 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2820 * draw shadow volume into stencil buffer
2821 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2822 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
2823 * turn off blending
2824 * draw color-less quad with light alpha (WARNING! don't touch color!)
2825 * glEnable(GL_BLEND);
2826 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
2827 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
2828 *)
2830 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
2831 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
2833 // setup OpenGL parameters
2834 glStencilMask($FFFFFFFF);
2835 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
2836 glEnable(GL_STENCIL_TEST);
2837 glEnable(GL_SCISSOR_TEST);
2838 glClear(GL_STENCIL_BUFFER_BIT);
2839 glStencilFunc(GL_EQUAL, 0, $ff);
2841 for lln := 0 to g_dynLightCount-1 do
2842 begin
2843 lx := g_dynLights[lln].x;
2844 ly := g_dynLights[lln].y;
2845 lrad := g_dynLights[lln].radius;
2846 if (lrad < 3) then continue;
2848 if (lx-sX+lrad < 0) then continue;
2849 if (ly-sY+lrad < 0) then continue;
2850 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
2851 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
2853 // set scissor to optimize drawing
2854 if (g_dbg_scale = 1.0) then
2855 begin
2856 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
2857 end
2858 else
2859 begin
2860 glScissor(0, 0, gWinSizeX, gWinSizeY);
2861 end;
2862 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
2863 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
2864 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2865 // draw extruded panels
2866 glDisable(GL_TEXTURE_2D);
2867 glDisable(GL_BLEND);
2868 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
2869 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
2870 // render light texture
2871 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2872 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
2873 // blend it
2874 glEnable(GL_BLEND);
2875 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2876 glEnable(GL_TEXTURE_2D);
2877 // color and opacity
2878 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
2879 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
2880 glBegin(GL_QUADS);
2881 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
2882 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
2883 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
2884 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
2885 glEnd();
2886 end;
2888 // done
2889 glDisable(GL_STENCIL_TEST);
2890 glDisable(GL_BLEND);
2891 glDisable(GL_SCISSOR_TEST);
2892 //glScissor(0, 0, sWidth, sHeight);
2894 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
2895 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
2896 end;
2899 function fixViewportForScale (): Boolean;
2900 var
2901 nx0, ny0, nw, nh: Integer;
2902 begin
2903 result := false;
2904 if (g_dbg_scale <> 1.0) then
2905 begin
2906 result := true;
2907 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
2908 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
2909 nw := round(sWidth/g_dbg_scale);
2910 nh := round(sHeight/g_dbg_scale);
2911 sX := nx0;
2912 sY := ny0;
2913 sWidth := nw;
2914 sHeight := nh;
2915 end;
2916 end;
2919 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2920 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
2921 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
2922 type
2923 TDrawCB = procedure ();
2925 var
2926 hasAmbient: Boolean;
2927 ambColor: TDFColor;
2928 doAmbient: Boolean = false;
2930 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
2931 var
2932 tagmask: Integer;
2933 pan: TPanel;
2934 begin
2935 profileFrameDraw.sectionBegin(profname);
2936 if gdbg_map_use_accel_render then
2937 begin
2938 tagmask := panelTypeToTag(panType);
2939 while (gDrawPanelList.count > 0) do
2940 begin
2941 pan := TPanel(gDrawPanelList.front());
2942 if ((pan.tag and tagmask) = 0) then break;
2943 if doDraw then pan.Draw(doAmbient, ambColor);
2944 gDrawPanelList.popFront();
2945 end;
2946 end
2947 else
2948 begin
2949 if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor);
2950 end;
2951 profileFrameDraw.sectionEnd();
2952 end;
2954 procedure drawOther (profname: AnsiString; cb: TDrawCB);
2955 begin
2956 profileFrameDraw.sectionBegin(profname);
2957 if assigned(cb) then cb();
2958 profileFrameDraw.sectionEnd();
2959 end;
2961 begin
2962 profileFrameDraw.sectionBegin('total');
2964 // our accelerated renderer will collect all panels to gDrawPanelList
2965 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
2966 profileFrameDraw.sectionBegin('collect');
2967 if gdbg_map_use_accel_render then
2968 begin
2969 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
2970 end;
2971 profileFrameDraw.sectionEnd();
2973 profileFrameDraw.sectionBegin('skyback');
2974 g_Map_DrawBack(backXOfs, backYOfs);
2975 profileFrameDraw.sectionEnd();
2977 if setTransMatrix then
2978 begin
2979 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
2980 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
2981 glTranslatef(-sX, -sY, 0);
2982 end;
2984 // rendering mode
2985 ambColor := gCurrentMap['light_ambient'].rgba;
2986 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
2989 if hasAmbient then
2990 begin
2991 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
2992 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
2993 glClear(GL_COLOR_BUFFER_BIT);
2994 end;
2996 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
2999 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
3000 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
3001 drawOther('items', @g_Items_Draw);
3002 drawOther('weapons', @g_Weapon_Draw);
3003 drawOther('shells', @g_Player_DrawShells);
3004 drawOther('drawall', @g_Player_DrawAll);
3005 drawOther('corpses', @g_Player_DrawCorpses);
3006 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
3007 drawOther('monsters', @g_Monsters_Draw);
3008 drawOther('itemdrop', @g_Items_DrawDrop);
3009 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
3010 drawOther('gfx', @g_GFX_Draw);
3011 drawOther('flags', @g_Map_DrawFlags);
3012 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
3013 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
3014 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
3015 drawOther('dynlights', @renderDynLightsInternal);
3017 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3018 begin
3019 renderAmbientQuad(hasAmbient, ambColor);
3020 end;
3022 doAmbient := true;
3023 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
3026 if g_debug_HealthBar then
3027 begin
3028 g_Monsters_DrawHealth();
3029 g_Player_DrawHealth();
3030 end;
3032 profileFrameDraw.mainEnd(); // map rendering
3033 end;
3036 procedure DrawMapView(x, y, w, h: Integer);
3038 var
3039 bx, by: Integer;
3040 begin
3041 glPushMatrix();
3043 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
3044 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
3046 sX := x;
3047 sY := y;
3048 sWidth := w;
3049 sHeight := h;
3051 fixViewportForScale();
3052 renderMapInternal(-bx, -by, true);
3054 glPopMatrix();
3055 end;
3058 procedure DrawPlayer(p: TPlayer);
3059 var
3060 px, py, a, b, c, d: Integer;
3061 //R: TRect;
3062 begin
3063 if (p = nil) or (p.FDummy) then
3064 begin
3065 glPushMatrix();
3066 g_Map_DrawBack(0, 0);
3067 glPopMatrix();
3068 Exit;
3069 end;
3071 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3072 profileFrameDraw.mainBegin(g_profile_frame_draw);
3074 gPlayerDrawn := p;
3076 glPushMatrix();
3078 px := p.GameX + PLAYER_RECT_CX;
3079 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
3081 if (g_dbg_scale = 1.0) then
3082 begin
3083 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3084 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3086 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3087 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3089 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3090 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3091 begin
3092 // hcenter
3093 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3094 end;
3096 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3097 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3098 begin
3099 // vcenter
3100 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3101 end;
3102 end
3103 else
3104 begin
3105 // scaled, ignore level bounds
3106 a := -px+(gPlayerScreenSize.X div 2);
3107 b := -py+(gPlayerScreenSize.Y div 2);
3108 end;
3110 if p.IncCam <> 0 then
3111 begin
3112 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
3113 begin
3114 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
3115 begin
3116 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
3117 end;
3118 end;
3120 if py < gPlayerScreenSize.Y div 2 then
3121 begin
3122 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
3123 begin
3124 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
3125 end;
3126 end;
3128 if p.IncCam < 0 then
3129 begin
3130 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
3131 end;
3133 if p.IncCam > 0 then
3134 begin
3135 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
3136 end;
3137 end;
3139 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
3140 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
3141 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
3143 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
3144 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
3145 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
3147 sX := -a;
3148 sY := -(b+p.IncCam);
3149 sWidth := gPlayerScreenSize.X;
3150 sHeight := gPlayerScreenSize.Y;
3152 //glTranslatef(a, b+p.IncCam, 0);
3154 //if (p = gPlayer1) and (g_dbg_scale >= 1.0) then g_Holmes_plrViewSize(sWidth, sHeight);
3156 //conwritefln('OLD: (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3157 fixViewportForScale();
3158 //conwritefln(' (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3159 p.viewPortX := sX;
3160 p.viewPortY := sY;
3161 p.viewPortW := sWidth;
3162 p.viewPortH := sHeight;
3164 if (p = gPlayer1) then
3165 begin
3166 g_Holmes_plrViewPos(sX, sY);
3167 g_Holmes_plrViewSize(sWidth, sHeight);
3168 end;
3170 renderMapInternal(-c, -d, true);
3172 if p.FSpectator then
3173 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3174 p.GameY + PLAYER_RECT_CY - 4,
3175 'X', gStdFont, 255, 255, 255, 1, True);
3177 for a := 0 to High(gCollideMap) do
3178 for b := 0 to High(gCollideMap[a]) do
3179 begin
3180 d := 0;
3181 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3182 d := d + 1;
3183 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3184 d := d + 2;
3186 case d of
3187 1: e_DrawPoint(1, b, a, 200, 200, 200);
3188 2: e_DrawPoint(1, b, a, 64, 64, 255);
3189 3: e_DrawPoint(1, b, a, 255, 0, 255);
3190 end;
3191 end;
3194 glPopMatrix();
3196 p.DrawPain();
3197 p.DrawPickup();
3198 p.DrawRulez();
3199 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3200 if g_Debug_Player then
3201 g_Player_DrawDebug(p);
3202 p.DrawGUI();
3203 end;
3205 procedure drawProfilers ();
3206 var
3207 px: Integer = -1;
3208 py: Integer = -1;
3209 begin
3210 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
3211 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3212 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3213 end;
3215 procedure g_Game_Draw();
3216 var
3217 ID: DWORD;
3218 w, h: Word;
3219 ww, hh: Byte;
3220 Time: Int64;
3221 back: string;
3222 plView1, plView2: TPlayer;
3223 Split: Boolean;
3224 begin
3225 if gExit = EXIT_QUIT then Exit;
3227 Time := GetTimer() {div 1000};
3228 FPSCounter := FPSCounter+1;
3229 if Time - FPSTime >= 1000 then
3230 begin
3231 FPS := FPSCounter;
3232 FPSCounter := 0;
3233 FPSTime := Time;
3234 end;
3236 if gGameOn or (gState = STATE_FOLD) then
3237 begin
3238 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3239 begin
3240 gSpectMode := SPECT_NONE;
3241 if not gRevertPlayers then
3242 begin
3243 plView1 := gPlayer1;
3244 plView2 := gPlayer2;
3245 end
3246 else
3247 begin
3248 plView1 := gPlayer2;
3249 plView2 := gPlayer1;
3250 end;
3251 end
3252 else
3253 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3254 begin
3255 gSpectMode := SPECT_NONE;
3256 if gPlayer2 = nil then
3257 plView1 := gPlayer1
3258 else
3259 plView1 := gPlayer2;
3260 plView2 := nil;
3261 end
3262 else
3263 begin
3264 plView1 := nil;
3265 plView2 := nil;
3266 end;
3268 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3269 gSpectMode := SPECT_STATS;
3271 if gSpectMode = SPECT_PLAYERS then
3272 if gPlayers <> nil then
3273 begin
3274 plView1 := GetActivePlayer_ByID(gSpectPID1);
3275 if plView1 = nil then
3276 begin
3277 gSpectPID1 := GetActivePlayerID_Next();
3278 plView1 := GetActivePlayer_ByID(gSpectPID1);
3279 end;
3280 if gSpectViewTwo then
3281 begin
3282 plView2 := GetActivePlayer_ByID(gSpectPID2);
3283 if plView2 = nil then
3284 begin
3285 gSpectPID2 := GetActivePlayerID_Next();
3286 plView2 := GetActivePlayer_ByID(gSpectPID2);
3287 end;
3288 end;
3289 end;
3291 if gSpectMode = SPECT_MAPVIEW then
3292 begin
3293 // Ðåæèì ïðîñìîòðà êàðòû
3294 Split := False;
3295 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3296 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3297 gHearPoint1.Active := True;
3298 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3299 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3300 gHearPoint2.Active := False;
3301 end
3302 else
3303 begin
3304 Split := (plView1 <> nil) and (plView2 <> nil);
3306 // Òî÷êè ñëóõà èãðîêîâ
3307 if plView1 <> nil then
3308 begin
3309 gHearPoint1.Active := True;
3310 gHearPoint1.Coords.X := plView1.GameX;
3311 gHearPoint1.Coords.Y := plView1.GameY;
3312 end else
3313 gHearPoint1.Active := False;
3314 if plView2 <> nil then
3315 begin
3316 gHearPoint2.Active := True;
3317 gHearPoint2.Coords.X := plView2.GameX;
3318 gHearPoint2.Coords.Y := plView2.GameY;
3319 end else
3320 gHearPoint2.Active := False;
3322 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3323 gPlayerScreenSize.X := gScreenWidth-196;
3324 if Split then
3325 begin
3326 gPlayerScreenSize.Y := gScreenHeight div 2;
3327 if gScreenHeight mod 2 = 0 then
3328 Dec(gPlayerScreenSize.Y);
3329 end
3330 else
3331 gPlayerScreenSize.Y := gScreenHeight;
3333 if Split then
3334 if gScreenHeight mod 2 = 0 then
3335 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3336 else
3337 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3339 DrawPlayer(plView1);
3340 gPlayer1ScreenCoord.X := sX;
3341 gPlayer1ScreenCoord.Y := sY;
3343 if Split then
3344 begin
3345 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3347 DrawPlayer(plView2);
3348 gPlayer2ScreenCoord.X := sX;
3349 gPlayer2ScreenCoord.Y := sY;
3350 end;
3352 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3354 if Split then
3355 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3356 end;
3358 // draw inspector
3359 if (g_holmes_enabled) then g_Holmes_Draw();
3361 if MessageText <> '' then
3362 begin
3363 w := 0;
3364 h := 0;
3365 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3366 if Split then
3367 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3368 (gScreenHeight div 2)-(h div 2), MessageText)
3369 else
3370 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3371 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3372 end;
3374 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3376 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3377 begin
3378 // Draw spectator GUI
3379 ww := 0;
3380 hh := 0;
3381 e_TextureFontGetSize(gStdFont, ww, hh);
3382 case gSpectMode of
3383 SPECT_STATS:
3384 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3385 SPECT_MAPVIEW:
3386 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3387 SPECT_PLAYERS:
3388 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3389 end;
3390 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3391 if gSpectMode = SPECT_MAPVIEW then
3392 begin
3393 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3394 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3395 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3396 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3397 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3398 end;
3399 if gSpectMode = SPECT_PLAYERS then
3400 begin
3401 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3402 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3403 if gSpectViewTwo then
3404 begin
3405 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3406 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3407 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3408 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3409 end
3410 else
3411 begin
3412 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3413 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3414 end;
3415 end;
3416 end;
3417 end;
3419 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
3420 begin
3421 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3422 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3424 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3425 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3426 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3427 end;
3429 if not gGameOn then
3430 begin
3431 if (gState = STATE_MENU) then
3432 begin
3433 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3434 begin
3435 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3436 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3437 end;
3438 // F3 at menu will show game loading dialog
3439 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3440 if (g_ActiveWindow <> nil) then
3441 begin
3442 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3443 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3444 end
3445 else
3446 begin
3447 // F3 at titlepic will show game loading dialog
3448 if e_KeyPressed(IK_F3) then
3449 begin
3450 g_Menu_Show_LoadMenu(true);
3451 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3452 end;
3453 end;
3454 end;
3456 if gState = STATE_FOLD then
3457 begin
3458 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3459 end;
3461 if gState = STATE_INTERCUSTOM then
3462 begin
3463 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3464 begin
3465 back := 'TEXTURE_endpic';
3466 if not g_Texture_Get(back, ID) then
3467 back := _lc[I_TEXTURE_ENDPIC];
3468 end
3469 else
3470 back := 'INTER';
3472 if g_Texture_Get(back, ID) then
3473 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3474 else
3475 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3477 DrawCustomStat();
3479 if g_ActiveWindow <> nil then
3480 begin
3481 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3482 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3483 end;
3484 end;
3486 if gState = STATE_INTERSINGLE then
3487 begin
3488 if EndingGameCounter > 0 then
3489 begin
3490 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3491 end
3492 else
3493 begin
3494 back := 'INTER';
3496 if g_Texture_Get(back, ID) then
3497 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3498 else
3499 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3501 DrawSingleStat();
3503 if g_ActiveWindow <> nil then
3504 begin
3505 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3506 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3507 end;
3508 end;
3509 end;
3511 if gState = STATE_ENDPIC then
3512 begin
3513 ID := DWORD(-1);
3514 if not g_Texture_Get('TEXTURE_endpic', ID) then
3515 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3517 if ID <> DWORD(-1) then
3518 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3519 else
3520 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3522 if g_ActiveWindow <> nil then
3523 begin
3524 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3525 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3526 end;
3527 end;
3529 if gState = STATE_SLIST then
3530 begin
3531 if g_Texture_Get('MENU_BACKGROUND', ID) then
3532 begin
3533 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3534 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3535 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3536 end;
3537 g_Serverlist_Draw(slCurrent);
3538 end;
3539 end;
3541 if g_ActiveWindow <> nil then
3542 begin
3543 if gGameOn then
3544 begin
3545 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3546 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3547 end;
3548 g_ActiveWindow.Draw();
3549 end;
3551 g_Console_Draw();
3553 if g_debug_Sounds and gGameOn then
3554 begin
3555 for w := 0 to High(e_SoundsArray) do
3556 for h := 0 to e_SoundsArray[w].nRefs do
3557 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3558 end;
3560 if gShowFPS then
3561 begin
3562 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3563 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3564 end;
3566 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3567 drawTime(gScreenWidth-72, gScreenHeight-16);
3569 if gGameOn then drawProfilers();
3571 g_Holmes_DrawUI();
3572 end;
3574 procedure g_Game_Quit();
3575 begin
3576 g_Game_StopAllSounds(True);
3577 gMusic.Free();
3578 g_Game_SaveOptions();
3579 g_Game_FreeData();
3580 g_PlayerModel_FreeData();
3581 g_Texture_DeleteAll();
3582 g_Frames_DeleteAll();
3583 g_Menu_Free();
3585 if NetInitDone then g_Net_Free;
3587 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3588 if gMapToDelete <> '' then
3589 g_Game_DeleteTestMap();
3591 gExit := EXIT_QUIT;
3592 PushExitEvent();
3593 end;
3595 procedure g_FatalError(Text: String);
3596 begin
3597 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3598 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning);
3600 gExit := EXIT_SIMPLE;
3601 end;
3603 procedure g_SimpleError(Text: String);
3604 begin
3605 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3606 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
3607 end;
3609 procedure g_Game_SetupScreenSize();
3610 const
3611 RES_FACTOR = 4.0 / 3.0;
3612 var
3613 s: Single;
3614 rf: Single;
3615 bw, bh: Word;
3616 begin
3617 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3618 gPlayerScreenSize.X := gScreenWidth-196;
3619 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3620 gPlayerScreenSize.Y := gScreenHeight div 2
3621 else
3622 gPlayerScreenSize.Y := gScreenHeight;
3624 // Ðàçìåð çàäíåãî ïëàíà:
3625 if BackID <> DWORD(-1) then
3626 begin
3627 s := SKY_STRETCH;
3628 if (gScreenWidth*s > gMapInfo.Width) or
3629 (gScreenHeight*s > gMapInfo.Height) then
3630 begin
3631 gBackSize.X := gScreenWidth;
3632 gBackSize.Y := gScreenHeight;
3633 end
3634 else
3635 begin
3636 e_GetTextureSize(BackID, @bw, @bh);
3637 rf := Single(bw) / Single(bh);
3638 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3639 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3640 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3641 if (s < 1.0) then s := 1.0;
3642 gBackSize.X := Round(bw*s);
3643 gBackSize.Y := Round(bh*s);
3644 end;
3645 end;
3646 end;
3648 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3649 begin
3650 g_Window_SetSize(newWidth, newHeight, nowFull);
3651 end;
3653 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3654 begin
3655 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3656 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3657 Exit;
3658 if gPlayer1 = nil then
3659 begin
3660 if g_Game_IsClient then
3661 begin
3662 if NetPlrUID1 > -1 then
3663 begin
3664 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3665 gPlayer1 := g_Player_Get(NetPlrUID1);
3666 end;
3667 Exit;
3668 end;
3670 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3671 Team := gPlayer1Settings.Team;
3673 // Ñîçäàíèå ïåðâîãî èãðîêà:
3674 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3675 gPlayer1Settings.Color,
3676 Team, False));
3677 if gPlayer1 = nil then
3678 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3679 else
3680 begin
3681 gPlayer1.Name := gPlayer1Settings.Name;
3682 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3683 if g_Game_IsServer and g_Game_IsNet then
3684 MH_SEND_PlayerCreate(gPlayer1.UID);
3685 gPlayer1.Respawn(False, True);
3687 if g_Game_IsNet and NetUseMaster then
3688 g_Net_Slist_Update;
3689 end;
3691 Exit;
3692 end;
3693 if gPlayer2 = nil then
3694 begin
3695 if g_Game_IsClient then
3696 begin
3697 if NetPlrUID2 > -1 then
3698 gPlayer2 := g_Player_Get(NetPlrUID2);
3699 Exit;
3700 end;
3702 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3703 Team := gPlayer2Settings.Team;
3705 // Ñîçäàíèå âòîðîãî èãðîêà:
3706 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3707 gPlayer2Settings.Color,
3708 Team, False));
3709 if gPlayer2 = nil then
3710 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3711 else
3712 begin
3713 gPlayer2.Name := gPlayer2Settings.Name;
3714 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3715 if g_Game_IsServer and g_Game_IsNet then
3716 MH_SEND_PlayerCreate(gPlayer2.UID);
3717 gPlayer2.Respawn(False, True);
3719 if g_Game_IsNet and NetUseMaster then
3720 g_Net_Slist_Update;
3721 end;
3723 Exit;
3724 end;
3725 end;
3727 procedure g_Game_RemovePlayer();
3728 var
3729 Pl: TPlayer;
3730 begin
3731 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3732 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3733 Exit;
3734 Pl := gPlayer2;
3735 if Pl <> nil then
3736 begin
3737 if g_Game_IsServer then
3738 begin
3739 Pl.Lives := 0;
3740 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3741 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3742 g_Player_Remove(Pl.UID);
3744 if g_Game_IsNet and NetUseMaster then
3745 g_Net_Slist_Update;
3746 end else
3747 gPlayer2 := nil;
3748 Exit;
3749 end;
3750 Pl := gPlayer1;
3751 if Pl <> nil then
3752 begin
3753 if g_Game_IsServer then
3754 begin
3755 Pl.Lives := 0;
3756 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3757 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3758 g_Player_Remove(Pl.UID);
3760 if g_Game_IsNet and NetUseMaster then
3761 g_Net_Slist_Update;
3762 end else
3763 begin
3764 gPlayer1 := nil;
3765 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3766 end;
3767 Exit;
3768 end;
3769 end;
3771 procedure g_Game_Spectate();
3772 begin
3773 g_Game_RemovePlayer();
3774 if gPlayer1 <> nil then
3775 g_Game_RemovePlayer();
3776 end;
3778 procedure g_Game_SpectateCenterView();
3779 begin
3780 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3781 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3782 end;
3784 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3785 var
3786 i, nPl: Integer;
3787 begin
3788 g_Game_Free();
3790 e_WriteLog('Starting singleplayer game...', TMsgType.Notify);
3792 g_Game_ClearLoading();
3794 // Íàñòðîéêè èãðû:
3795 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3796 gAimLine := False;
3797 gShowMap := False;
3798 gGameSettings.GameType := GT_SINGLE;
3799 gGameSettings.MaxLives := 0;
3800 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3801 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3802 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3803 gSwitchGameMode := GM_SINGLE;
3805 g_Game_ExecuteEvent('ongamestart');
3807 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3808 g_Game_SetupScreenSize();
3810 // Ñîçäàíèå ïåðâîãî èãðîêà:
3811 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3812 gPlayer1Settings.Color,
3813 gPlayer1Settings.Team, False));
3814 if gPlayer1 = nil then
3815 begin
3816 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3817 Exit;
3818 end;
3820 gPlayer1.Name := gPlayer1Settings.Name;
3821 nPl := 1;
3823 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3824 if TwoPlayers then
3825 begin
3826 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3827 gPlayer2Settings.Color,
3828 gPlayer2Settings.Team, False));
3829 if gPlayer2 = nil then
3830 begin
3831 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3832 Exit;
3833 end;
3835 gPlayer2.Name := gPlayer2Settings.Name;
3836 Inc(nPl);
3837 end;
3839 // Çàãðóçêà è çàïóñê êàðòû:
3840 if not g_Game_StartMap(MAP, True) then
3841 begin
3842 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3843 Exit;
3844 end;
3846 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3847 g_Player_Init();
3849 // Ñîçäàåì áîòîâ:
3850 for i := nPl+1 to nPlayers do
3851 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3852 end;
3854 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3855 TimeLimit, GoalLimit: Word;
3856 MaxLives: Byte;
3857 Options: LongWord; nPlayers: Byte);
3858 var
3859 i, nPl: Integer;
3860 begin
3861 g_Game_Free();
3863 e_WriteLog('Starting custom game...', TMsgType.Notify);
3865 g_Game_ClearLoading();
3867 // Íàñòðîéêè èãðû:
3868 gGameSettings.GameType := GT_CUSTOM;
3869 gGameSettings.GameMode := GameMode;
3870 gSwitchGameMode := GameMode;
3871 gGameSettings.TimeLimit := TimeLimit;
3872 gGameSettings.GoalLimit := GoalLimit;
3873 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3874 gGameSettings.Options := Options;
3876 gCoopTotalMonstersKilled := 0;
3877 gCoopTotalSecretsFound := 0;
3878 gCoopTotalMonsters := 0;
3879 gCoopTotalSecrets := 0;
3880 gAimLine := False;
3881 gShowMap := False;
3883 g_Game_ExecuteEvent('ongamestart');
3885 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3886 g_Game_SetupScreenSize();
3888 // Ðåæèì íàáëþäàòåëÿ:
3889 if nPlayers = 0 then
3890 begin
3891 gPlayer1 := nil;
3892 gPlayer2 := nil;
3893 end;
3895 nPl := 0;
3896 if nPlayers >= 1 then
3897 begin
3898 // Ñîçäàíèå ïåðâîãî èãðîêà:
3899 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3900 gPlayer1Settings.Color,
3901 gPlayer1Settings.Team, False));
3902 if gPlayer1 = nil then
3903 begin
3904 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3905 Exit;
3906 end;
3908 gPlayer1.Name := gPlayer1Settings.Name;
3909 Inc(nPl);
3910 end;
3912 if nPlayers >= 2 then
3913 begin
3914 // Ñîçäàíèå âòîðîãî èãðîêà:
3915 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3916 gPlayer2Settings.Color,
3917 gPlayer2Settings.Team, False));
3918 if gPlayer2 = nil then
3919 begin
3920 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3921 Exit;
3922 end;
3924 gPlayer2.Name := gPlayer2Settings.Name;
3925 Inc(nPl);
3926 end;
3928 // Çàãðóçêà è çàïóñê êàðòû:
3929 if not g_Game_StartMap(Map, True) then
3930 begin
3931 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3932 Exit;
3933 end;
3935 // Íåò òî÷åê ïîÿâëåíèÿ:
3936 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3937 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3938 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3939 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3940 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3941 begin
3942 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3943 Exit;
3944 end;
3946 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3947 g_Player_Init();
3949 // Ñîçäàåì áîòîâ:
3950 for i := nPl+1 to nPlayers do
3951 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3952 end;
3954 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3955 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3956 Options: LongWord; nPlayers: Byte;
3957 IPAddr: LongWord; Port: Word);
3958 begin
3959 g_Game_Free();
3961 e_WriteLog('Starting net game (server)...', TMsgType.Notify);
3963 g_Game_ClearLoading();
3965 // Íàñòðîéêè èãðû:
3966 gGameSettings.GameType := GT_SERVER;
3967 gGameSettings.GameMode := GameMode;
3968 gSwitchGameMode := GameMode;
3969 gGameSettings.TimeLimit := TimeLimit;
3970 gGameSettings.GoalLimit := GoalLimit;
3971 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3972 gGameSettings.Options := Options;
3974 gCoopTotalMonstersKilled := 0;
3975 gCoopTotalSecretsFound := 0;
3976 gCoopTotalMonsters := 0;
3977 gCoopTotalSecrets := 0;
3978 gAimLine := False;
3979 gShowMap := False;
3981 g_Game_ExecuteEvent('ongamestart');
3983 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3984 g_Game_SetupScreenSize();
3986 // Ðåæèì íàáëþäàòåëÿ:
3987 if nPlayers = 0 then
3988 begin
3989 gPlayer1 := nil;
3990 gPlayer2 := nil;
3991 end;
3993 if nPlayers >= 1 then
3994 begin
3995 // Ñîçäàíèå ïåðâîãî èãðîêà:
3996 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3997 gPlayer1Settings.Color,
3998 gPlayer1Settings.Team, False));
3999 if gPlayer1 = nil then
4000 begin
4001 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4002 Exit;
4003 end;
4005 gPlayer1.Name := gPlayer1Settings.Name;
4006 end;
4008 if nPlayers >= 2 then
4009 begin
4010 // Ñîçäàíèå âòîðîãî èãðîêà:
4011 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4012 gPlayer2Settings.Color,
4013 gPlayer2Settings.Team, False));
4014 if gPlayer2 = nil then
4015 begin
4016 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4017 Exit;
4018 end;
4020 gPlayer2.Name := gPlayer2Settings.Name;
4021 end;
4023 // Ñòàðòóåì ñåðâåð
4024 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
4025 begin
4026 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
4027 Exit;
4028 end;
4030 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
4032 // Çàãðóçêà è çàïóñê êàðòû:
4033 if not g_Game_StartMap(Map, True) then
4034 begin
4035 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4036 Exit;
4037 end;
4039 // Íåò òî÷åê ïîÿâëåíèÿ:
4040 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4041 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4042 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4043 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4044 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4045 begin
4046 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4047 Exit;
4048 end;
4050 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4051 g_Player_Init();
4053 NetState := NET_STATE_GAME;
4054 end;
4056 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
4057 var
4058 Map: String;
4059 WadName: string;
4060 Ptr: Pointer;
4061 T: Cardinal;
4062 MID: Byte;
4063 State: Byte;
4064 OuterLoop: Boolean;
4065 newResPath: string;
4066 InMsg: TMsg;
4067 begin
4068 g_Game_Free();
4070 State := 0;
4071 e_WriteLog('Starting net game (client)...', TMsgType.Notify);
4072 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', TMsgType.Notify);
4074 g_Game_ClearLoading();
4076 // Íàñòðîéêè èãðû:
4077 gGameSettings.GameType := GT_CLIENT;
4079 gCoopTotalMonstersKilled := 0;
4080 gCoopTotalSecretsFound := 0;
4081 gCoopTotalMonsters := 0;
4082 gCoopTotalSecrets := 0;
4083 gAimLine := False;
4084 gShowMap := False;
4086 g_Game_ExecuteEvent('ongamestart');
4088 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4089 g_Game_SetupScreenSize();
4091 NetState := NET_STATE_AUTH;
4093 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4094 // Ñòàðòóåì êëèåíò
4095 if not g_Net_Connect(Addr, Port) then
4096 begin
4097 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4098 NetState := NET_STATE_NONE;
4099 Exit;
4100 end;
4102 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4103 MC_SEND_Info(PW);
4104 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4106 OuterLoop := True;
4107 while OuterLoop do
4108 begin
4109 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4110 begin
4111 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4112 begin
4113 Ptr := NetEvent.packet^.data;
4114 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4115 continue;
4117 MID := InMsg.ReadByte();
4119 if (MID = NET_MSG_INFO) and (State = 0) then
4120 begin
4121 NetMyID := InMsg.ReadByte();
4122 NetPlrUID1 := InMsg.ReadWord();
4124 WadName := InMsg.ReadString();
4125 Map := InMsg.ReadString();
4127 gWADHash := InMsg.ReadMD5();
4129 gGameSettings.GameMode := InMsg.ReadByte();
4130 gSwitchGameMode := gGameSettings.GameMode;
4131 gGameSettings.GoalLimit := InMsg.ReadWord();
4132 gGameSettings.TimeLimit := InMsg.ReadWord();
4133 gGameSettings.MaxLives := InMsg.ReadByte();
4134 gGameSettings.Options := InMsg.ReadLongWord();
4135 T := InMsg.ReadLongWord();
4137 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4138 if newResPath = '' then
4139 begin
4140 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4141 newResPath := g_Res_DownloadWAD(WadName);
4142 if newResPath = '' then
4143 begin
4144 g_FatalError(_lc[I_NET_ERR_HASH]);
4145 enet_packet_destroy(NetEvent.packet);
4146 NetState := NET_STATE_NONE;
4147 Exit;
4148 end;
4149 end;
4150 newResPath := ExtractRelativePath(MapsDir, newResPath);
4152 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4153 gPlayer1Settings.Color,
4154 gPlayer1Settings.Team, False));
4156 if gPlayer1 = nil then
4157 begin
4158 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4160 enet_packet_destroy(NetEvent.packet);
4161 NetState := NET_STATE_NONE;
4162 Exit;
4163 end;
4165 gPlayer1.Name := gPlayer1Settings.Name;
4166 gPlayer1.UID := NetPlrUID1;
4167 gPlayer1.Reset(True);
4169 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4170 begin
4171 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4173 enet_packet_destroy(NetEvent.packet);
4174 NetState := NET_STATE_NONE;
4175 Exit;
4176 end;
4178 gTime := T;
4180 State := 1;
4181 OuterLoop := False;
4182 enet_packet_destroy(NetEvent.packet);
4183 break;
4184 end
4185 else
4186 enet_packet_destroy(NetEvent.packet);
4187 end
4188 else
4189 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4190 begin
4191 State := 0;
4192 if (NetEvent.data <= NET_DISC_MAX) then
4193 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4194 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4195 OuterLoop := False;
4196 Break;
4197 end;
4198 end;
4200 ProcessLoading(true);
4202 e_PollInput();
4204 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
4205 begin
4206 State := 0;
4207 break;
4208 end;
4209 end;
4211 if State <> 1 then
4212 begin
4213 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4214 NetState := NET_STATE_NONE;
4215 Exit;
4216 end;
4218 gLMSRespawn := LMS_RESPAWN_NONE;
4219 gLMSRespawnTime := 0;
4221 g_Player_Init();
4222 NetState := NET_STATE_GAME;
4223 MC_SEND_FullStateRequest;
4224 e_WriteLog('NET: Connection successful.', TMsgType.Notify);
4225 end;
4227 procedure g_Game_SaveOptions();
4228 begin
4229 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4230 end;
4232 procedure g_Game_ChangeMap(const MapPath: String);
4233 var
4234 Force: Boolean;
4235 begin
4236 g_Game_ClearLoading();
4238 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4239 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4240 if gExitByTrigger then
4241 begin
4242 Force := False;
4243 gExitByTrigger := False;
4244 end;
4245 if not g_Game_StartMap(MapPath, Force) then
4246 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4247 end;
4249 procedure g_Game_Restart();
4250 var
4251 Map: string;
4252 begin
4253 if g_Game_IsClient then
4254 Exit;
4255 map := g_ExtractFileName(gMapInfo.Map);
4257 MessageTime := 0;
4258 gGameOn := False;
4259 g_Game_ClearLoading();
4260 g_Game_StartMap(Map, True, gCurrentMapFileName);
4261 end;
4263 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4264 var
4265 NewWAD, ResName: String;
4266 I: Integer;
4267 begin
4268 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4269 g_Player_RemoveAllCorpses();
4271 if (not g_Game_IsClient) and
4272 (gSwitchGameMode <> gGameSettings.GameMode) and
4273 (gGameSettings.GameMode <> GM_SINGLE) then
4274 begin
4275 if gSwitchGameMode = GM_CTF then
4276 gGameSettings.MaxLives := 0;
4277 gGameSettings.GameMode := gSwitchGameMode;
4278 Force := True;
4279 end else
4280 gSwitchGameMode := gGameSettings.GameMode;
4282 g_Player_ResetTeams();
4284 if isWadPath(Map) then
4285 begin
4286 NewWAD := g_ExtractWadName(Map);
4287 ResName := g_ExtractFileName(Map);
4288 if g_Game_IsServer then
4289 begin
4290 gWADHash := MD5File(MapsDir + NewWAD);
4291 g_Game_LoadWAD(NewWAD);
4292 end else
4293 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4294 g_Game_ClientWAD(NewWAD, gWADHash);
4295 end else
4296 ResName := Map;
4298 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4299 if Result then
4300 begin
4301 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4303 gState := STATE_NONE;
4304 g_ActiveWindow := nil;
4305 gGameOn := True;
4307 DisableCheats();
4308 ResetTimer();
4310 if gGameSettings.GameMode = GM_CTF then
4311 begin
4312 g_Map_ResetFlag(FLAG_RED);
4313 g_Map_ResetFlag(FLAG_BLUE);
4314 // CTF, à ôëàãîâ íåò:
4315 if not g_Map_HaveFlagPoints() then
4316 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4317 end;
4318 end
4319 else
4320 begin
4321 gState := STATE_MENU;
4322 gGameOn := False;
4323 end;
4325 gExit := 0;
4326 gPauseMain := false;
4327 gPauseHolmes := false;
4328 gTime := 0;
4329 NetTimeToUpdate := 1;
4330 NetTimeToReliable := 0;
4331 NetTimeToMaster := NetMasterRate;
4332 gLMSRespawn := LMS_RESPAWN_NONE;
4333 gLMSRespawnTime := 0;
4334 gMissionFailed := False;
4335 gNextMap := '';
4337 gCoopMonstersKilled := 0;
4338 gCoopSecretsFound := 0;
4340 gVoteInProgress := False;
4341 gVotePassed := False;
4342 gVoteCount := 0;
4343 gVoted := False;
4345 gStatsOff := False;
4347 if not gGameOn then Exit;
4349 g_Game_SpectateCenterView();
4351 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4352 begin
4353 gLMSRespawn := LMS_RESPAWN_WARMUP;
4354 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4355 gLMSSoftSpawn := True;
4356 if NetMode = NET_SERVER then
4357 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4358 else
4359 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4360 end;
4362 if NetMode = NET_SERVER then
4363 begin
4364 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4366 // Ìàñòåðñåðâåð
4367 if NetUseMaster then
4368 begin
4369 if (NetMHost = nil) or (NetMPeer = nil) then
4370 if not g_Net_Slist_Connect then
4371 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4373 g_Net_Slist_Update;
4374 end;
4376 if NetClients <> nil then
4377 for I := 0 to High(NetClients) do
4378 if NetClients[I].Used then
4379 begin
4380 NetClients[I].Voted := False;
4381 if NetClients[I].RequestedFullUpdate then
4382 begin
4383 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4384 NetClients[I].RequestedFullUpdate := False;
4385 end;
4386 end;
4388 g_Net_UnbanNonPermHosts();
4389 end;
4391 if gLastMap then
4392 begin
4393 gCoopTotalMonstersKilled := 0;
4394 gCoopTotalSecretsFound := 0;
4395 gCoopTotalMonsters := 0;
4396 gCoopTotalSecrets := 0;
4397 gLastMap := False;
4398 end;
4400 g_Game_ExecuteEvent('onmapstart');
4401 end;
4403 procedure SetFirstLevel();
4404 begin
4405 gNextMap := '';
4407 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4408 if MapList = nil then
4409 Exit;
4411 SortSArray(MapList);
4412 gNextMap := MapList[Low(MapList)];
4414 MapList := nil;
4415 end;
4417 procedure g_Game_ExitLevel(const Map: AnsiString);
4418 begin
4419 gNextMap := Map;
4421 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4422 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4423 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4424 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4426 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4427 if gGameSettings.GameType = GT_SINGLE then
4428 gExit := EXIT_ENDLEVELSINGLE
4429 else // Âûøëè â âûõîä â Ñâîåé èãðå
4430 begin
4431 gExit := EXIT_ENDLEVELCUSTOM;
4432 if gGameSettings.GameMode = GM_COOP then
4433 g_Player_RememberAll;
4435 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4436 begin
4437 gLastMap := True;
4438 if gGameSettings.GameMode = GM_COOP then
4439 gStatsOff := True;
4441 gStatsPressed := True;
4442 gNextMap := 'MAP01';
4444 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4445 g_Game_NextLevel;
4447 if g_Game_IsNet then
4448 begin
4449 MH_SEND_GameStats();
4450 MH_SEND_CoopStats();
4451 end;
4452 end;
4453 end;
4454 end;
4456 procedure g_Game_RestartLevel();
4457 var
4458 Map: string;
4459 begin
4460 if gGameSettings.GameMode = GM_SINGLE then
4461 begin
4462 g_Game_Restart();
4463 Exit;
4464 end;
4465 gExit := EXIT_ENDLEVELCUSTOM;
4466 Map := g_ExtractFileName(gMapInfo.Map);
4467 gNextMap := Map;
4468 end;
4470 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4471 var
4472 gWAD: String;
4473 begin
4474 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4475 Exit;
4476 if not g_Game_IsClient then
4477 Exit;
4478 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4479 if gWAD = '' then
4480 begin
4481 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4482 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4483 if gWAD = '' then
4484 begin
4485 g_Game_Free();
4486 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4487 Exit;
4488 end;
4489 end;
4490 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4491 g_Game_LoadWAD(NewWAD);
4492 end;
4494 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4495 var
4496 i, n, nb, nr: Integer;
4498 function monRespawn (mon: TMonster): Boolean;
4499 begin
4500 result := false; // don't stop
4501 if not mon.FNoRespawn then mon.Respawn();
4502 end;
4504 begin
4505 if not g_Game_IsServer then Exit;
4506 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4507 gLMSRespawn := LMS_RESPAWN_NONE;
4508 gLMSRespawnTime := 0;
4509 MessageTime := 0;
4511 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4512 begin
4513 gMissionFailed := True;
4514 g_Game_RestartLevel;
4515 Exit;
4516 end;
4518 n := 0; nb := 0; nr := 0;
4519 for i := Low(gPlayers) to High(gPlayers) do
4520 if (gPlayers[i] <> nil) and
4521 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4522 (gPlayers[i] is TBot)) then
4523 begin
4524 Inc(n);
4525 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4526 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4527 end;
4529 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4530 begin
4531 // wait a second until the fuckers finally decide to join
4532 gLMSRespawn := LMS_RESPAWN_WARMUP;
4533 gLMSRespawnTime := gTime + 1000;
4534 gLMSSoftSpawn := NoMapRestart;
4535 Exit;
4536 end;
4538 g_Player_RemoveAllCorpses;
4539 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4540 if g_Game_IsNet then
4541 MH_SEND_GameEvent(NET_EV_LMS_START);
4543 for i := Low(gPlayers) to High(gPlayers) do
4544 begin
4545 if gPlayers[i] = nil then continue;
4546 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4547 // don't touch normal spectators
4548 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4549 begin
4550 gPlayers[i].FNoRespawn := True;
4551 gPlayers[i].Lives := 0;
4552 if g_Game_IsNet then
4553 MH_SEND_PlayerStats(gPlayers[I].UID);
4554 continue;
4555 end;
4556 gPlayers[i].FNoRespawn := False;
4557 gPlayers[i].Lives := gGameSettings.MaxLives;
4558 gPlayers[i].Respawn(False, True);
4559 if gGameSettings.GameMode = GM_COOP then
4560 begin
4561 gPlayers[i].Frags := 0;
4562 gPlayers[i].RecallState;
4563 end;
4564 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4565 gPlayer1 := g_Player_Get(gLMSPID1);
4566 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4567 gPlayer2 := g_Player_Get(gLMSPID2);
4568 end;
4570 g_Items_RestartRound();
4573 g_Mons_ForEach(monRespawn);
4575 gLMSSoftSpawn := False;
4576 end;
4578 function g_Game_GetFirstMap(WAD: String): String;
4579 begin
4580 Result := '';
4582 MapList := g_Map_GetMapsList(WAD);
4583 if MapList = nil then
4584 Exit;
4586 SortSArray(MapList);
4587 Result := MapList[Low(MapList)];
4589 if not g_Map_Exist(WAD + ':\' + Result) then
4590 Result := '';
4592 MapList := nil;
4593 end;
4595 function g_Game_GetNextMap(): String;
4596 var
4597 I: Integer;
4598 Map: string;
4599 begin
4600 Result := '';
4602 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4603 if MapList = nil then
4604 Exit;
4606 Map := g_ExtractFileName(gMapInfo.Map);
4608 SortSArray(MapList);
4609 MapIndex := -255;
4610 for I := Low(MapList) to High(MapList) do
4611 if Map = MapList[I] then
4612 begin
4613 MapIndex := I;
4614 Break;
4615 end;
4617 if MapIndex <> -255 then
4618 begin
4619 if MapIndex = High(MapList) then
4620 Result := MapList[Low(MapList)]
4621 else
4622 Result := MapList[MapIndex + 1];
4624 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4625 end;
4627 MapList := nil;
4628 end;
4630 procedure g_Game_NextLevel();
4631 begin
4632 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4633 gExit := EXIT_ENDLEVELCUSTOM
4634 else
4635 begin
4636 gExit := EXIT_ENDLEVELSINGLE;
4637 Exit;
4638 end;
4640 if gNextMap <> '' then Exit;
4641 gNextMap := g_Game_GetNextMap();
4642 end;
4644 function g_Game_IsTestMap(): Boolean;
4645 begin
4646 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4647 end;
4649 procedure g_Game_DeleteTestMap();
4650 var
4651 a: Integer;
4652 //MapName: AnsiString;
4653 WadName: string;
4655 WAD: TWADFile;
4656 MapList: SSArray;
4657 time: Integer;
4659 begin
4660 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
4661 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
4662 if (a = 0) then exit;
4664 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
4665 WadName := Copy(gMapToDelete, 1, a+3);
4666 Delete(gMapToDelete, 1, a+5);
4667 gMapToDelete := UpperCase(gMapToDelete);
4668 //MapName := '';
4669 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4672 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4673 if MapName <> TEST_MAP_NAME then
4674 Exit;
4676 if not gTempDelete then
4677 begin
4678 time := g_GetFileTime(WadName);
4679 WAD := TWADFile.Create();
4681 // ×èòàåì Wad-ôàéë:
4682 if not WAD.ReadFile(WadName) then
4683 begin // Íåò òàêîãî WAD-ôàéëà
4684 WAD.Free();
4685 Exit;
4686 end;
4688 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4689 WAD.CreateImage();
4690 MapList := WAD.GetResourcesList('');
4692 if MapList <> nil then
4693 for a := 0 to High(MapList) do
4694 if MapList[a] = MapName then
4695 begin
4696 // Óäàëÿåì è ñîõðàíÿåì:
4697 WAD.RemoveResource('', MapName);
4698 WAD.SaveTo(WadName);
4699 Break;
4700 end;
4702 WAD.Free();
4703 g_SetFileTime(WadName, time);
4704 end else
4706 if gTempDelete then DeleteFile(WadName);
4707 end;
4709 procedure GameCVars(P: SSArray);
4710 var
4711 a, b: Integer;
4712 stat: TPlayerStatArray;
4713 cmd, s: string;
4714 config: TConfig;
4715 begin
4716 stat := nil;
4717 cmd := LowerCase(P[0]);
4718 if cmd = 'r_showfps' then
4719 begin
4720 if (Length(P) > 1) and
4721 ((P[1] = '1') or (P[1] = '0')) then
4722 gShowFPS := (P[1][1] = '1');
4724 if gShowFPS then
4725 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4726 else
4727 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4728 end
4729 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4730 begin
4731 with gGameSettings do
4732 begin
4733 if (Length(P) > 1) and
4734 ((P[1] = '1') or (P[1] = '0')) then
4735 begin
4736 if (P[1][1] = '1') then
4737 Options := Options or GAME_OPTION_TEAMDAMAGE
4738 else
4739 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4740 end;
4742 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4743 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4744 else
4745 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4747 if g_Game_IsNet then MH_SEND_GameSettings;
4748 end;
4749 end
4750 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4751 begin
4752 with gGameSettings do
4753 begin
4754 if (Length(P) > 1) and
4755 ((P[1] = '1') or (P[1] = '0')) then
4756 begin
4757 if (P[1][1] = '1') then
4758 Options := Options or GAME_OPTION_WEAPONSTAY
4759 else
4760 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4761 end;
4763 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4764 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4765 else
4766 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4768 if g_Game_IsNet then MH_SEND_GameSettings;
4769 end;
4770 end
4771 else if cmd = 'g_gamemode' then
4772 begin
4773 a := g_Game_TextToMode(P[1]);
4774 if a = GM_SINGLE then a := GM_COOP;
4775 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4776 begin
4777 gSwitchGameMode := a;
4778 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4779 (gState = STATE_INTERSINGLE) then
4780 gSwitchGameMode := GM_SINGLE;
4781 if not gGameOn then
4782 gGameSettings.GameMode := gSwitchGameMode;
4783 end;
4784 if gSwitchGameMode = gGameSettings.GameMode then
4785 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4786 [g_Game_ModeToText(gGameSettings.GameMode)]))
4787 else
4788 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4789 [g_Game_ModeToText(gGameSettings.GameMode),
4790 g_Game_ModeToText(gSwitchGameMode)]));
4791 end
4792 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4793 begin
4794 with gGameSettings do
4795 begin
4796 if (Length(P) > 1) and
4797 ((P[1] = '1') or (P[1] = '0')) then
4798 begin
4799 if (P[1][1] = '1') then
4800 Options := Options or GAME_OPTION_ALLOWEXIT
4801 else
4802 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4803 end;
4805 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4806 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4807 else
4808 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4809 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4811 if g_Game_IsNet then MH_SEND_GameSettings;
4812 end;
4813 end
4814 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4815 begin
4816 with gGameSettings do
4817 begin
4818 if (Length(P) > 1) and
4819 ((P[1] = '1') or (P[1] = '0')) then
4820 begin
4821 if (P[1][1] = '1') then
4822 Options := Options or GAME_OPTION_MONSTERS
4823 else
4824 Options := Options and (not GAME_OPTION_MONSTERS);
4825 end;
4827 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4828 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4829 else
4830 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4831 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4833 if g_Game_IsNet then MH_SEND_GameSettings;
4834 end;
4835 end
4836 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4837 begin
4838 with gGameSettings do
4839 begin
4840 if (Length(P) > 1) and
4841 ((P[1] = '1') or (P[1] = '0')) then
4842 begin
4843 if (P[1][1] = '1') then
4844 Options := Options or GAME_OPTION_BOTVSPLAYER
4845 else
4846 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4847 end;
4849 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4850 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4851 else
4852 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4854 if g_Game_IsNet then MH_SEND_GameSettings;
4855 end;
4856 end
4857 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4858 begin
4859 with gGameSettings do
4860 begin
4861 if (Length(P) > 1) and
4862 ((P[1] = '1') or (P[1] = '0')) then
4863 begin
4864 if (P[1][1] = '1') then
4865 Options := Options or GAME_OPTION_BOTVSMONSTER
4866 else
4867 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4868 end;
4870 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4871 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4872 else
4873 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4875 if g_Game_IsNet then MH_SEND_GameSettings;
4876 end;
4877 end
4878 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4879 begin
4880 if Length(P) > 1 then
4881 begin
4882 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4883 gGameSettings.WarmupTime := 30
4884 else
4885 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4886 end;
4888 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4889 [gGameSettings.WarmupTime]));
4890 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4891 end
4892 else if cmd = 'net_interp' then
4893 begin
4894 if (Length(P) > 1) then
4895 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4897 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4898 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4899 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4900 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4901 config.Free();
4902 end
4903 else if cmd = 'net_forceplayerupdate' then
4904 begin
4905 if (Length(P) > 1) and
4906 ((P[1] = '1') or (P[1] = '0')) then
4907 NetForcePlayerUpdate := (P[1][1] = '1');
4909 if NetForcePlayerUpdate then
4910 g_Console_Add('net_forceplayerupdate = 1')
4911 else
4912 g_Console_Add('net_forceplayerupdate = 0');
4913 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4914 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4915 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4916 config.Free();
4917 end
4918 else if cmd = 'net_predictself' then
4919 begin
4920 if (Length(P) > 1) and
4921 ((P[1] = '1') or (P[1] = '0')) then
4922 NetPredictSelf := (P[1][1] = '1');
4924 if NetPredictSelf then
4925 g_Console_Add('net_predictself = 1')
4926 else
4927 g_Console_Add('net_predictself = 0');
4928 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4929 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4930 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4931 config.Free();
4932 end
4933 else if cmd = 'sv_name' then
4934 begin
4935 if (Length(P) > 1) and (Length(P[1]) > 0) then
4936 begin
4937 NetServerName := P[1];
4938 if Length(NetServerName) > 64 then
4939 SetLength(NetServerName, 64);
4940 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4941 g_Net_Slist_Update;
4942 end;
4944 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4945 end
4946 else if cmd = 'sv_passwd' then
4947 begin
4948 if (Length(P) > 1) and (Length(P[1]) > 0) then
4949 begin
4950 NetPassword := P[1];
4951 if Length(NetPassword) > 24 then
4952 SetLength(NetPassword, 24);
4953 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4954 g_Net_Slist_Update;
4955 end;
4957 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4958 end
4959 else if cmd = 'sv_maxplrs' then
4960 begin
4961 if (Length(P) > 1) then
4962 begin
4963 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4964 if g_Game_IsServer and g_Game_IsNet then
4965 begin
4966 b := 0;
4967 for a := 0 to High(NetClients) do
4968 if NetClients[a].Used then
4969 begin
4970 Inc(b);
4971 if b > NetMaxClients then
4972 begin
4973 s := g_Player_Get(NetClients[a].Player).Name;
4974 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4975 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4976 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4977 end;
4978 end;
4979 if NetUseMaster then
4980 g_Net_Slist_Update;
4981 end;
4982 end;
4984 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4985 end
4986 else if cmd = 'sv_public' then
4987 begin
4988 if (Length(P) > 1) then
4989 begin
4990 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4991 if g_Game_IsServer and g_Game_IsNet then
4992 if NetUseMaster then
4993 begin
4994 if NetMPeer = nil then
4995 if not g_Net_Slist_Connect() then
4996 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4997 g_Net_Slist_Update();
4998 end
4999 else
5000 if NetMPeer <> nil then
5001 g_Net_Slist_Disconnect();
5002 end;
5004 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
5005 end
5006 else if cmd = 'sv_intertime' then
5007 begin
5008 if (Length(P) > 1) then
5009 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
5011 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
5012 end
5013 else if cmd = 'p1_name' then
5014 begin
5015 if (Length(P) > 1) and gGameOn then
5016 begin
5017 if g_Game_IsClient then
5018 begin
5019 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5020 MC_SEND_PlayerSettings;
5021 end
5022 else
5023 if gPlayer1 <> nil then
5024 begin
5025 gPlayer1.Name := b_Text_Unformat(P[1]);
5026 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5027 end
5028 else
5029 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5030 end;
5031 end
5032 else if cmd = 'p2_name' then
5033 begin
5034 if (Length(P) > 1) and gGameOn then
5035 begin
5036 if g_Game_IsClient then
5037 begin
5038 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5039 MC_SEND_PlayerSettings;
5040 end
5041 else
5042 if gPlayer2 <> nil then
5043 begin
5044 gPlayer2.Name := b_Text_Unformat(P[1]);
5045 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5046 end
5047 else
5048 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5049 end;
5050 end
5051 else if cmd = 'p1_color' then
5052 begin
5053 if Length(P) > 3 then
5054 if g_Game_IsClient then
5055 begin
5056 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5057 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5058 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5059 MC_SEND_PlayerSettings;
5060 end
5061 else
5062 if gPlayer1 <> nil then
5063 begin
5064 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5065 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5066 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5067 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5068 end
5069 else
5070 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5071 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5072 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5073 end
5074 else if (cmd = 'p2_color') and not g_Game_IsNet then
5075 begin
5076 if Length(P) > 3 then
5077 if g_Game_IsClient then
5078 begin
5079 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5080 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5081 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5082 MC_SEND_PlayerSettings;
5083 end
5084 else
5085 if gPlayer2 <> nil then
5086 begin
5087 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5088 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5089 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5090 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5091 end
5092 else
5093 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5094 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5095 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5096 end
5097 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5098 begin
5099 if cmd = 'r_showtime' then
5100 begin
5101 if (Length(P) > 1) and
5102 ((P[1] = '1') or (P[1] = '0')) then
5103 gShowTime := (P[1][1] = '1');
5105 if gShowTime then
5106 g_Console_Add(_lc[I_MSG_TIME_ON])
5107 else
5108 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5109 end
5110 else if cmd = 'r_showscore' then
5111 begin
5112 if (Length(P) > 1) and
5113 ((P[1] = '1') or (P[1] = '0')) then
5114 gShowGoals := (P[1][1] = '1');
5116 if gShowGoals then
5117 g_Console_Add(_lc[I_MSG_SCORE_ON])
5118 else
5119 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5120 end
5121 else if cmd = 'r_showstat' then
5122 begin
5123 if (Length(P) > 1) and
5124 ((P[1] = '1') or (P[1] = '0')) then
5125 gShowStat := (P[1][1] = '1');
5127 if gShowStat then
5128 g_Console_Add(_lc[I_MSG_STATS_ON])
5129 else
5130 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5131 end
5132 else if cmd = 'r_showkillmsg' then
5133 begin
5134 if (Length(P) > 1) and
5135 ((P[1] = '1') or (P[1] = '0')) then
5136 gShowKillMsg := (P[1][1] = '1');
5138 if gShowKillMsg then
5139 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5140 else
5141 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5142 end
5143 else if cmd = 'r_showlives' then
5144 begin
5145 if (Length(P) > 1) and
5146 ((P[1] = '1') or (P[1] = '0')) then
5147 gShowLives := (P[1][1] = '1');
5149 if gShowLives then
5150 g_Console_Add(_lc[I_MSG_LIVES_ON])
5151 else
5152 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5153 end
5154 else if cmd = 'r_showspect' then
5155 begin
5156 if (Length(P) > 1) and
5157 ((P[1] = '1') or (P[1] = '0')) then
5158 gSpectHUD := (P[1][1] = '1');
5160 if gSpectHUD then
5161 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5162 else
5163 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5164 end
5165 else if cmd = 'r_showping' then
5166 begin
5167 if (Length(P) > 1) and
5168 ((P[1] = '1') or (P[1] = '0')) then
5169 gShowPing := (P[1][1] = '1');
5171 if gShowPing then
5172 g_Console_Add(_lc[I_MSG_PING_ON])
5173 else
5174 g_Console_Add(_lc[I_MSG_PING_OFF]);
5175 end
5176 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5177 begin
5178 if Length(P) > 1 then
5179 begin
5180 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5181 gGameSettings.GoalLimit := 0
5182 else
5183 begin
5184 b := 0;
5186 if gGameSettings.GameMode = GM_DM then
5187 begin // DM
5188 stat := g_Player_GetStats();
5189 if stat <> nil then
5190 for a := 0 to High(stat) do
5191 if stat[a].Frags > b then
5192 b := stat[a].Frags;
5193 end
5194 else // TDM/CTF
5195 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5197 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5198 end;
5200 if g_Game_IsNet then MH_SEND_GameSettings;
5201 end;
5203 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5204 end
5205 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5206 begin
5207 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5208 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5210 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5211 [gGameSettings.TimeLimit div 3600,
5212 (gGameSettings.TimeLimit div 60) mod 60,
5213 gGameSettings.TimeLimit mod 60]));
5214 if g_Game_IsNet then MH_SEND_GameSettings;
5215 end
5216 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5217 begin
5218 if Length(P) > 1 then
5219 begin
5220 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5221 gGameSettings.MaxLives := 0
5222 else
5223 begin
5224 b := 0;
5225 stat := g_Player_GetStats();
5226 if stat <> nil then
5227 for a := 0 to High(stat) do
5228 if stat[a].Lives > b then
5229 b := stat[a].Lives;
5230 gGameSettings.MaxLives :=
5231 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5232 end;
5233 end;
5235 g_Console_Add(Format(_lc[I_MSG_LIVES],
5236 [gGameSettings.MaxLives]));
5237 if g_Game_IsNet then MH_SEND_GameSettings;
5238 end;
5239 end;
5240 end;
5242 procedure PrintHeapStats();
5243 var
5244 hs: TFPCHeapStatus;
5245 begin
5246 hs := GetFPCHeapStatus();
5247 e_LogWriteLn ('v===== heap status =====v');
5248 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
5249 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
5250 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
5251 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
5252 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
5253 e_LogWriteLn ('^=======================^');
5254 end;
5256 procedure DebugCommands(P: SSArray);
5257 var
5258 a, b: Integer;
5259 cmd: string;
5260 //pt: TDFPoint;
5261 mon: TMonster;
5262 begin
5263 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5264 if {gDebugMode}conIsCheatsEnabled then
5265 begin
5266 cmd := LowerCase(P[0]);
5267 if cmd = 'd_window' then
5268 begin
5269 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5270 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5271 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5272 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5273 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5274 end
5275 else if cmd = 'd_sounds' then
5276 begin
5277 if (Length(P) > 1) and
5278 ((P[1] = '1') or (P[1] = '0')) then
5279 g_Debug_Sounds := (P[1][1] = '1');
5281 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5282 end
5283 else if cmd = 'd_frames' then
5284 begin
5285 if (Length(P) > 1) and
5286 ((P[1] = '1') or (P[1] = '0')) then
5287 g_Debug_Frames := (P[1][1] = '1');
5289 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5290 end
5291 else if cmd = 'd_winmsg' then
5292 begin
5293 if (Length(P) > 1) and
5294 ((P[1] = '1') or (P[1] = '0')) then
5295 g_Debug_WinMsgs := (P[1][1] = '1');
5297 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5298 end
5299 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5300 begin
5301 if (Length(P) > 1) and
5302 ((P[1] = '1') or (P[1] = '0')) then
5303 g_Debug_MonsterOff := (P[1][1] = '1');
5305 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5306 end
5307 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5308 begin
5309 if Length(P) > 1 then
5310 case P[1][1] of
5311 '0': g_debug_BotAIOff := 0;
5312 '1': g_debug_BotAIOff := 1;
5313 '2': g_debug_BotAIOff := 2;
5314 '3': g_debug_BotAIOff := 3;
5315 end;
5317 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5318 end
5319 else if cmd = 'd_monster' then
5320 begin
5321 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5322 if Length(P) < 2 then
5323 begin
5324 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5325 g_Console_Add('ID | Name');
5326 for b := MONSTER_DEMON to MONSTER_MAN do
5327 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5328 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
5329 end else
5330 begin
5331 a := StrToIntDef(P[1], 0);
5332 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5333 a := g_Mons_TypeIdByName(P[1]);
5335 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5336 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5337 else
5338 begin
5339 with gPlayer1.Obj do
5340 begin
5341 mon := g_Monsters_Create(a,
5342 X + Rect.X + (Rect.Width div 2),
5343 Y + Rect.Y + Rect.Height,
5344 gPlayer1.Direction, True);
5345 end;
5346 if (Length(P) > 2) and (mon <> nil) then
5347 begin
5348 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
5349 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
5350 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
5351 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
5352 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
5353 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
5354 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
5355 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
5356 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5357 end;
5358 end;
5359 end;
5360 end
5361 else if (cmd = 'd_health') then
5362 begin
5363 if (Length(P) > 1) and
5364 ((P[1] = '1') or (P[1] = '0')) then
5365 g_debug_HealthBar := (P[1][1] = '1');
5367 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5368 end
5369 else if (cmd = 'd_player') then
5370 begin
5371 if (Length(P) > 1) and
5372 ((P[1] = '1') or (P[1] = '0')) then
5373 g_debug_Player := (P[1][1] = '1');
5375 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5376 end
5377 else if (cmd = 'd_joy') then
5378 begin
5379 for a := 1 to 8 do
5380 g_Console_Add(e_JoystickStateToString(a));
5381 end
5382 else if (cmd = 'd_mem') then
5383 begin
5384 PrintHeapStats();
5385 end;
5386 end
5387 else
5388 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5389 end;
5392 procedure GameCheats(P: SSArray);
5393 var
5394 cmd: string;
5395 f, a: Integer;
5396 plr: TPlayer;
5397 begin
5398 if (not gGameOn) or (not conIsCheatsEnabled) then
5399 begin
5400 g_Console_Add('not available');
5401 exit;
5402 end;
5403 plr := gPlayer1;
5404 if plr = nil then
5405 begin
5406 g_Console_Add('where is the player?!');
5407 exit;
5408 end;
5409 cmd := LowerCase(P[0]);
5410 // god
5411 if cmd = 'god' then
5412 begin
5413 plr.GodMode := not plr.GodMode;
5414 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5415 exit;
5416 end;
5417 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5418 if cmd = 'give' then
5419 begin
5420 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5421 for f := 1 to High(P) do
5422 begin
5423 cmd := LowerCase(P[f]);
5424 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5425 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5426 if cmd = 'exit' then
5427 begin
5428 if gTriggers <> nil then
5429 begin
5430 for a := 0 to High(gTriggers) do
5431 begin
5432 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5433 begin
5434 g_Console_Add('player left the map');
5435 gExitByTrigger := True;
5436 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5437 g_Game_ExitLevel(gTriggers[a].tgcMap);
5438 break;
5439 end;
5440 end;
5441 end;
5442 continue;
5443 end;
5445 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5446 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5447 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5448 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5449 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5451 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5452 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5454 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5455 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;
5457 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5458 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5460 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5461 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5463 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5464 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5466 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5467 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5468 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5470 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5471 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5472 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5473 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;
5474 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5475 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5477 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;
5478 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;
5479 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;
5480 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;
5481 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;
5482 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;
5484 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5485 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;
5487 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;
5488 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;
5490 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5492 if cmd = 'ammo' then
5493 begin
5494 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5495 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5496 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5497 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5498 plr.GiveItem(ITEM_AMMO_FUELCAN);
5499 g_Console_Add('player got some ammo');
5500 continue;
5501 end;
5503 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5504 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5506 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5507 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5509 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5510 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5512 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5513 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5515 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5517 if cmd = 'weapons' then
5518 begin
5519 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5520 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5521 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5522 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5523 plr.GiveItem(ITEM_WEAPON_PLASMA);
5524 plr.GiveItem(ITEM_WEAPON_BFG);
5525 g_Console_Add('player got weapons');
5526 continue;
5527 end;
5529 if cmd = 'keys' then
5530 begin
5531 plr.GiveItem(ITEM_KEY_RED);
5532 plr.GiveItem(ITEM_KEY_GREEN);
5533 plr.GiveItem(ITEM_KEY_BLUE);
5534 g_Console_Add('player got all keys');
5535 continue;
5536 end;
5538 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5539 end;
5540 exit;
5541 end;
5542 // open
5543 if cmd = 'open' then
5544 begin
5545 g_Console_Add('player activated sesame');
5546 g_Triggers_OpenAll();
5547 exit;
5548 end;
5549 // fly
5550 if cmd = 'fly' then
5551 begin
5552 gFly := not gFly;
5553 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5554 exit;
5555 end;
5556 // noclip
5557 if cmd = 'noclip' then
5558 begin
5559 plr.SwitchNoClip;
5560 g_Console_Add('wall hardeness adjusted');
5561 exit;
5562 end;
5563 // notarget
5564 if cmd = 'notarget' then
5565 begin
5566 plr.NoTarget := not plr.NoTarget;
5567 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5568 exit;
5569 end;
5570 // noreload
5571 if cmd = 'noreload' then
5572 begin
5573 plr.NoReload := not plr.NoReload;
5574 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5575 exit;
5576 end;
5577 // speedy
5578 if cmd = 'speedy' then
5579 begin
5580 MAX_RUNVEL := 32-MAX_RUNVEL;
5581 g_Console_Add('speed adjusted');
5582 exit;
5583 end;
5584 // jumpy
5585 if cmd = 'jumpy' then
5586 begin
5587 VEL_JUMP := 30-VEL_JUMP;
5588 g_Console_Add('jump height adjusted');
5589 exit;
5590 end;
5591 // automap
5592 if cmd = 'automap' then
5593 begin
5594 gShowMap := not gShowMap;
5595 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5596 exit;
5597 end;
5598 // aimline
5599 if cmd = 'aimline' then
5600 begin
5601 gAimLine := not gAimLine;
5602 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5603 exit;
5604 end;
5605 end;
5607 procedure GameCommands(P: SSArray);
5608 var
5609 a, b: Integer;
5610 s, pw: String;
5611 chstr: string;
5612 cmd: string;
5613 pl: pTNetClient = nil;
5614 plr: TPlayer;
5615 prt: Word;
5616 nm: Boolean;
5617 listen: LongWord;
5618 begin
5619 // Îáùèå êîìàíäû:
5620 cmd := LowerCase(P[0]);
5621 chstr := '';
5622 if (cmd = 'quit') or
5623 (cmd = 'exit') then
5624 begin
5625 g_Game_Free();
5626 g_Game_Quit();
5627 Exit;
5628 end
5629 else if cmd = 'pause' then
5630 begin
5631 if (g_ActiveWindow = nil) then
5632 g_Game_Pause(not gPauseMain);
5633 end
5634 else if cmd = 'endgame' then
5635 gExit := EXIT_SIMPLE
5636 else if cmd = 'restart' then
5637 begin
5638 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5639 begin
5640 if g_Game_IsClient then
5641 begin
5642 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5643 Exit;
5644 end;
5645 g_Game_Restart();
5646 end else
5647 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5648 end
5649 else if cmd = 'kick' then
5650 begin
5651 if g_Game_IsServer then
5652 begin
5653 if Length(P) < 2 then
5654 begin
5655 g_Console_Add('kick <name>');
5656 Exit;
5657 end;
5658 if P[1] = '' then
5659 begin
5660 g_Console_Add('kick <name>');
5661 Exit;
5662 end;
5664 if g_Game_IsNet then
5665 pl := g_Net_Client_ByName(P[1]);
5666 if (pl <> nil) then
5667 begin
5668 s := g_Net_ClientName_ByID(pl^.ID);
5669 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5670 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5671 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5672 if NetUseMaster then
5673 g_Net_Slist_Update;
5674 end else if gPlayers <> nil then
5675 for a := Low(gPlayers) to High(gPlayers) do
5676 if gPlayers[a] <> nil then
5677 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5678 begin
5679 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5680 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5681 continue;
5682 gPlayers[a].Lives := 0;
5683 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5684 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5685 g_Player_Remove(gPlayers[a].UID);
5686 if NetUseMaster then
5687 g_Net_Slist_Update;
5688 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5689 g_Bot_MixNames();
5690 end;
5691 end else
5692 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5693 end
5694 else if cmd = 'kick_id' then
5695 begin
5696 if g_Game_IsServer and g_Game_IsNet then
5697 begin
5698 if Length(P) < 2 then
5699 begin
5700 g_Console_Add('kick_id <client ID>');
5701 Exit;
5702 end;
5703 if P[1] = '' then
5704 begin
5705 g_Console_Add('kick_id <client ID>');
5706 Exit;
5707 end;
5709 a := StrToIntDef(P[1], 0);
5710 if (NetClients <> nil) and (a <= High(NetClients)) then
5711 begin
5712 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5713 begin
5714 s := g_Net_ClientName_ByID(NetClients[a].ID);
5715 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5716 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5717 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5718 if NetUseMaster then
5719 g_Net_Slist_Update;
5720 end;
5721 end;
5722 end else
5723 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5724 end
5725 else if cmd = 'ban' then
5726 begin
5727 if g_Game_IsServer and g_Game_IsNet then
5728 begin
5729 if Length(P) < 2 then
5730 begin
5731 g_Console_Add('ban <name>');
5732 Exit;
5733 end;
5734 if P[1] = '' then
5735 begin
5736 g_Console_Add('ban <name>');
5737 Exit;
5738 end;
5740 pl := g_Net_Client_ByName(P[1]);
5741 if (pl <> nil) then
5742 begin
5743 s := g_Net_ClientName_ByID(pl^.ID);
5744 g_Net_BanHost(pl^.Peer^.address.host, False);
5745 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5746 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5747 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5748 if NetUseMaster then
5749 g_Net_Slist_Update;
5750 end else
5751 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5752 end else
5753 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5754 end
5755 else if cmd = 'ban_id' then
5756 begin
5757 if g_Game_IsServer and g_Game_IsNet then
5758 begin
5759 if Length(P) < 2 then
5760 begin
5761 g_Console_Add('ban_id <client ID>');
5762 Exit;
5763 end;
5764 if P[1] = '' then
5765 begin
5766 g_Console_Add('ban_id <client ID>');
5767 Exit;
5768 end;
5770 a := StrToIntDef(P[1], 0);
5771 if (NetClients <> nil) and (a <= High(NetClients)) then
5772 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5773 begin
5774 s := g_Net_ClientName_ByID(NetClients[a].ID);
5775 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5776 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5777 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5778 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5779 if NetUseMaster then
5780 g_Net_Slist_Update;
5781 end;
5782 end else
5783 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5784 end
5785 else if cmd = 'permban' then
5786 begin
5787 if g_Game_IsServer and g_Game_IsNet then
5788 begin
5789 if Length(P) < 2 then
5790 begin
5791 g_Console_Add('permban <name>');
5792 Exit;
5793 end;
5794 if P[1] = '' then
5795 begin
5796 g_Console_Add('permban <name>');
5797 Exit;
5798 end;
5800 pl := g_Net_Client_ByName(P[1]);
5801 if (pl <> nil) then
5802 begin
5803 s := g_Net_ClientName_ByID(pl^.ID);
5804 g_Net_BanHost(pl^.Peer^.address.host);
5805 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5806 g_Net_SaveBanList();
5807 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5808 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5809 if NetUseMaster then
5810 g_Net_Slist_Update;
5811 end else
5812 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5813 end else
5814 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5815 end
5816 else if cmd = 'permban_id' then
5817 begin
5818 if g_Game_IsServer and g_Game_IsNet then
5819 begin
5820 if Length(P) < 2 then
5821 begin
5822 g_Console_Add('permban_id <client ID>');
5823 Exit;
5824 end;
5825 if P[1] = '' then
5826 begin
5827 g_Console_Add('permban_id <client ID>');
5828 Exit;
5829 end;
5831 a := StrToIntDef(P[1], 0);
5832 if (NetClients <> nil) and (a <= High(NetClients)) then
5833 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5834 begin
5835 s := g_Net_ClientName_ByID(NetClients[a].ID);
5836 g_Net_BanHost(NetClients[a].Peer^.address.host);
5837 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5838 g_Net_SaveBanList();
5839 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5840 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5841 if NetUseMaster then
5842 g_Net_Slist_Update;
5843 end;
5844 end else
5845 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5846 end
5847 else if cmd = 'unban' then
5848 begin
5849 if g_Game_IsServer and g_Game_IsNet then
5850 begin
5851 if Length(P) < 2 then
5852 begin
5853 g_Console_Add('unban <IP Address>');
5854 Exit;
5855 end;
5856 if P[1] = '' then
5857 begin
5858 g_Console_Add('unban <IP Address>');
5859 Exit;
5860 end;
5862 if g_Net_UnbanHost(P[1]) then
5863 begin
5864 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5865 g_Net_SaveBanList();
5866 end else
5867 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5868 end else
5869 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5870 end
5871 else if cmd = 'clientlist' then
5872 begin
5873 if g_Game_IsServer and g_Game_IsNet then
5874 begin
5875 b := 0;
5876 if NetClients <> nil then
5877 for a := Low(NetClients) to High(NetClients) do
5878 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5879 begin
5880 plr := g_Player_Get(NetClients[a].Player);
5881 if plr = nil then continue;
5882 Inc(b);
5883 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5884 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5885 end;
5886 if b = 0 then
5887 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5888 end else
5889 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5890 end
5891 else if cmd = 'connect' then
5892 begin
5893 if (NetMode = NET_NONE) then
5894 begin
5895 if Length(P) < 2 then
5896 begin
5897 g_Console_Add('connect <IP> [port] [password]');
5898 Exit;
5899 end;
5900 if P[1] = '' then
5901 begin
5902 g_Console_Add('connect <IP> [port] [password]');
5903 Exit;
5904 end;
5906 if Length(P) > 2 then
5907 prt := StrToIntDef(P[2], 25666)
5908 else
5909 prt := 25666;
5911 if Length(P) > 3 then
5912 pw := P[3]
5913 else
5914 pw := '';
5916 g_Game_StartClient(P[1], prt, pw);
5917 end;
5918 end
5919 else if cmd = 'disconnect' then
5920 begin
5921 if (NetMode = NET_CLIENT) then
5922 g_Net_Disconnect();
5923 end
5924 else if cmd = 'reconnect' then
5925 begin
5926 if (NetMode = NET_SERVER) then
5927 Exit;
5929 if (NetMode = NET_CLIENT) then
5930 begin
5931 g_Net_Disconnect();
5932 gExit := EXIT_SIMPLE;
5933 EndGame;
5934 end;
5936 //TODO: Use last successful password to reconnect, instead of ''
5937 g_Game_StartClient(NetClientIP, NetClientPort, '');
5938 end
5939 else if (cmd = 'addbot') or
5940 (cmd = 'bot_add') then
5941 begin
5942 if Length(P) > 1 then
5943 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5944 else
5945 g_Bot_Add(TEAM_NONE, 2);
5946 end
5947 else if cmd = 'bot_addlist' then
5948 begin
5949 if Length(P) > 1 then
5950 if Length(P) = 2 then
5951 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5952 else
5953 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5954 end
5955 else if cmd = 'bot_removeall' then
5956 g_Bot_RemoveAll()
5957 else if cmd = 'chat' then
5958 begin
5959 if g_Game_IsNet then
5960 begin
5961 if Length(P) > 1 then
5962 begin
5963 for a := 1 to High(P) do
5964 chstr := chstr + P[a] + ' ';
5966 if Length(chstr) > 200 then SetLength(chstr, 200);
5968 if Length(chstr) < 1 then
5969 begin
5970 g_Console_Add('chat <text>');
5971 Exit;
5972 end;
5974 chstr := b_Text_Format(chstr);
5975 if g_Game_IsClient then
5976 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5977 else
5978 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5979 end
5980 else
5981 g_Console_Add('chat <text>');
5982 end else
5983 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5984 end
5985 else if cmd = 'teamchat' then
5986 begin
5987 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5988 begin
5989 if Length(P) > 1 then
5990 begin
5991 for a := 1 to High(P) do
5992 chstr := chstr + P[a] + ' ';
5994 if Length(chstr) > 200 then SetLength(chstr, 200);
5996 if Length(chstr) < 1 then
5997 begin
5998 g_Console_Add('teamchat <text>');
5999 Exit;
6000 end;
6002 chstr := b_Text_Format(chstr);
6003 if g_Game_IsClient then
6004 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
6005 else
6006 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
6007 gPlayer1Settings.Team);
6008 end
6009 else
6010 g_Console_Add('teamchat <text>');
6011 end else
6012 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6013 end
6014 else if cmd = 'game' then
6015 begin
6016 if gGameSettings.GameType <> GT_NONE then
6017 begin
6018 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6019 Exit;
6020 end;
6021 if Length(P) = 1 then
6022 begin
6023 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
6024 Exit;
6025 end;
6026 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
6027 P[1] := addWadExtension(P[1]);
6028 if FileExists(MapsDir + P[1]) then
6029 begin
6030 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6031 if Length(P) < 3 then
6032 begin
6033 SetLength(P, 3);
6034 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6035 end;
6037 s := P[1] + ':\' + UpperCase(P[2]);
6039 if g_Map_Exist(MapsDir + s) then
6040 begin
6041 // Çàïóñêàåì ñâîþ èãðó
6042 g_Game_Free();
6043 with gGameSettings do
6044 begin
6045 GameMode := g_Game_TextToMode(gcGameMode);
6046 if gSwitchGameMode <> GM_NONE then
6047 GameMode := gSwitchGameMode;
6048 if GameMode = GM_NONE then GameMode := GM_DM;
6049 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6050 b := 1;
6051 if Length(P) >= 4 then
6052 b := StrToIntDef(P[3], 1);
6053 g_Game_StartCustom(s, GameMode, TimeLimit,
6054 GoalLimit, MaxLives, Options, b);
6055 end;
6056 end
6057 else
6058 if P[2] = '' then
6059 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6060 else
6061 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[2]), P[1]]));
6062 end else
6063 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6064 end
6065 else if cmd = 'host' then
6066 begin
6067 if gGameSettings.GameType <> GT_NONE then
6068 begin
6069 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6070 Exit;
6071 end;
6072 if Length(P) < 4 then
6073 begin
6074 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
6075 Exit;
6076 end;
6077 if not StrToIp(P[1], listen) then
6078 Exit;
6079 prt := StrToIntDef(P[2], 25666);
6081 P[3] := addWadExtension(P[3]);
6082 if FileExists(MapsDir + P[3]) then
6083 begin
6084 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6085 if Length(P) < 5 then
6086 begin
6087 SetLength(P, 5);
6088 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
6089 end;
6091 s := P[3] + ':\' + UpperCase(P[4]);
6093 if g_Map_Exist(MapsDir + s) then
6094 begin
6095 // Çàïóñêàåì ñâîþ èãðó
6096 g_Game_Free();
6097 with gGameSettings do
6098 begin
6099 GameMode := g_Game_TextToMode(gcGameMode);
6100 if gSwitchGameMode <> GM_NONE then
6101 GameMode := gSwitchGameMode;
6102 if GameMode = GM_NONE then GameMode := GM_DM;
6103 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6104 b := 0;
6105 if Length(P) >= 6 then
6106 b := StrToIntDef(P[5], 0);
6107 g_Game_StartServer(s, GameMode, TimeLimit,
6108 GoalLimit, MaxLives, Options, b, listen, prt);
6109 end;
6110 end
6111 else
6112 if P[4] = '' then
6113 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6114 else
6115 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]));
6116 end else
6117 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6118 end
6119 else if cmd = 'map' then
6120 begin
6121 if Length(P) = 1 then
6122 begin
6123 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6124 begin
6125 g_Console_Add(cmd + ' <MAP>');
6126 g_Console_Add(cmd + ' <WAD> [MAP]');
6127 end else
6128 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6129 end else
6130 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6131 begin
6132 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6133 if Length(P) < 3 then
6134 begin
6135 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6136 s := UpperCase(P[1]);
6137 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6138 begin // Êàðòà íàøëàñü
6139 gExitByTrigger := False;
6140 if gGameOn then
6141 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6142 gNextMap := s;
6143 gExit := EXIT_ENDLEVELCUSTOM;
6144 end
6145 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6146 g_Game_ChangeMap(s);
6147 end else
6148 begin
6149 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6150 P[1] := addWadExtension(P[1]);
6151 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6152 if FileExists(MapsDir + P[1]) then
6153 begin
6154 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6155 SetLength(P, 3);
6156 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6158 s := P[1] + ':\' + P[2];
6160 if g_Map_Exist(MapsDir + s) then
6161 begin
6162 gExitByTrigger := False;
6163 if gGameOn then
6164 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6165 gNextMap := s;
6166 gExit := EXIT_ENDLEVELCUSTOM;
6167 end
6168 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6169 g_Game_ChangeMap(s);
6170 end else
6171 if P[2] = '' then
6172 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6173 else
6174 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6175 end else
6176 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6177 end;
6178 end else
6179 begin
6180 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6181 P[1] := addWadExtension(P[1]);
6182 if FileExists(MapsDir + P[1]) then
6183 begin
6184 // Íàøëè WAD ôàéë
6185 P[2] := UpperCase(P[2]);
6186 s := P[1] + ':\' + P[2];
6188 if g_Map_Exist(MapsDir + s) then
6189 begin // Íàøëè êàðòó
6190 gExitByTrigger := False;
6191 if gGameOn then
6192 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6193 gNextMap := s;
6194 gExit := EXIT_ENDLEVELCUSTOM;
6195 end
6196 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6197 g_Game_ChangeMap(s);
6198 end else
6199 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6200 end else
6201 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6202 end;
6203 end else
6204 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6205 end
6206 else if cmd = 'nextmap' then
6207 begin
6208 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6209 g_Console_Add(_lc[I_MSG_NOT_GAME])
6210 else begin
6211 nm := True;
6212 if Length(P) = 1 then
6213 begin
6214 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6215 begin
6216 g_Console_Add(cmd + ' <MAP>');
6217 g_Console_Add(cmd + ' <WAD> [MAP]');
6218 end else begin
6219 nm := False;
6220 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6221 end;
6222 end else
6223 begin
6224 nm := False;
6225 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6226 begin
6227 if Length(P) < 3 then
6228 begin
6229 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6230 s := UpperCase(P[1]);
6231 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6232 begin // Êàðòà íàøëàñü
6233 gExitByTrigger := False;
6234 gNextMap := s;
6235 nm := True;
6236 end else
6237 begin
6238 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6239 P[1] := addWadExtension(P[1]);
6240 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6241 if FileExists(MapsDir + P[1]) then
6242 begin
6243 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6244 SetLength(P, 3);
6245 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6247 s := P[1] + ':\' + P[2];
6249 if g_Map_Exist(MapsDir + s) then
6250 begin // Óñòàíàâëèâàåì êàðòó
6251 gExitByTrigger := False;
6252 gNextMap := s;
6253 nm := True;
6254 end else
6255 if P[2] = '' then
6256 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6257 else
6258 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6259 end else
6260 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6261 end;
6262 end else
6263 begin
6264 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6265 P[1] := addWadExtension(P[1]);
6266 if FileExists(MapsDir + P[1]) then
6267 begin
6268 // Íàøëè WAD ôàéë
6269 P[2] := UpperCase(P[2]);
6270 s := P[1] + ':\' + P[2];
6272 if g_Map_Exist(MapsDir + s) then
6273 begin // Íàøëè êàðòó
6274 gExitByTrigger := False;
6275 gNextMap := s;
6276 nm := True;
6277 end else
6278 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6279 end else
6280 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6281 end;
6282 end else
6283 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6284 end;
6285 if nm then
6286 if gNextMap = '' then
6287 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6288 else
6289 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6290 end;
6291 end
6292 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6293 begin
6294 if not gGameOn then
6295 g_Console_Add(_lc[I_MSG_NOT_GAME])
6296 else
6297 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6298 begin
6299 gExitByTrigger := False;
6300 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6301 if (gNextMap = '') and (gTriggers <> nil) then
6302 for a := 0 to High(gTriggers) do
6303 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6304 begin
6305 gExitByTrigger := True;
6306 //gNextMap := gTriggers[a].Data.MapName;
6307 gNextMap := gTriggers[a].tgcMap;
6308 Break;
6309 end;
6310 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6311 if gNextMap = '' then
6312 gNextMap := g_Game_GetNextMap();
6313 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6314 if not isWadPath(gNextMap) then
6315 s := gGameSettings.WAD + ':\' + gNextMap
6316 else
6317 s := gNextMap;
6318 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6319 if g_Map_Exist(MapsDir + s) then
6320 gExit := EXIT_ENDLEVELCUSTOM
6321 else
6322 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6323 end else
6324 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6325 end
6326 else if (cmd = 'event') then
6327 begin
6328 if (Length(P) <= 1) then
6329 begin
6330 for a := 0 to High(gEvents) do
6331 if gEvents[a].Command = '' then
6332 g_Console_Add(gEvents[a].Name + ' <none>')
6333 else
6334 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6335 Exit;
6336 end;
6337 if (Length(P) = 2) then
6338 begin
6339 for a := 0 to High(gEvents) do
6340 if gEvents[a].Name = P[1] then
6341 if gEvents[a].Command = '' then
6342 g_Console_Add(gEvents[a].Name + ' <none>')
6343 else
6344 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6345 Exit;
6346 end;
6347 for a := 0 to High(gEvents) do
6348 if gEvents[a].Name = P[1] then
6349 begin
6350 gEvents[a].Command := '';
6351 for b := 2 to High(P) do
6352 if Pos(' ', P[b]) = 0 then
6353 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6354 else
6355 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6356 gEvents[a].Command := Trim(gEvents[a].Command);
6357 Exit;
6358 end;
6359 end
6360 else if cmd = 'suicide' then
6361 begin
6362 if gGameOn then
6363 begin
6364 if g_Game_IsClient then
6365 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6366 else
6367 begin
6368 if gPlayer1 <> nil then
6369 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6370 if gPlayer2 <> nil then
6371 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6372 end;
6373 end;
6374 end
6375 // Êîìàíäû Ñâîåé èãðû:
6376 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6377 begin
6378 if cmd = 'bot_addred' then
6379 begin
6380 if Length(P) > 1 then
6381 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6382 else
6383 g_Bot_Add(TEAM_RED, 2);
6384 end
6385 else if cmd = 'bot_addblue' then
6386 begin
6387 if Length(P) > 1 then
6388 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6389 else
6390 g_Bot_Add(TEAM_BLUE, 2);
6391 end
6392 else if cmd = 'spectate' then
6393 begin
6394 if not gGameOn then
6395 Exit;
6396 g_Game_Spectate();
6397 end
6398 else if cmd = 'say' then
6399 begin
6400 if g_Game_IsServer and g_Game_IsNet then
6401 begin
6402 if Length(P) > 1 then
6403 begin
6404 chstr := '';
6405 for a := 1 to High(P) do
6406 chstr := chstr + P[a] + ' ';
6408 if Length(chstr) > 200 then SetLength(chstr, 200);
6410 if Length(chstr) < 1 then
6411 begin
6412 g_Console_Add('say <text>');
6413 Exit;
6414 end;
6416 chstr := b_Text_Format(chstr);
6417 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6418 end
6419 else g_Console_Add('say <text>');
6420 end else
6421 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6422 end
6423 else if cmd = 'tell' then
6424 begin
6425 if g_Game_IsServer and g_Game_IsNet then
6426 begin
6427 if (Length(P) > 2) and (P[1] <> '') then
6428 begin
6429 chstr := '';
6430 for a := 2 to High(P) do
6431 chstr := chstr + P[a] + ' ';
6433 if Length(chstr) > 200 then SetLength(chstr, 200);
6435 if Length(chstr) < 1 then
6436 begin
6437 g_Console_Add('tell <playername> <text>');
6438 Exit;
6439 end;
6441 pl := g_Net_Client_ByName(P[1]);
6442 if pl <> nil then
6443 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6444 else
6445 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6446 end
6447 else g_Console_Add('tell <playername> <text>');
6448 end else
6449 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6450 end
6451 else if (cmd = 'overtime') and not g_Game_IsClient then
6452 begin
6453 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6454 Exit;
6455 // Äîïîëíèòåëüíîå âðåìÿ:
6456 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6458 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6459 [gGameSettings.TimeLimit div 3600,
6460 (gGameSettings.TimeLimit div 60) mod 60,
6461 gGameSettings.TimeLimit mod 60]));
6462 if g_Game_IsNet then MH_SEND_GameSettings;
6463 end
6464 else if (cmd = 'rcon_password') and g_Game_IsClient then
6465 begin
6466 if (Length(P) <= 1) then
6467 g_Console_Add('rcon_password <password>')
6468 else
6469 MC_SEND_RCONPassword(P[1]);
6470 end
6471 else if cmd = 'rcon' then
6472 begin
6473 if g_Game_IsClient then
6474 begin
6475 if Length(P) > 1 then
6476 begin
6477 chstr := '';
6478 for a := 1 to High(P) do
6479 chstr := chstr + P[a] + ' ';
6481 if Length(chstr) > 200 then SetLength(chstr, 200);
6483 if Length(chstr) < 1 then
6484 begin
6485 g_Console_Add('rcon <command>');
6486 Exit;
6487 end;
6489 MC_SEND_RCONCommand(chstr);
6490 end
6491 else g_Console_Add('rcon <command>');
6492 end;
6493 end
6494 else if cmd = 'ready' then
6495 begin
6496 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6497 gLMSRespawnTime := gTime + 100;
6498 end
6499 else if (cmd = 'callvote') and g_Game_IsNet then
6500 begin
6501 if Length(P) > 1 then
6502 begin
6503 chstr := '';
6504 for a := 1 to High(P) do begin
6505 if a > 1 then chstr := chstr + ' ';
6506 chstr := chstr + P[a];
6507 end;
6509 if Length(chstr) > 200 then SetLength(chstr, 200);
6511 if Length(chstr) < 1 then
6512 begin
6513 g_Console_Add('callvote <command>');
6514 Exit;
6515 end;
6517 if g_Game_IsClient then
6518 MC_SEND_Vote(True, chstr)
6519 else
6520 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6521 g_Console_Process('vote', True);
6522 end
6523 else
6524 g_Console_Add('callvote <command>');
6525 end
6526 else if (cmd = 'vote') and g_Game_IsNet then
6527 begin
6528 if g_Game_IsClient then
6529 MC_SEND_Vote(False)
6530 else if gVoteInProgress then
6531 begin
6532 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6533 a := Floor((NetClientCount+1)/2.0) + 1
6534 else
6535 a := Floor(NetClientCount/2.0) + 1;
6536 if gVoted then
6537 begin
6538 Dec(gVoteCount);
6539 gVoted := False;
6540 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6541 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6542 end
6543 else
6544 begin
6545 Inc(gVoteCount);
6546 gVoted := True;
6547 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6548 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6549 g_Game_CheckVote;
6550 end;
6551 end;
6552 end
6553 end;
6554 end;
6556 procedure g_TakeScreenShot();
6557 var
6558 a: Word;
6559 FileName: string;
6560 ssdir, t: string;
6561 st: TStream;
6562 ok: Boolean;
6563 begin
6564 if e_NoGraphics then Exit;
6565 ssdir := GameDir+'/screenshots';
6566 if not findFileCI(ssdir, true) then
6567 begin
6568 // try to create dir
6569 try
6570 CreateDir(ssdir);
6571 except
6572 end;
6573 if not findFileCI(ssdir, true) then exit; // alas
6574 end;
6575 try
6576 for a := 1 to High(Word) do
6577 begin
6578 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6579 t := FileName;
6580 if findFileCI(t, true) then continue;
6581 if not findFileCI(FileName) then
6582 begin
6583 ok := false;
6584 st := createDiskFile(FileName);
6585 try
6586 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6587 ok := true;
6588 finally
6589 st.Free();
6590 end;
6591 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6592 break;
6593 end;
6594 end;
6595 except
6596 end;
6597 end;
6599 procedure g_Game_InGameMenu(Show: Boolean);
6600 begin
6601 if (g_ActiveWindow = nil) and Show then
6602 begin
6603 if gGameSettings.GameType = GT_SINGLE then
6604 g_GUI_ShowWindow('GameSingleMenu')
6605 else
6606 begin
6607 if g_Game_IsClient then
6608 g_GUI_ShowWindow('GameClientMenu')
6609 else
6610 if g_Game_IsNet then
6611 g_GUI_ShowWindow('GameServerMenu')
6612 else
6613 g_GUI_ShowWindow('GameCustomMenu');
6614 end;
6615 g_Sound_PlayEx('MENU_OPEN');
6617 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6618 if (not g_Game_IsNet) then
6619 g_Game_Pause(True);
6620 end
6621 else
6622 if (g_ActiveWindow <> nil) and (not Show) then
6623 begin
6624 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6625 if (not g_Game_IsNet) then
6626 g_Game_Pause(False);
6627 end;
6628 end;
6630 procedure g_Game_Pause (Enable: Boolean);
6631 var
6632 oldPause: Boolean;
6633 begin
6634 if not gGameOn then exit;
6636 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6638 oldPause := gPause;
6639 gPauseMain := Enable;
6641 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6642 end;
6644 procedure g_Game_HolmesPause (Enable: Boolean);
6645 var
6646 oldPause: Boolean;
6647 begin
6648 if not gGameOn then exit;
6649 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6651 oldPause := gPause;
6652 gPauseHolmes := Enable;
6654 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6655 end;
6657 procedure g_Game_PauseAllSounds(Enable: Boolean);
6658 var
6659 i: Integer;
6660 begin
6661 // Òðèããåðû:
6662 if gTriggers <> nil then
6663 for i := 0 to High(gTriggers) do
6664 with gTriggers[i] do
6665 if (TriggerType = TRIGGER_SOUND) and
6666 (Sound <> nil) and
6667 Sound.IsPlaying() then
6668 begin
6669 Sound.Pause(Enable);
6670 end;
6672 // Çâóêè èãðîêîâ:
6673 if gPlayers <> nil then
6674 for i := 0 to High(gPlayers) do
6675 if gPlayers[i] <> nil then
6676 gPlayers[i].PauseSounds(Enable);
6678 // Ìóçûêà:
6679 if gMusic <> nil then
6680 gMusic.Pause(Enable);
6681 end;
6683 procedure g_Game_StopAllSounds(all: Boolean);
6684 var
6685 i: Integer;
6686 begin
6687 if gTriggers <> nil then
6688 for i := 0 to High(gTriggers) do
6689 with gTriggers[i] do
6690 if (TriggerType = TRIGGER_SOUND) and
6691 (Sound <> nil) then
6692 Sound.Stop();
6694 if gMusic <> nil then
6695 gMusic.Stop();
6697 if all then
6698 e_StopChannels();
6699 end;
6701 procedure g_Game_UpdateTriggerSounds();
6702 var
6703 i: Integer;
6704 begin
6705 if gTriggers <> nil then
6706 for i := 0 to High(gTriggers) do
6707 with gTriggers[i] do
6708 if (TriggerType = TRIGGER_SOUND) and
6709 (Sound <> nil) and
6710 (tgcLocal) and
6711 Sound.IsPlaying() then
6712 begin
6713 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6714 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6715 begin
6716 Sound.SetPan(0.5 - tgcPan/255.0);
6717 Sound.SetVolume(tgcVolume/255.0);
6718 end
6719 else
6720 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
6721 end;
6722 end;
6724 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6725 begin
6726 Result := False;
6727 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6728 begin
6729 Result := True;
6730 Exit;
6731 end;
6732 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6733 begin
6734 Result := True;
6735 Exit;
6736 end;
6737 if gSpectMode <> SPECT_PLAYERS then
6738 Exit;
6739 if gSpectPID1 = UID then
6740 begin
6741 Result := True;
6742 Exit;
6743 end;
6744 if gSpectViewTwo and (gSpectPID2 = UID) then
6745 begin
6746 Result := True;
6747 Exit;
6748 end;
6749 end;
6751 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6752 var
6753 Pl: TPlayer;
6754 begin
6755 Result := False;
6756 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6757 begin
6758 Result := True;
6759 Exit;
6760 end;
6761 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6762 begin
6763 Result := True;
6764 Exit;
6765 end;
6766 if gSpectMode <> SPECT_PLAYERS then
6767 Exit;
6768 Pl := g_Player_Get(gSpectPID1);
6769 if (Pl <> nil) and (Pl.Team = Team) then
6770 begin
6771 Result := True;
6772 Exit;
6773 end;
6774 if gSpectViewTwo then
6775 begin
6776 Pl := g_Player_Get(gSpectPID2);
6777 if (Pl <> nil) and (Pl.Team = Team) then
6778 begin
6779 Result := True;
6780 Exit;
6781 end;
6782 end;
6783 end;
6785 procedure g_Game_Message(Msg: string; Time: Word);
6786 begin
6787 MessageText := b_Text_Format(Msg);
6788 MessageTime := Time;
6789 end;
6791 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6792 var
6793 a: Integer;
6794 begin
6795 case gAnnouncer of
6796 ANNOUNCE_NONE:
6797 Exit;
6798 ANNOUNCE_ME,
6799 ANNOUNCE_MEPLUS:
6800 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6801 Exit;
6802 end;
6803 for a := 0 to 3 do
6804 if goodsnd[a].IsPlaying() then
6805 Exit;
6807 goodsnd[Random(4)].Play();
6808 end;
6810 procedure g_Game_Announce_KillCombo(Param: Integer);
6811 var
6812 UID: Word;
6813 c, n: Byte;
6814 Pl: TPlayer;
6815 Name: String;
6816 begin
6817 UID := Param and $FFFF;
6818 c := Param shr 16;
6819 if c < 2 then
6820 Exit;
6822 Pl := g_Player_Get(UID);
6823 if Pl = nil then
6824 Name := '?'
6825 else
6826 Name := Pl.Name;
6828 case c of
6829 2: begin
6830 n := 0;
6831 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6832 end;
6833 3: begin
6834 n := 1;
6835 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6836 end;
6837 4: begin
6838 n := 2;
6839 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6840 end;
6841 else begin
6842 n := 3;
6843 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6844 end;
6845 end;
6847 case gAnnouncer of
6848 ANNOUNCE_NONE:
6849 Exit;
6850 ANNOUNCE_ME:
6851 if not g_Game_IsWatchedPlayer(UID) then
6852 Exit;
6853 ANNOUNCE_MEPLUS:
6854 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6855 Exit;
6856 end;
6858 if killsnd[n].IsPlaying() then
6859 killsnd[n].Stop();
6860 killsnd[n].Play();
6861 end;
6863 procedure g_Game_StartVote(Command, Initiator: string);
6864 var
6865 Need: Integer;
6866 begin
6867 if not gVotesEnabled then Exit;
6868 if gGameSettings.GameType <> GT_SERVER then Exit;
6869 if gVoteInProgress or gVotePassed then
6870 begin
6871 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6872 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6873 Exit;
6874 end;
6875 gVoteInProgress := True;
6876 gVotePassed := False;
6877 gVoteTimer := gTime + gVoteTimeout * 1000;
6878 gVoteCount := 0;
6879 gVoted := False;
6880 gVoteCommand := Command;
6882 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6883 Need := Floor((NetClientCount+1)/2.0)+1
6884 else
6885 Need := Floor(NetClientCount/2.0)+1;
6886 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6887 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6888 end;
6890 procedure g_Game_CheckVote;
6891 var
6892 I, Need: Integer;
6893 begin
6894 if gGameSettings.GameType <> GT_SERVER then Exit;
6895 if not gVoteInProgress then Exit;
6897 if (gTime >= gVoteTimer) then
6898 begin
6899 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6900 Need := Floor((NetClientCount+1)/2.0) + 1
6901 else
6902 Need := Floor(NetClientCount/2.0) + 1;
6903 if gVoteCount >= Need then
6904 begin
6905 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6906 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6907 gVotePassed := True;
6908 gVoteCmdTimer := gTime + 5000;
6909 end
6910 else
6911 begin
6912 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6913 MH_SEND_VoteEvent(NET_VE_FAILED);
6914 end;
6915 if NetClients <> nil then
6916 for I := Low(NetClients) to High(NetClients) do
6917 if NetClients[i].Used then
6918 NetClients[i].Voted := False;
6919 gVoteInProgress := False;
6920 gVoted := False;
6921 gVoteCount := 0;
6922 end
6923 else
6924 begin
6925 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6926 Need := Floor((NetClientCount+1)/2.0) + 1
6927 else
6928 Need := Floor(NetClientCount/2.0) + 1;
6929 if gVoteCount >= Need then
6930 begin
6931 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6932 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6933 gVoteInProgress := False;
6934 gVotePassed := True;
6935 gVoteCmdTimer := gTime + 5000;
6936 gVoted := False;
6937 gVoteCount := 0;
6938 if NetClients <> nil then
6939 for I := Low(NetClients) to High(NetClients) do
6940 if NetClients[i].Used then
6941 NetClients[i].Voted := False;
6942 end;
6943 end;
6944 end;
6946 procedure g_Game_LoadMapList(FileName: string);
6947 var
6948 ListFile: TextFile;
6949 s: string;
6950 begin
6951 MapList := nil;
6952 MapIndex := -1;
6954 if not FileExists(FileName) then Exit;
6956 AssignFile(ListFile, FileName);
6957 Reset(ListFile);
6958 while not EOF(ListFile) do
6959 begin
6960 ReadLn(ListFile, s);
6962 s := Trim(s);
6963 if s = '' then Continue;
6965 SetLength(MapList, Length(MapList)+1);
6966 MapList[High(MapList)] := s;
6967 end;
6968 CloseFile(ListFile);
6969 end;
6971 procedure g_Game_SetDebugMode();
6972 begin
6973 gDebugMode := True;
6974 // ×èòû (äàæå â ñâîåé èãðå):
6975 gCheats := True;
6976 end;
6978 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6979 var
6980 i: Word;
6981 begin
6982 if Length(LoadingStat.Msgs) = 0 then
6983 Exit;
6985 with LoadingStat do
6986 begin
6987 if not reWrite then
6988 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6989 if NextMsg = Length(Msgs) then
6990 begin // scroll
6991 for i := 0 to High(Msgs)-1 do
6992 Msgs[i] := Msgs[i+1];
6993 end
6994 else
6995 Inc(NextMsg);
6996 end else
6997 if NextMsg = 0 then
6998 Inc(NextMsg);
7000 Msgs[NextMsg-1] := Text;
7001 CurValue := 0;
7002 MaxValue := Max;
7003 ShowCount := 0;
7004 end;
7006 g_ActiveWindow := nil;
7008 ProcessLoading(true);
7009 end;
7011 procedure g_Game_StepLoading();
7012 begin
7013 with LoadingStat do
7014 begin
7015 Inc(CurValue);
7016 Inc(ShowCount);
7017 if (ShowCount > LOADING_SHOW_STEP) then
7018 begin
7019 ShowCount := 0;
7020 ProcessLoading();
7021 end;
7022 end;
7023 end;
7025 procedure g_Game_ClearLoading();
7026 var
7027 len: Word;
7028 begin
7029 with LoadingStat do
7030 begin
7031 CurValue := 0;
7032 MaxValue := 0;
7033 ShowCount := 0;
7034 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
7035 if len < 1 then len := 1;
7036 SetLength(Msgs, len);
7037 for len := Low(Msgs) to High(Msgs) do
7038 Msgs[len] := '';
7039 NextMsg := 0;
7040 end;
7041 end;
7043 procedure Parse_Params(var pars: TParamStrValues);
7044 var
7045 i: Integer;
7046 s: String;
7047 begin
7048 SetLength(pars, 0);
7049 i := 1;
7050 while i <= ParamCount do
7051 begin
7052 s := ParamStr(i);
7053 if (s[1] = '-') and (Length(s) > 1) then
7054 begin
7055 if (s[2] = '-') and (Length(s) > 2) then
7056 begin // Îäèíî÷íûé ïàðàìåòð
7057 SetLength(pars, Length(pars) + 1);
7058 with pars[High(pars)] do
7059 begin
7060 Name := LowerCase(s);
7061 Value := '+';
7062 end;
7063 end
7064 else
7065 if (i < ParamCount) then
7066 begin // Ïàðàìåòð ñî çíà÷åíèåì
7067 Inc(i);
7068 SetLength(pars, Length(pars) + 1);
7069 with pars[High(pars)] do
7070 begin
7071 Name := LowerCase(s);
7072 Value := LowerCase(ParamStr(i));
7073 end;
7074 end;
7075 end;
7077 Inc(i);
7078 end;
7079 end;
7081 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
7082 var
7083 i: Integer;
7084 begin
7085 Result := '';
7086 for i := 0 to High(pars) do
7087 if pars[i].Name = aName then
7088 begin
7089 Result := pars[i].Value;
7090 Break;
7091 end;
7092 end;
7094 procedure g_Game_Process_Params();
7095 var
7096 pars: TParamStrValues;
7097 map: String;
7098 GMode, n: Byte;
7099 LimT, LimS: Integer;
7100 Opt: LongWord;
7101 Lives: Integer;
7102 s: String;
7103 Port: Integer;
7104 ip: String;
7105 F: TextFile;
7106 begin
7107 Parse_Params(pars);
7109 // Debug mode:
7110 s := Find_Param_Value(pars, '--debug');
7111 if (s <> '') then
7112 begin
7113 g_Game_SetDebugMode();
7114 s := Find_Param_Value(pars, '--netdump');
7115 if (s <> '') then
7116 NetDump := True;
7117 end;
7119 // Connect when game loads
7120 ip := Find_Param_Value(pars, '-connect');
7122 if ip <> '' then
7123 begin
7124 s := Find_Param_Value(pars, '-port');
7125 if (s = '') or not TryStrToInt(s, Port) then
7126 Port := 25666;
7128 s := Find_Param_Value(pars, '-pw');
7130 g_Game_StartClient(ip, Port, s);
7131 Exit;
7132 end;
7134 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7135 if (s <> '') then
7136 begin
7137 gDefaultMegawadStart := s;
7138 end;
7140 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7141 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7142 begin
7143 gDefaultMegawadStart := DF_Default_Megawad_Start;
7144 end;
7146 // Start map when game loads:
7147 map := LowerCase(Find_Param_Value(pars, '-map'));
7148 if isWadPath(map) then
7149 begin
7150 // Game mode:
7151 s := Find_Param_Value(pars, '-gm');
7152 GMode := g_Game_TextToMode(s);
7153 if GMode = GM_NONE then GMode := GM_DM;
7154 if GMode = GM_SINGLE then GMode := GM_COOP;
7156 // Time limit:
7157 s := Find_Param_Value(pars, '-limt');
7158 if (s = '') or (not TryStrToInt(s, LimT)) then
7159 LimT := 0;
7160 if LimT < 0 then
7161 LimT := 0;
7163 // Goal limit:
7164 s := Find_Param_Value(pars, '-lims');
7165 if (s = '') or (not TryStrToInt(s, LimS)) then
7166 LimS := 0;
7167 if LimS < 0 then
7168 LimS := 0;
7170 // Lives limit:
7171 s := Find_Param_Value(pars, '-lives');
7172 if (s = '') or (not TryStrToInt(s, Lives)) then
7173 Lives := 0;
7174 if Lives < 0 then
7175 Lives := 0;
7177 // Options:
7178 s := Find_Param_Value(pars, '-opt');
7179 if (s = '') then
7180 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7181 else
7182 Opt := StrToIntDef(s, 0);
7183 if Opt = 0 then
7184 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7186 // Close after map:
7187 s := Find_Param_Value(pars, '--close');
7188 if (s <> '') then
7189 gMapOnce := True;
7191 // Override map to test:
7192 s := LowerCase(Find_Param_Value(pars, '-testmap'));
7193 if s <> '' then
7194 gTestMap := MapsDir + s;
7196 // Delete test map after play:
7197 s := Find_Param_Value(pars, '--testdelete');
7198 if (s <> '') then
7199 begin
7200 gMapToDelete := MapsDir + map;
7201 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
7202 Halt(1);
7203 end;
7205 // Delete temporary WAD after play:
7206 s := Find_Param_Value(pars, '--tempdelete');
7207 if (s <> '') and (gTestMap <> '') then
7208 begin
7209 gMapToDelete := gTestMap;
7210 gTempDelete := True;
7211 end;
7213 // Number of players:
7214 s := Find_Param_Value(pars, '-pl');
7215 if (s = '') then
7216 n := 1
7217 else
7218 n := StrToIntDef(s, 1);
7220 // Start:
7221 s := Find_Param_Value(pars, '-port');
7222 if (s = '') or not TryStrToInt(s, Port) then
7223 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7224 else
7225 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7226 end;
7228 // Execute script when game loads:
7229 s := Find_Param_Value(pars, '-exec');
7230 if s <> '' then
7231 begin
7232 if not isWadPath(s) then
7233 s := GameDir + '/' + s;
7235 {$I-}
7236 AssignFile(F, s);
7237 Reset(F);
7238 if IOResult <> 0 then
7239 begin
7240 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7241 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7242 CloseFile(F);
7243 Exit;
7244 end;
7245 e_WriteLog('Executing script: ' + s, TMsgType.Notify);
7246 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7248 while not EOF(F) do
7249 begin
7250 ReadLn(F, s);
7251 if IOResult <> 0 then
7252 begin
7253 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7254 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7255 CloseFile(F);
7256 Exit;
7257 end;
7258 if Pos('#', s) <> 1 then // script comment
7259 g_Console_Process(s, True);
7260 end;
7262 CloseFile(F);
7263 {$I+}
7264 end;
7266 SetLength(pars, 0);
7267 end;
7269 begin
7270 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7271 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7272 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7273 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7275 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7276 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7277 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7278 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7280 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7281 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7283 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7284 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7286 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7288 conRegVar('dbg_scale', @g_dbg_scale, 0.01, 100.0, 'experimental deBUG scale mode', '', false);
7290 conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
7291 conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
7292 end.