DEADSOFTWARE

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