DEADSOFTWARE

renamed "dbg_scale" to "r_scale"; scaled mode now obeys level bounds; added "dbg_igno...
[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;
3339 end;
3340 p.viewPortX := sX;
3341 p.viewPortY := sY;
3342 p.viewPortW := sWidth;
3343 p.viewPortH := sHeight;
3345 if (p = gPlayer1) then
3346 begin
3347 g_Holmes_plrViewPos(sX, sY);
3348 g_Holmes_plrViewSize(sWidth, sHeight);
3349 end;
3351 renderMapInternal(-c, -d, true);
3353 if p.FSpectator then
3354 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3355 p.GameY + PLAYER_RECT_CY - 4,
3356 'X', gStdFont, 255, 255, 255, 1, True);
3358 for a := 0 to High(gCollideMap) do
3359 for b := 0 to High(gCollideMap[a]) do
3360 begin
3361 d := 0;
3362 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3363 d := d + 1;
3364 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3365 d := d + 2;
3367 case d of
3368 1: e_DrawPoint(1, b, a, 200, 200, 200);
3369 2: e_DrawPoint(1, b, a, 64, 64, 255);
3370 3: e_DrawPoint(1, b, a, 255, 0, 255);
3371 end;
3372 end;
3375 glPopMatrix();
3377 p.DrawPain();
3378 p.DrawPickup();
3379 p.DrawRulez();
3380 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3381 if g_Debug_Player then
3382 g_Player_DrawDebug(p);
3383 p.DrawGUI();
3384 end;
3386 procedure drawProfilers ();
3387 var
3388 px: Integer = -1;
3389 py: Integer = -1;
3390 begin
3391 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
3392 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3393 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3394 end;
3396 procedure g_Game_Draw();
3397 var
3398 ID: DWORD;
3399 w, h: Word;
3400 ww, hh: Byte;
3401 Time: Int64;
3402 back: string;
3403 plView1, plView2: TPlayer;
3404 Split: Boolean;
3405 begin
3406 if gExit = EXIT_QUIT then Exit;
3408 Time := GetTimer() {div 1000};
3409 FPSCounter := FPSCounter+1;
3410 if Time - FPSTime >= 1000 then
3411 begin
3412 FPS := FPSCounter;
3413 FPSCounter := 0;
3414 FPSTime := Time;
3415 end;
3417 if gGameOn or (gState = STATE_FOLD) then
3418 begin
3419 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3420 begin
3421 gSpectMode := SPECT_NONE;
3422 if not gRevertPlayers then
3423 begin
3424 plView1 := gPlayer1;
3425 plView2 := gPlayer2;
3426 end
3427 else
3428 begin
3429 plView1 := gPlayer2;
3430 plView2 := gPlayer1;
3431 end;
3432 end
3433 else
3434 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3435 begin
3436 gSpectMode := SPECT_NONE;
3437 if gPlayer2 = nil then
3438 plView1 := gPlayer1
3439 else
3440 plView1 := gPlayer2;
3441 plView2 := nil;
3442 end
3443 else
3444 begin
3445 plView1 := nil;
3446 plView2 := nil;
3447 end;
3449 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3450 gSpectMode := SPECT_STATS;
3452 if gSpectMode = SPECT_PLAYERS then
3453 if gPlayers <> nil then
3454 begin
3455 plView1 := GetActivePlayer_ByID(gSpectPID1);
3456 if plView1 = nil then
3457 begin
3458 gSpectPID1 := GetActivePlayerID_Next();
3459 plView1 := GetActivePlayer_ByID(gSpectPID1);
3460 end;
3461 if gSpectViewTwo then
3462 begin
3463 plView2 := GetActivePlayer_ByID(gSpectPID2);
3464 if plView2 = nil then
3465 begin
3466 gSpectPID2 := GetActivePlayerID_Next();
3467 plView2 := GetActivePlayer_ByID(gSpectPID2);
3468 end;
3469 end;
3470 end;
3472 if gSpectMode = SPECT_MAPVIEW then
3473 begin
3474 // Ðåæèì ïðîñìîòðà êàðòû
3475 Split := False;
3476 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3477 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3478 gHearPoint1.Active := True;
3479 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3480 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3481 gHearPoint2.Active := False;
3482 end
3483 else
3484 begin
3485 Split := (plView1 <> nil) and (plView2 <> nil);
3487 // Òî÷êè ñëóõà èãðîêîâ
3488 if plView1 <> nil then
3489 begin
3490 gHearPoint1.Active := True;
3491 gHearPoint1.Coords.X := plView1.GameX;
3492 gHearPoint1.Coords.Y := plView1.GameY;
3493 end else
3494 gHearPoint1.Active := False;
3495 if plView2 <> nil then
3496 begin
3497 gHearPoint2.Active := True;
3498 gHearPoint2.Coords.X := plView2.GameX;
3499 gHearPoint2.Coords.Y := plView2.GameY;
3500 end else
3501 gHearPoint2.Active := False;
3503 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3504 gPlayerScreenSize.X := gScreenWidth-196;
3505 if Split then
3506 begin
3507 gPlayerScreenSize.Y := gScreenHeight div 2;
3508 if gScreenHeight mod 2 = 0 then
3509 Dec(gPlayerScreenSize.Y);
3510 end
3511 else
3512 gPlayerScreenSize.Y := gScreenHeight;
3514 if Split then
3515 if gScreenHeight mod 2 = 0 then
3516 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3517 else
3518 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3520 DrawPlayer(plView1);
3521 gPlayer1ScreenCoord.X := sX;
3522 gPlayer1ScreenCoord.Y := sY;
3524 if Split then
3525 begin
3526 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3528 DrawPlayer(plView2);
3529 gPlayer2ScreenCoord.X := sX;
3530 gPlayer2ScreenCoord.Y := sY;
3531 end;
3533 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3535 if Split then
3536 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3537 end;
3539 // draw inspector
3540 if (g_holmes_enabled) then g_Holmes_Draw();
3542 if MessageText <> '' then
3543 begin
3544 w := 0;
3545 h := 0;
3546 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3547 if Split then
3548 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3549 (gScreenHeight div 2)-(h div 2), MessageText)
3550 else
3551 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3552 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3553 end;
3555 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3557 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3558 begin
3559 // Draw spectator GUI
3560 ww := 0;
3561 hh := 0;
3562 e_TextureFontGetSize(gStdFont, ww, hh);
3563 case gSpectMode of
3564 SPECT_STATS:
3565 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3566 SPECT_MAPVIEW:
3567 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3568 SPECT_PLAYERS:
3569 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3570 end;
3571 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3572 if gSpectMode = SPECT_MAPVIEW then
3573 begin
3574 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3575 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3576 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3577 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3578 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3579 end;
3580 if gSpectMode = SPECT_PLAYERS then
3581 begin
3582 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3583 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3584 if gSpectViewTwo then
3585 begin
3586 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3587 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3588 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3589 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3590 end
3591 else
3592 begin
3593 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3594 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3595 end;
3596 end;
3597 end;
3598 end;
3600 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
3601 begin
3602 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3603 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3605 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3606 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3607 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3608 end;
3610 if not gGameOn then
3611 begin
3612 if (gState = STATE_MENU) then
3613 begin
3614 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3615 begin
3616 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3617 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3618 end;
3619 // F3 at menu will show game loading dialog
3620 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3621 if (g_ActiveWindow <> nil) then
3622 begin
3623 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3624 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3625 end
3626 else
3627 begin
3628 // F3 at titlepic will show game loading dialog
3629 if e_KeyPressed(IK_F3) then
3630 begin
3631 g_Menu_Show_LoadMenu(true);
3632 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3633 end;
3634 end;
3635 end;
3637 if gState = STATE_FOLD then
3638 begin
3639 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3640 end;
3642 if gState = STATE_INTERCUSTOM then
3643 begin
3644 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3645 begin
3646 back := 'TEXTURE_endpic';
3647 if not g_Texture_Get(back, ID) then
3648 back := _lc[I_TEXTURE_ENDPIC];
3649 end
3650 else
3651 back := 'INTER';
3653 if g_Texture_Get(back, ID) then
3654 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3655 else
3656 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3658 DrawCustomStat();
3660 if g_ActiveWindow <> nil then
3661 begin
3662 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3663 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3664 end;
3665 end;
3667 if gState = STATE_INTERSINGLE then
3668 begin
3669 if EndingGameCounter > 0 then
3670 begin
3671 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3672 end
3673 else
3674 begin
3675 back := 'INTER';
3677 if g_Texture_Get(back, ID) then
3678 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3679 else
3680 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3682 DrawSingleStat();
3684 if g_ActiveWindow <> nil then
3685 begin
3686 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3687 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3688 end;
3689 end;
3690 end;
3692 if gState = STATE_ENDPIC then
3693 begin
3694 ID := DWORD(-1);
3695 if not g_Texture_Get('TEXTURE_endpic', ID) then
3696 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3698 if ID <> DWORD(-1) then
3699 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3700 else
3701 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3703 if g_ActiveWindow <> nil then
3704 begin
3705 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3706 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3707 end;
3708 end;
3710 if gState = STATE_SLIST then
3711 begin
3712 if g_Texture_Get('MENU_BACKGROUND', ID) then
3713 begin
3714 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3715 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3716 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3717 end;
3718 g_Serverlist_Draw(slCurrent);
3719 end;
3720 end;
3722 if g_ActiveWindow <> nil then
3723 begin
3724 if gGameOn then
3725 begin
3726 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3727 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3728 end;
3729 g_ActiveWindow.Draw();
3730 end;
3732 g_Console_Draw();
3734 if g_debug_Sounds and gGameOn then
3735 begin
3736 for w := 0 to High(e_SoundsArray) do
3737 for h := 0 to e_SoundsArray[w].nRefs do
3738 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3739 end;
3741 if gShowFPS then
3742 begin
3743 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3744 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3745 end;
3747 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3748 drawTime(gScreenWidth-72, gScreenHeight-16);
3750 if gGameOn then drawProfilers();
3752 g_Holmes_DrawUI();
3753 end;
3755 procedure g_Game_Quit();
3756 begin
3757 g_Game_StopAllSounds(True);
3758 gMusic.Free();
3759 g_Game_SaveOptions();
3760 g_Game_FreeData();
3761 g_PlayerModel_FreeData();
3762 g_Texture_DeleteAll();
3763 g_Frames_DeleteAll();
3764 g_Menu_Free();
3766 if NetInitDone then g_Net_Free;
3768 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3769 if gMapToDelete <> '' then
3770 g_Game_DeleteTestMap();
3772 gExit := EXIT_QUIT;
3773 PushExitEvent();
3774 end;
3776 procedure g_FatalError(Text: String);
3777 begin
3778 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3779 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning);
3781 gExit := EXIT_SIMPLE;
3782 end;
3784 procedure g_SimpleError(Text: String);
3785 begin
3786 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3787 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
3788 end;
3790 procedure g_Game_SetupScreenSize();
3791 const
3792 RES_FACTOR = 4.0 / 3.0;
3793 var
3794 s: Single;
3795 rf: Single;
3796 bw, bh: Word;
3797 begin
3798 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3799 gPlayerScreenSize.X := gScreenWidth-196;
3800 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3801 gPlayerScreenSize.Y := gScreenHeight div 2
3802 else
3803 gPlayerScreenSize.Y := gScreenHeight;
3805 // Ðàçìåð çàäíåãî ïëàíà:
3806 if BackID <> DWORD(-1) then
3807 begin
3808 s := SKY_STRETCH;
3809 if (gScreenWidth*s > gMapInfo.Width) or
3810 (gScreenHeight*s > gMapInfo.Height) then
3811 begin
3812 gBackSize.X := gScreenWidth;
3813 gBackSize.Y := gScreenHeight;
3814 end
3815 else
3816 begin
3817 e_GetTextureSize(BackID, @bw, @bh);
3818 rf := Single(bw) / Single(bh);
3819 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3820 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3821 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3822 if (s < 1.0) then s := 1.0;
3823 gBackSize.X := Round(bw*s);
3824 gBackSize.Y := Round(bh*s);
3825 end;
3826 end;
3827 end;
3829 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3830 begin
3831 g_Window_SetSize(newWidth, newHeight, nowFull);
3832 end;
3834 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3835 begin
3836 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3837 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3838 Exit;
3839 if gPlayer1 = nil then
3840 begin
3841 if g_Game_IsClient then
3842 begin
3843 if NetPlrUID1 > -1 then
3844 begin
3845 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3846 gPlayer1 := g_Player_Get(NetPlrUID1);
3847 end;
3848 Exit;
3849 end;
3851 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3852 Team := gPlayer1Settings.Team;
3854 // Ñîçäàíèå ïåðâîãî èãðîêà:
3855 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3856 gPlayer1Settings.Color,
3857 Team, False));
3858 if gPlayer1 = nil then
3859 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3860 else
3861 begin
3862 gPlayer1.Name := gPlayer1Settings.Name;
3863 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3864 if g_Game_IsServer and g_Game_IsNet then
3865 MH_SEND_PlayerCreate(gPlayer1.UID);
3866 gPlayer1.Respawn(False, True);
3868 if g_Game_IsNet and NetUseMaster then
3869 g_Net_Slist_Update;
3870 end;
3872 Exit;
3873 end;
3874 if gPlayer2 = nil then
3875 begin
3876 if g_Game_IsClient then
3877 begin
3878 if NetPlrUID2 > -1 then
3879 gPlayer2 := g_Player_Get(NetPlrUID2);
3880 Exit;
3881 end;
3883 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3884 Team := gPlayer2Settings.Team;
3886 // Ñîçäàíèå âòîðîãî èãðîêà:
3887 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3888 gPlayer2Settings.Color,
3889 Team, False));
3890 if gPlayer2 = nil then
3891 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3892 else
3893 begin
3894 gPlayer2.Name := gPlayer2Settings.Name;
3895 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3896 if g_Game_IsServer and g_Game_IsNet then
3897 MH_SEND_PlayerCreate(gPlayer2.UID);
3898 gPlayer2.Respawn(False, True);
3900 if g_Game_IsNet and NetUseMaster then
3901 g_Net_Slist_Update;
3902 end;
3904 Exit;
3905 end;
3906 end;
3908 procedure g_Game_RemovePlayer();
3909 var
3910 Pl: TPlayer;
3911 begin
3912 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3913 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3914 Exit;
3915 Pl := gPlayer2;
3916 if Pl <> nil then
3917 begin
3918 if g_Game_IsServer then
3919 begin
3920 Pl.Lives := 0;
3921 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3922 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3923 g_Player_Remove(Pl.UID);
3925 if g_Game_IsNet and NetUseMaster then
3926 g_Net_Slist_Update;
3927 end else
3928 gPlayer2 := nil;
3929 Exit;
3930 end;
3931 Pl := gPlayer1;
3932 if Pl <> nil then
3933 begin
3934 if g_Game_IsServer then
3935 begin
3936 Pl.Lives := 0;
3937 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3938 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3939 g_Player_Remove(Pl.UID);
3941 if g_Game_IsNet and NetUseMaster then
3942 g_Net_Slist_Update;
3943 end else
3944 begin
3945 gPlayer1 := nil;
3946 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3947 end;
3948 Exit;
3949 end;
3950 end;
3952 procedure g_Game_Spectate();
3953 begin
3954 g_Game_RemovePlayer();
3955 if gPlayer1 <> nil then
3956 g_Game_RemovePlayer();
3957 end;
3959 procedure g_Game_SpectateCenterView();
3960 begin
3961 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3962 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3963 end;
3965 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3966 var
3967 i, nPl: Integer;
3968 tmps: AnsiString;
3969 begin
3970 g_Game_Free();
3972 e_WriteLog('Starting singleplayer game...', TMsgType.Notify);
3974 g_Game_ClearLoading();
3976 // Íàñòðîéêè èãðû:
3977 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3978 gAimLine := False;
3979 gShowMap := False;
3980 gGameSettings.GameType := GT_SINGLE;
3981 gGameSettings.MaxLives := 0;
3982 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3983 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3984 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3985 gSwitchGameMode := GM_SINGLE;
3987 g_Game_ExecuteEvent('ongamestart');
3989 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3990 g_Game_SetupScreenSize();
3992 // Ñîçäàíèå ïåðâîãî èãðîêà:
3993 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3994 gPlayer1Settings.Color,
3995 gPlayer1Settings.Team, False));
3996 if gPlayer1 = nil then
3997 begin
3998 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3999 Exit;
4000 end;
4002 gPlayer1.Name := gPlayer1Settings.Name;
4003 nPl := 1;
4005 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
4006 if TwoPlayers then
4007 begin
4008 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4009 gPlayer2Settings.Color,
4010 gPlayer2Settings.Team, False));
4011 if gPlayer2 = nil then
4012 begin
4013 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4014 Exit;
4015 end;
4017 gPlayer2.Name := gPlayer2Settings.Name;
4018 Inc(nPl);
4019 end;
4021 // Çàãðóçêà è çàïóñê êàðòû:
4022 if not g_Game_StartMap(MAP, True) then
4023 begin
4024 if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP;
4025 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps]));
4026 Exit;
4027 end;
4029 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4030 g_Player_Init();
4032 // Ñîçäàåì áîòîâ:
4033 for i := nPl+1 to nPlayers do
4034 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4035 end;
4037 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
4038 TimeLimit, GoalLimit: Word;
4039 MaxLives: Byte;
4040 Options: LongWord; nPlayers: Byte);
4041 var
4042 i, nPl: Integer;
4043 begin
4044 g_Game_Free();
4046 e_WriteLog('Starting custom game...', TMsgType.Notify);
4048 g_Game_ClearLoading();
4050 // Íàñòðîéêè èãðû:
4051 gGameSettings.GameType := GT_CUSTOM;
4052 gGameSettings.GameMode := GameMode;
4053 gSwitchGameMode := GameMode;
4054 gGameSettings.TimeLimit := TimeLimit;
4055 gGameSettings.GoalLimit := GoalLimit;
4056 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4057 gGameSettings.Options := Options;
4059 gCoopTotalMonstersKilled := 0;
4060 gCoopTotalSecretsFound := 0;
4061 gCoopTotalMonsters := 0;
4062 gCoopTotalSecrets := 0;
4063 gAimLine := False;
4064 gShowMap := False;
4066 g_Game_ExecuteEvent('ongamestart');
4068 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4069 g_Game_SetupScreenSize();
4071 // Ðåæèì íàáëþäàòåëÿ:
4072 if nPlayers = 0 then
4073 begin
4074 gPlayer1 := nil;
4075 gPlayer2 := nil;
4076 end;
4078 nPl := 0;
4079 if nPlayers >= 1 then
4080 begin
4081 // Ñîçäàíèå ïåðâîãî èãðîêà:
4082 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4083 gPlayer1Settings.Color,
4084 gPlayer1Settings.Team, False));
4085 if gPlayer1 = nil then
4086 begin
4087 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4088 Exit;
4089 end;
4091 gPlayer1.Name := gPlayer1Settings.Name;
4092 Inc(nPl);
4093 end;
4095 if nPlayers >= 2 then
4096 begin
4097 // Ñîçäàíèå âòîðîãî èãðîêà:
4098 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4099 gPlayer2Settings.Color,
4100 gPlayer2Settings.Team, False));
4101 if gPlayer2 = nil then
4102 begin
4103 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4104 Exit;
4105 end;
4107 gPlayer2.Name := gPlayer2Settings.Name;
4108 Inc(nPl);
4109 end;
4111 // Çàãðóçêà è çàïóñê êàðòû:
4112 if not g_Game_StartMap(Map, True) then
4113 begin
4114 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4115 Exit;
4116 end;
4118 // Íåò òî÷åê ïîÿâëåíèÿ:
4119 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4120 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4121 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4122 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4123 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4124 begin
4125 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4126 Exit;
4127 end;
4129 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4130 g_Player_Init();
4132 // Ñîçäàåì áîòîâ:
4133 for i := nPl+1 to nPlayers do
4134 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4135 end;
4137 procedure g_Game_StartServer(Map: String; GameMode: Byte;
4138 TimeLimit, GoalLimit: Word; MaxLives: Byte;
4139 Options: LongWord; nPlayers: Byte;
4140 IPAddr: LongWord; Port: Word);
4141 begin
4142 g_Game_Free();
4144 e_WriteLog('Starting net game (server)...', TMsgType.Notify);
4146 g_Game_ClearLoading();
4148 // Íàñòðîéêè èãðû:
4149 gGameSettings.GameType := GT_SERVER;
4150 gGameSettings.GameMode := GameMode;
4151 gSwitchGameMode := GameMode;
4152 gGameSettings.TimeLimit := TimeLimit;
4153 gGameSettings.GoalLimit := GoalLimit;
4154 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4155 gGameSettings.Options := Options;
4157 gCoopTotalMonstersKilled := 0;
4158 gCoopTotalSecretsFound := 0;
4159 gCoopTotalMonsters := 0;
4160 gCoopTotalSecrets := 0;
4161 gAimLine := False;
4162 gShowMap := False;
4164 g_Game_ExecuteEvent('ongamestart');
4166 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
4167 g_Game_SetupScreenSize();
4169 // Ðåæèì íàáëþäàòåëÿ:
4170 if nPlayers = 0 then
4171 begin
4172 gPlayer1 := nil;
4173 gPlayer2 := nil;
4174 end;
4176 if nPlayers >= 1 then
4177 begin
4178 // Ñîçäàíèå ïåðâîãî èãðîêà:
4179 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4180 gPlayer1Settings.Color,
4181 gPlayer1Settings.Team, False));
4182 if gPlayer1 = nil then
4183 begin
4184 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4185 Exit;
4186 end;
4188 gPlayer1.Name := gPlayer1Settings.Name;
4189 end;
4191 if nPlayers >= 2 then
4192 begin
4193 // Ñîçäàíèå âòîðîãî èãðîêà:
4194 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4195 gPlayer2Settings.Color,
4196 gPlayer2Settings.Team, False));
4197 if gPlayer2 = nil then
4198 begin
4199 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4200 Exit;
4201 end;
4203 gPlayer2.Name := gPlayer2Settings.Name;
4204 end;
4206 // Ñòàðòóåì ñåðâåð
4207 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
4208 begin
4209 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
4210 Exit;
4211 end;
4213 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
4215 // Çàãðóçêà è çàïóñê êàðòû:
4216 if not g_Game_StartMap(Map, True) then
4217 begin
4218 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4219 Exit;
4220 end;
4222 // Íåò òî÷åê ïîÿâëåíèÿ:
4223 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4224 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4225 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4226 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4227 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4228 begin
4229 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4230 Exit;
4231 end;
4233 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4234 g_Player_Init();
4236 NetState := NET_STATE_GAME;
4237 end;
4239 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
4240 var
4241 Map: String;
4242 WadName: string;
4243 Ptr: Pointer;
4244 T: Cardinal;
4245 MID: Byte;
4246 State: Byte;
4247 OuterLoop: Boolean;
4248 newResPath: string;
4249 InMsg: TMsg;
4250 begin
4251 g_Game_Free();
4253 State := 0;
4254 e_WriteLog('Starting net game (client)...', TMsgType.Notify);
4255 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', TMsgType.Notify);
4257 g_Game_ClearLoading();
4259 // Íàñòðîéêè èãðû:
4260 gGameSettings.GameType := GT_CLIENT;
4262 gCoopTotalMonstersKilled := 0;
4263 gCoopTotalSecretsFound := 0;
4264 gCoopTotalMonsters := 0;
4265 gCoopTotalSecrets := 0;
4266 gAimLine := False;
4267 gShowMap := False;
4269 g_Game_ExecuteEvent('ongamestart');
4271 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4272 g_Game_SetupScreenSize();
4274 NetState := NET_STATE_AUTH;
4276 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4277 // Ñòàðòóåì êëèåíò
4278 if not g_Net_Connect(Addr, Port) then
4279 begin
4280 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4281 NetState := NET_STATE_NONE;
4282 Exit;
4283 end;
4285 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4286 MC_SEND_Info(PW);
4287 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4289 OuterLoop := True;
4290 while OuterLoop do
4291 begin
4292 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4293 begin
4294 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4295 begin
4296 Ptr := NetEvent.packet^.data;
4297 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4298 continue;
4300 MID := InMsg.ReadByte();
4302 if (MID = NET_MSG_INFO) and (State = 0) then
4303 begin
4304 NetMyID := InMsg.ReadByte();
4305 NetPlrUID1 := InMsg.ReadWord();
4307 WadName := InMsg.ReadString();
4308 Map := InMsg.ReadString();
4310 gWADHash := InMsg.ReadMD5();
4312 gGameSettings.GameMode := InMsg.ReadByte();
4313 gSwitchGameMode := gGameSettings.GameMode;
4314 gGameSettings.GoalLimit := InMsg.ReadWord();
4315 gGameSettings.TimeLimit := InMsg.ReadWord();
4316 gGameSettings.MaxLives := InMsg.ReadByte();
4317 gGameSettings.Options := InMsg.ReadLongWord();
4318 T := InMsg.ReadLongWord();
4320 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4321 if newResPath = '' then
4322 begin
4323 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4324 newResPath := g_Res_DownloadWAD(WadName);
4325 if newResPath = '' then
4326 begin
4327 g_FatalError(_lc[I_NET_ERR_HASH]);
4328 enet_packet_destroy(NetEvent.packet);
4329 NetState := NET_STATE_NONE;
4330 Exit;
4331 end;
4332 end;
4333 newResPath := ExtractRelativePath(MapsDir, newResPath);
4335 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4336 gPlayer1Settings.Color,
4337 gPlayer1Settings.Team, False));
4339 if gPlayer1 = nil then
4340 begin
4341 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4343 enet_packet_destroy(NetEvent.packet);
4344 NetState := NET_STATE_NONE;
4345 Exit;
4346 end;
4348 gPlayer1.Name := gPlayer1Settings.Name;
4349 gPlayer1.UID := NetPlrUID1;
4350 gPlayer1.Reset(True);
4352 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4353 begin
4354 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4356 enet_packet_destroy(NetEvent.packet);
4357 NetState := NET_STATE_NONE;
4358 Exit;
4359 end;
4361 gTime := T;
4363 State := 1;
4364 OuterLoop := False;
4365 enet_packet_destroy(NetEvent.packet);
4366 break;
4367 end
4368 else
4369 enet_packet_destroy(NetEvent.packet);
4370 end
4371 else
4372 begin
4373 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4374 begin
4375 State := 0;
4376 if (NetEvent.data <= NET_DISC_MAX) then
4377 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4378 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4379 OuterLoop := False;
4380 Break;
4381 end;
4382 end;
4383 end;
4385 ProcessLoading(true);
4387 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
4388 begin
4389 State := 0;
4390 break;
4391 end;
4392 end;
4394 if State <> 1 then
4395 begin
4396 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4397 NetState := NET_STATE_NONE;
4398 Exit;
4399 end;
4401 gLMSRespawn := LMS_RESPAWN_NONE;
4402 gLMSRespawnTime := 0;
4404 g_Player_Init();
4405 NetState := NET_STATE_GAME;
4406 MC_SEND_FullStateRequest;
4407 e_WriteLog('NET: Connection successful.', TMsgType.Notify);
4408 end;
4410 procedure g_Game_SaveOptions();
4411 begin
4412 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4413 end;
4415 procedure g_Game_ChangeMap(const MapPath: String);
4416 var
4417 Force: Boolean;
4418 begin
4419 g_Game_ClearLoading();
4421 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4422 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4423 if gExitByTrigger then
4424 begin
4425 Force := False;
4426 gExitByTrigger := False;
4427 end;
4428 if not g_Game_StartMap(MapPath, Force) then
4429 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4430 end;
4432 procedure g_Game_Restart();
4433 var
4434 Map: string;
4435 begin
4436 if g_Game_IsClient then
4437 Exit;
4438 map := g_ExtractFileName(gMapInfo.Map);
4440 MessageTime := 0;
4441 gGameOn := False;
4442 g_Game_ClearLoading();
4443 g_Game_StartMap(Map, True, gCurrentMapFileName);
4444 end;
4446 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4447 var
4448 NewWAD, ResName: String;
4449 I: Integer;
4450 begin
4451 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4452 g_Player_RemoveAllCorpses();
4454 if (not g_Game_IsClient) and
4455 (gSwitchGameMode <> gGameSettings.GameMode) and
4456 (gGameSettings.GameMode <> GM_SINGLE) then
4457 begin
4458 if gSwitchGameMode = GM_CTF then
4459 gGameSettings.MaxLives := 0;
4460 gGameSettings.GameMode := gSwitchGameMode;
4461 Force := True;
4462 end else
4463 gSwitchGameMode := gGameSettings.GameMode;
4465 g_Player_ResetTeams();
4467 if isWadPath(Map) then
4468 begin
4469 NewWAD := g_ExtractWadName(Map);
4470 ResName := g_ExtractFileName(Map);
4471 if g_Game_IsServer then
4472 begin
4473 gWADHash := MD5File(MapsDir + NewWAD);
4474 g_Game_LoadWAD(NewWAD);
4475 end else
4476 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4477 g_Game_ClientWAD(NewWAD, gWADHash);
4478 end else
4479 ResName := Map;
4481 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4482 if Result then
4483 begin
4484 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4486 gState := STATE_NONE;
4487 g_ActiveWindow := nil;
4488 gGameOn := True;
4490 DisableCheats();
4491 ResetTimer();
4493 if gGameSettings.GameMode = GM_CTF then
4494 begin
4495 g_Map_ResetFlag(FLAG_RED);
4496 g_Map_ResetFlag(FLAG_BLUE);
4497 // CTF, à ôëàãîâ íåò:
4498 if not g_Map_HaveFlagPoints() then
4499 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4500 end;
4501 end
4502 else
4503 begin
4504 gState := STATE_MENU;
4505 gGameOn := False;
4506 end;
4508 gExit := 0;
4509 gPauseMain := false;
4510 gPauseHolmes := false;
4511 gTime := 0;
4512 NetTimeToUpdate := 1;
4513 NetTimeToReliable := 0;
4514 NetTimeToMaster := NetMasterRate;
4515 gLMSRespawn := LMS_RESPAWN_NONE;
4516 gLMSRespawnTime := 0;
4517 gMissionFailed := False;
4518 gNextMap := '';
4520 gCoopMonstersKilled := 0;
4521 gCoopSecretsFound := 0;
4523 gVoteInProgress := False;
4524 gVotePassed := False;
4525 gVoteCount := 0;
4526 gVoted := False;
4528 gStatsOff := False;
4530 if not gGameOn then Exit;
4532 g_Game_SpectateCenterView();
4534 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4535 begin
4536 gLMSRespawn := LMS_RESPAWN_WARMUP;
4537 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4538 gLMSSoftSpawn := True;
4539 if NetMode = NET_SERVER then
4540 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4541 else
4542 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4543 end;
4545 if NetMode = NET_SERVER then
4546 begin
4547 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4549 // Ìàñòåðñåðâåð
4550 if NetUseMaster then
4551 begin
4552 if (NetMHost = nil) or (NetMPeer = nil) then
4553 if not g_Net_Slist_Connect then
4554 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4556 g_Net_Slist_Update;
4557 end;
4559 if NetClients <> nil then
4560 for I := 0 to High(NetClients) do
4561 if NetClients[I].Used then
4562 begin
4563 NetClients[I].Voted := False;
4564 if NetClients[I].RequestedFullUpdate then
4565 begin
4566 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4567 NetClients[I].RequestedFullUpdate := False;
4568 end;
4569 end;
4571 g_Net_UnbanNonPermHosts();
4572 end;
4574 if gLastMap then
4575 begin
4576 gCoopTotalMonstersKilled := 0;
4577 gCoopTotalSecretsFound := 0;
4578 gCoopTotalMonsters := 0;
4579 gCoopTotalSecrets := 0;
4580 gLastMap := False;
4581 end;
4583 g_Game_ExecuteEvent('onmapstart');
4584 end;
4586 procedure SetFirstLevel();
4587 begin
4588 gNextMap := '';
4590 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4591 if MapList = nil then
4592 Exit;
4594 SortSArray(MapList);
4595 gNextMap := MapList[Low(MapList)];
4597 MapList := nil;
4598 end;
4600 procedure g_Game_ExitLevel(const Map: AnsiString);
4601 begin
4602 gNextMap := Map;
4604 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4605 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4606 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4607 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4609 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4610 if gGameSettings.GameType = GT_SINGLE then
4611 gExit := EXIT_ENDLEVELSINGLE
4612 else // Âûøëè â âûõîä â Ñâîåé èãðå
4613 begin
4614 gExit := EXIT_ENDLEVELCUSTOM;
4615 if gGameSettings.GameMode = GM_COOP then
4616 g_Player_RememberAll;
4618 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4619 begin
4620 gLastMap := True;
4621 if gGameSettings.GameMode = GM_COOP then
4622 gStatsOff := True;
4624 gStatsPressed := True;
4625 gNextMap := 'MAP01';
4627 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4628 g_Game_NextLevel;
4630 if g_Game_IsNet then
4631 begin
4632 MH_SEND_GameStats();
4633 MH_SEND_CoopStats();
4634 end;
4635 end;
4636 end;
4637 end;
4639 procedure g_Game_RestartLevel();
4640 var
4641 Map: string;
4642 begin
4643 if gGameSettings.GameMode = GM_SINGLE then
4644 begin
4645 g_Game_Restart();
4646 Exit;
4647 end;
4648 gExit := EXIT_ENDLEVELCUSTOM;
4649 Map := g_ExtractFileName(gMapInfo.Map);
4650 gNextMap := Map;
4651 end;
4653 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4654 var
4655 gWAD: String;
4656 begin
4657 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4658 Exit;
4659 if not g_Game_IsClient then
4660 Exit;
4661 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4662 if gWAD = '' then
4663 begin
4664 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4665 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4666 if gWAD = '' then
4667 begin
4668 g_Game_Free();
4669 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4670 Exit;
4671 end;
4672 end;
4673 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4674 g_Game_LoadWAD(NewWAD);
4675 end;
4677 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4678 var
4679 i, n, nb, nr: Integer;
4681 function monRespawn (mon: TMonster): Boolean;
4682 begin
4683 result := false; // don't stop
4684 if not mon.FNoRespawn then mon.Respawn();
4685 end;
4687 begin
4688 if not g_Game_IsServer then Exit;
4689 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4690 gLMSRespawn := LMS_RESPAWN_NONE;
4691 gLMSRespawnTime := 0;
4692 MessageTime := 0;
4694 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4695 begin
4696 gMissionFailed := True;
4697 g_Game_RestartLevel;
4698 Exit;
4699 end;
4701 n := 0; nb := 0; nr := 0;
4702 for i := Low(gPlayers) to High(gPlayers) do
4703 if (gPlayers[i] <> nil) and
4704 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4705 (gPlayers[i] is TBot)) then
4706 begin
4707 Inc(n);
4708 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4709 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4710 end;
4712 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4713 begin
4714 // wait a second until the fuckers finally decide to join
4715 gLMSRespawn := LMS_RESPAWN_WARMUP;
4716 gLMSRespawnTime := gTime + 1000;
4717 gLMSSoftSpawn := NoMapRestart;
4718 Exit;
4719 end;
4721 g_Player_RemoveAllCorpses;
4722 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4723 if g_Game_IsNet then
4724 MH_SEND_GameEvent(NET_EV_LMS_START);
4726 for i := Low(gPlayers) to High(gPlayers) do
4727 begin
4728 if gPlayers[i] = nil then continue;
4729 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4730 // don't touch normal spectators
4731 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4732 begin
4733 gPlayers[i].FNoRespawn := True;
4734 gPlayers[i].Lives := 0;
4735 if g_Game_IsNet then
4736 MH_SEND_PlayerStats(gPlayers[I].UID);
4737 continue;
4738 end;
4739 gPlayers[i].FNoRespawn := False;
4740 gPlayers[i].Lives := gGameSettings.MaxLives;
4741 gPlayers[i].Respawn(False, True);
4742 if gGameSettings.GameMode = GM_COOP then
4743 begin
4744 gPlayers[i].Frags := 0;
4745 gPlayers[i].RecallState;
4746 end;
4747 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4748 gPlayer1 := g_Player_Get(gLMSPID1);
4749 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4750 gPlayer2 := g_Player_Get(gLMSPID2);
4751 end;
4753 g_Items_RestartRound();
4756 g_Mons_ForEach(monRespawn);
4758 gLMSSoftSpawn := False;
4759 end;
4761 function g_Game_GetFirstMap(WAD: String): String;
4762 begin
4763 Result := '';
4765 MapList := g_Map_GetMapsList(WAD);
4766 if MapList = nil then
4767 Exit;
4769 SortSArray(MapList);
4770 Result := MapList[Low(MapList)];
4772 if not g_Map_Exist(WAD + ':\' + Result) then
4773 Result := '';
4775 MapList := nil;
4776 end;
4778 function g_Game_GetNextMap(): String;
4779 var
4780 I: Integer;
4781 Map: string;
4782 begin
4783 Result := '';
4785 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4786 if MapList = nil then
4787 Exit;
4789 Map := g_ExtractFileName(gMapInfo.Map);
4791 SortSArray(MapList);
4792 MapIndex := -255;
4793 for I := Low(MapList) to High(MapList) do
4794 if Map = MapList[I] then
4795 begin
4796 MapIndex := I;
4797 Break;
4798 end;
4800 if MapIndex <> -255 then
4801 begin
4802 if MapIndex = High(MapList) then
4803 Result := MapList[Low(MapList)]
4804 else
4805 Result := MapList[MapIndex + 1];
4807 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4808 end;
4810 MapList := nil;
4811 end;
4813 procedure g_Game_NextLevel();
4814 begin
4815 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4816 gExit := EXIT_ENDLEVELCUSTOM
4817 else
4818 begin
4819 gExit := EXIT_ENDLEVELSINGLE;
4820 Exit;
4821 end;
4823 if gNextMap <> '' then Exit;
4824 gNextMap := g_Game_GetNextMap();
4825 end;
4827 function g_Game_IsTestMap(): Boolean;
4828 begin
4829 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4830 end;
4832 procedure g_Game_DeleteTestMap();
4833 var
4834 a: Integer;
4835 //MapName: AnsiString;
4836 WadName: string;
4838 WAD: TWADFile;
4839 MapList: SSArray;
4840 time: Integer;
4842 begin
4843 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
4844 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
4845 if (a = 0) then exit;
4847 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
4848 WadName := Copy(gMapToDelete, 1, a+3);
4849 Delete(gMapToDelete, 1, a+5);
4850 gMapToDelete := UpperCase(gMapToDelete);
4851 //MapName := '';
4852 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4855 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4856 if MapName <> TEST_MAP_NAME then
4857 Exit;
4859 if not gTempDelete then
4860 begin
4861 time := g_GetFileTime(WadName);
4862 WAD := TWADFile.Create();
4864 // ×èòàåì Wad-ôàéë:
4865 if not WAD.ReadFile(WadName) then
4866 begin // Íåò òàêîãî WAD-ôàéëà
4867 WAD.Free();
4868 Exit;
4869 end;
4871 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4872 WAD.CreateImage();
4873 MapList := WAD.GetResourcesList('');
4875 if MapList <> nil then
4876 for a := 0 to High(MapList) do
4877 if MapList[a] = MapName then
4878 begin
4879 // Óäàëÿåì è ñîõðàíÿåì:
4880 WAD.RemoveResource('', MapName);
4881 WAD.SaveTo(WadName);
4882 Break;
4883 end;
4885 WAD.Free();
4886 g_SetFileTime(WadName, time);
4887 end else
4889 if gTempDelete then DeleteFile(WadName);
4890 end;
4892 procedure GameCVars(P: SSArray);
4893 var
4894 a, b: Integer;
4895 stat: TPlayerStatArray;
4896 cmd, s: string;
4897 config: TConfig;
4898 begin
4899 stat := nil;
4900 cmd := LowerCase(P[0]);
4901 if cmd = 'r_showfps' then
4902 begin
4903 if (Length(P) > 1) and
4904 ((P[1] = '1') or (P[1] = '0')) then
4905 gShowFPS := (P[1][1] = '1');
4907 if gShowFPS then
4908 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4909 else
4910 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4911 end
4912 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4913 begin
4914 with gGameSettings do
4915 begin
4916 if (Length(P) > 1) and
4917 ((P[1] = '1') or (P[1] = '0')) then
4918 begin
4919 if (P[1][1] = '1') then
4920 Options := Options or GAME_OPTION_TEAMDAMAGE
4921 else
4922 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4923 end;
4925 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4926 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4927 else
4928 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4930 if g_Game_IsNet then MH_SEND_GameSettings;
4931 end;
4932 end
4933 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4934 begin
4935 with gGameSettings do
4936 begin
4937 if (Length(P) > 1) and
4938 ((P[1] = '1') or (P[1] = '0')) then
4939 begin
4940 if (P[1][1] = '1') then
4941 Options := Options or GAME_OPTION_WEAPONSTAY
4942 else
4943 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4944 end;
4946 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4947 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4948 else
4949 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4951 if g_Game_IsNet then MH_SEND_GameSettings;
4952 end;
4953 end
4954 else if cmd = 'g_gamemode' then
4955 begin
4956 a := g_Game_TextToMode(P[1]);
4957 if a = GM_SINGLE then a := GM_COOP;
4958 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4959 begin
4960 gSwitchGameMode := a;
4961 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4962 (gState = STATE_INTERSINGLE) then
4963 gSwitchGameMode := GM_SINGLE;
4964 if not gGameOn then
4965 gGameSettings.GameMode := gSwitchGameMode;
4966 end;
4967 if gSwitchGameMode = gGameSettings.GameMode then
4968 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4969 [g_Game_ModeToText(gGameSettings.GameMode)]))
4970 else
4971 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4972 [g_Game_ModeToText(gGameSettings.GameMode),
4973 g_Game_ModeToText(gSwitchGameMode)]));
4974 end
4975 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4976 begin
4977 with gGameSettings do
4978 begin
4979 if (Length(P) > 1) and
4980 ((P[1] = '1') or (P[1] = '0')) then
4981 begin
4982 if (P[1][1] = '1') then
4983 Options := Options or GAME_OPTION_ALLOWEXIT
4984 else
4985 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4986 end;
4988 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4989 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4990 else
4991 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4992 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4994 if g_Game_IsNet then MH_SEND_GameSettings;
4995 end;
4996 end
4997 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4998 begin
4999 with gGameSettings do
5000 begin
5001 if (Length(P) > 1) and
5002 ((P[1] = '1') or (P[1] = '0')) then
5003 begin
5004 if (P[1][1] = '1') then
5005 Options := Options or GAME_OPTION_MONSTERS
5006 else
5007 Options := Options and (not GAME_OPTION_MONSTERS);
5008 end;
5010 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
5011 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
5012 else
5013 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
5014 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5016 if g_Game_IsNet then MH_SEND_GameSettings;
5017 end;
5018 end
5019 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
5020 begin
5021 with gGameSettings do
5022 begin
5023 if (Length(P) > 1) and
5024 ((P[1] = '1') or (P[1] = '0')) then
5025 begin
5026 if (P[1][1] = '1') then
5027 Options := Options or GAME_OPTION_BOTVSPLAYER
5028 else
5029 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
5030 end;
5032 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
5033 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
5034 else
5035 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
5037 if g_Game_IsNet then MH_SEND_GameSettings;
5038 end;
5039 end
5040 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
5041 begin
5042 with gGameSettings do
5043 begin
5044 if (Length(P) > 1) and
5045 ((P[1] = '1') or (P[1] = '0')) then
5046 begin
5047 if (P[1][1] = '1') then
5048 Options := Options or GAME_OPTION_BOTVSMONSTER
5049 else
5050 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
5051 end;
5053 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
5054 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
5055 else
5056 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
5058 if g_Game_IsNet then MH_SEND_GameSettings;
5059 end;
5060 end
5061 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
5062 begin
5063 if Length(P) > 1 then
5064 begin
5065 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
5066 gGameSettings.WarmupTime := 30
5067 else
5068 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
5069 end;
5071 g_Console_Add(Format(_lc[I_MSG_WARMUP],
5072 [gGameSettings.WarmupTime]));
5073 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5074 end
5075 else if cmd = 'net_interp' then
5076 begin
5077 if (Length(P) > 1) then
5078 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
5080 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
5081 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5082 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
5083 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5084 config.Free();
5085 end
5086 else if cmd = 'net_forceplayerupdate' then
5087 begin
5088 if (Length(P) > 1) and
5089 ((P[1] = '1') or (P[1] = '0')) then
5090 NetForcePlayerUpdate := (P[1][1] = '1');
5092 if NetForcePlayerUpdate then
5093 g_Console_Add('net_forceplayerupdate = 1')
5094 else
5095 g_Console_Add('net_forceplayerupdate = 0');
5096 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5097 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
5098 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5099 config.Free();
5100 end
5101 else if cmd = 'net_predictself' then
5102 begin
5103 if (Length(P) > 1) and
5104 ((P[1] = '1') or (P[1] = '0')) then
5105 NetPredictSelf := (P[1][1] = '1');
5107 if NetPredictSelf then
5108 g_Console_Add('net_predictself = 1')
5109 else
5110 g_Console_Add('net_predictself = 0');
5111 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5112 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
5113 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5114 config.Free();
5115 end
5116 else if cmd = 'sv_name' then
5117 begin
5118 if (Length(P) > 1) and (Length(P[1]) > 0) then
5119 begin
5120 NetServerName := P[1];
5121 if Length(NetServerName) > 64 then
5122 SetLength(NetServerName, 64);
5123 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5124 g_Net_Slist_Update;
5125 end;
5127 g_Console_Add(cmd + ' = "' + NetServerName + '"');
5128 end
5129 else if cmd = 'sv_passwd' then
5130 begin
5131 if (Length(P) > 1) and (Length(P[1]) > 0) then
5132 begin
5133 NetPassword := P[1];
5134 if Length(NetPassword) > 24 then
5135 SetLength(NetPassword, 24);
5136 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5137 g_Net_Slist_Update;
5138 end;
5140 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
5141 end
5142 else if cmd = 'sv_maxplrs' then
5143 begin
5144 if (Length(P) > 1) then
5145 begin
5146 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
5147 if g_Game_IsServer and g_Game_IsNet then
5148 begin
5149 b := 0;
5150 for a := 0 to High(NetClients) do
5151 if NetClients[a].Used then
5152 begin
5153 Inc(b);
5154 if b > NetMaxClients then
5155 begin
5156 s := g_Player_Get(NetClients[a].Player).Name;
5157 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
5158 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5159 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5160 end;
5161 end;
5162 if NetUseMaster then
5163 g_Net_Slist_Update;
5164 end;
5165 end;
5167 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
5168 end
5169 else if cmd = 'sv_public' then
5170 begin
5171 if (Length(P) > 1) then
5172 begin
5173 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
5174 if g_Game_IsServer and g_Game_IsNet then
5175 if NetUseMaster then
5176 begin
5177 if NetMPeer = nil then
5178 if not g_Net_Slist_Connect() then
5179 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
5180 g_Net_Slist_Update();
5181 end
5182 else
5183 if NetMPeer <> nil then
5184 g_Net_Slist_Disconnect();
5185 end;
5187 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
5188 end
5189 else if cmd = 'sv_intertime' then
5190 begin
5191 if (Length(P) > 1) then
5192 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
5194 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
5195 end
5196 else if cmd = 'p1_name' then
5197 begin
5198 if (Length(P) > 1) and gGameOn then
5199 begin
5200 if g_Game_IsClient then
5201 begin
5202 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5203 MC_SEND_PlayerSettings;
5204 end
5205 else
5206 if gPlayer1 <> nil then
5207 begin
5208 gPlayer1.Name := b_Text_Unformat(P[1]);
5209 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5210 end
5211 else
5212 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5213 end;
5214 end
5215 else if cmd = 'p2_name' then
5216 begin
5217 if (Length(P) > 1) and gGameOn then
5218 begin
5219 if g_Game_IsClient then
5220 begin
5221 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5222 MC_SEND_PlayerSettings;
5223 end
5224 else
5225 if gPlayer2 <> nil then
5226 begin
5227 gPlayer2.Name := b_Text_Unformat(P[1]);
5228 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5229 end
5230 else
5231 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5232 end;
5233 end
5234 else if cmd = 'p1_color' then
5235 begin
5236 if Length(P) > 3 then
5237 if g_Game_IsClient then
5238 begin
5239 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5240 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5241 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5242 MC_SEND_PlayerSettings;
5243 end
5244 else
5245 if gPlayer1 <> nil then
5246 begin
5247 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5248 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5249 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5250 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5251 end
5252 else
5253 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5254 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5255 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5256 end
5257 else if (cmd = 'p2_color') and not g_Game_IsNet then
5258 begin
5259 if Length(P) > 3 then
5260 if g_Game_IsClient then
5261 begin
5262 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5263 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5264 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5265 MC_SEND_PlayerSettings;
5266 end
5267 else
5268 if gPlayer2 <> nil then
5269 begin
5270 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5271 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5272 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5273 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5274 end
5275 else
5276 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5277 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5278 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5279 end
5280 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5281 begin
5282 if cmd = 'r_showtime' then
5283 begin
5284 if (Length(P) > 1) and
5285 ((P[1] = '1') or (P[1] = '0')) then
5286 gShowTime := (P[1][1] = '1');
5288 if gShowTime then
5289 g_Console_Add(_lc[I_MSG_TIME_ON])
5290 else
5291 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5292 end
5293 else if cmd = 'r_showscore' then
5294 begin
5295 if (Length(P) > 1) and
5296 ((P[1] = '1') or (P[1] = '0')) then
5297 gShowGoals := (P[1][1] = '1');
5299 if gShowGoals then
5300 g_Console_Add(_lc[I_MSG_SCORE_ON])
5301 else
5302 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5303 end
5304 else if cmd = 'r_showstat' then
5305 begin
5306 if (Length(P) > 1) and
5307 ((P[1] = '1') or (P[1] = '0')) then
5308 gShowStat := (P[1][1] = '1');
5310 if gShowStat then
5311 g_Console_Add(_lc[I_MSG_STATS_ON])
5312 else
5313 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5314 end
5315 else if cmd = 'r_showkillmsg' then
5316 begin
5317 if (Length(P) > 1) and
5318 ((P[1] = '1') or (P[1] = '0')) then
5319 gShowKillMsg := (P[1][1] = '1');
5321 if gShowKillMsg then
5322 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5323 else
5324 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5325 end
5326 else if cmd = 'r_showlives' then
5327 begin
5328 if (Length(P) > 1) and
5329 ((P[1] = '1') or (P[1] = '0')) then
5330 gShowLives := (P[1][1] = '1');
5332 if gShowLives then
5333 g_Console_Add(_lc[I_MSG_LIVES_ON])
5334 else
5335 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5336 end
5337 else if cmd = 'r_showspect' then
5338 begin
5339 if (Length(P) > 1) and
5340 ((P[1] = '1') or (P[1] = '0')) then
5341 gSpectHUD := (P[1][1] = '1');
5343 if gSpectHUD then
5344 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5345 else
5346 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5347 end
5348 else if cmd = 'r_showping' then
5349 begin
5350 if (Length(P) > 1) and
5351 ((P[1] = '1') or (P[1] = '0')) then
5352 gShowPing := (P[1][1] = '1');
5354 if gShowPing then
5355 g_Console_Add(_lc[I_MSG_PING_ON])
5356 else
5357 g_Console_Add(_lc[I_MSG_PING_OFF]);
5358 end
5359 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5360 begin
5361 if Length(P) > 1 then
5362 begin
5363 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5364 gGameSettings.GoalLimit := 0
5365 else
5366 begin
5367 b := 0;
5369 if gGameSettings.GameMode = GM_DM then
5370 begin // DM
5371 stat := g_Player_GetStats();
5372 if stat <> nil then
5373 for a := 0 to High(stat) do
5374 if stat[a].Frags > b then
5375 b := stat[a].Frags;
5376 end
5377 else // TDM/CTF
5378 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5380 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5381 end;
5383 if g_Game_IsNet then MH_SEND_GameSettings;
5384 end;
5386 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5387 end
5388 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5389 begin
5390 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5391 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5393 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5394 [gGameSettings.TimeLimit div 3600,
5395 (gGameSettings.TimeLimit div 60) mod 60,
5396 gGameSettings.TimeLimit mod 60]));
5397 if g_Game_IsNet then MH_SEND_GameSettings;
5398 end
5399 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5400 begin
5401 if Length(P) > 1 then
5402 begin
5403 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5404 gGameSettings.MaxLives := 0
5405 else
5406 begin
5407 b := 0;
5408 stat := g_Player_GetStats();
5409 if stat <> nil then
5410 for a := 0 to High(stat) do
5411 if stat[a].Lives > b then
5412 b := stat[a].Lives;
5413 gGameSettings.MaxLives :=
5414 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5415 end;
5416 end;
5418 g_Console_Add(Format(_lc[I_MSG_LIVES],
5419 [gGameSettings.MaxLives]));
5420 if g_Game_IsNet then MH_SEND_GameSettings;
5421 end;
5422 end;
5423 end;
5425 procedure PrintHeapStats();
5426 var
5427 hs: TFPCHeapStatus;
5428 begin
5429 hs := GetFPCHeapStatus();
5430 e_LogWriteLn ('v===== heap status =====v');
5431 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
5432 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
5433 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
5434 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
5435 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
5436 e_LogWriteLn ('^=======================^');
5437 end;
5439 procedure DebugCommands(P: SSArray);
5440 var
5441 a, b: Integer;
5442 cmd: string;
5443 //pt: TDFPoint;
5444 mon: TMonster;
5445 begin
5446 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5447 if {gDebugMode}conIsCheatsEnabled then
5448 begin
5449 cmd := LowerCase(P[0]);
5450 if cmd = 'd_window' then
5451 begin
5452 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5453 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5454 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5455 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5456 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5457 end
5458 else if cmd = 'd_sounds' then
5459 begin
5460 if (Length(P) > 1) and
5461 ((P[1] = '1') or (P[1] = '0')) then
5462 g_Debug_Sounds := (P[1][1] = '1');
5464 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5465 end
5466 else if cmd = 'd_frames' then
5467 begin
5468 if (Length(P) > 1) and
5469 ((P[1] = '1') or (P[1] = '0')) then
5470 g_Debug_Frames := (P[1][1] = '1');
5472 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5473 end
5474 else if cmd = 'd_winmsg' then
5475 begin
5476 if (Length(P) > 1) and
5477 ((P[1] = '1') or (P[1] = '0')) then
5478 g_Debug_WinMsgs := (P[1][1] = '1');
5480 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5481 end
5482 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5483 begin
5484 if (Length(P) > 1) and
5485 ((P[1] = '1') or (P[1] = '0')) then
5486 g_Debug_MonsterOff := (P[1][1] = '1');
5488 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5489 end
5490 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5491 begin
5492 if Length(P) > 1 then
5493 case P[1][1] of
5494 '0': g_debug_BotAIOff := 0;
5495 '1': g_debug_BotAIOff := 1;
5496 '2': g_debug_BotAIOff := 2;
5497 '3': g_debug_BotAIOff := 3;
5498 end;
5500 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5501 end
5502 else if cmd = 'd_monster' then
5503 begin
5504 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5505 if Length(P) < 2 then
5506 begin
5507 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5508 g_Console_Add('ID | Name');
5509 for b := MONSTER_DEMON to MONSTER_MAN do
5510 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5511 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
5512 end else
5513 begin
5514 a := StrToIntDef(P[1], 0);
5515 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5516 a := g_Mons_TypeIdByName(P[1]);
5518 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5519 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5520 else
5521 begin
5522 with gPlayer1.Obj do
5523 begin
5524 mon := g_Monsters_Create(a,
5525 X + Rect.X + (Rect.Width div 2),
5526 Y + Rect.Y + Rect.Height,
5527 gPlayer1.Direction, True);
5528 end;
5529 if (Length(P) > 2) and (mon <> nil) then
5530 begin
5531 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
5532 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
5533 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
5534 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
5535 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
5536 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
5537 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
5538 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
5539 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5540 end;
5541 end;
5542 end;
5543 end
5544 else if (cmd = 'd_health') then
5545 begin
5546 if (Length(P) > 1) and
5547 ((P[1] = '1') or (P[1] = '0')) then
5548 g_debug_HealthBar := (P[1][1] = '1');
5550 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5551 end
5552 else if (cmd = 'd_player') then
5553 begin
5554 if (Length(P) > 1) and
5555 ((P[1] = '1') or (P[1] = '0')) then
5556 g_debug_Player := (P[1][1] = '1');
5558 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5559 end
5560 else if (cmd = 'd_joy') then
5561 begin
5562 for a := 1 to 8 do
5563 g_Console_Add(e_JoystickStateToString(a));
5564 end
5565 else if (cmd = 'd_mem') then
5566 begin
5567 PrintHeapStats();
5568 end;
5569 end
5570 else
5571 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5572 end;
5575 procedure GameCheats(P: SSArray);
5576 var
5577 cmd: string;
5578 f, a: Integer;
5579 plr: TPlayer;
5580 begin
5581 if (not gGameOn) or (not conIsCheatsEnabled) then
5582 begin
5583 g_Console_Add('not available');
5584 exit;
5585 end;
5586 plr := gPlayer1;
5587 if plr = nil then
5588 begin
5589 g_Console_Add('where is the player?!');
5590 exit;
5591 end;
5592 cmd := LowerCase(P[0]);
5593 // god
5594 if cmd = 'god' then
5595 begin
5596 plr.GodMode := not plr.GodMode;
5597 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5598 exit;
5599 end;
5600 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5601 if cmd = 'give' then
5602 begin
5603 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5604 for f := 1 to High(P) do
5605 begin
5606 cmd := LowerCase(P[f]);
5607 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5608 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5609 if cmd = 'exit' then
5610 begin
5611 if gTriggers <> nil then
5612 begin
5613 for a := 0 to High(gTriggers) do
5614 begin
5615 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5616 begin
5617 g_Console_Add('player left the map');
5618 gExitByTrigger := True;
5619 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5620 g_Game_ExitLevel(gTriggers[a].tgcMap);
5621 break;
5622 end;
5623 end;
5624 end;
5625 continue;
5626 end;
5628 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5629 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5630 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5631 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5632 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5634 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5635 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5637 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5638 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;
5640 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5641 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5643 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5644 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5646 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5647 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5649 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5650 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5651 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5653 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5654 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5655 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5656 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;
5657 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5658 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5660 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;
5661 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;
5662 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;
5663 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;
5664 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;
5665 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;
5667 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5668 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;
5670 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;
5671 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;
5673 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5675 if cmd = 'ammo' then
5676 begin
5677 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5678 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5679 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5680 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5681 plr.GiveItem(ITEM_AMMO_FUELCAN);
5682 g_Console_Add('player got some ammo');
5683 continue;
5684 end;
5686 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5687 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5689 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5690 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5692 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5693 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5695 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5696 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5698 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5700 if cmd = 'weapons' then
5701 begin
5702 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5703 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5704 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5705 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5706 plr.GiveItem(ITEM_WEAPON_PLASMA);
5707 plr.GiveItem(ITEM_WEAPON_BFG);
5708 g_Console_Add('player got weapons');
5709 continue;
5710 end;
5712 if cmd = 'keys' then
5713 begin
5714 plr.GiveItem(ITEM_KEY_RED);
5715 plr.GiveItem(ITEM_KEY_GREEN);
5716 plr.GiveItem(ITEM_KEY_BLUE);
5717 g_Console_Add('player got all keys');
5718 continue;
5719 end;
5721 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5722 end;
5723 exit;
5724 end;
5725 // open
5726 if cmd = 'open' then
5727 begin
5728 g_Console_Add('player activated sesame');
5729 g_Triggers_OpenAll();
5730 exit;
5731 end;
5732 // fly
5733 if cmd = 'fly' then
5734 begin
5735 gFly := not gFly;
5736 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5737 exit;
5738 end;
5739 // noclip
5740 if cmd = 'noclip' then
5741 begin
5742 plr.SwitchNoClip;
5743 g_Console_Add('wall hardeness adjusted');
5744 exit;
5745 end;
5746 // notarget
5747 if cmd = 'notarget' then
5748 begin
5749 plr.NoTarget := not plr.NoTarget;
5750 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5751 exit;
5752 end;
5753 // noreload
5754 if cmd = 'noreload' then
5755 begin
5756 plr.NoReload := not plr.NoReload;
5757 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5758 exit;
5759 end;
5760 // speedy
5761 if cmd = 'speedy' then
5762 begin
5763 MAX_RUNVEL := 32-MAX_RUNVEL;
5764 g_Console_Add('speed adjusted');
5765 exit;
5766 end;
5767 // jumpy
5768 if cmd = 'jumpy' then
5769 begin
5770 VEL_JUMP := 30-VEL_JUMP;
5771 g_Console_Add('jump height adjusted');
5772 exit;
5773 end;
5774 // automap
5775 if cmd = 'automap' then
5776 begin
5777 gShowMap := not gShowMap;
5778 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5779 exit;
5780 end;
5781 // aimline
5782 if cmd = 'aimline' then
5783 begin
5784 gAimLine := not gAimLine;
5785 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5786 exit;
5787 end;
5788 end;
5790 procedure GameCommands(P: SSArray);
5791 var
5792 a, b: Integer;
5793 s, pw: String;
5794 chstr: string;
5795 cmd: string;
5796 pl: pTNetClient = nil;
5797 plr: TPlayer;
5798 prt: Word;
5799 nm: Boolean;
5800 listen: LongWord;
5801 begin
5802 // Îáùèå êîìàíäû:
5803 cmd := LowerCase(P[0]);
5804 chstr := '';
5805 if (cmd = 'quit') or
5806 (cmd = 'exit') then
5807 begin
5808 g_Game_Free();
5809 g_Game_Quit();
5810 Exit;
5811 end
5812 else if cmd = 'pause' then
5813 begin
5814 if (g_ActiveWindow = nil) then
5815 g_Game_Pause(not gPauseMain);
5816 end
5817 else if cmd = 'endgame' then
5818 gExit := EXIT_SIMPLE
5819 else if cmd = 'restart' then
5820 begin
5821 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5822 begin
5823 if g_Game_IsClient then
5824 begin
5825 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5826 Exit;
5827 end;
5828 g_Game_Restart();
5829 end else
5830 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5831 end
5832 else if cmd = 'kick' then
5833 begin
5834 if g_Game_IsServer then
5835 begin
5836 if Length(P) < 2 then
5837 begin
5838 g_Console_Add('kick <name>');
5839 Exit;
5840 end;
5841 if P[1] = '' then
5842 begin
5843 g_Console_Add('kick <name>');
5844 Exit;
5845 end;
5847 if g_Game_IsNet then
5848 pl := g_Net_Client_ByName(P[1]);
5849 if (pl <> nil) then
5850 begin
5851 s := g_Net_ClientName_ByID(pl^.ID);
5852 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5853 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5854 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5855 if NetUseMaster then
5856 g_Net_Slist_Update;
5857 end else if gPlayers <> nil then
5858 for a := Low(gPlayers) to High(gPlayers) do
5859 if gPlayers[a] <> nil then
5860 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5861 begin
5862 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5863 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5864 continue;
5865 gPlayers[a].Lives := 0;
5866 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5867 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5868 g_Player_Remove(gPlayers[a].UID);
5869 if NetUseMaster then
5870 g_Net_Slist_Update;
5871 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5872 g_Bot_MixNames();
5873 end;
5874 end else
5875 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5876 end
5877 else if cmd = 'kick_id' then
5878 begin
5879 if g_Game_IsServer and g_Game_IsNet then
5880 begin
5881 if Length(P) < 2 then
5882 begin
5883 g_Console_Add('kick_id <client ID>');
5884 Exit;
5885 end;
5886 if P[1] = '' then
5887 begin
5888 g_Console_Add('kick_id <client ID>');
5889 Exit;
5890 end;
5892 a := StrToIntDef(P[1], 0);
5893 if (NetClients <> nil) and (a <= High(NetClients)) then
5894 begin
5895 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5896 begin
5897 s := g_Net_ClientName_ByID(NetClients[a].ID);
5898 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5899 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5900 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5901 if NetUseMaster then
5902 g_Net_Slist_Update;
5903 end;
5904 end;
5905 end else
5906 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5907 end
5908 else if cmd = 'ban' then
5909 begin
5910 if g_Game_IsServer and g_Game_IsNet then
5911 begin
5912 if Length(P) < 2 then
5913 begin
5914 g_Console_Add('ban <name>');
5915 Exit;
5916 end;
5917 if P[1] = '' then
5918 begin
5919 g_Console_Add('ban <name>');
5920 Exit;
5921 end;
5923 pl := g_Net_Client_ByName(P[1]);
5924 if (pl <> nil) then
5925 begin
5926 s := g_Net_ClientName_ByID(pl^.ID);
5927 g_Net_BanHost(pl^.Peer^.address.host, False);
5928 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5929 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5930 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5931 if NetUseMaster then
5932 g_Net_Slist_Update;
5933 end else
5934 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5935 end else
5936 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5937 end
5938 else if cmd = 'ban_id' then
5939 begin
5940 if g_Game_IsServer and g_Game_IsNet then
5941 begin
5942 if Length(P) < 2 then
5943 begin
5944 g_Console_Add('ban_id <client ID>');
5945 Exit;
5946 end;
5947 if P[1] = '' then
5948 begin
5949 g_Console_Add('ban_id <client ID>');
5950 Exit;
5951 end;
5953 a := StrToIntDef(P[1], 0);
5954 if (NetClients <> nil) and (a <= High(NetClients)) then
5955 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5956 begin
5957 s := g_Net_ClientName_ByID(NetClients[a].ID);
5958 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5959 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5960 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5961 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5962 if NetUseMaster then
5963 g_Net_Slist_Update;
5964 end;
5965 end else
5966 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5967 end
5968 else if cmd = 'permban' then
5969 begin
5970 if g_Game_IsServer and g_Game_IsNet then
5971 begin
5972 if Length(P) < 2 then
5973 begin
5974 g_Console_Add('permban <name>');
5975 Exit;
5976 end;
5977 if P[1] = '' then
5978 begin
5979 g_Console_Add('permban <name>');
5980 Exit;
5981 end;
5983 pl := g_Net_Client_ByName(P[1]);
5984 if (pl <> nil) then
5985 begin
5986 s := g_Net_ClientName_ByID(pl^.ID);
5987 g_Net_BanHost(pl^.Peer^.address.host);
5988 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5989 g_Net_SaveBanList();
5990 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5991 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5992 if NetUseMaster then
5993 g_Net_Slist_Update;
5994 end else
5995 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5996 end else
5997 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5998 end
5999 else if cmd = 'permban_id' then
6000 begin
6001 if g_Game_IsServer and g_Game_IsNet then
6002 begin
6003 if Length(P) < 2 then
6004 begin
6005 g_Console_Add('permban_id <client ID>');
6006 Exit;
6007 end;
6008 if P[1] = '' then
6009 begin
6010 g_Console_Add('permban_id <client ID>');
6011 Exit;
6012 end;
6014 a := StrToIntDef(P[1], 0);
6015 if (NetClients <> nil) and (a <= High(NetClients)) then
6016 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6017 begin
6018 s := g_Net_ClientName_ByID(NetClients[a].ID);
6019 g_Net_BanHost(NetClients[a].Peer^.address.host);
6020 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
6021 g_Net_SaveBanList();
6022 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6023 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6024 if NetUseMaster then
6025 g_Net_Slist_Update;
6026 end;
6027 end else
6028 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6029 end
6030 else if cmd = 'unban' then
6031 begin
6032 if g_Game_IsServer and g_Game_IsNet then
6033 begin
6034 if Length(P) < 2 then
6035 begin
6036 g_Console_Add('unban <IP Address>');
6037 Exit;
6038 end;
6039 if P[1] = '' then
6040 begin
6041 g_Console_Add('unban <IP Address>');
6042 Exit;
6043 end;
6045 if g_Net_UnbanHost(P[1]) then
6046 begin
6047 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
6048 g_Net_SaveBanList();
6049 end else
6050 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
6051 end else
6052 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6053 end
6054 else if cmd = 'clientlist' then
6055 begin
6056 if g_Game_IsServer and g_Game_IsNet then
6057 begin
6058 b := 0;
6059 if NetClients <> nil then
6060 for a := Low(NetClients) to High(NetClients) do
6061 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6062 begin
6063 plr := g_Player_Get(NetClients[a].Player);
6064 if plr = nil then continue;
6065 Inc(b);
6066 g_Console_Add(Format('#%2d: %-15s | %s', [a,
6067 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
6068 end;
6069 if b = 0 then
6070 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
6071 end else
6072 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6073 end
6074 else if cmd = 'connect' then
6075 begin
6076 if (NetMode = NET_NONE) then
6077 begin
6078 if Length(P) < 2 then
6079 begin
6080 g_Console_Add('connect <IP> [port] [password]');
6081 Exit;
6082 end;
6083 if P[1] = '' then
6084 begin
6085 g_Console_Add('connect <IP> [port] [password]');
6086 Exit;
6087 end;
6089 if Length(P) > 2 then
6090 prt := StrToIntDef(P[2], 25666)
6091 else
6092 prt := 25666;
6094 if Length(P) > 3 then
6095 pw := P[3]
6096 else
6097 pw := '';
6099 g_Game_StartClient(P[1], prt, pw);
6100 end;
6101 end
6102 else if cmd = 'disconnect' then
6103 begin
6104 if (NetMode = NET_CLIENT) then
6105 g_Net_Disconnect();
6106 end
6107 else if cmd = 'reconnect' then
6108 begin
6109 if (NetMode = NET_SERVER) then
6110 Exit;
6112 if (NetMode = NET_CLIENT) then
6113 begin
6114 g_Net_Disconnect();
6115 gExit := EXIT_SIMPLE;
6116 EndGame;
6117 end;
6119 //TODO: Use last successful password to reconnect, instead of ''
6120 g_Game_StartClient(NetClientIP, NetClientPort, '');
6121 end
6122 else if (cmd = 'addbot') or
6123 (cmd = 'bot_add') then
6124 begin
6125 if Length(P) > 1 then
6126 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
6127 else
6128 g_Bot_Add(TEAM_NONE, 2);
6129 end
6130 else if cmd = 'bot_addlist' then
6131 begin
6132 if Length(P) > 1 then
6133 if Length(P) = 2 then
6134 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
6135 else
6136 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
6137 end
6138 else if cmd = 'bot_removeall' then
6139 g_Bot_RemoveAll()
6140 else if cmd = 'chat' then
6141 begin
6142 if g_Game_IsNet then
6143 begin
6144 if Length(P) > 1 then
6145 begin
6146 for a := 1 to High(P) do
6147 chstr := chstr + P[a] + ' ';
6149 if Length(chstr) > 200 then SetLength(chstr, 200);
6151 if Length(chstr) < 1 then
6152 begin
6153 g_Console_Add('chat <text>');
6154 Exit;
6155 end;
6157 chstr := b_Text_Format(chstr);
6158 if g_Game_IsClient then
6159 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
6160 else
6161 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
6162 end
6163 else
6164 g_Console_Add('chat <text>');
6165 end else
6166 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6167 end
6168 else if cmd = 'teamchat' then
6169 begin
6170 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
6171 begin
6172 if Length(P) > 1 then
6173 begin
6174 for a := 1 to High(P) do
6175 chstr := chstr + P[a] + ' ';
6177 if Length(chstr) > 200 then SetLength(chstr, 200);
6179 if Length(chstr) < 1 then
6180 begin
6181 g_Console_Add('teamchat <text>');
6182 Exit;
6183 end;
6185 chstr := b_Text_Format(chstr);
6186 if g_Game_IsClient then
6187 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
6188 else
6189 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
6190 gPlayer1Settings.Team);
6191 end
6192 else
6193 g_Console_Add('teamchat <text>');
6194 end else
6195 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6196 end
6197 else if cmd = 'game' then
6198 begin
6199 if gGameSettings.GameType <> GT_NONE then
6200 begin
6201 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6202 Exit;
6203 end;
6204 if Length(P) = 1 then
6205 begin
6206 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
6207 Exit;
6208 end;
6209 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
6210 P[1] := addWadExtension(P[1]);
6211 if FileExists(MapsDir + P[1]) then
6212 begin
6213 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6214 if Length(P) < 3 then
6215 begin
6216 SetLength(P, 3);
6217 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6218 end;
6220 s := P[1] + ':\' + UpperCase(P[2]);
6222 if g_Map_Exist(MapsDir + s) then
6223 begin
6224 // Çàïóñêàåì ñâîþ èãðó
6225 g_Game_Free();
6226 with gGameSettings do
6227 begin
6228 GameMode := g_Game_TextToMode(gcGameMode);
6229 if gSwitchGameMode <> GM_NONE then
6230 GameMode := gSwitchGameMode;
6231 if GameMode = GM_NONE then GameMode := GM_DM;
6232 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6233 b := 1;
6234 if Length(P) >= 4 then
6235 b := StrToIntDef(P[3], 1);
6236 g_Game_StartCustom(s, GameMode, TimeLimit,
6237 GoalLimit, MaxLives, Options, b);
6238 end;
6239 end
6240 else
6241 if P[2] = '' then
6242 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6243 else
6244 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[2]), P[1]]));
6245 end else
6246 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6247 end
6248 else if cmd = 'host' then
6249 begin
6250 if gGameSettings.GameType <> GT_NONE then
6251 begin
6252 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6253 Exit;
6254 end;
6255 if Length(P) < 4 then
6256 begin
6257 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
6258 Exit;
6259 end;
6260 if not StrToIp(P[1], listen) then
6261 Exit;
6262 prt := StrToIntDef(P[2], 25666);
6264 P[3] := addWadExtension(P[3]);
6265 if FileExists(MapsDir + P[3]) then
6266 begin
6267 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6268 if Length(P) < 5 then
6269 begin
6270 SetLength(P, 5);
6271 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
6272 end;
6274 s := P[3] + ':\' + UpperCase(P[4]);
6276 if g_Map_Exist(MapsDir + s) then
6277 begin
6278 // Çàïóñêàåì ñâîþ èãðó
6279 g_Game_Free();
6280 with gGameSettings do
6281 begin
6282 GameMode := g_Game_TextToMode(gcGameMode);
6283 if gSwitchGameMode <> GM_NONE then
6284 GameMode := gSwitchGameMode;
6285 if GameMode = GM_NONE then GameMode := GM_DM;
6286 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6287 b := 0;
6288 if Length(P) >= 6 then
6289 b := StrToIntDef(P[5], 0);
6290 g_Game_StartServer(s, GameMode, TimeLimit,
6291 GoalLimit, MaxLives, Options, b, listen, prt);
6292 end;
6293 end
6294 else
6295 if P[4] = '' then
6296 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6297 else
6298 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]));
6299 end else
6300 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6301 end
6302 else if cmd = 'map' then
6303 begin
6304 if Length(P) = 1 then
6305 begin
6306 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6307 begin
6308 g_Console_Add(cmd + ' <MAP>');
6309 g_Console_Add(cmd + ' <WAD> [MAP]');
6310 end else
6311 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6312 end else
6313 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6314 begin
6315 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6316 if Length(P) < 3 then
6317 begin
6318 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6319 s := UpperCase(P[1]);
6320 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6321 begin // Êàðòà íàøëàñü
6322 gExitByTrigger := False;
6323 if gGameOn then
6324 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6325 gNextMap := s;
6326 gExit := EXIT_ENDLEVELCUSTOM;
6327 end
6328 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6329 g_Game_ChangeMap(s);
6330 end else
6331 begin
6332 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6333 P[1] := addWadExtension(P[1]);
6334 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6335 if FileExists(MapsDir + P[1]) then
6336 begin
6337 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6338 SetLength(P, 3);
6339 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6341 s := P[1] + ':\' + P[2];
6343 if g_Map_Exist(MapsDir + s) then
6344 begin
6345 gExitByTrigger := False;
6346 if gGameOn then
6347 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6348 gNextMap := s;
6349 gExit := EXIT_ENDLEVELCUSTOM;
6350 end
6351 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6352 g_Game_ChangeMap(s);
6353 end else
6354 if P[2] = '' then
6355 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6356 else
6357 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6358 end else
6359 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6360 end;
6361 end else
6362 begin
6363 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6364 P[1] := addWadExtension(P[1]);
6365 if FileExists(MapsDir + P[1]) then
6366 begin
6367 // Íàøëè WAD ôàéë
6368 P[2] := UpperCase(P[2]);
6369 s := P[1] + ':\' + P[2];
6371 if g_Map_Exist(MapsDir + s) then
6372 begin // Íàøëè êàðòó
6373 gExitByTrigger := False;
6374 if gGameOn then
6375 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6376 gNextMap := s;
6377 gExit := EXIT_ENDLEVELCUSTOM;
6378 end
6379 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6380 g_Game_ChangeMap(s);
6381 end else
6382 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6383 end else
6384 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6385 end;
6386 end else
6387 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6388 end
6389 else if cmd = 'nextmap' then
6390 begin
6391 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6392 g_Console_Add(_lc[I_MSG_NOT_GAME])
6393 else begin
6394 nm := True;
6395 if Length(P) = 1 then
6396 begin
6397 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6398 begin
6399 g_Console_Add(cmd + ' <MAP>');
6400 g_Console_Add(cmd + ' <WAD> [MAP]');
6401 end else begin
6402 nm := False;
6403 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6404 end;
6405 end else
6406 begin
6407 nm := False;
6408 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6409 begin
6410 if Length(P) < 3 then
6411 begin
6412 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6413 s := UpperCase(P[1]);
6414 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6415 begin // Êàðòà íàøëàñü
6416 gExitByTrigger := False;
6417 gNextMap := s;
6418 nm := True;
6419 end else
6420 begin
6421 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6422 P[1] := addWadExtension(P[1]);
6423 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6424 if FileExists(MapsDir + P[1]) then
6425 begin
6426 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6427 SetLength(P, 3);
6428 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6430 s := P[1] + ':\' + P[2];
6432 if g_Map_Exist(MapsDir + s) then
6433 begin // Óñòàíàâëèâàåì êàðòó
6434 gExitByTrigger := False;
6435 gNextMap := s;
6436 nm := True;
6437 end else
6438 if P[2] = '' then
6439 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6440 else
6441 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6442 end else
6443 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6444 end;
6445 end else
6446 begin
6447 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6448 P[1] := addWadExtension(P[1]);
6449 if FileExists(MapsDir + P[1]) then
6450 begin
6451 // Íàøëè WAD ôàéë
6452 P[2] := UpperCase(P[2]);
6453 s := P[1] + ':\' + P[2];
6455 if g_Map_Exist(MapsDir + s) then
6456 begin // Íàøëè êàðòó
6457 gExitByTrigger := False;
6458 gNextMap := s;
6459 nm := True;
6460 end else
6461 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6462 end else
6463 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6464 end;
6465 end else
6466 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6467 end;
6468 if nm then
6469 if gNextMap = '' then
6470 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6471 else
6472 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6473 end;
6474 end
6475 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6476 begin
6477 if not gGameOn then
6478 g_Console_Add(_lc[I_MSG_NOT_GAME])
6479 else
6480 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6481 begin
6482 gExitByTrigger := False;
6483 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6484 if (gNextMap = '') and (gTriggers <> nil) then
6485 for a := 0 to High(gTriggers) do
6486 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6487 begin
6488 gExitByTrigger := True;
6489 //gNextMap := gTriggers[a].Data.MapName;
6490 gNextMap := gTriggers[a].tgcMap;
6491 Break;
6492 end;
6493 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6494 if gNextMap = '' then
6495 gNextMap := g_Game_GetNextMap();
6496 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6497 if not isWadPath(gNextMap) then
6498 s := gGameSettings.WAD + ':\' + gNextMap
6499 else
6500 s := gNextMap;
6501 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6502 if g_Map_Exist(MapsDir + s) then
6503 gExit := EXIT_ENDLEVELCUSTOM
6504 else
6505 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6506 end else
6507 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6508 end
6509 else if (cmd = 'event') then
6510 begin
6511 if (Length(P) <= 1) then
6512 begin
6513 for a := 0 to High(gEvents) do
6514 if gEvents[a].Command = '' then
6515 g_Console_Add(gEvents[a].Name + ' <none>')
6516 else
6517 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6518 Exit;
6519 end;
6520 if (Length(P) = 2) then
6521 begin
6522 for a := 0 to High(gEvents) do
6523 if gEvents[a].Name = P[1] then
6524 if gEvents[a].Command = '' then
6525 g_Console_Add(gEvents[a].Name + ' <none>')
6526 else
6527 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6528 Exit;
6529 end;
6530 for a := 0 to High(gEvents) do
6531 if gEvents[a].Name = P[1] then
6532 begin
6533 gEvents[a].Command := '';
6534 for b := 2 to High(P) do
6535 if Pos(' ', P[b]) = 0 then
6536 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6537 else
6538 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6539 gEvents[a].Command := Trim(gEvents[a].Command);
6540 Exit;
6541 end;
6542 end
6543 else if cmd = 'suicide' then
6544 begin
6545 if gGameOn then
6546 begin
6547 if g_Game_IsClient then
6548 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6549 else
6550 begin
6551 if gPlayer1 <> nil then
6552 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6553 if gPlayer2 <> nil then
6554 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6555 end;
6556 end;
6557 end
6558 // Êîìàíäû Ñâîåé èãðû:
6559 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6560 begin
6561 if cmd = 'bot_addred' then
6562 begin
6563 if Length(P) > 1 then
6564 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6565 else
6566 g_Bot_Add(TEAM_RED, 2);
6567 end
6568 else if cmd = 'bot_addblue' then
6569 begin
6570 if Length(P) > 1 then
6571 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6572 else
6573 g_Bot_Add(TEAM_BLUE, 2);
6574 end
6575 else if cmd = 'spectate' then
6576 begin
6577 if not gGameOn then
6578 Exit;
6579 g_Game_Spectate();
6580 end
6581 else if cmd = 'say' then
6582 begin
6583 if g_Game_IsServer and g_Game_IsNet then
6584 begin
6585 if Length(P) > 1 then
6586 begin
6587 chstr := '';
6588 for a := 1 to High(P) do
6589 chstr := chstr + P[a] + ' ';
6591 if Length(chstr) > 200 then SetLength(chstr, 200);
6593 if Length(chstr) < 1 then
6594 begin
6595 g_Console_Add('say <text>');
6596 Exit;
6597 end;
6599 chstr := b_Text_Format(chstr);
6600 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6601 end
6602 else g_Console_Add('say <text>');
6603 end else
6604 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6605 end
6606 else if cmd = 'tell' then
6607 begin
6608 if g_Game_IsServer and g_Game_IsNet then
6609 begin
6610 if (Length(P) > 2) and (P[1] <> '') then
6611 begin
6612 chstr := '';
6613 for a := 2 to High(P) do
6614 chstr := chstr + P[a] + ' ';
6616 if Length(chstr) > 200 then SetLength(chstr, 200);
6618 if Length(chstr) < 1 then
6619 begin
6620 g_Console_Add('tell <playername> <text>');
6621 Exit;
6622 end;
6624 pl := g_Net_Client_ByName(P[1]);
6625 if pl <> nil then
6626 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6627 else
6628 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6629 end
6630 else g_Console_Add('tell <playername> <text>');
6631 end else
6632 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6633 end
6634 else if (cmd = 'overtime') and not g_Game_IsClient then
6635 begin
6636 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6637 Exit;
6638 // Äîïîëíèòåëüíîå âðåìÿ:
6639 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6641 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6642 [gGameSettings.TimeLimit div 3600,
6643 (gGameSettings.TimeLimit div 60) mod 60,
6644 gGameSettings.TimeLimit mod 60]));
6645 if g_Game_IsNet then MH_SEND_GameSettings;
6646 end
6647 else if (cmd = 'rcon_password') and g_Game_IsClient then
6648 begin
6649 if (Length(P) <= 1) then
6650 g_Console_Add('rcon_password <password>')
6651 else
6652 MC_SEND_RCONPassword(P[1]);
6653 end
6654 else if cmd = 'rcon' then
6655 begin
6656 if g_Game_IsClient then
6657 begin
6658 if Length(P) > 1 then
6659 begin
6660 chstr := '';
6661 for a := 1 to High(P) do
6662 chstr := chstr + P[a] + ' ';
6664 if Length(chstr) > 200 then SetLength(chstr, 200);
6666 if Length(chstr) < 1 then
6667 begin
6668 g_Console_Add('rcon <command>');
6669 Exit;
6670 end;
6672 MC_SEND_RCONCommand(chstr);
6673 end
6674 else g_Console_Add('rcon <command>');
6675 end;
6676 end
6677 else if cmd = 'ready' then
6678 begin
6679 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6680 gLMSRespawnTime := gTime + 100;
6681 end
6682 else if (cmd = 'callvote') and g_Game_IsNet then
6683 begin
6684 if Length(P) > 1 then
6685 begin
6686 chstr := '';
6687 for a := 1 to High(P) do begin
6688 if a > 1 then chstr := chstr + ' ';
6689 chstr := chstr + P[a];
6690 end;
6692 if Length(chstr) > 200 then SetLength(chstr, 200);
6694 if Length(chstr) < 1 then
6695 begin
6696 g_Console_Add('callvote <command>');
6697 Exit;
6698 end;
6700 if g_Game_IsClient then
6701 MC_SEND_Vote(True, chstr)
6702 else
6703 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6704 g_Console_Process('vote', True);
6705 end
6706 else
6707 g_Console_Add('callvote <command>');
6708 end
6709 else if (cmd = 'vote') and g_Game_IsNet then
6710 begin
6711 if g_Game_IsClient then
6712 MC_SEND_Vote(False)
6713 else if gVoteInProgress then
6714 begin
6715 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6716 a := Floor((NetClientCount+1)/2.0) + 1
6717 else
6718 a := Floor(NetClientCount/2.0) + 1;
6719 if gVoted then
6720 begin
6721 Dec(gVoteCount);
6722 gVoted := False;
6723 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6724 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6725 end
6726 else
6727 begin
6728 Inc(gVoteCount);
6729 gVoted := True;
6730 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6731 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6732 g_Game_CheckVote;
6733 end;
6734 end;
6735 end
6736 end;
6737 end;
6739 procedure g_TakeScreenShot();
6740 var
6741 a: Word;
6742 FileName: string;
6743 ssdir, t: string;
6744 st: TStream;
6745 ok: Boolean;
6746 begin
6747 if e_NoGraphics then Exit;
6748 ssdir := GameDir+'/screenshots';
6749 if not findFileCI(ssdir, true) then
6750 begin
6751 // try to create dir
6752 try
6753 CreateDir(ssdir);
6754 except
6755 end;
6756 if not findFileCI(ssdir, true) then exit; // alas
6757 end;
6758 try
6759 for a := 1 to High(Word) do
6760 begin
6761 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6762 t := FileName;
6763 if findFileCI(t, true) then continue;
6764 if not findFileCI(FileName) then
6765 begin
6766 ok := false;
6767 st := createDiskFile(FileName);
6768 try
6769 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6770 ok := true;
6771 finally
6772 st.Free();
6773 end;
6774 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6775 break;
6776 end;
6777 end;
6778 except
6779 end;
6780 end;
6782 procedure g_Game_InGameMenu(Show: Boolean);
6783 begin
6784 if (g_ActiveWindow = nil) and Show then
6785 begin
6786 if gGameSettings.GameType = GT_SINGLE then
6787 g_GUI_ShowWindow('GameSingleMenu')
6788 else
6789 begin
6790 if g_Game_IsClient then
6791 g_GUI_ShowWindow('GameClientMenu')
6792 else
6793 if g_Game_IsNet then
6794 g_GUI_ShowWindow('GameServerMenu')
6795 else
6796 g_GUI_ShowWindow('GameCustomMenu');
6797 end;
6798 g_Sound_PlayEx('MENU_OPEN');
6800 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6801 if (not g_Game_IsNet) then
6802 g_Game_Pause(True);
6803 end
6804 else
6805 if (g_ActiveWindow <> nil) and (not Show) then
6806 begin
6807 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6808 if (not g_Game_IsNet) then
6809 g_Game_Pause(False);
6810 end;
6811 end;
6813 procedure g_Game_Pause (Enable: Boolean);
6814 var
6815 oldPause: Boolean;
6816 begin
6817 if not gGameOn then exit;
6819 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6821 oldPause := gPause;
6822 gPauseMain := Enable;
6824 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6825 end;
6827 procedure g_Game_HolmesPause (Enable: Boolean);
6828 var
6829 oldPause: Boolean;
6830 begin
6831 if not gGameOn then exit;
6832 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6834 oldPause := gPause;
6835 gPauseHolmes := Enable;
6837 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6838 end;
6840 procedure g_Game_PauseAllSounds(Enable: Boolean);
6841 var
6842 i: Integer;
6843 begin
6844 // Òðèããåðû:
6845 if gTriggers <> nil then
6846 for i := 0 to High(gTriggers) do
6847 with gTriggers[i] do
6848 if (TriggerType = TRIGGER_SOUND) and
6849 (Sound <> nil) and
6850 Sound.IsPlaying() then
6851 begin
6852 Sound.Pause(Enable);
6853 end;
6855 // Çâóêè èãðîêîâ:
6856 if gPlayers <> nil then
6857 for i := 0 to High(gPlayers) do
6858 if gPlayers[i] <> nil then
6859 gPlayers[i].PauseSounds(Enable);
6861 // Ìóçûêà:
6862 if gMusic <> nil then
6863 gMusic.Pause(Enable);
6864 end;
6866 procedure g_Game_StopAllSounds(all: Boolean);
6867 var
6868 i: Integer;
6869 begin
6870 if gTriggers <> nil then
6871 for i := 0 to High(gTriggers) do
6872 with gTriggers[i] do
6873 if (TriggerType = TRIGGER_SOUND) and
6874 (Sound <> nil) then
6875 Sound.Stop();
6877 if gMusic <> nil then
6878 gMusic.Stop();
6880 if all then
6881 e_StopChannels();
6882 end;
6884 procedure g_Game_UpdateTriggerSounds();
6885 var
6886 i: Integer;
6887 begin
6888 if gTriggers <> nil then
6889 for i := 0 to High(gTriggers) do
6890 with gTriggers[i] do
6891 if (TriggerType = TRIGGER_SOUND) and
6892 (Sound <> nil) and
6893 (tgcLocal) and
6894 Sound.IsPlaying() then
6895 begin
6896 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6897 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6898 begin
6899 Sound.SetPan(0.5 - tgcPan/255.0);
6900 Sound.SetVolume(tgcVolume/255.0);
6901 end
6902 else
6903 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
6904 end;
6905 end;
6907 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6908 begin
6909 Result := False;
6910 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6911 begin
6912 Result := True;
6913 Exit;
6914 end;
6915 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6916 begin
6917 Result := True;
6918 Exit;
6919 end;
6920 if gSpectMode <> SPECT_PLAYERS then
6921 Exit;
6922 if gSpectPID1 = UID then
6923 begin
6924 Result := True;
6925 Exit;
6926 end;
6927 if gSpectViewTwo and (gSpectPID2 = UID) then
6928 begin
6929 Result := True;
6930 Exit;
6931 end;
6932 end;
6934 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6935 var
6936 Pl: TPlayer;
6937 begin
6938 Result := False;
6939 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6940 begin
6941 Result := True;
6942 Exit;
6943 end;
6944 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6945 begin
6946 Result := True;
6947 Exit;
6948 end;
6949 if gSpectMode <> SPECT_PLAYERS then
6950 Exit;
6951 Pl := g_Player_Get(gSpectPID1);
6952 if (Pl <> nil) and (Pl.Team = Team) then
6953 begin
6954 Result := True;
6955 Exit;
6956 end;
6957 if gSpectViewTwo then
6958 begin
6959 Pl := g_Player_Get(gSpectPID2);
6960 if (Pl <> nil) and (Pl.Team = Team) then
6961 begin
6962 Result := True;
6963 Exit;
6964 end;
6965 end;
6966 end;
6968 procedure g_Game_Message(Msg: string; Time: Word);
6969 begin
6970 MessageText := b_Text_Format(Msg);
6971 MessageTime := Time;
6972 end;
6974 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
6975 const
6976 punct: Array[0..13] of String =
6977 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
6978 var
6979 i, j: Integer;
6980 ok: Boolean;
6981 fpText: String;
6983 function IsPunctuation(S: String): Boolean;
6984 var
6985 i: Integer;
6986 begin
6987 Result := False;
6988 if Length(S) <> 1 then
6989 Exit;
6990 for i := Low(punct) to High(punct) do
6991 if S = punct[i] then
6992 begin
6993 Result := True;
6994 break;
6995 end;
6996 end;
6997 function FilterPunctuation(S: String): String;
6998 var
6999 i: Integer;
7000 begin
7001 for i := Low(punct) to High(punct) do
7002 S := StringReplace(S, punct[i], ' ', [rfReplaceAll]);
7003 Result := S;
7004 end;
7005 begin
7006 ok := False;
7008 if gUseChatSounds and Taunt and (gChatSounds <> nil) and (Pos(': ', Text) > 0) then
7009 begin
7010 // remove player name
7011 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
7012 // for FullWord check
7013 Text := toLowerCase1251(' ' + Text + ' ');
7014 fpText := FilterPunctuation(Text);
7016 for i := 0 to Length(gChatSounds) - 1 do
7017 begin
7018 ok := True;
7019 for j := 0 to Length(gChatSounds[i].Tags) - 1 do
7020 begin
7021 if gChatSounds[i].FullWord and (not IsPunctuation(gChatSounds[i].Tags[j])) then
7022 ok := Pos(' ' + gChatSounds[i].Tags[j] + ' ', fpText) > 0
7023 else
7024 ok := Pos(gChatSounds[i].Tags[j], Text) > 0;
7025 if not ok then
7026 break;
7027 end;
7028 if ok then
7029 begin
7030 gChatSounds[i].Sound.Play();
7031 break;
7032 end;
7033 end;
7034 end;
7035 if not ok then
7036 g_Sound_PlayEx('SOUND_GAME_RADIO');
7037 end;
7039 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
7040 var
7041 a: Integer;
7042 begin
7043 case gAnnouncer of
7044 ANNOUNCE_NONE:
7045 Exit;
7046 ANNOUNCE_ME,
7047 ANNOUNCE_MEPLUS:
7048 if not g_Game_IsWatchedPlayer(SpawnerUID) then
7049 Exit;
7050 end;
7051 for a := 0 to 3 do
7052 if goodsnd[a].IsPlaying() then
7053 Exit;
7055 goodsnd[Random(4)].Play();
7056 end;
7058 procedure g_Game_Announce_KillCombo(Param: Integer);
7059 var
7060 UID: Word;
7061 c, n: Byte;
7062 Pl: TPlayer;
7063 Name: String;
7064 begin
7065 UID := Param and $FFFF;
7066 c := Param shr 16;
7067 if c < 2 then
7068 Exit;
7070 Pl := g_Player_Get(UID);
7071 if Pl = nil then
7072 Name := '?'
7073 else
7074 Name := Pl.Name;
7076 case c of
7077 2: begin
7078 n := 0;
7079 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
7080 end;
7081 3: begin
7082 n := 1;
7083 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
7084 end;
7085 4: begin
7086 n := 2;
7087 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
7088 end;
7089 else begin
7090 n := 3;
7091 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
7092 end;
7093 end;
7095 case gAnnouncer of
7096 ANNOUNCE_NONE:
7097 Exit;
7098 ANNOUNCE_ME:
7099 if not g_Game_IsWatchedPlayer(UID) then
7100 Exit;
7101 ANNOUNCE_MEPLUS:
7102 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
7103 Exit;
7104 end;
7106 if killsnd[n].IsPlaying() then
7107 killsnd[n].Stop();
7108 killsnd[n].Play();
7109 end;
7111 procedure g_Game_StartVote(Command, Initiator: string);
7112 var
7113 Need: Integer;
7114 begin
7115 if not gVotesEnabled then Exit;
7116 if gGameSettings.GameType <> GT_SERVER then Exit;
7117 if gVoteInProgress or gVotePassed then
7118 begin
7119 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
7120 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
7121 Exit;
7122 end;
7123 gVoteInProgress := True;
7124 gVotePassed := False;
7125 gVoteTimer := gTime + gVoteTimeout * 1000;
7126 gVoteCount := 0;
7127 gVoted := False;
7128 gVoteCommand := Command;
7130 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7131 Need := Floor((NetClientCount+1)/2.0)+1
7132 else
7133 Need := Floor(NetClientCount/2.0)+1;
7134 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
7135 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
7136 end;
7138 procedure g_Game_CheckVote;
7139 var
7140 I, Need: Integer;
7141 begin
7142 if gGameSettings.GameType <> GT_SERVER then Exit;
7143 if not gVoteInProgress then Exit;
7145 if (gTime >= gVoteTimer) then
7146 begin
7147 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7148 Need := Floor((NetClientCount+1)/2.0) + 1
7149 else
7150 Need := Floor(NetClientCount/2.0) + 1;
7151 if gVoteCount >= Need then
7152 begin
7153 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7154 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7155 gVotePassed := True;
7156 gVoteCmdTimer := gTime + 5000;
7157 end
7158 else
7159 begin
7160 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
7161 MH_SEND_VoteEvent(NET_VE_FAILED);
7162 end;
7163 if NetClients <> nil then
7164 for I := Low(NetClients) to High(NetClients) do
7165 if NetClients[i].Used then
7166 NetClients[i].Voted := False;
7167 gVoteInProgress := False;
7168 gVoted := False;
7169 gVoteCount := 0;
7170 end
7171 else
7172 begin
7173 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7174 Need := Floor((NetClientCount+1)/2.0) + 1
7175 else
7176 Need := Floor(NetClientCount/2.0) + 1;
7177 if gVoteCount >= Need then
7178 begin
7179 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7180 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7181 gVoteInProgress := False;
7182 gVotePassed := True;
7183 gVoteCmdTimer := gTime + 5000;
7184 gVoted := False;
7185 gVoteCount := 0;
7186 if NetClients <> nil then
7187 for I := Low(NetClients) to High(NetClients) do
7188 if NetClients[i].Used then
7189 NetClients[i].Voted := False;
7190 end;
7191 end;
7192 end;
7194 procedure g_Game_LoadMapList(FileName: string);
7195 var
7196 ListFile: TextFile;
7197 s: string;
7198 begin
7199 MapList := nil;
7200 MapIndex := -1;
7202 if not FileExists(FileName) then Exit;
7204 AssignFile(ListFile, FileName);
7205 Reset(ListFile);
7206 while not EOF(ListFile) do
7207 begin
7208 ReadLn(ListFile, s);
7210 s := Trim(s);
7211 if s = '' then Continue;
7213 SetLength(MapList, Length(MapList)+1);
7214 MapList[High(MapList)] := s;
7215 end;
7216 CloseFile(ListFile);
7217 end;
7219 procedure g_Game_SetDebugMode();
7220 begin
7221 gDebugMode := True;
7222 // ×èòû (äàæå â ñâîåé èãðå):
7223 gCheats := True;
7224 end;
7226 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
7227 var
7228 i: Word;
7229 begin
7230 if Length(LoadingStat.Msgs) = 0 then
7231 Exit;
7233 with LoadingStat do
7234 begin
7235 if not reWrite then
7236 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
7237 if NextMsg = Length(Msgs) then
7238 begin // scroll
7239 for i := 0 to High(Msgs)-1 do
7240 Msgs[i] := Msgs[i+1];
7241 end
7242 else
7243 Inc(NextMsg);
7244 end else
7245 if NextMsg = 0 then
7246 Inc(NextMsg);
7248 Msgs[NextMsg-1] := Text;
7249 CurValue := 0;
7250 MaxValue := Max;
7251 ShowCount := 0;
7252 PBarWasHere := false;
7253 end;
7255 g_ActiveWindow := nil;
7257 ProcessLoading(true);
7258 end;
7260 procedure g_Game_StepLoading();
7261 begin
7262 with LoadingStat do
7263 begin
7264 Inc(CurValue);
7265 Inc(ShowCount);
7266 if (ShowCount > LOADING_SHOW_STEP) then
7267 begin
7268 ShowCount := 0;
7269 ProcessLoading();
7270 end;
7271 end;
7272 end;
7274 procedure g_Game_ClearLoading();
7275 var
7276 len: Word;
7277 begin
7278 with LoadingStat do
7279 begin
7280 CurValue := 0;
7281 MaxValue := 0;
7282 ShowCount := 0;
7283 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
7284 if len < 1 then len := 1;
7285 SetLength(Msgs, len);
7286 for len := Low(Msgs) to High(Msgs) do
7287 Msgs[len] := '';
7288 NextMsg := 0;
7289 PBarWasHere := false;
7290 end;
7291 end;
7293 procedure Parse_Params(var pars: TParamStrValues);
7294 var
7295 i: Integer;
7296 s: String;
7297 begin
7298 SetLength(pars, 0);
7299 i := 1;
7300 while i <= ParamCount do
7301 begin
7302 s := ParamStr(i);
7303 if (s[1] = '-') and (Length(s) > 1) then
7304 begin
7305 if (s[2] = '-') and (Length(s) > 2) then
7306 begin // Îäèíî÷íûé ïàðàìåòð
7307 SetLength(pars, Length(pars) + 1);
7308 with pars[High(pars)] do
7309 begin
7310 Name := LowerCase(s);
7311 Value := '+';
7312 end;
7313 end
7314 else
7315 if (i < ParamCount) then
7316 begin // Ïàðàìåòð ñî çíà÷åíèåì
7317 Inc(i);
7318 SetLength(pars, Length(pars) + 1);
7319 with pars[High(pars)] do
7320 begin
7321 Name := LowerCase(s);
7322 Value := LowerCase(ParamStr(i));
7323 end;
7324 end;
7325 end;
7327 Inc(i);
7328 end;
7329 end;
7331 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
7332 var
7333 i: Integer;
7334 begin
7335 Result := '';
7336 for i := 0 to High(pars) do
7337 if pars[i].Name = aName then
7338 begin
7339 Result := pars[i].Value;
7340 Break;
7341 end;
7342 end;
7344 procedure g_Game_Process_Params();
7345 var
7346 pars: TParamStrValues;
7347 map: String;
7348 GMode, n: Byte;
7349 LimT, LimS: Integer;
7350 Opt: LongWord;
7351 Lives: Integer;
7352 s: String;
7353 Port: Integer;
7354 ip: String;
7355 F: TextFile;
7356 begin
7357 Parse_Params(pars);
7359 // Debug mode:
7360 s := Find_Param_Value(pars, '--debug');
7361 if (s <> '') then
7362 begin
7363 g_Game_SetDebugMode();
7364 s := Find_Param_Value(pars, '--netdump');
7365 if (s <> '') then
7366 NetDump := True;
7367 end;
7369 // Connect when game loads
7370 ip := Find_Param_Value(pars, '-connect');
7372 if ip <> '' then
7373 begin
7374 s := Find_Param_Value(pars, '-port');
7375 if (s = '') or not TryStrToInt(s, Port) then
7376 Port := 25666;
7378 s := Find_Param_Value(pars, '-pw');
7380 g_Game_StartClient(ip, Port, s);
7381 Exit;
7382 end;
7384 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7385 if (s <> '') then
7386 begin
7387 gDefaultMegawadStart := s;
7388 end;
7390 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7391 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7392 begin
7393 gDefaultMegawadStart := DF_Default_Megawad_Start;
7394 end;
7396 // Start map when game loads:
7397 map := LowerCase(Find_Param_Value(pars, '-map'));
7398 if isWadPath(map) then
7399 begin
7400 // Game mode:
7401 s := Find_Param_Value(pars, '-gm');
7402 GMode := g_Game_TextToMode(s);
7403 if GMode = GM_NONE then GMode := GM_DM;
7404 if GMode = GM_SINGLE then GMode := GM_COOP;
7406 // Time limit:
7407 s := Find_Param_Value(pars, '-limt');
7408 if (s = '') or (not TryStrToInt(s, LimT)) then
7409 LimT := 0;
7410 if LimT < 0 then
7411 LimT := 0;
7413 // Goal limit:
7414 s := Find_Param_Value(pars, '-lims');
7415 if (s = '') or (not TryStrToInt(s, LimS)) then
7416 LimS := 0;
7417 if LimS < 0 then
7418 LimS := 0;
7420 // Lives limit:
7421 s := Find_Param_Value(pars, '-lives');
7422 if (s = '') or (not TryStrToInt(s, Lives)) then
7423 Lives := 0;
7424 if Lives < 0 then
7425 Lives := 0;
7427 // Options:
7428 s := Find_Param_Value(pars, '-opt');
7429 if (s = '') then
7430 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7431 else
7432 Opt := StrToIntDef(s, 0);
7433 if Opt = 0 then
7434 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7436 // Close after map:
7437 s := Find_Param_Value(pars, '--close');
7438 if (s <> '') then
7439 gMapOnce := True;
7441 // Override map to test:
7442 s := LowerCase(Find_Param_Value(pars, '-testmap'));
7443 if s <> '' then
7444 gTestMap := MapsDir + s;
7446 // Delete test map after play:
7447 s := Find_Param_Value(pars, '--testdelete');
7448 if (s <> '') then
7449 begin
7450 gMapToDelete := MapsDir + map;
7451 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
7452 Halt(1);
7453 end;
7455 // Delete temporary WAD after play:
7456 s := Find_Param_Value(pars, '--tempdelete');
7457 if (s <> '') and (gTestMap <> '') then
7458 begin
7459 gMapToDelete := gTestMap;
7460 gTempDelete := True;
7461 end;
7463 // Number of players:
7464 s := Find_Param_Value(pars, '-pl');
7465 if (s = '') then
7466 n := 1
7467 else
7468 n := StrToIntDef(s, 1);
7470 // Start:
7471 s := Find_Param_Value(pars, '-port');
7472 if (s = '') or not TryStrToInt(s, Port) then
7473 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7474 else
7475 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7476 end;
7478 // Execute script when game loads:
7479 s := Find_Param_Value(pars, '-exec');
7480 if s <> '' then
7481 begin
7482 if not isWadPath(s) then
7483 s := GameDir + '/' + s;
7485 {$I-}
7486 AssignFile(F, s);
7487 Reset(F);
7488 if IOResult <> 0 then
7489 begin
7490 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7491 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7492 CloseFile(F);
7493 Exit;
7494 end;
7495 e_WriteLog('Executing script: ' + s, TMsgType.Notify);
7496 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7498 while not EOF(F) do
7499 begin
7500 ReadLn(F, s);
7501 if IOResult <> 0 then
7502 begin
7503 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7504 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7505 CloseFile(F);
7506 Exit;
7507 end;
7508 if Pos('#', s) <> 1 then // script comment
7509 g_Console_Process(s, True);
7510 end;
7512 CloseFile(F);
7513 {$I+}
7514 end;
7516 SetLength(pars, 0);
7517 end;
7519 begin
7520 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7521 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7522 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7523 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7525 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7526 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7527 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7528 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7530 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7531 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7533 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7534 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7536 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7538 conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds, 'ignore level bounds', '', false);
7540 conRegVar('r_scale', @g_dbg_scale, 0.01, 100.0, 'render scale', '', false);
7542 conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
7543 conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
7544 end.