DEADSOFTWARE

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