DEADSOFTWARE

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