DEADSOFTWARE

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