DEADSOFTWARE

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