DEADSOFTWARE

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