DEADSOFTWARE

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