DEADSOFTWARE

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