DEADSOFTWARE

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