DEADSOFTWARE

`Grid.forEachInAABB()`: no more callbacks
[d2df-sdl.git] / src / game / g_game.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit g_game;
19 interface
21 uses
22 SysUtils, Classes,
23 MAPDEF,
24 g_basic, g_player, e_graphics, g_res_downloader,
25 g_sound, g_gui, utils, md5, mempool, xprofiler;
27 type
28 TGameSettings = record
29 GameType: Byte;
30 GameMode: Byte;
31 TimeLimit: Word;
32 GoalLimit: Word;
33 WarmupTime: Word;
34 MaxLives: Byte;
35 Options: LongWord;
36 WAD: String;
37 end;
39 TGameEvent = record
40 Name: String;
41 Command: String;
42 end;
44 TDelayedEvent = record
45 Pending: Boolean;
46 Time: LongWord;
47 DEType: Byte;
48 DENum: Integer;
49 DEStr: String;
50 end;
52 TChatSound = record
53 Sound: TPlayableSound;
54 Tags: Array of String;
55 FullWord: Boolean;
56 end;
58 TPlayerSettings = record
59 Name: String;
60 Model: String;
61 Color: TRGB;
62 Team: Byte;
63 end;
65 TMegaWADInfo = record
66 Name: String;
67 Description: String;
68 Author: String;
69 Pic: String;
70 end;
72 THearPoint = record
73 Active: Boolean;
74 Coords: TDFPoint;
75 end;
77 function g_Game_IsNet(): Boolean;
78 function g_Game_IsServer(): Boolean;
79 function g_Game_IsClient(): Boolean;
80 procedure g_Game_Init();
81 procedure g_Game_Free (freeTextures: Boolean=true);
82 procedure g_Game_LoadData();
83 procedure g_Game_FreeData();
84 procedure g_Game_Update();
85 procedure g_Game_Draw();
86 procedure g_Game_Quit();
87 procedure g_Game_SetupScreenSize();
88 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
89 function g_Game_ModeToText(Mode: Byte): string;
90 function g_Game_TextToMode(Mode: string): Byte;
91 procedure g_Game_ExecuteEvent(Name: String);
92 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
93 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
94 procedure g_Game_RemovePlayer();
95 procedure g_Game_Spectate();
96 procedure g_Game_SpectateCenterView();
97 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
98 procedure g_Game_StartCustom(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte);
99 procedure g_Game_StartServer(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte; IPAddr: LongWord; Port: Word);
100 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
101 procedure g_Game_Restart();
102 procedure g_Game_RestartLevel();
103 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
104 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
105 procedure g_Game_SaveOptions();
106 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
107 procedure g_Game_ChangeMap(const MapPath: String);
108 procedure g_Game_ExitLevel(const Map: AnsiString);
109 function g_Game_GetFirstMap(WAD: String): String;
110 function g_Game_GetNextMap(): String;
111 procedure g_Game_NextLevel();
112 procedure g_Game_Pause(Enable: Boolean);
113 procedure g_Game_HolmesPause(Enable: Boolean);
114 procedure g_Game_InGameMenu(Show: Boolean);
115 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
116 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
117 procedure g_Game_Message(Msg: String; Time: Word);
118 procedure g_Game_LoadMapList(FileName: String);
119 procedure g_Game_PauseAllSounds(Enable: Boolean);
120 procedure g_Game_StopAllSounds(all: Boolean);
121 procedure g_Game_UpdateTriggerSounds();
122 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
123 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
124 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
125 procedure g_Game_Announce_KillCombo(Param: Integer);
126 procedure g_Game_StartVote(Command, Initiator: string);
127 procedure g_Game_CheckVote;
128 procedure g_TakeScreenShot();
129 procedure g_FatalError(Text: String);
130 procedure g_SimpleError(Text: String);
131 function g_Game_IsTestMap(): Boolean;
132 procedure g_Game_DeleteTestMap();
133 procedure GameCVars(P: SSArray);
134 procedure GameCommands(P: SSArray);
135 procedure GameCheats(P: SSArray);
136 procedure DebugCommands(P: SSArray);
137 procedure g_Game_Process_Params;
138 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
139 procedure g_Game_StepLoading();
140 procedure g_Game_ClearLoading();
141 procedure g_Game_SetDebugMode();
142 procedure DrawLoadingStat();
144 { procedure SetWinPause(Enable: Boolean); }
146 const
147 GAME_TICK = 28;
149 LOADING_SHOW_STEP = 100;
150 LOADING_INTERLINE = 20;
152 GT_NONE = 0;
153 GT_SINGLE = 1;
154 GT_CUSTOM = 2;
155 GT_SERVER = 3;
156 GT_CLIENT = 4;
158 GM_NONE = 0;
159 GM_DM = 1;
160 GM_TDM = 2;
161 GM_CTF = 3;
162 GM_COOP = 4;
163 GM_SINGLE = 5;
165 MESSAGE_DIKEY = WM_USER + 1;
167 EXIT_QUIT = 1;
168 EXIT_SIMPLE = 2;
169 EXIT_RESTART = 3;
170 EXIT_ENDLEVELSINGLE = 4;
171 EXIT_ENDLEVELCUSTOM = 5;
173 GAME_OPTION_RESERVED = 1;
174 GAME_OPTION_TEAMDAMAGE = 2;
175 GAME_OPTION_ALLOWEXIT = 4;
176 GAME_OPTION_WEAPONSTAY = 8;
177 GAME_OPTION_MONSTERS = 16;
178 GAME_OPTION_BOTVSPLAYER = 32;
179 GAME_OPTION_BOTVSMONSTER = 64;
181 STATE_NONE = 0;
182 STATE_MENU = 1;
183 STATE_FOLD = 2;
184 STATE_INTERCUSTOM = 3;
185 STATE_INTERSINGLE = 4;
186 STATE_INTERTEXT = 5;
187 STATE_INTERPIC = 6;
188 STATE_ENDPIC = 7;
189 STATE_SLIST = 8;
191 LMS_RESPAWN_NONE = 0;
192 LMS_RESPAWN_WARMUP = 1;
193 LMS_RESPAWN_FINAL = 2;
195 SPECT_NONE = 0;
196 SPECT_STATS = 1;
197 SPECT_MAPVIEW = 2;
198 SPECT_PLAYERS = 3;
200 DE_GLOBEVENT = 0;
201 DE_BFGHIT = 1;
202 DE_KILLCOMBO = 2;
204 ANNOUNCE_NONE = 0;
205 ANNOUNCE_ME = 1;
206 ANNOUNCE_MEPLUS = 2;
207 ANNOUNCE_ALL = 3;
209 CONFIG_FILENAME = 'Doom2DF.cfg';
210 LOG_FILENAME = 'Doom2DF.log';
212 TEST_MAP_NAME = '$$$_TEST_$$$';
214 STD_PLAYER_MODEL = 'Doomer';
216 var
217 gStdFont: DWORD;
218 gGameSettings: TGameSettings;
219 gPlayer1Settings: TPlayerSettings;
220 gPlayer2Settings: TPlayerSettings;
221 gGameOn: Boolean;
222 gPlayerScreenSize: TDFPoint;
223 gPlayer1ScreenCoord: TDFPoint;
224 gPlayer2ScreenCoord: TDFPoint;
225 gPlayer1: TPlayer = nil;
226 gPlayer2: TPlayer = nil;
227 gPlayerDrawn: TPlayer = nil;
228 gTime: LongWord;
229 gSwitchGameMode: Byte = GM_DM;
230 gHearPoint1, gHearPoint2: THearPoint;
231 gSoundEffectsDF: Boolean = False;
232 gSoundTriggerTime: Word = 0;
233 gAnnouncer: Byte = ANNOUNCE_NONE;
234 goodsnd: array[0..3] of TPlayableSound;
235 killsnd: array[0..3] of TPlayableSound;
236 gDefInterTime: ShortInt = -1;
237 gInterEndTime: LongWord = 0;
238 gInterTime: LongWord = 0;
239 gServInterTime: Byte = 0;
240 gGameStartTime: LongWord = 0;
241 gTotalMonsters: Integer = 0;
242 gPauseMain: Boolean = false;
243 gPauseHolmes: Boolean = false;
244 gShowTime: Boolean = True;
245 gShowFPS: Boolean = False;
246 gShowGoals: Boolean = True;
247 gShowStat: Boolean = True;
248 gShowKillMsg: Boolean = True;
249 gShowLives: Boolean = True;
250 gShowPing: Boolean = False;
251 gShowMap: Boolean = False;
252 gExit: Byte = 0;
253 gState: Byte = STATE_NONE;
254 sX, sY: Integer;
255 sWidth, sHeight: Word;
256 gSpectMode: Byte = SPECT_NONE;
257 gSpectHUD: Boolean = True;
258 gSpectKeyPress: Boolean = False;
259 gSpectX: Integer = 0;
260 gSpectY: Integer = 0;
261 gSpectStep: Byte = 8;
262 gSpectViewTwo: Boolean = False;
263 gSpectPID1: Integer = -1;
264 gSpectPID2: Integer = -1;
265 gMusic: TMusic = nil;
266 gLoadGameMode: Boolean;
267 gCheats: Boolean = False;
268 gMapOnce: Boolean = False;
269 gMapToDelete: String;
270 gTempDelete: Boolean = False;
271 gLastMap: Boolean = False;
272 gWinPosX, gWinPosY: Integer;
273 gWinSizeX, gWinSizeY: Integer;
274 gWinFrameX, gWinFrameY, gWinCaption: Integer;
275 gWinActive: Boolean = True; // by default window is active, lol
276 gResolutionChange: Boolean = False;
277 gRC_Width, gRC_Height: Word;
278 gRC_FullScreen, gRC_Maximized: Boolean;
279 gLanguageChange: Boolean = False;
280 gDebugMode: Boolean = False;
281 g_debug_Sounds: Boolean = False;
282 g_debug_Frames: Boolean = False;
283 g_debug_WinMsgs: Boolean = False;
284 g_debug_MonsterOff: Boolean = False;
285 g_debug_BotAIOff: Byte = 0;
286 g_debug_HealthBar: Boolean = False;
287 g_Debug_Player: Boolean = False;
288 gCoopMonstersKilled: Word = 0;
289 gCoopSecretsFound: Word = 0;
290 gCoopTotalMonstersKilled: Word = 0;
291 gCoopTotalSecretsFound: Word = 0;
292 gCoopTotalMonsters: Word = 0;
293 gCoopTotalSecrets: Word = 0;
294 gStatsOff: Boolean = False;
295 gStatsPressed: Boolean = False;
296 gExitByTrigger: Boolean = False;
297 gNextMap: String = '';
298 gLMSRespawn: Byte = LMS_RESPAWN_NONE;
299 gLMSRespawnTime: Cardinal = 0;
300 gLMSSoftSpawn: Boolean = False;
301 gMissionFailed: Boolean = False;
302 gVoteInProgress: Boolean = False;
303 gVotePassed: Boolean = False;
304 gVoteCommand: string = '';
305 gVoteTimer: Cardinal = 0;
306 gVoteCmdTimer: Cardinal = 0;
307 gVoteCount: Integer = 0;
308 gVoteTimeout: Cardinal = 30;
309 gVoted: Boolean = False;
310 gVotesEnabled: Boolean = True;
311 gEvents: Array of TGameEvent;
312 gDelayedEvents: Array of TDelayedEvent;
313 gUseChatSounds: Boolean = True;
314 gChatSounds: Array of TChatSound;
316 // 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 framePool.reset();
1546 // Ïîðà âûêëþ÷àòü èãðó:
1547 if gExit = EXIT_QUIT then
1548 Exit;
1549 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1550 if gExit <> 0 then
1551 begin
1552 EndGame();
1553 if gExit = EXIT_QUIT then
1554 Exit;
1555 end;
1557 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî
1558 // no need to, as we'll do it in event handler
1560 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1561 g_Console_Update();
1563 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1564 begin
1565 gExit := EXIT_SIMPLE;
1566 EndGame();
1567 Exit;
1568 end;
1570 case gState of
1571 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1572 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1573 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1574 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1575 begin
1576 if g_Game_IsNet and g_Game_IsServer then
1577 begin
1578 gInterTime := gInterTime + GAME_TICK;
1579 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1580 if a <> gServInterTime then
1581 begin
1582 gServInterTime := a;
1583 MH_SEND_TimeSync(gServInterTime);
1584 end;
1585 end;
1587 if (not g_Game_IsClient) and
1590 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1591 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1592 and (g_ActiveWindow = nil)
1594 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1596 then
1597 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1598 g_Game_StopAllSounds(True);
1600 if gMapOnce then // Ýòî áûë òåñò
1601 gExit := EXIT_SIMPLE
1602 else
1603 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1604 g_Game_ChangeMap(gNextMap)
1605 else // Ñëåäóþùåé êàðòû íåò
1606 begin
1607 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1608 begin
1609 // Âûõîä â ãëàâíîå ìåíþ:
1610 g_Game_Free;
1611 g_GUI_ShowWindow('MainMenu');
1612 gMusic.SetByName('MUSIC_MENU');
1613 gMusic.Play();
1614 gState := STATE_MENU;
1615 end else
1616 begin
1617 // Ôèíàëüíàÿ êàðòèíêà:
1618 g_Game_ExecuteEvent('onwadend');
1619 g_Game_Free();
1620 if not gMusic.SetByName('MUSIC_endmus') then
1621 gMusic.SetByName('MUSIC_STDENDMUS');
1622 gMusic.Play();
1623 gState := STATE_ENDPIC;
1624 end;
1625 g_Game_ExecuteEvent('ongameend');
1626 end;
1628 Exit;
1629 end;
1631 if gState = STATE_INTERTEXT then
1632 if InterText.counter > 0 then
1633 InterText.counter := InterText.counter - 1;
1634 end;
1636 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1637 begin
1638 if EndingGameCounter = 0 then
1639 begin
1640 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1641 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1642 begin
1643 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1644 begin
1645 g_Game_ExecuteEvent('onwadend');
1646 if not gMusic.SetByName('MUSIC_endmus') then
1647 gMusic.SetByName('MUSIC_STDENDMUS');
1648 end
1649 else
1650 gMusic.SetByName('MUSIC_ROUNDMUS');
1652 gMusic.Play();
1653 gState := STATE_INTERCUSTOM;
1654 end
1655 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1656 begin
1657 gMusic.SetByName('MUSIC_INTERMUS');
1658 gMusic.Play();
1659 gState := STATE_INTERSINGLE;
1660 end;
1661 g_Game_ExecuteEvent('oninter');
1662 end
1663 else
1664 DecMin(EndingGameCounter, 6, 0);
1665 end;
1667 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1668 begin
1669 if gMapOnce then // Ýòî áûë òåñò
1670 begin
1671 gExit := EXIT_SIMPLE;
1672 Exit;
1673 end;
1674 end;
1676 STATE_SLIST:
1677 g_Serverlist_Control(slCurrent);
1678 end;
1680 if g_Game_IsNet then
1681 if not gConsoleShow then
1682 if not gChatShow then
1683 begin
1684 if g_ActiveWindow = nil then
1685 begin
1686 if e_KeyPressed(gGameControls.GameControls.Chat) then
1687 g_Console_Chat_Switch(False)
1688 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1689 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1690 g_Console_Chat_Switch(True);
1691 end;
1692 end else
1693 if not gChatEnter then
1694 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1695 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1696 gChatEnter := True;
1698 // Ñòàòèñòèêà ïî Tab:
1699 if gGameOn then
1700 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1701 (gGameSettings.GameType <> GT_SINGLE) and
1702 e_KeyPressed(gGameControls.GameControls.Stat);
1704 // Èãðà èäåò:
1705 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1706 begin
1707 // Âðåìÿ += 28 ìèëëèñåêóíä:
1708 gTime := gTime + GAME_TICK;
1710 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1711 if MessageTime = 0 then
1712 MessageText := '';
1713 if MessageTime > 0 then
1714 MessageTime := MessageTime - 1;
1716 if (g_Game_IsServer) then
1717 begin
1718 // Áûë çàäàí ëèìèò âðåìåíè:
1719 if (gGameSettings.TimeLimit > 0) then
1720 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1721 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1722 g_Game_NextLevel();
1723 Exit;
1724 end;
1726 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1727 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1728 g_Game_RestartRound(gLMSSoftSpawn);
1730 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1731 if gVoteInProgress and (gVoteTimer < gTime) then
1732 g_Game_CheckVote
1733 else if gVotePassed and (gVoteCmdTimer < gTime) then
1734 begin
1735 g_Console_Process(gVoteCommand);
1736 gVoteCommand := '';
1737 gVotePassed := False;
1738 end;
1740 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1741 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1742 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1743 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1744 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1746 // Áûë çàäàí ëèìèò ïîáåä:
1747 if (gGameSettings.GoalLimit > 0) then
1748 begin
1749 b := 0;
1751 if gGameSettings.GameMode = GM_DM then
1752 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1753 for i := 0 to High(gPlayers) do
1754 if gPlayers[i] <> nil then
1755 if gPlayers[i].Frags > b then
1756 b := gPlayers[i].Frags;
1757 end
1758 else
1759 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1760 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1761 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1762 end;
1764 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1765 if b >= gGameSettings.GoalLimit then
1766 begin
1767 g_Game_NextLevel();
1768 Exit;
1769 end;
1770 end;
1772 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1773 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1774 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1775 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1776 begin
1777 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1778 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1779 end // if not console
1780 else
1781 begin
1782 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1783 end;
1784 // process weapon switch queue
1785 end; // if server
1787 // Íàáëþäàòåëü
1788 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1789 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1790 begin
1791 if not gSpectKeyPress then
1792 begin
1793 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
1794 begin
1795 // switch spect mode
1796 case gSpectMode of
1797 SPECT_NONE: ; // not spectator
1798 SPECT_STATS,
1799 SPECT_MAPVIEW: Inc(gSpectMode);
1800 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1801 end;
1802 gSpectKeyPress := True;
1803 end;
1804 if gSpectMode = SPECT_MAPVIEW then
1805 begin
1806 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1807 gSpectX := Max(gSpectX - gSpectStep, 0);
1808 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1809 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1810 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1811 gSpectY := Max(gSpectY - gSpectStep, 0);
1812 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1813 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1814 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1815 begin
1816 // decrease step
1817 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1818 gSpectKeyPress := True;
1819 end;
1820 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1821 begin
1822 // increase step
1823 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1824 gSpectKeyPress := True;
1825 end;
1826 end;
1827 if gSpectMode = SPECT_PLAYERS then
1828 begin
1829 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1830 begin
1831 // add second view
1832 gSpectViewTwo := True;
1833 gSpectKeyPress := True;
1834 end;
1835 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1836 begin
1837 // remove second view
1838 gSpectViewTwo := False;
1839 gSpectKeyPress := True;
1840 end;
1841 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1842 begin
1843 // prev player (view 1)
1844 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1845 gSpectKeyPress := True;
1846 end;
1847 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1848 begin
1849 // next player (view 1)
1850 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1851 gSpectKeyPress := True;
1852 end;
1853 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1854 begin
1855 // prev player (view 2)
1856 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1857 gSpectKeyPress := True;
1858 end;
1859 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1860 begin
1861 // next player (view 2)
1862 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1863 gSpectKeyPress := True;
1864 end;
1865 end;
1866 end
1867 else
1868 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1869 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1870 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1871 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1872 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1873 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1874 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1875 gSpectKeyPress := False;
1876 end;
1878 // Îáíîâëÿåì âñå îñòàëüíîå:
1879 g_Map_Update();
1880 g_Items_Update();
1881 g_Triggers_Update();
1882 g_Weapon_Update();
1883 g_Monsters_Update();
1884 g_GFX_Update();
1885 g_Player_UpdateAll();
1886 g_Player_UpdatePhysicalObjects();
1888 // server: send newly spawned monsters unconditionally
1889 if (gGameSettings.GameType = GT_SERVER) then
1890 begin
1891 if (Length(gMonstersSpawned) > 0) then
1892 begin
1893 for I := 0 to High(gMonstersSpawned) do MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
1894 SetLength(gMonstersSpawned, 0);
1895 end;
1896 end;
1898 if (gSoundTriggerTime > 8) then
1899 begin
1900 g_Game_UpdateTriggerSounds();
1901 gSoundTriggerTime := 0;
1902 end
1903 else
1904 begin
1905 Inc(gSoundTriggerTime);
1906 end;
1908 if (NetMode = NET_SERVER) then
1909 begin
1910 Inc(NetTimeToUpdate);
1911 Inc(NetTimeToReliable);
1913 // send monster updates
1914 if (NetTimeToReliable >= NetRelupdRate) or (NetTimeToUpdate >= NetUpdateRate) then
1915 begin
1916 // send all monsters (periodic sync)
1917 reliableUpdate := (NetTimeToReliable >= NetRelupdRate);
1919 for I := 0 to High(gPlayers) do
1920 begin
1921 if (gPlayers[I] <> nil) then MH_SEND_PlayerPos(reliableUpdate, gPlayers[I].UID);
1922 end;
1924 g_Mons_ForEach(sendMonsPos);
1926 if reliableUpdate then
1927 begin
1928 NetTimeToReliable := 0;
1929 NetTimeToUpdate := NetUpdateRate;
1930 end
1931 else
1932 begin
1933 NetTimeToUpdate := 0;
1934 end;
1935 end
1936 else
1937 begin
1938 // send only mosters with some unexpected changes
1939 g_Mons_ForEach(sendMonsPosUnexpected);
1940 end;
1942 // send unexpected platform changes
1943 g_Map_NetSendInterestingPanels();
1945 if NetUseMaster then
1946 begin
1947 if gTime >= NetTimeToMaster then
1948 begin
1949 if (NetMHost = nil) or (NetMPeer = nil) then
1950 begin
1951 if not g_Net_Slist_Connect then g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
1952 end;
1954 g_Net_Slist_Update;
1955 NetTimeToMaster := gTime + NetMasterRate;
1956 end;
1957 end;
1958 end
1959 else if (NetMode = NET_CLIENT) then
1960 begin
1961 MC_SEND_PlayerPos();
1962 end;
1963 end; // if gameOn ...
1965 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
1966 if g_ActiveWindow <> nil then
1967 begin
1968 w := e_GetFirstKeyPressed();
1970 if (w <> IK_INVALID) then
1971 begin
1972 Msg.Msg := MESSAGE_DIKEY;
1973 Msg.wParam := w;
1974 g_ActiveWindow.OnMessage(Msg);
1975 end;
1977 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
1978 if g_ActiveWindow <> nil then
1979 g_ActiveWindow.Update();
1981 // Íóæíî ñìåíèòü ðàçðåøåíèå:
1982 if gResolutionChange then
1983 begin
1984 e_WriteLog('Changing resolution', TMsgType.Notify);
1985 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
1986 gResolutionChange := False;
1987 end;
1989 // Íóæíî ñìåíèòü ÿçûê:
1990 if gLanguageChange then
1991 begin
1992 //e_WriteLog('Read language file', MSG_NOTIFY);
1993 //g_Language_Load(DataDir + gLanguage + '.txt');
1994 g_Language_Set(gLanguage);
1995 g_Menu_Reset();
1996 gLanguageChange := False;
1997 end;
1998 end;
2000 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
2001 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
2002 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
2003 begin
2004 g_TakeScreenShot();
2005 LastScreenShot := GetTimer();
2006 end;
2008 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
2009 if e_KeyPressed(IK_F10) and
2010 gGameOn and
2011 (not gConsoleShow) and
2012 (g_ActiveWindow = nil) then
2013 begin
2014 KeyPress(IK_F10);
2015 end;
2017 Time := GetTimer() {div 1000};
2019 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
2020 if gDelayedEvents <> nil then
2021 for a := 0 to High(gDelayedEvents) do
2022 if gDelayedEvents[a].Pending and
2024 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
2025 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
2026 ) then
2027 begin
2028 case gDelayedEvents[a].DEType of
2029 DE_GLOBEVENT:
2030 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
2031 DE_BFGHIT:
2032 if gGameOn then
2033 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
2034 DE_KILLCOMBO:
2035 if gGameOn then
2036 begin
2037 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
2038 if g_Game_IsNet and g_Game_IsServer then
2039 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
2040 end;
2041 end;
2042 gDelayedEvents[a].Pending := False;
2043 end;
2045 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2046 UPSCounter := UPSCounter + 1;
2047 if Time - UPSTime >= 1000 then
2048 begin
2049 UPS := UPSCounter;
2050 UPSCounter := 0;
2051 UPSTime := Time;
2052 end;
2054 if gGameOn then
2055 begin
2056 g_Weapon_AddDynLights();
2057 g_Items_AddDynLights();
2058 end;
2059 end;
2061 procedure g_Game_LoadChatSounds(Resource: string);
2062 var
2063 WAD: TWADFile;
2064 FileName, Snd: string;
2065 p: Pointer;
2066 len, cnt, tags, i, j: Integer;
2067 cfg: TConfig;
2068 begin
2069 FileName := g_ExtractWadName(Resource);
2071 WAD := TWADFile.Create();
2072 WAD.ReadFile(FileName);
2074 if not WAD.GetResource(g_ExtractFilePathName(Resource), p, len) then
2075 begin
2076 gChatSounds := nil;
2077 WAD.Free();
2078 Exit;
2079 end;
2081 cfg := TConfig.CreateMem(p, len);
2082 cnt := cfg.ReadInt('ChatSounds', 'Count', 0);
2084 SetLength(gChatSounds, cnt);
2085 for i := 0 to Length(gChatSounds) - 1 do
2086 begin
2087 gChatSounds[i].Sound := nil;
2088 Snd := Trim(cfg.ReadStr(IntToStr(i), 'Sound', ''));
2089 tags := cfg.ReadInt(IntToStr(i), 'Tags', 0);
2090 if (Snd = '') or (Tags <= 0) then
2091 continue;
2092 g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i), GameWAD+':'+Snd);
2093 gChatSounds[i].Sound := TPlayableSound.Create();
2094 gChatSounds[i].Sound.SetByName('SOUND_CHAT_MACRO' + IntToStr(i));
2095 SetLength(gChatSounds[i].Tags, tags);
2096 for j := 0 to tags - 1 do
2097 gChatSounds[i].Tags[j] := toLowerCase1251(cfg.ReadStr(IntToStr(i), 'Tag' + IntToStr(j), ''));
2098 gChatSounds[i].FullWord := cfg.ReadBool(IntToStr(i), 'FullWord', False);
2099 end;
2101 cfg.Free();
2102 WAD.Free();
2103 end;
2105 procedure g_Game_FreeChatSounds();
2106 var
2107 i: Integer;
2108 begin
2109 for i := 0 to Length(gChatSounds) - 1 do
2110 begin
2111 gChatSounds[i].Sound.Free();
2112 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i));
2113 end;
2114 SetLength(gChatSounds, 0);
2115 gChatSounds := nil;
2116 end;
2118 procedure g_Game_LoadData();
2119 var
2120 wl, hl: Integer;
2121 wr, hr: Integer;
2122 wb, hb: Integer;
2123 wm, hm: Integer;
2124 begin
2125 if DataLoaded then Exit;
2127 e_WriteLog('Loading game data...', TMsgType.Notify);
2129 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2130 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2131 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2132 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2133 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2134 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2135 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
2136 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
2137 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
2138 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
2139 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
2140 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
2141 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2142 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2144 hasPBarGfx := true;
2145 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false;
2146 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false;
2147 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false;
2148 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false;
2150 if hasPBarGfx then
2151 begin
2152 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2153 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2154 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2155 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2156 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
2157 begin
2158 // yay!
2159 end
2160 else
2161 begin
2162 hasPBarGfx := false;
2163 end;
2164 end;
2166 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2167 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2168 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2169 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2170 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2171 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2172 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2173 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2174 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2175 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2176 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2177 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2178 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2179 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2180 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2181 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2182 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2183 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2184 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2185 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2187 goodsnd[0] := TPlayableSound.Create();
2188 goodsnd[1] := TPlayableSound.Create();
2189 goodsnd[2] := TPlayableSound.Create();
2190 goodsnd[3] := TPlayableSound.Create();
2192 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2193 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2194 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2195 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2197 killsnd[0] := TPlayableSound.Create();
2198 killsnd[1] := TPlayableSound.Create();
2199 killsnd[2] := TPlayableSound.Create();
2200 killsnd[3] := TPlayableSound.Create();
2202 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2203 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2204 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2205 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2207 g_Game_LoadChatSounds(GameWAD+':CHATSND\SNDCFG');
2209 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2210 g_Items_LoadData();
2212 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2213 g_Weapon_LoadData();
2215 g_Monsters_LoadData();
2217 DataLoaded := True;
2218 end;
2220 procedure g_Game_FreeData();
2221 begin
2222 if not DataLoaded then Exit;
2224 g_Items_FreeData();
2225 g_Weapon_FreeData();
2226 g_Monsters_FreeData();
2228 e_WriteLog('Releasing game data...', TMsgType.Notify);
2230 g_Texture_Delete('NOTEXTURE');
2231 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2232 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2233 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2234 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2235 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2236 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2237 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2238 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2239 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2240 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2241 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2242 g_Frames_DeleteByName('FRAMES_TELEPORT');
2243 g_Sound_Delete('SOUND_GAME_TELEPORT');
2244 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2245 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2246 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2247 g_Sound_Delete('SOUND_GAME_BULK1');
2248 g_Sound_Delete('SOUND_GAME_BULK2');
2249 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2250 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2251 g_Sound_Delete('SOUND_GAME_SWITCH1');
2252 g_Sound_Delete('SOUND_GAME_SWITCH0');
2254 goodsnd[0].Free();
2255 goodsnd[1].Free();
2256 goodsnd[2].Free();
2257 goodsnd[3].Free();
2259 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2260 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2261 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2262 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2264 killsnd[0].Free();
2265 killsnd[1].Free();
2266 killsnd[2].Free();
2267 killsnd[3].Free();
2269 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2270 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2271 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2272 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2274 g_Game_FreeChatSounds();
2276 DataLoaded := False;
2277 end;
2279 procedure DrawCustomStat();
2280 var
2281 pc, x, y, w, _y,
2282 w1, w2, w3,
2283 t, p, m: Integer;
2284 ww1, hh1: Word;
2285 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2286 s1, s2, topstr: String;
2287 begin
2288 e_TextureFontGetSize(gStdFont, ww2, hh2);
2290 g_ProcessMessages();
2292 if e_KeyPressed(IK_TAB) then
2293 begin
2294 if not gStatsPressed then
2295 begin
2296 gStatsOff := not gStatsOff;
2297 gStatsPressed := True;
2298 end;
2299 end
2300 else
2301 gStatsPressed := False;
2303 if gStatsOff then
2304 begin
2305 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2306 w := (Length(s1) * ww2) div 2;
2307 x := gScreenWidth div 2 - w;
2308 y := 8;
2309 e_TextureFontPrint(x, y, s1, gStdFont);
2310 Exit;
2311 end;
2313 if (gGameSettings.GameMode = GM_COOP) then
2314 begin
2315 if gMissionFailed then
2316 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2317 else
2318 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2319 end
2320 else
2321 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2323 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2324 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2326 if g_Game_IsNet then
2327 begin
2328 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2329 if not gChatShow then
2330 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2331 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2332 end;
2334 if g_Game_IsClient then
2335 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2336 else
2337 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2338 if not gChatShow then
2339 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2340 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2342 x := 32;
2343 y := 16+hh1+16;
2345 w := gScreenWidth-x*2;
2347 w2 := (w-16) div 6;
2348 w3 := w2;
2349 w1 := w-16-w2-w3;
2351 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2352 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2354 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2356 case CustomStat.GameMode of
2357 GM_DM:
2358 begin
2359 if gGameSettings.MaxLives = 0 then
2360 s1 := _lc[I_GAME_DM]
2361 else
2362 s1 := _lc[I_GAME_LMS];
2363 end;
2364 GM_TDM:
2365 begin
2366 if gGameSettings.MaxLives = 0 then
2367 s1 := _lc[I_GAME_TDM]
2368 else
2369 s1 := _lc[I_GAME_TLMS];
2370 end;
2371 GM_CTF: s1 := _lc[I_GAME_CTF];
2372 GM_COOP:
2373 begin
2374 if gGameSettings.MaxLives = 0 then
2375 s1 := _lc[I_GAME_COOP]
2376 else
2377 s1 := _lc[I_GAME_SURV];
2378 end;
2379 else s1 := '';
2380 end;
2382 _y := y+16;
2383 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2384 _y := _y+8;
2386 _y := _y+16;
2387 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2388 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2390 _y := _y+16;
2391 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2392 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2393 (CustomStat.GameTime div 1000 div 60) mod 60,
2394 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2396 pc := Length(CustomStat.PlayerStat);
2397 if pc = 0 then Exit;
2399 if CustomStat.GameMode = GM_COOP then
2400 begin
2401 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2402 _y := _y+32;
2403 s2 := _lc[I_GAME_MONSTERS];
2404 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2405 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2406 _y := _y+16;
2407 s2 := _lc[I_GAME_SECRETS];
2408 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2409 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2410 if gLastMap then
2411 begin
2412 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2413 _y := _y-16;
2414 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2415 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2416 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2417 _y := _y+16;
2418 s2 := _lc[I_GAME_SECRETS_TOTAL];
2419 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2420 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2421 end;
2422 end;
2424 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2425 begin
2426 _y := _y+16+16;
2428 with CustomStat do
2429 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2430 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2431 else s1 := _lc[I_GAME_WIN_DRAW];
2433 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2434 _y := _y+40;
2436 for t := TEAM_RED to TEAM_BLUE do
2437 begin
2438 if t = TEAM_RED then
2439 begin
2440 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2441 gStdFont, 255, 0, 0, 1);
2442 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2443 gStdFont, 255, 0, 0, 1);
2444 r := 255;
2445 g := 0;
2446 b := 0;
2447 end
2448 else
2449 begin
2450 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2451 gStdFont, 0, 0, 255, 1);
2452 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2453 gStdFont, 0, 0, 255, 1);
2454 r := 0;
2455 g := 0;
2456 b := 255;
2457 end;
2459 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2460 _y := _y+24;
2462 for p := 0 to High(CustomStat.PlayerStat) do
2463 if CustomStat.PlayerStat[p].Team = t then
2464 with CustomStat.PlayerStat[p] do
2465 begin
2466 if Spectator then
2467 begin
2468 rr := r div 2;
2469 gg := g div 2;
2470 bb := b div 2;
2471 end
2472 else
2473 begin
2474 rr := r;
2475 gg := g;
2476 bb := b;
2477 end;
2478 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2479 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2480 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2481 _y := _y+24;
2482 end;
2484 _y := _y+16+16;
2485 end;
2486 end
2487 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2488 begin
2489 _y := _y+40;
2490 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2491 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2492 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2494 _y := _y+24;
2495 for p := 0 to High(CustomStat.PlayerStat) do
2496 with CustomStat.PlayerStat[p] do
2497 begin
2498 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2500 if Spectator then
2501 r := 127
2502 else
2503 r := 255;
2505 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2506 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2507 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2508 _y := _y+24;
2509 end;
2510 end;
2511 end;
2513 procedure DrawSingleStat();
2514 var
2515 tm, key_x, val_x, y: Integer;
2516 w1, w2, h: Word;
2517 s1, s2: String;
2519 procedure player_stat(n: Integer);
2520 var
2521 kpm: Real;
2523 begin
2524 // "Kills: # / #":
2525 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2526 s2 := Format(' %d', [gTotalMonsters]);
2528 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2529 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2530 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2531 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2532 s1 := s1 + '/';
2533 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2534 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2536 // "Kills-per-minute: ##.#":
2537 s1 := _lc[I_MENU_INTER_KPM];
2538 if tm > 0 then
2539 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2540 else
2541 kpm := SingleStat.PlayerStat[n].Kills;
2542 s2 := Format(' %.1f', [kpm]);
2544 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2545 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2547 // "Secrets found: # / #":
2548 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2549 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2551 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2552 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2553 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2554 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2555 s1 := s1 + '/';
2556 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2557 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2558 end;
2560 begin
2561 // "Level Complete":
2562 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2563 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2565 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2566 s1 := _lc[I_MENU_INTER_KPM];
2567 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2568 Inc(w1, 16);
2569 s1 := ' 9999.9';
2570 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2572 key_x := (gScreenWidth-w1-w2) div 2;
2573 val_x := key_x + w1;
2575 // "Time: #:##:##":
2576 tm := SingleStat.GameTime div 1000;
2577 s1 := _lc[I_MENU_INTER_TIME];
2578 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2580 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2581 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2583 if SingleStat.TwoPlayers then
2584 begin
2585 // "Player 1":
2586 s1 := _lc[I_MENU_PLAYER_1];
2587 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2588 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2590 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2591 y := 176;
2592 player_stat(0);
2594 // "Player 2":
2595 s1 := _lc[I_MENU_PLAYER_2];
2596 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2597 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2599 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2600 y := 336;
2601 player_stat(1);
2602 end
2603 else
2604 begin
2605 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2606 y := 128;
2607 player_stat(0);
2608 end;
2609 end;
2611 procedure DrawLoadingStat();
2612 procedure drawRect (x, y, w, h: Integer);
2613 begin
2614 if (w < 1) or (h < 1) then exit;
2615 glBegin(GL_QUADS);
2616 glVertex2f(x+0.375, y+0.375);
2617 glVertex2f(x+w+0.375, y+0.375);
2618 glVertex2f(x+w+0.375, y+h+0.375);
2619 glVertex2f(x+0.375, y+h+0.375);
2620 glEnd();
2621 end;
2623 function drawPBar (cur, total: Integer; washere: Boolean): Boolean;
2624 var
2625 rectW, rectH: Integer;
2626 x0, y0: Integer;
2627 wdt: Integer;
2628 wl, hl: Integer;
2629 wr, hr: Integer;
2630 wb, hb: Integer;
2631 wm, hm: Integer;
2632 idl, idr, idb, idm: LongWord;
2633 f, my: Integer;
2634 begin
2635 result := false;
2636 if (total < 1) then exit;
2637 if (cur < 1) then exit; // don't blink
2638 if (not washere) and (cur >= total) then exit; // don't blink
2639 //if (cur < 0) then cur := 0;
2640 //if (cur > total) then cur := total;
2641 result := true;
2643 if (hasPBarGfx) then
2644 begin
2645 g_Texture_Get('UI_GFX_PBAR_LEFT', idl);
2646 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2647 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr);
2648 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2649 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb);
2650 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2651 g_Texture_Get('UI_GFX_PBAR_MARKER', idm);
2652 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2654 //rectW := gScreenWidth-360;
2655 rectW := trunc(624.0*gScreenWidth/1024.0);
2656 rectH := hl;
2658 x0 := (gScreenWidth-rectW) div 2;
2659 y0 := gScreenHeight-rectH-64;
2660 if (y0 < 2) then y0 := 2;
2662 glEnable(GL_SCISSOR_TEST);
2664 // left and right
2665 glScissor(x0, gScreenHeight-y0-rectH, rectW, rectH);
2666 e_DrawSize(idl, x0, y0, 0, true, false, wl, hl);
2667 e_DrawSize(idr, x0+rectW-wr, y0, 0, true, false, wr, hr);
2669 // body
2670 glScissor(x0+wl, gScreenHeight-y0-rectH, rectW-wl-wr, rectH);
2671 f := x0+wl;
2672 while (f < x0+rectW) do
2673 begin
2674 e_DrawSize(idb, f, y0, 0, true, false, wb, hb);
2675 f += wb;
2676 end;
2678 // filled part
2679 wdt := (rectW-wl-wr)*cur div total;
2680 if (wdt > rectW-wl-wr) then wdt := rectW-wr-wr;
2681 if (wdt > 0) then
2682 begin
2683 my := y0; // don't be so smart, ketmar: +(rectH-wm) div 2;
2684 glScissor(x0+wl, gScreenHeight-my-rectH, wdt, hm);
2685 f := x0+wl;
2686 while (wdt > 0) do
2687 begin
2688 e_DrawSize(idm, f, y0, 0, true, false, wm, hm);
2689 f += wm;
2690 wdt -= wm;
2691 end;
2692 end;
2694 glScissor(0, 0, gScreenWidth, gScreenHeight);
2695 end
2696 else
2697 begin
2698 rectW := gScreenWidth-64;
2699 rectH := 16;
2701 x0 := (gScreenWidth-rectW) div 2;
2702 y0 := gScreenHeight-rectH-64;
2703 if (y0 < 2) then y0 := 2;
2705 glDisable(GL_BLEND);
2706 glDisable(GL_TEXTURE_2D);
2708 //glClearColor(0, 0, 0, 0);
2709 //glClear(GL_COLOR_BUFFER_BIT);
2711 glColor4ub(127, 127, 127, 255);
2712 drawRect(x0-2, y0-2, rectW+4, rectH+4);
2714 glColor4ub(0, 0, 0, 255);
2715 drawRect(x0-1, y0-1, rectW+2, rectH+2);
2717 glColor4ub(127, 127, 127, 255);
2718 wdt := rectW*cur div total;
2719 if (wdt > rectW) then wdt := rectW;
2720 drawRect(x0, y0, wdt, rectH);
2721 end;
2722 end;
2724 var
2725 ww, hh: Word;
2726 xx, yy, i: Integer;
2727 s: String;
2728 begin
2729 if (Length(LoadingStat.Msgs) = 0) then exit;
2731 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2732 yy := (gScreenHeight div 3);
2733 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2734 xx := (gScreenWidth div 3);
2736 with LoadingStat do
2737 begin
2738 for i := 0 to NextMsg-1 do
2739 begin
2740 if (i = (NextMsg-1)) and (MaxValue > 0) then
2741 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2742 else
2743 s := Msgs[i];
2745 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2746 yy := yy + LOADING_INTERLINE;
2747 PBarWasHere := drawPBar(CurValue, MaxValue, PBarWasHere);
2748 end;
2749 end;
2750 end;
2752 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2753 var
2754 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2756 function monDraw (mon: TMonster): Boolean;
2757 begin
2758 result := false; // don't stop
2759 with mon do
2760 begin
2761 if alive then
2762 begin
2763 // Ëåâûé âåðõíèé óãîë
2764 aX := Obj.X div ScaleSz + 1;
2765 aY := Obj.Y div ScaleSz + 1;
2766 // Ðàçìåðû
2767 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2768 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2769 // Ïðàâûé íèæíèé óãîë
2770 aX2 := aX + aX2 - 1;
2771 aY2 := aY + aY2 - 1;
2772 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2773 end;
2774 end;
2775 end;
2777 begin
2778 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2779 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2780 begin
2781 Scale := 1;
2782 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2783 ScaleSz := 16 div Scale;
2784 // Ðàçìåðû ìèíè-êàðòû:
2785 aX := max(gMapInfo.Width div ScaleSz, 1);
2786 aY := max(gMapInfo.Height div ScaleSz, 1);
2787 // Ðàìêà êàðòû:
2788 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2790 if gWalls <> nil then
2791 begin
2792 // Ðèñóåì ñòåíû:
2793 for a := 0 to High(gWalls) do
2794 with gWalls[a] do
2795 if PanelType <> 0 then
2796 begin
2797 // Ëåâûé âåðõíèé óãîë:
2798 aX := X div ScaleSz;
2799 aY := Y div ScaleSz;
2800 // Ðàçìåðû:
2801 aX2 := max(Width div ScaleSz, 1);
2802 aY2 := max(Height div ScaleSz, 1);
2803 // Ïðàâûé íèæíèé óãîë:
2804 aX2 := aX + aX2 - 1;
2805 aY2 := aY + aY2 - 1;
2807 case PanelType of
2808 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2809 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2810 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2811 end;
2812 end;
2813 end;
2814 if gSteps <> nil then
2815 begin
2816 // Ðèñóåì ñòóïåíè:
2817 for a := 0 to High(gSteps) do
2818 with gSteps[a] do
2819 if PanelType <> 0 then
2820 begin
2821 // Ëåâûé âåðõíèé óãîë:
2822 aX := X div ScaleSz;
2823 aY := Y div ScaleSz;
2824 // Ðàçìåðû:
2825 aX2 := max(Width div ScaleSz, 1);
2826 aY2 := max(Height div ScaleSz, 1);
2827 // Ïðàâûé íèæíèé óãîë:
2828 aX2 := aX + aX2 - 1;
2829 aY2 := aY + aY2 - 1;
2831 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2832 end;
2833 end;
2834 if gLifts <> nil then
2835 begin
2836 // Ðèñóåì ëèôòû:
2837 for a := 0 to High(gLifts) do
2838 with gLifts[a] do
2839 if PanelType <> 0 then
2840 begin
2841 // Ëåâûé âåðõíèé óãîë:
2842 aX := X div ScaleSz;
2843 aY := Y div ScaleSz;
2844 // Ðàçìåðû:
2845 aX2 := max(Width div ScaleSz, 1);
2846 aY2 := max(Height div ScaleSz, 1);
2847 // Ïðàâûé íèæíèé óãîë:
2848 aX2 := aX + aX2 - 1;
2849 aY2 := aY + aY2 - 1;
2851 case LiftType of
2852 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2853 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2854 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2855 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2856 end;
2857 end;
2858 end;
2859 if gWater <> nil then
2860 begin
2861 // Ðèñóåì âîäó:
2862 for a := 0 to High(gWater) do
2863 with gWater[a] do
2864 if PanelType <> 0 then
2865 begin
2866 // Ëåâûé âåðõíèé óãîë:
2867 aX := X div ScaleSz;
2868 aY := Y div ScaleSz;
2869 // Ðàçìåðû:
2870 aX2 := max(Width div ScaleSz, 1);
2871 aY2 := max(Height div ScaleSz, 1);
2872 // Ïðàâûé íèæíèé óãîë:
2873 aX2 := aX + aX2 - 1;
2874 aY2 := aY + aY2 - 1;
2876 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2877 end;
2878 end;
2879 if gAcid1 <> nil then
2880 begin
2881 // Ðèñóåì êèñëîòó 1:
2882 for a := 0 to High(gAcid1) do
2883 with gAcid1[a] do
2884 if PanelType <> 0 then
2885 begin
2886 // Ëåâûé âåðõíèé óãîë:
2887 aX := X div ScaleSz;
2888 aY := Y div ScaleSz;
2889 // Ðàçìåðû:
2890 aX2 := max(Width div ScaleSz, 1);
2891 aY2 := max(Height div ScaleSz, 1);
2892 // Ïðàâûé íèæíèé óãîë:
2893 aX2 := aX + aX2 - 1;
2894 aY2 := aY + aY2 - 1;
2896 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2897 end;
2898 end;
2899 if gAcid2 <> nil then
2900 begin
2901 // Ðèñóåì êèñëîòó 2:
2902 for a := 0 to High(gAcid2) do
2903 with gAcid2[a] do
2904 if PanelType <> 0 then
2905 begin
2906 // Ëåâûé âåðõíèé óãîë:
2907 aX := X div ScaleSz;
2908 aY := Y div ScaleSz;
2909 // Ðàçìåðû:
2910 aX2 := max(Width div ScaleSz, 1);
2911 aY2 := max(Height div ScaleSz, 1);
2912 // Ïðàâûé íèæíèé óãîë:
2913 aX2 := aX + aX2 - 1;
2914 aY2 := aY + aY2 - 1;
2916 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2917 end;
2918 end;
2919 if gPlayers <> nil then
2920 begin
2921 // Ðèñóåì èãðîêîâ:
2922 for a := 0 to High(gPlayers) do
2923 if gPlayers[a] <> nil then with gPlayers[a] do
2924 if alive then begin
2925 // Ëåâûé âåðõíèé óãîë:
2926 aX := Obj.X div ScaleSz + 1;
2927 aY := Obj.Y div ScaleSz + 1;
2928 // Ðàçìåðû:
2929 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2930 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2931 // Ïðàâûé íèæíèé óãîë:
2932 aX2 := aX + aX2 - 1;
2933 aY2 := aY + aY2 - 1;
2935 if gPlayers[a] = p then
2936 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2937 else
2938 case Team of
2939 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2940 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2941 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2942 end;
2943 end;
2944 end;
2945 // Ðèñóåì ìîíñòðîâ
2946 g_Mons_ForEach(monDraw);
2947 end;
2948 end;
2951 procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor);
2952 begin
2953 if not hasAmbient then exit;
2954 e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
2955 end;
2958 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2959 //FIXME: broken for splitscreen mode
2960 procedure renderDynLightsInternal ();
2961 var
2962 //hasAmbient: Boolean;
2963 //ambColor: TDFColor;
2964 lln: Integer;
2965 lx, ly, lrad: Integer;
2966 scxywh: array[0..3] of GLint;
2967 wassc: Boolean;
2968 begin
2969 if e_NoGraphics then exit;
2971 //TODO: lights should be in separate grid, i think
2972 // but on the other side: grid may be slower for dynlights, as their lifetime is short
2973 if (not gwin_k8_enable_light_experiments) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit;
2975 // rendering mode
2976 //ambColor := gCurrentMap['light_ambient'].rgba;
2977 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
2979 { // this will multiply incoming color to alpha from framebuffer
2980 glEnable(GL_BLEND);
2981 glBlendFunc(GL_DST_ALPHA, GL_ONE);
2984 (*
2985 * light rendering: (INVALID!)
2986 * glStencilFunc(GL_EQUAL, 0, $ff);
2987 * for each light:
2988 * glClear(GL_STENCIL_BUFFER_BIT);
2989 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2990 * draw shadow volume into stencil buffer
2991 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2992 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
2993 * turn off blending
2994 * draw color-less quad with light alpha (WARNING! don't touch color!)
2995 * glEnable(GL_BLEND);
2996 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
2997 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
2998 *)
3000 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
3001 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
3003 // setup OpenGL parameters
3004 glStencilMask($FFFFFFFF);
3005 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
3006 glEnable(GL_STENCIL_TEST);
3007 glEnable(GL_SCISSOR_TEST);
3008 glClear(GL_STENCIL_BUFFER_BIT);
3009 glStencilFunc(GL_EQUAL, 0, $ff);
3011 for lln := 0 to g_dynLightCount-1 do
3012 begin
3013 lx := g_dynLights[lln].x;
3014 ly := g_dynLights[lln].y;
3015 lrad := g_dynLights[lln].radius;
3016 if (lrad < 3) then continue;
3018 if (lx-sX+lrad < 0) then continue;
3019 if (ly-sY+lrad < 0) then continue;
3020 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
3021 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
3023 // set scissor to optimize drawing
3024 if (g_dbg_scale = 1.0) then
3025 begin
3026 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
3027 end
3028 else
3029 begin
3030 glScissor(0, 0, gWinSizeX, gWinSizeY);
3031 end;
3032 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3033 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
3034 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3035 // draw extruded panels
3036 glDisable(GL_TEXTURE_2D);
3037 glDisable(GL_BLEND);
3038 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
3039 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
3040 // render light texture
3041 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3042 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
3043 // blend it
3044 glEnable(GL_BLEND);
3045 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3046 glEnable(GL_TEXTURE_2D);
3047 // color and opacity
3048 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
3049 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
3050 glBegin(GL_QUADS);
3051 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
3052 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
3053 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
3054 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
3055 glEnd();
3056 end;
3058 // done
3059 glDisable(GL_STENCIL_TEST);
3060 glDisable(GL_BLEND);
3061 glDisable(GL_SCISSOR_TEST);
3062 //glScissor(0, 0, sWidth, sHeight);
3064 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
3065 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
3066 end;
3069 function fixViewportForScale (): Boolean;
3070 var
3071 nx0, ny0, nw, nh: Integer;
3072 begin
3073 result := false;
3074 if (g_dbg_scale <> 1.0) then
3075 begin
3076 result := true;
3077 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
3078 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
3079 nw := round(sWidth/g_dbg_scale);
3080 nh := round(sHeight/g_dbg_scale);
3081 sX := nx0;
3082 sY := ny0;
3083 sWidth := nw;
3084 sHeight := nh;
3085 end;
3086 end;
3089 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3090 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3091 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
3092 type
3093 TDrawCB = procedure ();
3095 var
3096 hasAmbient: Boolean;
3097 ambColor: TDFColor;
3098 doAmbient: Boolean = false;
3100 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
3101 var
3102 tagmask: Integer;
3103 pan: TPanel;
3104 begin
3105 profileFrameDraw.sectionBegin(profname);
3106 if gdbg_map_use_accel_render then
3107 begin
3108 tagmask := panelTypeToTag(panType);
3109 while (gDrawPanelList.count > 0) do
3110 begin
3111 pan := TPanel(gDrawPanelList.front());
3112 if ((pan.tag and tagmask) = 0) then break;
3113 if doDraw then pan.Draw(doAmbient, ambColor);
3114 gDrawPanelList.popFront();
3115 end;
3116 end
3117 else
3118 begin
3119 if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor);
3120 end;
3121 profileFrameDraw.sectionEnd();
3122 end;
3124 procedure drawOther (profname: AnsiString; cb: TDrawCB);
3125 begin
3126 profileFrameDraw.sectionBegin(profname);
3127 if assigned(cb) then cb();
3128 profileFrameDraw.sectionEnd();
3129 end;
3131 begin
3132 profileFrameDraw.sectionBegin('total');
3134 // our accelerated renderer will collect all panels to gDrawPanelList
3135 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3136 profileFrameDraw.sectionBegin('collect');
3137 if gdbg_map_use_accel_render then
3138 begin
3139 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
3140 end;
3141 profileFrameDraw.sectionEnd();
3143 profileFrameDraw.sectionBegin('skyback');
3144 g_Map_DrawBack(backXOfs, backYOfs);
3145 profileFrameDraw.sectionEnd();
3147 if setTransMatrix then
3148 begin
3149 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3150 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
3151 glTranslatef(-sX, -sY, 0);
3152 end;
3154 // rendering mode
3155 ambColor := gCurrentMap['light_ambient'].rgba;
3156 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3159 if hasAmbient then
3160 begin
3161 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3162 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3163 glClear(GL_COLOR_BUFFER_BIT);
3164 end;
3166 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3169 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
3170 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
3171 drawOther('items', @g_Items_Draw);
3172 drawOther('weapons', @g_Weapon_Draw);
3173 drawOther('shells', @g_Player_DrawShells);
3174 drawOther('drawall', @g_Player_DrawAll);
3175 drawOther('corpses', @g_Player_DrawCorpses);
3176 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
3177 drawOther('monsters', @g_Monsters_Draw);
3178 drawOther('itemdrop', @g_Items_DrawDrop);
3179 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
3180 drawOther('gfx', @g_GFX_Draw);
3181 drawOther('flags', @g_Map_DrawFlags);
3182 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
3183 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
3184 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
3185 drawOther('dynlights', @renderDynLightsInternal);
3187 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3188 begin
3189 renderAmbientQuad(hasAmbient, ambColor);
3190 end;
3192 doAmbient := true;
3193 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
3196 if g_debug_HealthBar then
3197 begin
3198 g_Monsters_DrawHealth();
3199 g_Player_DrawHealth();
3200 end;
3202 profileFrameDraw.mainEnd(); // map rendering
3203 end;
3206 procedure DrawMapView(x, y, w, h: Integer);
3208 var
3209 bx, by: Integer;
3210 begin
3211 glPushMatrix();
3213 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
3214 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
3216 sX := x;
3217 sY := y;
3218 sWidth := w;
3219 sHeight := h;
3221 fixViewportForScale();
3222 renderMapInternal(-bx, -by, true);
3224 glPopMatrix();
3225 end;
3228 procedure DrawPlayer(p: TPlayer);
3229 var
3230 px, py, a, b, c, d: Integer;
3231 //R: TRect;
3232 begin
3233 if (p = nil) or (p.FDummy) then
3234 begin
3235 glPushMatrix();
3236 g_Map_DrawBack(0, 0);
3237 glPopMatrix();
3238 Exit;
3239 end;
3241 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3242 profileFrameDraw.mainBegin(g_profile_frame_draw);
3244 gPlayerDrawn := p;
3246 glPushMatrix();
3248 px := p.GameX + PLAYER_RECT_CX;
3249 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
3251 if (g_dbg_scale = 1.0) then
3252 begin
3253 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3254 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3256 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3257 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3259 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3260 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3261 begin
3262 // hcenter
3263 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3264 end;
3266 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3267 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3268 begin
3269 // vcenter
3270 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3271 end;
3272 end
3273 else
3274 begin
3275 // scaled, ignore level bounds
3276 a := -px+(gPlayerScreenSize.X div 2);
3277 b := -py+(gPlayerScreenSize.Y div 2);
3278 end;
3280 if p.IncCam <> 0 then
3281 begin
3282 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
3283 begin
3284 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
3285 begin
3286 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
3287 end;
3288 end;
3290 if py < gPlayerScreenSize.Y div 2 then
3291 begin
3292 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
3293 begin
3294 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
3295 end;
3296 end;
3298 if p.IncCam < 0 then
3299 begin
3300 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
3301 end;
3303 if p.IncCam > 0 then
3304 begin
3305 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
3306 end;
3307 end;
3309 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
3310 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
3311 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
3313 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
3314 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
3315 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
3317 sX := -a;
3318 sY := -(b+p.IncCam);
3319 sWidth := gPlayerScreenSize.X;
3320 sHeight := gPlayerScreenSize.Y;
3322 //glTranslatef(a, b+p.IncCam, 0);
3324 //if (p = gPlayer1) and (g_dbg_scale >= 1.0) then g_Holmes_plrViewSize(sWidth, sHeight);
3326 //conwritefln('OLD: (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3327 fixViewportForScale();
3328 //conwritefln(' (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3329 p.viewPortX := sX;
3330 p.viewPortY := sY;
3331 p.viewPortW := sWidth;
3332 p.viewPortH := sHeight;
3334 if (p = gPlayer1) then
3335 begin
3336 g_Holmes_plrViewPos(sX, sY);
3337 g_Holmes_plrViewSize(sWidth, sHeight);
3338 end;
3340 renderMapInternal(-c, -d, true);
3342 if p.FSpectator then
3343 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3344 p.GameY + PLAYER_RECT_CY - 4,
3345 'X', gStdFont, 255, 255, 255, 1, True);
3347 for a := 0 to High(gCollideMap) do
3348 for b := 0 to High(gCollideMap[a]) do
3349 begin
3350 d := 0;
3351 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3352 d := d + 1;
3353 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3354 d := d + 2;
3356 case d of
3357 1: e_DrawPoint(1, b, a, 200, 200, 200);
3358 2: e_DrawPoint(1, b, a, 64, 64, 255);
3359 3: e_DrawPoint(1, b, a, 255, 0, 255);
3360 end;
3361 end;
3364 glPopMatrix();
3366 p.DrawPain();
3367 p.DrawPickup();
3368 p.DrawRulez();
3369 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3370 if g_Debug_Player then
3371 g_Player_DrawDebug(p);
3372 p.DrawGUI();
3373 end;
3375 procedure drawProfilers ();
3376 var
3377 px: Integer = -1;
3378 py: Integer = -1;
3379 begin
3380 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
3381 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3382 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3383 end;
3385 procedure g_Game_Draw();
3386 var
3387 ID: DWORD;
3388 w, h: Word;
3389 ww, hh: Byte;
3390 Time: Int64;
3391 back: string;
3392 plView1, plView2: TPlayer;
3393 Split: Boolean;
3394 begin
3395 if gExit = EXIT_QUIT then Exit;
3397 Time := GetTimer() {div 1000};
3398 FPSCounter := FPSCounter+1;
3399 if Time - FPSTime >= 1000 then
3400 begin
3401 FPS := FPSCounter;
3402 FPSCounter := 0;
3403 FPSTime := Time;
3404 end;
3406 if gGameOn or (gState = STATE_FOLD) then
3407 begin
3408 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3409 begin
3410 gSpectMode := SPECT_NONE;
3411 if not gRevertPlayers then
3412 begin
3413 plView1 := gPlayer1;
3414 plView2 := gPlayer2;
3415 end
3416 else
3417 begin
3418 plView1 := gPlayer2;
3419 plView2 := gPlayer1;
3420 end;
3421 end
3422 else
3423 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3424 begin
3425 gSpectMode := SPECT_NONE;
3426 if gPlayer2 = nil then
3427 plView1 := gPlayer1
3428 else
3429 plView1 := gPlayer2;
3430 plView2 := nil;
3431 end
3432 else
3433 begin
3434 plView1 := nil;
3435 plView2 := nil;
3436 end;
3438 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3439 gSpectMode := SPECT_STATS;
3441 if gSpectMode = SPECT_PLAYERS then
3442 if gPlayers <> nil then
3443 begin
3444 plView1 := GetActivePlayer_ByID(gSpectPID1);
3445 if plView1 = nil then
3446 begin
3447 gSpectPID1 := GetActivePlayerID_Next();
3448 plView1 := GetActivePlayer_ByID(gSpectPID1);
3449 end;
3450 if gSpectViewTwo then
3451 begin
3452 plView2 := GetActivePlayer_ByID(gSpectPID2);
3453 if plView2 = nil then
3454 begin
3455 gSpectPID2 := GetActivePlayerID_Next();
3456 plView2 := GetActivePlayer_ByID(gSpectPID2);
3457 end;
3458 end;
3459 end;
3461 if gSpectMode = SPECT_MAPVIEW then
3462 begin
3463 // Ðåæèì ïðîñìîòðà êàðòû
3464 Split := False;
3465 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3466 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3467 gHearPoint1.Active := True;
3468 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3469 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3470 gHearPoint2.Active := False;
3471 end
3472 else
3473 begin
3474 Split := (plView1 <> nil) and (plView2 <> nil);
3476 // Òî÷êè ñëóõà èãðîêîâ
3477 if plView1 <> nil then
3478 begin
3479 gHearPoint1.Active := True;
3480 gHearPoint1.Coords.X := plView1.GameX;
3481 gHearPoint1.Coords.Y := plView1.GameY;
3482 end else
3483 gHearPoint1.Active := False;
3484 if plView2 <> nil then
3485 begin
3486 gHearPoint2.Active := True;
3487 gHearPoint2.Coords.X := plView2.GameX;
3488 gHearPoint2.Coords.Y := plView2.GameY;
3489 end else
3490 gHearPoint2.Active := False;
3492 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3493 gPlayerScreenSize.X := gScreenWidth-196;
3494 if Split then
3495 begin
3496 gPlayerScreenSize.Y := gScreenHeight div 2;
3497 if gScreenHeight mod 2 = 0 then
3498 Dec(gPlayerScreenSize.Y);
3499 end
3500 else
3501 gPlayerScreenSize.Y := gScreenHeight;
3503 if Split then
3504 if gScreenHeight mod 2 = 0 then
3505 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3506 else
3507 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3509 DrawPlayer(plView1);
3510 gPlayer1ScreenCoord.X := sX;
3511 gPlayer1ScreenCoord.Y := sY;
3513 if Split then
3514 begin
3515 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3517 DrawPlayer(plView2);
3518 gPlayer2ScreenCoord.X := sX;
3519 gPlayer2ScreenCoord.Y := sY;
3520 end;
3522 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3524 if Split then
3525 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3526 end;
3528 // draw inspector
3529 if (g_holmes_enabled) then g_Holmes_Draw();
3531 if MessageText <> '' then
3532 begin
3533 w := 0;
3534 h := 0;
3535 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3536 if Split then
3537 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3538 (gScreenHeight div 2)-(h div 2), MessageText)
3539 else
3540 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3541 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3542 end;
3544 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3546 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3547 begin
3548 // Draw spectator GUI
3549 ww := 0;
3550 hh := 0;
3551 e_TextureFontGetSize(gStdFont, ww, hh);
3552 case gSpectMode of
3553 SPECT_STATS:
3554 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3555 SPECT_MAPVIEW:
3556 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3557 SPECT_PLAYERS:
3558 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3559 end;
3560 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3561 if gSpectMode = SPECT_MAPVIEW then
3562 begin
3563 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3564 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3565 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3566 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3567 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3568 end;
3569 if gSpectMode = SPECT_PLAYERS then
3570 begin
3571 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3572 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3573 if gSpectViewTwo then
3574 begin
3575 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3576 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3577 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3578 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3579 end
3580 else
3581 begin
3582 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3583 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3584 end;
3585 end;
3586 end;
3587 end;
3589 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
3590 begin
3591 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3592 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3594 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3595 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3596 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3597 end;
3599 if not gGameOn then
3600 begin
3601 if (gState = STATE_MENU) then
3602 begin
3603 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3604 begin
3605 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3606 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3607 end;
3608 // F3 at menu will show game loading dialog
3609 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3610 if (g_ActiveWindow <> nil) then
3611 begin
3612 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3613 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3614 end
3615 else
3616 begin
3617 // F3 at titlepic will show game loading dialog
3618 if e_KeyPressed(IK_F3) then
3619 begin
3620 g_Menu_Show_LoadMenu(true);
3621 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3622 end;
3623 end;
3624 end;
3626 if gState = STATE_FOLD then
3627 begin
3628 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3629 end;
3631 if gState = STATE_INTERCUSTOM then
3632 begin
3633 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3634 begin
3635 back := 'TEXTURE_endpic';
3636 if not g_Texture_Get(back, ID) then
3637 back := _lc[I_TEXTURE_ENDPIC];
3638 end
3639 else
3640 back := 'INTER';
3642 if g_Texture_Get(back, ID) then
3643 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3644 else
3645 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3647 DrawCustomStat();
3649 if g_ActiveWindow <> nil then
3650 begin
3651 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3652 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3653 end;
3654 end;
3656 if gState = STATE_INTERSINGLE then
3657 begin
3658 if EndingGameCounter > 0 then
3659 begin
3660 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3661 end
3662 else
3663 begin
3664 back := 'INTER';
3666 if g_Texture_Get(back, ID) then
3667 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3668 else
3669 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3671 DrawSingleStat();
3673 if g_ActiveWindow <> nil then
3674 begin
3675 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3676 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3677 end;
3678 end;
3679 end;
3681 if gState = STATE_ENDPIC then
3682 begin
3683 ID := DWORD(-1);
3684 if not g_Texture_Get('TEXTURE_endpic', ID) then
3685 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3687 if ID <> DWORD(-1) then
3688 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3689 else
3690 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3692 if g_ActiveWindow <> nil then
3693 begin
3694 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3695 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3696 end;
3697 end;
3699 if gState = STATE_SLIST then
3700 begin
3701 if g_Texture_Get('MENU_BACKGROUND', ID) then
3702 begin
3703 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3704 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3705 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3706 end;
3707 g_Serverlist_Draw(slCurrent);
3708 end;
3709 end;
3711 if g_ActiveWindow <> nil then
3712 begin
3713 if gGameOn then
3714 begin
3715 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3716 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3717 end;
3718 g_ActiveWindow.Draw();
3719 end;
3721 g_Console_Draw();
3723 if g_debug_Sounds and gGameOn then
3724 begin
3725 for w := 0 to High(e_SoundsArray) do
3726 for h := 0 to e_SoundsArray[w].nRefs do
3727 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3728 end;
3730 if gShowFPS then
3731 begin
3732 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3733 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3734 end;
3736 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3737 drawTime(gScreenWidth-72, gScreenHeight-16);
3739 if gGameOn then drawProfilers();
3741 g_Holmes_DrawUI();
3742 end;
3744 procedure g_Game_Quit();
3745 begin
3746 g_Game_StopAllSounds(True);
3747 gMusic.Free();
3748 g_Game_SaveOptions();
3749 g_Game_FreeData();
3750 g_PlayerModel_FreeData();
3751 g_Texture_DeleteAll();
3752 g_Frames_DeleteAll();
3753 g_Menu_Free();
3755 if NetInitDone then g_Net_Free;
3757 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3758 if gMapToDelete <> '' then
3759 g_Game_DeleteTestMap();
3761 gExit := EXIT_QUIT;
3762 PushExitEvent();
3763 end;
3765 procedure g_FatalError(Text: String);
3766 begin
3767 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3768 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning);
3770 gExit := EXIT_SIMPLE;
3771 end;
3773 procedure g_SimpleError(Text: String);
3774 begin
3775 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3776 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
3777 end;
3779 procedure g_Game_SetupScreenSize();
3780 const
3781 RES_FACTOR = 4.0 / 3.0;
3782 var
3783 s: Single;
3784 rf: Single;
3785 bw, bh: Word;
3786 begin
3787 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3788 gPlayerScreenSize.X := gScreenWidth-196;
3789 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3790 gPlayerScreenSize.Y := gScreenHeight div 2
3791 else
3792 gPlayerScreenSize.Y := gScreenHeight;
3794 // Ðàçìåð çàäíåãî ïëàíà:
3795 if BackID <> DWORD(-1) then
3796 begin
3797 s := SKY_STRETCH;
3798 if (gScreenWidth*s > gMapInfo.Width) or
3799 (gScreenHeight*s > gMapInfo.Height) then
3800 begin
3801 gBackSize.X := gScreenWidth;
3802 gBackSize.Y := gScreenHeight;
3803 end
3804 else
3805 begin
3806 e_GetTextureSize(BackID, @bw, @bh);
3807 rf := Single(bw) / Single(bh);
3808 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3809 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3810 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3811 if (s < 1.0) then s := 1.0;
3812 gBackSize.X := Round(bw*s);
3813 gBackSize.Y := Round(bh*s);
3814 end;
3815 end;
3816 end;
3818 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3819 begin
3820 g_Window_SetSize(newWidth, newHeight, nowFull);
3821 end;
3823 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3824 begin
3825 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3826 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3827 Exit;
3828 if gPlayer1 = nil then
3829 begin
3830 if g_Game_IsClient then
3831 begin
3832 if NetPlrUID1 > -1 then
3833 begin
3834 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3835 gPlayer1 := g_Player_Get(NetPlrUID1);
3836 end;
3837 Exit;
3838 end;
3840 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3841 Team := gPlayer1Settings.Team;
3843 // Ñîçäàíèå ïåðâîãî èãðîêà:
3844 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3845 gPlayer1Settings.Color,
3846 Team, False));
3847 if gPlayer1 = nil then
3848 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3849 else
3850 begin
3851 gPlayer1.Name := gPlayer1Settings.Name;
3852 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3853 if g_Game_IsServer and g_Game_IsNet then
3854 MH_SEND_PlayerCreate(gPlayer1.UID);
3855 gPlayer1.Respawn(False, True);
3857 if g_Game_IsNet and NetUseMaster then
3858 g_Net_Slist_Update;
3859 end;
3861 Exit;
3862 end;
3863 if gPlayer2 = nil then
3864 begin
3865 if g_Game_IsClient then
3866 begin
3867 if NetPlrUID2 > -1 then
3868 gPlayer2 := g_Player_Get(NetPlrUID2);
3869 Exit;
3870 end;
3872 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3873 Team := gPlayer2Settings.Team;
3875 // Ñîçäàíèå âòîðîãî èãðîêà:
3876 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3877 gPlayer2Settings.Color,
3878 Team, False));
3879 if gPlayer2 = nil then
3880 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3881 else
3882 begin
3883 gPlayer2.Name := gPlayer2Settings.Name;
3884 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3885 if g_Game_IsServer and g_Game_IsNet then
3886 MH_SEND_PlayerCreate(gPlayer2.UID);
3887 gPlayer2.Respawn(False, True);
3889 if g_Game_IsNet and NetUseMaster then
3890 g_Net_Slist_Update;
3891 end;
3893 Exit;
3894 end;
3895 end;
3897 procedure g_Game_RemovePlayer();
3898 var
3899 Pl: TPlayer;
3900 begin
3901 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3902 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3903 Exit;
3904 Pl := gPlayer2;
3905 if Pl <> nil then
3906 begin
3907 if g_Game_IsServer then
3908 begin
3909 Pl.Lives := 0;
3910 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3911 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3912 g_Player_Remove(Pl.UID);
3914 if g_Game_IsNet and NetUseMaster then
3915 g_Net_Slist_Update;
3916 end else
3917 gPlayer2 := nil;
3918 Exit;
3919 end;
3920 Pl := gPlayer1;
3921 if Pl <> nil then
3922 begin
3923 if g_Game_IsServer then
3924 begin
3925 Pl.Lives := 0;
3926 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3927 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3928 g_Player_Remove(Pl.UID);
3930 if g_Game_IsNet and NetUseMaster then
3931 g_Net_Slist_Update;
3932 end else
3933 begin
3934 gPlayer1 := nil;
3935 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3936 end;
3937 Exit;
3938 end;
3939 end;
3941 procedure g_Game_Spectate();
3942 begin
3943 g_Game_RemovePlayer();
3944 if gPlayer1 <> nil then
3945 g_Game_RemovePlayer();
3946 end;
3948 procedure g_Game_SpectateCenterView();
3949 begin
3950 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3951 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3952 end;
3954 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3955 var
3956 i, nPl: Integer;
3957 tmps: AnsiString;
3958 begin
3959 g_Game_Free();
3961 e_WriteLog('Starting singleplayer game...', TMsgType.Notify);
3963 g_Game_ClearLoading();
3965 // Íàñòðîéêè èãðû:
3966 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3967 gAimLine := False;
3968 gShowMap := False;
3969 gGameSettings.GameType := GT_SINGLE;
3970 gGameSettings.MaxLives := 0;
3971 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3972 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3973 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3974 gSwitchGameMode := GM_SINGLE;
3976 g_Game_ExecuteEvent('ongamestart');
3978 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3979 g_Game_SetupScreenSize();
3981 // Ñîçäàíèå ïåðâîãî èãðîêà:
3982 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3983 gPlayer1Settings.Color,
3984 gPlayer1Settings.Team, False));
3985 if gPlayer1 = nil then
3986 begin
3987 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3988 Exit;
3989 end;
3991 gPlayer1.Name := gPlayer1Settings.Name;
3992 nPl := 1;
3994 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3995 if TwoPlayers then
3996 begin
3997 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3998 gPlayer2Settings.Color,
3999 gPlayer2Settings.Team, False));
4000 if gPlayer2 = nil then
4001 begin
4002 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4003 Exit;
4004 end;
4006 gPlayer2.Name := gPlayer2Settings.Name;
4007 Inc(nPl);
4008 end;
4010 // Çàãðóçêà è çàïóñê êàðòû:
4011 if not g_Game_StartMap(MAP, True) then
4012 begin
4013 if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP;
4014 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps]));
4015 Exit;
4016 end;
4018 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4019 g_Player_Init();
4021 // Ñîçäàåì áîòîâ:
4022 for i := nPl+1 to nPlayers do
4023 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4024 end;
4026 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
4027 TimeLimit, GoalLimit: Word;
4028 MaxLives: Byte;
4029 Options: LongWord; nPlayers: Byte);
4030 var
4031 i, nPl: Integer;
4032 begin
4033 g_Game_Free();
4035 e_WriteLog('Starting custom game...', TMsgType.Notify);
4037 g_Game_ClearLoading();
4039 // Íàñòðîéêè èãðû:
4040 gGameSettings.GameType := GT_CUSTOM;
4041 gGameSettings.GameMode := GameMode;
4042 gSwitchGameMode := GameMode;
4043 gGameSettings.TimeLimit := TimeLimit;
4044 gGameSettings.GoalLimit := GoalLimit;
4045 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4046 gGameSettings.Options := Options;
4048 gCoopTotalMonstersKilled := 0;
4049 gCoopTotalSecretsFound := 0;
4050 gCoopTotalMonsters := 0;
4051 gCoopTotalSecrets := 0;
4052 gAimLine := False;
4053 gShowMap := False;
4055 g_Game_ExecuteEvent('ongamestart');
4057 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4058 g_Game_SetupScreenSize();
4060 // Ðåæèì íàáëþäàòåëÿ:
4061 if nPlayers = 0 then
4062 begin
4063 gPlayer1 := nil;
4064 gPlayer2 := nil;
4065 end;
4067 nPl := 0;
4068 if nPlayers >= 1 then
4069 begin
4070 // Ñîçäàíèå ïåðâîãî èãðîêà:
4071 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4072 gPlayer1Settings.Color,
4073 gPlayer1Settings.Team, False));
4074 if gPlayer1 = nil then
4075 begin
4076 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4077 Exit;
4078 end;
4080 gPlayer1.Name := gPlayer1Settings.Name;
4081 Inc(nPl);
4082 end;
4084 if nPlayers >= 2 then
4085 begin
4086 // Ñîçäàíèå âòîðîãî èãðîêà:
4087 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4088 gPlayer2Settings.Color,
4089 gPlayer2Settings.Team, False));
4090 if gPlayer2 = nil then
4091 begin
4092 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4093 Exit;
4094 end;
4096 gPlayer2.Name := gPlayer2Settings.Name;
4097 Inc(nPl);
4098 end;
4100 // Çàãðóçêà è çàïóñê êàðòû:
4101 if not g_Game_StartMap(Map, True) then
4102 begin
4103 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4104 Exit;
4105 end;
4107 // Íåò òî÷åê ïîÿâëåíèÿ:
4108 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4109 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4110 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4111 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4112 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4113 begin
4114 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4115 Exit;
4116 end;
4118 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4119 g_Player_Init();
4121 // Ñîçäàåì áîòîâ:
4122 for i := nPl+1 to nPlayers do
4123 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4124 end;
4126 procedure g_Game_StartServer(Map: String; GameMode: Byte;
4127 TimeLimit, GoalLimit: Word; MaxLives: Byte;
4128 Options: LongWord; nPlayers: Byte;
4129 IPAddr: LongWord; Port: Word);
4130 begin
4131 g_Game_Free();
4133 e_WriteLog('Starting net game (server)...', TMsgType.Notify);
4135 g_Game_ClearLoading();
4137 // Íàñòðîéêè èãðû:
4138 gGameSettings.GameType := GT_SERVER;
4139 gGameSettings.GameMode := GameMode;
4140 gSwitchGameMode := GameMode;
4141 gGameSettings.TimeLimit := TimeLimit;
4142 gGameSettings.GoalLimit := GoalLimit;
4143 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4144 gGameSettings.Options := Options;
4146 gCoopTotalMonstersKilled := 0;
4147 gCoopTotalSecretsFound := 0;
4148 gCoopTotalMonsters := 0;
4149 gCoopTotalSecrets := 0;
4150 gAimLine := False;
4151 gShowMap := False;
4153 g_Game_ExecuteEvent('ongamestart');
4155 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
4156 g_Game_SetupScreenSize();
4158 // Ðåæèì íàáëþäàòåëÿ:
4159 if nPlayers = 0 then
4160 begin
4161 gPlayer1 := nil;
4162 gPlayer2 := nil;
4163 end;
4165 if nPlayers >= 1 then
4166 begin
4167 // Ñîçäàíèå ïåðâîãî èãðîêà:
4168 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4169 gPlayer1Settings.Color,
4170 gPlayer1Settings.Team, False));
4171 if gPlayer1 = nil then
4172 begin
4173 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4174 Exit;
4175 end;
4177 gPlayer1.Name := gPlayer1Settings.Name;
4178 end;
4180 if nPlayers >= 2 then
4181 begin
4182 // Ñîçäàíèå âòîðîãî èãðîêà:
4183 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4184 gPlayer2Settings.Color,
4185 gPlayer2Settings.Team, False));
4186 if gPlayer2 = nil then
4187 begin
4188 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4189 Exit;
4190 end;
4192 gPlayer2.Name := gPlayer2Settings.Name;
4193 end;
4195 // Ñòàðòóåì ñåðâåð
4196 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
4197 begin
4198 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
4199 Exit;
4200 end;
4202 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
4204 // Çàãðóçêà è çàïóñê êàðòû:
4205 if not g_Game_StartMap(Map, True) then
4206 begin
4207 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4208 Exit;
4209 end;
4211 // Íåò òî÷åê ïîÿâëåíèÿ:
4212 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4213 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4214 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4215 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4216 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4217 begin
4218 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4219 Exit;
4220 end;
4222 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4223 g_Player_Init();
4225 NetState := NET_STATE_GAME;
4226 end;
4228 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
4229 var
4230 Map: String;
4231 WadName: string;
4232 Ptr: Pointer;
4233 T: Cardinal;
4234 MID: Byte;
4235 State: Byte;
4236 OuterLoop: Boolean;
4237 newResPath: string;
4238 InMsg: TMsg;
4239 begin
4240 g_Game_Free();
4242 State := 0;
4243 e_WriteLog('Starting net game (client)...', TMsgType.Notify);
4244 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', TMsgType.Notify);
4246 g_Game_ClearLoading();
4248 // Íàñòðîéêè èãðû:
4249 gGameSettings.GameType := GT_CLIENT;
4251 gCoopTotalMonstersKilled := 0;
4252 gCoopTotalSecretsFound := 0;
4253 gCoopTotalMonsters := 0;
4254 gCoopTotalSecrets := 0;
4255 gAimLine := False;
4256 gShowMap := False;
4258 g_Game_ExecuteEvent('ongamestart');
4260 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4261 g_Game_SetupScreenSize();
4263 NetState := NET_STATE_AUTH;
4265 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4266 // Ñòàðòóåì êëèåíò
4267 if not g_Net_Connect(Addr, Port) then
4268 begin
4269 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4270 NetState := NET_STATE_NONE;
4271 Exit;
4272 end;
4274 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4275 MC_SEND_Info(PW);
4276 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4278 OuterLoop := True;
4279 while OuterLoop do
4280 begin
4281 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4282 begin
4283 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4284 begin
4285 Ptr := NetEvent.packet^.data;
4286 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4287 continue;
4289 MID := InMsg.ReadByte();
4291 if (MID = NET_MSG_INFO) and (State = 0) then
4292 begin
4293 NetMyID := InMsg.ReadByte();
4294 NetPlrUID1 := InMsg.ReadWord();
4296 WadName := InMsg.ReadString();
4297 Map := InMsg.ReadString();
4299 gWADHash := InMsg.ReadMD5();
4301 gGameSettings.GameMode := InMsg.ReadByte();
4302 gSwitchGameMode := gGameSettings.GameMode;
4303 gGameSettings.GoalLimit := InMsg.ReadWord();
4304 gGameSettings.TimeLimit := InMsg.ReadWord();
4305 gGameSettings.MaxLives := InMsg.ReadByte();
4306 gGameSettings.Options := InMsg.ReadLongWord();
4307 T := InMsg.ReadLongWord();
4309 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4310 if newResPath = '' then
4311 begin
4312 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4313 newResPath := g_Res_DownloadWAD(WadName);
4314 if newResPath = '' then
4315 begin
4316 g_FatalError(_lc[I_NET_ERR_HASH]);
4317 enet_packet_destroy(NetEvent.packet);
4318 NetState := NET_STATE_NONE;
4319 Exit;
4320 end;
4321 end;
4322 newResPath := ExtractRelativePath(MapsDir, newResPath);
4324 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4325 gPlayer1Settings.Color,
4326 gPlayer1Settings.Team, False));
4328 if gPlayer1 = nil then
4329 begin
4330 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4332 enet_packet_destroy(NetEvent.packet);
4333 NetState := NET_STATE_NONE;
4334 Exit;
4335 end;
4337 gPlayer1.Name := gPlayer1Settings.Name;
4338 gPlayer1.UID := NetPlrUID1;
4339 gPlayer1.Reset(True);
4341 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4342 begin
4343 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4345 enet_packet_destroy(NetEvent.packet);
4346 NetState := NET_STATE_NONE;
4347 Exit;
4348 end;
4350 gTime := T;
4352 State := 1;
4353 OuterLoop := False;
4354 enet_packet_destroy(NetEvent.packet);
4355 break;
4356 end
4357 else
4358 enet_packet_destroy(NetEvent.packet);
4359 end
4360 else
4361 begin
4362 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4363 begin
4364 State := 0;
4365 if (NetEvent.data <= NET_DISC_MAX) then
4366 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4367 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4368 OuterLoop := False;
4369 Break;
4370 end;
4371 end;
4372 end;
4374 ProcessLoading(true);
4376 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
4377 begin
4378 State := 0;
4379 break;
4380 end;
4381 end;
4383 if State <> 1 then
4384 begin
4385 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4386 NetState := NET_STATE_NONE;
4387 Exit;
4388 end;
4390 gLMSRespawn := LMS_RESPAWN_NONE;
4391 gLMSRespawnTime := 0;
4393 g_Player_Init();
4394 NetState := NET_STATE_GAME;
4395 MC_SEND_FullStateRequest;
4396 e_WriteLog('NET: Connection successful.', TMsgType.Notify);
4397 end;
4399 procedure g_Game_SaveOptions();
4400 begin
4401 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4402 end;
4404 procedure g_Game_ChangeMap(const MapPath: String);
4405 var
4406 Force: Boolean;
4407 begin
4408 g_Game_ClearLoading();
4410 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4411 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4412 if gExitByTrigger then
4413 begin
4414 Force := False;
4415 gExitByTrigger := False;
4416 end;
4417 if not g_Game_StartMap(MapPath, Force) then
4418 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4419 end;
4421 procedure g_Game_Restart();
4422 var
4423 Map: string;
4424 begin
4425 if g_Game_IsClient then
4426 Exit;
4427 map := g_ExtractFileName(gMapInfo.Map);
4429 MessageTime := 0;
4430 gGameOn := False;
4431 g_Game_ClearLoading();
4432 g_Game_StartMap(Map, True, gCurrentMapFileName);
4433 end;
4435 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4436 var
4437 NewWAD, ResName: String;
4438 I: Integer;
4439 begin
4440 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4441 g_Player_RemoveAllCorpses();
4443 if (not g_Game_IsClient) and
4444 (gSwitchGameMode <> gGameSettings.GameMode) and
4445 (gGameSettings.GameMode <> GM_SINGLE) then
4446 begin
4447 if gSwitchGameMode = GM_CTF then
4448 gGameSettings.MaxLives := 0;
4449 gGameSettings.GameMode := gSwitchGameMode;
4450 Force := True;
4451 end else
4452 gSwitchGameMode := gGameSettings.GameMode;
4454 g_Player_ResetTeams();
4456 if isWadPath(Map) then
4457 begin
4458 NewWAD := g_ExtractWadName(Map);
4459 ResName := g_ExtractFileName(Map);
4460 if g_Game_IsServer then
4461 begin
4462 gWADHash := MD5File(MapsDir + NewWAD);
4463 g_Game_LoadWAD(NewWAD);
4464 end else
4465 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4466 g_Game_ClientWAD(NewWAD, gWADHash);
4467 end else
4468 ResName := Map;
4470 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4471 if Result then
4472 begin
4473 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4475 gState := STATE_NONE;
4476 g_ActiveWindow := nil;
4477 gGameOn := True;
4479 DisableCheats();
4480 ResetTimer();
4482 if gGameSettings.GameMode = GM_CTF then
4483 begin
4484 g_Map_ResetFlag(FLAG_RED);
4485 g_Map_ResetFlag(FLAG_BLUE);
4486 // CTF, à ôëàãîâ íåò:
4487 if not g_Map_HaveFlagPoints() then
4488 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4489 end;
4490 end
4491 else
4492 begin
4493 gState := STATE_MENU;
4494 gGameOn := False;
4495 end;
4497 gExit := 0;
4498 gPauseMain := false;
4499 gPauseHolmes := false;
4500 gTime := 0;
4501 NetTimeToUpdate := 1;
4502 NetTimeToReliable := 0;
4503 NetTimeToMaster := NetMasterRate;
4504 gLMSRespawn := LMS_RESPAWN_NONE;
4505 gLMSRespawnTime := 0;
4506 gMissionFailed := False;
4507 gNextMap := '';
4509 gCoopMonstersKilled := 0;
4510 gCoopSecretsFound := 0;
4512 gVoteInProgress := False;
4513 gVotePassed := False;
4514 gVoteCount := 0;
4515 gVoted := False;
4517 gStatsOff := False;
4519 if not gGameOn then Exit;
4521 g_Game_SpectateCenterView();
4523 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4524 begin
4525 gLMSRespawn := LMS_RESPAWN_WARMUP;
4526 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4527 gLMSSoftSpawn := True;
4528 if NetMode = NET_SERVER then
4529 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4530 else
4531 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4532 end;
4534 if NetMode = NET_SERVER then
4535 begin
4536 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4538 // Ìàñòåðñåðâåð
4539 if NetUseMaster then
4540 begin
4541 if (NetMHost = nil) or (NetMPeer = nil) then
4542 if not g_Net_Slist_Connect then
4543 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4545 g_Net_Slist_Update;
4546 end;
4548 if NetClients <> nil then
4549 for I := 0 to High(NetClients) do
4550 if NetClients[I].Used then
4551 begin
4552 NetClients[I].Voted := False;
4553 if NetClients[I].RequestedFullUpdate then
4554 begin
4555 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4556 NetClients[I].RequestedFullUpdate := False;
4557 end;
4558 end;
4560 g_Net_UnbanNonPermHosts();
4561 end;
4563 if gLastMap then
4564 begin
4565 gCoopTotalMonstersKilled := 0;
4566 gCoopTotalSecretsFound := 0;
4567 gCoopTotalMonsters := 0;
4568 gCoopTotalSecrets := 0;
4569 gLastMap := False;
4570 end;
4572 g_Game_ExecuteEvent('onmapstart');
4573 end;
4575 procedure SetFirstLevel();
4576 begin
4577 gNextMap := '';
4579 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4580 if MapList = nil then
4581 Exit;
4583 SortSArray(MapList);
4584 gNextMap := MapList[Low(MapList)];
4586 MapList := nil;
4587 end;
4589 procedure g_Game_ExitLevel(const Map: AnsiString);
4590 begin
4591 gNextMap := Map;
4593 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4594 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4595 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4596 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4598 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4599 if gGameSettings.GameType = GT_SINGLE then
4600 gExit := EXIT_ENDLEVELSINGLE
4601 else // Âûøëè â âûõîä â Ñâîåé èãðå
4602 begin
4603 gExit := EXIT_ENDLEVELCUSTOM;
4604 if gGameSettings.GameMode = GM_COOP then
4605 g_Player_RememberAll;
4607 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4608 begin
4609 gLastMap := True;
4610 if gGameSettings.GameMode = GM_COOP then
4611 gStatsOff := True;
4613 gStatsPressed := True;
4614 gNextMap := 'MAP01';
4616 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4617 g_Game_NextLevel;
4619 if g_Game_IsNet then
4620 begin
4621 MH_SEND_GameStats();
4622 MH_SEND_CoopStats();
4623 end;
4624 end;
4625 end;
4626 end;
4628 procedure g_Game_RestartLevel();
4629 var
4630 Map: string;
4631 begin
4632 if gGameSettings.GameMode = GM_SINGLE then
4633 begin
4634 g_Game_Restart();
4635 Exit;
4636 end;
4637 gExit := EXIT_ENDLEVELCUSTOM;
4638 Map := g_ExtractFileName(gMapInfo.Map);
4639 gNextMap := Map;
4640 end;
4642 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4643 var
4644 gWAD: String;
4645 begin
4646 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4647 Exit;
4648 if not g_Game_IsClient then
4649 Exit;
4650 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4651 if gWAD = '' then
4652 begin
4653 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4654 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4655 if gWAD = '' then
4656 begin
4657 g_Game_Free();
4658 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4659 Exit;
4660 end;
4661 end;
4662 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4663 g_Game_LoadWAD(NewWAD);
4664 end;
4666 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4667 var
4668 i, n, nb, nr: Integer;
4670 function monRespawn (mon: TMonster): Boolean;
4671 begin
4672 result := false; // don't stop
4673 if not mon.FNoRespawn then mon.Respawn();
4674 end;
4676 begin
4677 if not g_Game_IsServer then Exit;
4678 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4679 gLMSRespawn := LMS_RESPAWN_NONE;
4680 gLMSRespawnTime := 0;
4681 MessageTime := 0;
4683 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4684 begin
4685 gMissionFailed := True;
4686 g_Game_RestartLevel;
4687 Exit;
4688 end;
4690 n := 0; nb := 0; nr := 0;
4691 for i := Low(gPlayers) to High(gPlayers) do
4692 if (gPlayers[i] <> nil) and
4693 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4694 (gPlayers[i] is TBot)) then
4695 begin
4696 Inc(n);
4697 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4698 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4699 end;
4701 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4702 begin
4703 // wait a second until the fuckers finally decide to join
4704 gLMSRespawn := LMS_RESPAWN_WARMUP;
4705 gLMSRespawnTime := gTime + 1000;
4706 gLMSSoftSpawn := NoMapRestart;
4707 Exit;
4708 end;
4710 g_Player_RemoveAllCorpses;
4711 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4712 if g_Game_IsNet then
4713 MH_SEND_GameEvent(NET_EV_LMS_START);
4715 for i := Low(gPlayers) to High(gPlayers) do
4716 begin
4717 if gPlayers[i] = nil then continue;
4718 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4719 // don't touch normal spectators
4720 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4721 begin
4722 gPlayers[i].FNoRespawn := True;
4723 gPlayers[i].Lives := 0;
4724 if g_Game_IsNet then
4725 MH_SEND_PlayerStats(gPlayers[I].UID);
4726 continue;
4727 end;
4728 gPlayers[i].FNoRespawn := False;
4729 gPlayers[i].Lives := gGameSettings.MaxLives;
4730 gPlayers[i].Respawn(False, True);
4731 if gGameSettings.GameMode = GM_COOP then
4732 begin
4733 gPlayers[i].Frags := 0;
4734 gPlayers[i].RecallState;
4735 end;
4736 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4737 gPlayer1 := g_Player_Get(gLMSPID1);
4738 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4739 gPlayer2 := g_Player_Get(gLMSPID2);
4740 end;
4742 g_Items_RestartRound();
4745 g_Mons_ForEach(monRespawn);
4747 gLMSSoftSpawn := False;
4748 end;
4750 function g_Game_GetFirstMap(WAD: String): String;
4751 begin
4752 Result := '';
4754 MapList := g_Map_GetMapsList(WAD);
4755 if MapList = nil then
4756 Exit;
4758 SortSArray(MapList);
4759 Result := MapList[Low(MapList)];
4761 if not g_Map_Exist(WAD + ':\' + Result) then
4762 Result := '';
4764 MapList := nil;
4765 end;
4767 function g_Game_GetNextMap(): String;
4768 var
4769 I: Integer;
4770 Map: string;
4771 begin
4772 Result := '';
4774 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4775 if MapList = nil then
4776 Exit;
4778 Map := g_ExtractFileName(gMapInfo.Map);
4780 SortSArray(MapList);
4781 MapIndex := -255;
4782 for I := Low(MapList) to High(MapList) do
4783 if Map = MapList[I] then
4784 begin
4785 MapIndex := I;
4786 Break;
4787 end;
4789 if MapIndex <> -255 then
4790 begin
4791 if MapIndex = High(MapList) then
4792 Result := MapList[Low(MapList)]
4793 else
4794 Result := MapList[MapIndex + 1];
4796 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4797 end;
4799 MapList := nil;
4800 end;
4802 procedure g_Game_NextLevel();
4803 begin
4804 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4805 gExit := EXIT_ENDLEVELCUSTOM
4806 else
4807 begin
4808 gExit := EXIT_ENDLEVELSINGLE;
4809 Exit;
4810 end;
4812 if gNextMap <> '' then Exit;
4813 gNextMap := g_Game_GetNextMap();
4814 end;
4816 function g_Game_IsTestMap(): Boolean;
4817 begin
4818 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4819 end;
4821 procedure g_Game_DeleteTestMap();
4822 var
4823 a: Integer;
4824 //MapName: AnsiString;
4825 WadName: string;
4827 WAD: TWADFile;
4828 MapList: SSArray;
4829 time: Integer;
4831 begin
4832 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
4833 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
4834 if (a = 0) then exit;
4836 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
4837 WadName := Copy(gMapToDelete, 1, a+3);
4838 Delete(gMapToDelete, 1, a+5);
4839 gMapToDelete := UpperCase(gMapToDelete);
4840 //MapName := '';
4841 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4844 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4845 if MapName <> TEST_MAP_NAME then
4846 Exit;
4848 if not gTempDelete then
4849 begin
4850 time := g_GetFileTime(WadName);
4851 WAD := TWADFile.Create();
4853 // ×èòàåì Wad-ôàéë:
4854 if not WAD.ReadFile(WadName) then
4855 begin // Íåò òàêîãî WAD-ôàéëà
4856 WAD.Free();
4857 Exit;
4858 end;
4860 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4861 WAD.CreateImage();
4862 MapList := WAD.GetResourcesList('');
4864 if MapList <> nil then
4865 for a := 0 to High(MapList) do
4866 if MapList[a] = MapName then
4867 begin
4868 // Óäàëÿåì è ñîõðàíÿåì:
4869 WAD.RemoveResource('', MapName);
4870 WAD.SaveTo(WadName);
4871 Break;
4872 end;
4874 WAD.Free();
4875 g_SetFileTime(WadName, time);
4876 end else
4878 if gTempDelete then DeleteFile(WadName);
4879 end;
4881 procedure GameCVars(P: SSArray);
4882 var
4883 a, b: Integer;
4884 stat: TPlayerStatArray;
4885 cmd, s: string;
4886 config: TConfig;
4887 begin
4888 stat := nil;
4889 cmd := LowerCase(P[0]);
4890 if cmd = 'r_showfps' then
4891 begin
4892 if (Length(P) > 1) and
4893 ((P[1] = '1') or (P[1] = '0')) then
4894 gShowFPS := (P[1][1] = '1');
4896 if gShowFPS then
4897 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4898 else
4899 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4900 end
4901 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4902 begin
4903 with gGameSettings do
4904 begin
4905 if (Length(P) > 1) and
4906 ((P[1] = '1') or (P[1] = '0')) then
4907 begin
4908 if (P[1][1] = '1') then
4909 Options := Options or GAME_OPTION_TEAMDAMAGE
4910 else
4911 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4912 end;
4914 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4915 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4916 else
4917 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4919 if g_Game_IsNet then MH_SEND_GameSettings;
4920 end;
4921 end
4922 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4923 begin
4924 with gGameSettings do
4925 begin
4926 if (Length(P) > 1) and
4927 ((P[1] = '1') or (P[1] = '0')) then
4928 begin
4929 if (P[1][1] = '1') then
4930 Options := Options or GAME_OPTION_WEAPONSTAY
4931 else
4932 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4933 end;
4935 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4936 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4937 else
4938 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4940 if g_Game_IsNet then MH_SEND_GameSettings;
4941 end;
4942 end
4943 else if cmd = 'g_gamemode' then
4944 begin
4945 a := g_Game_TextToMode(P[1]);
4946 if a = GM_SINGLE then a := GM_COOP;
4947 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4948 begin
4949 gSwitchGameMode := a;
4950 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4951 (gState = STATE_INTERSINGLE) then
4952 gSwitchGameMode := GM_SINGLE;
4953 if not gGameOn then
4954 gGameSettings.GameMode := gSwitchGameMode;
4955 end;
4956 if gSwitchGameMode = gGameSettings.GameMode then
4957 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4958 [g_Game_ModeToText(gGameSettings.GameMode)]))
4959 else
4960 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4961 [g_Game_ModeToText(gGameSettings.GameMode),
4962 g_Game_ModeToText(gSwitchGameMode)]));
4963 end
4964 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4965 begin
4966 with gGameSettings do
4967 begin
4968 if (Length(P) > 1) and
4969 ((P[1] = '1') or (P[1] = '0')) then
4970 begin
4971 if (P[1][1] = '1') then
4972 Options := Options or GAME_OPTION_ALLOWEXIT
4973 else
4974 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4975 end;
4977 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4978 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4979 else
4980 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4981 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4983 if g_Game_IsNet then MH_SEND_GameSettings;
4984 end;
4985 end
4986 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4987 begin
4988 with gGameSettings do
4989 begin
4990 if (Length(P) > 1) and
4991 ((P[1] = '1') or (P[1] = '0')) then
4992 begin
4993 if (P[1][1] = '1') then
4994 Options := Options or GAME_OPTION_MONSTERS
4995 else
4996 Options := Options and (not GAME_OPTION_MONSTERS);
4997 end;
4999 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
5000 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
5001 else
5002 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
5003 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5005 if g_Game_IsNet then MH_SEND_GameSettings;
5006 end;
5007 end
5008 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
5009 begin
5010 with gGameSettings do
5011 begin
5012 if (Length(P) > 1) and
5013 ((P[1] = '1') or (P[1] = '0')) then
5014 begin
5015 if (P[1][1] = '1') then
5016 Options := Options or GAME_OPTION_BOTVSPLAYER
5017 else
5018 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
5019 end;
5021 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
5022 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
5023 else
5024 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
5026 if g_Game_IsNet then MH_SEND_GameSettings;
5027 end;
5028 end
5029 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
5030 begin
5031 with gGameSettings do
5032 begin
5033 if (Length(P) > 1) and
5034 ((P[1] = '1') or (P[1] = '0')) then
5035 begin
5036 if (P[1][1] = '1') then
5037 Options := Options or GAME_OPTION_BOTVSMONSTER
5038 else
5039 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
5040 end;
5042 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
5043 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
5044 else
5045 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
5047 if g_Game_IsNet then MH_SEND_GameSettings;
5048 end;
5049 end
5050 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
5051 begin
5052 if Length(P) > 1 then
5053 begin
5054 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
5055 gGameSettings.WarmupTime := 30
5056 else
5057 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
5058 end;
5060 g_Console_Add(Format(_lc[I_MSG_WARMUP],
5061 [gGameSettings.WarmupTime]));
5062 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5063 end
5064 else if cmd = 'net_interp' then
5065 begin
5066 if (Length(P) > 1) then
5067 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
5069 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
5070 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5071 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
5072 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5073 config.Free();
5074 end
5075 else if cmd = 'net_forceplayerupdate' then
5076 begin
5077 if (Length(P) > 1) and
5078 ((P[1] = '1') or (P[1] = '0')) then
5079 NetForcePlayerUpdate := (P[1][1] = '1');
5081 if NetForcePlayerUpdate then
5082 g_Console_Add('net_forceplayerupdate = 1')
5083 else
5084 g_Console_Add('net_forceplayerupdate = 0');
5085 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5086 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
5087 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5088 config.Free();
5089 end
5090 else if cmd = 'net_predictself' then
5091 begin
5092 if (Length(P) > 1) and
5093 ((P[1] = '1') or (P[1] = '0')) then
5094 NetPredictSelf := (P[1][1] = '1');
5096 if NetPredictSelf then
5097 g_Console_Add('net_predictself = 1')
5098 else
5099 g_Console_Add('net_predictself = 0');
5100 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5101 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
5102 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5103 config.Free();
5104 end
5105 else if cmd = 'sv_name' then
5106 begin
5107 if (Length(P) > 1) and (Length(P[1]) > 0) then
5108 begin
5109 NetServerName := P[1];
5110 if Length(NetServerName) > 64 then
5111 SetLength(NetServerName, 64);
5112 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5113 g_Net_Slist_Update;
5114 end;
5116 g_Console_Add(cmd + ' = "' + NetServerName + '"');
5117 end
5118 else if cmd = 'sv_passwd' then
5119 begin
5120 if (Length(P) > 1) and (Length(P[1]) > 0) then
5121 begin
5122 NetPassword := P[1];
5123 if Length(NetPassword) > 24 then
5124 SetLength(NetPassword, 24);
5125 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5126 g_Net_Slist_Update;
5127 end;
5129 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
5130 end
5131 else if cmd = 'sv_maxplrs' then
5132 begin
5133 if (Length(P) > 1) then
5134 begin
5135 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
5136 if g_Game_IsServer and g_Game_IsNet then
5137 begin
5138 b := 0;
5139 for a := 0 to High(NetClients) do
5140 if NetClients[a].Used then
5141 begin
5142 Inc(b);
5143 if b > NetMaxClients then
5144 begin
5145 s := g_Player_Get(NetClients[a].Player).Name;
5146 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
5147 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5148 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5149 end;
5150 end;
5151 if NetUseMaster then
5152 g_Net_Slist_Update;
5153 end;
5154 end;
5156 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
5157 end
5158 else if cmd = 'sv_public' then
5159 begin
5160 if (Length(P) > 1) then
5161 begin
5162 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
5163 if g_Game_IsServer and g_Game_IsNet then
5164 if NetUseMaster then
5165 begin
5166 if NetMPeer = nil then
5167 if not g_Net_Slist_Connect() then
5168 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
5169 g_Net_Slist_Update();
5170 end
5171 else
5172 if NetMPeer <> nil then
5173 g_Net_Slist_Disconnect();
5174 end;
5176 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
5177 end
5178 else if cmd = 'sv_intertime' then
5179 begin
5180 if (Length(P) > 1) then
5181 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
5183 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
5184 end
5185 else if cmd = 'p1_name' then
5186 begin
5187 if (Length(P) > 1) and gGameOn then
5188 begin
5189 if g_Game_IsClient then
5190 begin
5191 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5192 MC_SEND_PlayerSettings;
5193 end
5194 else
5195 if gPlayer1 <> nil then
5196 begin
5197 gPlayer1.Name := b_Text_Unformat(P[1]);
5198 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5199 end
5200 else
5201 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5202 end;
5203 end
5204 else if cmd = 'p2_name' then
5205 begin
5206 if (Length(P) > 1) and gGameOn then
5207 begin
5208 if g_Game_IsClient then
5209 begin
5210 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5211 MC_SEND_PlayerSettings;
5212 end
5213 else
5214 if gPlayer2 <> nil then
5215 begin
5216 gPlayer2.Name := b_Text_Unformat(P[1]);
5217 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5218 end
5219 else
5220 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5221 end;
5222 end
5223 else if cmd = 'p1_color' then
5224 begin
5225 if Length(P) > 3 then
5226 if g_Game_IsClient then
5227 begin
5228 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5229 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5230 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5231 MC_SEND_PlayerSettings;
5232 end
5233 else
5234 if gPlayer1 <> nil then
5235 begin
5236 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5237 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5238 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5239 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5240 end
5241 else
5242 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5243 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5244 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5245 end
5246 else if (cmd = 'p2_color') and not g_Game_IsNet then
5247 begin
5248 if Length(P) > 3 then
5249 if g_Game_IsClient then
5250 begin
5251 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5252 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5253 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5254 MC_SEND_PlayerSettings;
5255 end
5256 else
5257 if gPlayer2 <> nil then
5258 begin
5259 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5260 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5261 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5262 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5263 end
5264 else
5265 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5266 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5267 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5268 end
5269 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5270 begin
5271 if cmd = 'r_showtime' then
5272 begin
5273 if (Length(P) > 1) and
5274 ((P[1] = '1') or (P[1] = '0')) then
5275 gShowTime := (P[1][1] = '1');
5277 if gShowTime then
5278 g_Console_Add(_lc[I_MSG_TIME_ON])
5279 else
5280 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5281 end
5282 else if cmd = 'r_showscore' then
5283 begin
5284 if (Length(P) > 1) and
5285 ((P[1] = '1') or (P[1] = '0')) then
5286 gShowGoals := (P[1][1] = '1');
5288 if gShowGoals then
5289 g_Console_Add(_lc[I_MSG_SCORE_ON])
5290 else
5291 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5292 end
5293 else if cmd = 'r_showstat' then
5294 begin
5295 if (Length(P) > 1) and
5296 ((P[1] = '1') or (P[1] = '0')) then
5297 gShowStat := (P[1][1] = '1');
5299 if gShowStat then
5300 g_Console_Add(_lc[I_MSG_STATS_ON])
5301 else
5302 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5303 end
5304 else if cmd = 'r_showkillmsg' then
5305 begin
5306 if (Length(P) > 1) and
5307 ((P[1] = '1') or (P[1] = '0')) then
5308 gShowKillMsg := (P[1][1] = '1');
5310 if gShowKillMsg then
5311 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5312 else
5313 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5314 end
5315 else if cmd = 'r_showlives' then
5316 begin
5317 if (Length(P) > 1) and
5318 ((P[1] = '1') or (P[1] = '0')) then
5319 gShowLives := (P[1][1] = '1');
5321 if gShowLives then
5322 g_Console_Add(_lc[I_MSG_LIVES_ON])
5323 else
5324 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5325 end
5326 else if cmd = 'r_showspect' then
5327 begin
5328 if (Length(P) > 1) and
5329 ((P[1] = '1') or (P[1] = '0')) then
5330 gSpectHUD := (P[1][1] = '1');
5332 if gSpectHUD then
5333 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5334 else
5335 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5336 end
5337 else if cmd = 'r_showping' then
5338 begin
5339 if (Length(P) > 1) and
5340 ((P[1] = '1') or (P[1] = '0')) then
5341 gShowPing := (P[1][1] = '1');
5343 if gShowPing then
5344 g_Console_Add(_lc[I_MSG_PING_ON])
5345 else
5346 g_Console_Add(_lc[I_MSG_PING_OFF]);
5347 end
5348 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5349 begin
5350 if Length(P) > 1 then
5351 begin
5352 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5353 gGameSettings.GoalLimit := 0
5354 else
5355 begin
5356 b := 0;
5358 if gGameSettings.GameMode = GM_DM then
5359 begin // DM
5360 stat := g_Player_GetStats();
5361 if stat <> nil then
5362 for a := 0 to High(stat) do
5363 if stat[a].Frags > b then
5364 b := stat[a].Frags;
5365 end
5366 else // TDM/CTF
5367 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5369 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5370 end;
5372 if g_Game_IsNet then MH_SEND_GameSettings;
5373 end;
5375 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5376 end
5377 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5378 begin
5379 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5380 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5382 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5383 [gGameSettings.TimeLimit div 3600,
5384 (gGameSettings.TimeLimit div 60) mod 60,
5385 gGameSettings.TimeLimit mod 60]));
5386 if g_Game_IsNet then MH_SEND_GameSettings;
5387 end
5388 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5389 begin
5390 if Length(P) > 1 then
5391 begin
5392 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5393 gGameSettings.MaxLives := 0
5394 else
5395 begin
5396 b := 0;
5397 stat := g_Player_GetStats();
5398 if stat <> nil then
5399 for a := 0 to High(stat) do
5400 if stat[a].Lives > b then
5401 b := stat[a].Lives;
5402 gGameSettings.MaxLives :=
5403 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5404 end;
5405 end;
5407 g_Console_Add(Format(_lc[I_MSG_LIVES],
5408 [gGameSettings.MaxLives]));
5409 if g_Game_IsNet then MH_SEND_GameSettings;
5410 end;
5411 end;
5412 end;
5414 procedure PrintHeapStats();
5415 var
5416 hs: TFPCHeapStatus;
5417 begin
5418 hs := GetFPCHeapStatus();
5419 e_LogWriteLn ('v===== heap status =====v');
5420 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
5421 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
5422 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
5423 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
5424 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
5425 e_LogWriteLn ('^=======================^');
5426 end;
5428 procedure DebugCommands(P: SSArray);
5429 var
5430 a, b: Integer;
5431 cmd: string;
5432 //pt: TDFPoint;
5433 mon: TMonster;
5434 begin
5435 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5436 if {gDebugMode}conIsCheatsEnabled then
5437 begin
5438 cmd := LowerCase(P[0]);
5439 if cmd = 'd_window' then
5440 begin
5441 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5442 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5443 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5444 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5445 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5446 end
5447 else if cmd = 'd_sounds' then
5448 begin
5449 if (Length(P) > 1) and
5450 ((P[1] = '1') or (P[1] = '0')) then
5451 g_Debug_Sounds := (P[1][1] = '1');
5453 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5454 end
5455 else if cmd = 'd_frames' then
5456 begin
5457 if (Length(P) > 1) and
5458 ((P[1] = '1') or (P[1] = '0')) then
5459 g_Debug_Frames := (P[1][1] = '1');
5461 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5462 end
5463 else if cmd = 'd_winmsg' then
5464 begin
5465 if (Length(P) > 1) and
5466 ((P[1] = '1') or (P[1] = '0')) then
5467 g_Debug_WinMsgs := (P[1][1] = '1');
5469 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5470 end
5471 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5472 begin
5473 if (Length(P) > 1) and
5474 ((P[1] = '1') or (P[1] = '0')) then
5475 g_Debug_MonsterOff := (P[1][1] = '1');
5477 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5478 end
5479 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5480 begin
5481 if Length(P) > 1 then
5482 case P[1][1] of
5483 '0': g_debug_BotAIOff := 0;
5484 '1': g_debug_BotAIOff := 1;
5485 '2': g_debug_BotAIOff := 2;
5486 '3': g_debug_BotAIOff := 3;
5487 end;
5489 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5490 end
5491 else if cmd = 'd_monster' then
5492 begin
5493 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5494 if Length(P) < 2 then
5495 begin
5496 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5497 g_Console_Add('ID | Name');
5498 for b := MONSTER_DEMON to MONSTER_MAN do
5499 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5500 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
5501 end else
5502 begin
5503 a := StrToIntDef(P[1], 0);
5504 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5505 a := g_Mons_TypeIdByName(P[1]);
5507 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5508 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5509 else
5510 begin
5511 with gPlayer1.Obj do
5512 begin
5513 mon := g_Monsters_Create(a,
5514 X + Rect.X + (Rect.Width div 2),
5515 Y + Rect.Y + Rect.Height,
5516 gPlayer1.Direction, True);
5517 end;
5518 if (Length(P) > 2) and (mon <> nil) then
5519 begin
5520 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
5521 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
5522 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
5523 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
5524 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
5525 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
5526 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
5527 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
5528 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5529 end;
5530 end;
5531 end;
5532 end
5533 else if (cmd = 'd_health') then
5534 begin
5535 if (Length(P) > 1) and
5536 ((P[1] = '1') or (P[1] = '0')) then
5537 g_debug_HealthBar := (P[1][1] = '1');
5539 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5540 end
5541 else if (cmd = 'd_player') then
5542 begin
5543 if (Length(P) > 1) and
5544 ((P[1] = '1') or (P[1] = '0')) then
5545 g_debug_Player := (P[1][1] = '1');
5547 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5548 end
5549 else if (cmd = 'd_joy') then
5550 begin
5551 for a := 1 to 8 do
5552 g_Console_Add(e_JoystickStateToString(a));
5553 end
5554 else if (cmd = 'd_mem') then
5555 begin
5556 PrintHeapStats();
5557 end;
5558 end
5559 else
5560 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5561 end;
5564 procedure GameCheats(P: SSArray);
5565 var
5566 cmd: string;
5567 f, a: Integer;
5568 plr: TPlayer;
5569 begin
5570 if (not gGameOn) or (not conIsCheatsEnabled) then
5571 begin
5572 g_Console_Add('not available');
5573 exit;
5574 end;
5575 plr := gPlayer1;
5576 if plr = nil then
5577 begin
5578 g_Console_Add('where is the player?!');
5579 exit;
5580 end;
5581 cmd := LowerCase(P[0]);
5582 // god
5583 if cmd = 'god' then
5584 begin
5585 plr.GodMode := not plr.GodMode;
5586 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5587 exit;
5588 end;
5589 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5590 if cmd = 'give' then
5591 begin
5592 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5593 for f := 1 to High(P) do
5594 begin
5595 cmd := LowerCase(P[f]);
5596 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5597 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5598 if cmd = 'exit' then
5599 begin
5600 if gTriggers <> nil then
5601 begin
5602 for a := 0 to High(gTriggers) do
5603 begin
5604 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5605 begin
5606 g_Console_Add('player left the map');
5607 gExitByTrigger := True;
5608 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5609 g_Game_ExitLevel(gTriggers[a].tgcMap);
5610 break;
5611 end;
5612 end;
5613 end;
5614 continue;
5615 end;
5617 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5618 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5619 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5620 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5621 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5623 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5624 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5626 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5627 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;
5629 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5630 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5632 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5633 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5635 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5636 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5638 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5639 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5640 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5642 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5643 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5644 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5645 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;
5646 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5647 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5649 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;
5650 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;
5651 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;
5652 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;
5653 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;
5654 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;
5656 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5657 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;
5659 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;
5660 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;
5662 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5664 if cmd = 'ammo' then
5665 begin
5666 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5667 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5668 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5669 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5670 plr.GiveItem(ITEM_AMMO_FUELCAN);
5671 g_Console_Add('player got some ammo');
5672 continue;
5673 end;
5675 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5676 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5678 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5679 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5681 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5682 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5684 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5685 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5687 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5689 if cmd = 'weapons' then
5690 begin
5691 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5692 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5693 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5694 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5695 plr.GiveItem(ITEM_WEAPON_PLASMA);
5696 plr.GiveItem(ITEM_WEAPON_BFG);
5697 g_Console_Add('player got weapons');
5698 continue;
5699 end;
5701 if cmd = 'keys' then
5702 begin
5703 plr.GiveItem(ITEM_KEY_RED);
5704 plr.GiveItem(ITEM_KEY_GREEN);
5705 plr.GiveItem(ITEM_KEY_BLUE);
5706 g_Console_Add('player got all keys');
5707 continue;
5708 end;
5710 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5711 end;
5712 exit;
5713 end;
5714 // open
5715 if cmd = 'open' then
5716 begin
5717 g_Console_Add('player activated sesame');
5718 g_Triggers_OpenAll();
5719 exit;
5720 end;
5721 // fly
5722 if cmd = 'fly' then
5723 begin
5724 gFly := not gFly;
5725 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5726 exit;
5727 end;
5728 // noclip
5729 if cmd = 'noclip' then
5730 begin
5731 plr.SwitchNoClip;
5732 g_Console_Add('wall hardeness adjusted');
5733 exit;
5734 end;
5735 // notarget
5736 if cmd = 'notarget' then
5737 begin
5738 plr.NoTarget := not plr.NoTarget;
5739 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5740 exit;
5741 end;
5742 // noreload
5743 if cmd = 'noreload' then
5744 begin
5745 plr.NoReload := not plr.NoReload;
5746 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5747 exit;
5748 end;
5749 // speedy
5750 if cmd = 'speedy' then
5751 begin
5752 MAX_RUNVEL := 32-MAX_RUNVEL;
5753 g_Console_Add('speed adjusted');
5754 exit;
5755 end;
5756 // jumpy
5757 if cmd = 'jumpy' then
5758 begin
5759 VEL_JUMP := 30-VEL_JUMP;
5760 g_Console_Add('jump height adjusted');
5761 exit;
5762 end;
5763 // automap
5764 if cmd = 'automap' then
5765 begin
5766 gShowMap := not gShowMap;
5767 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5768 exit;
5769 end;
5770 // aimline
5771 if cmd = 'aimline' then
5772 begin
5773 gAimLine := not gAimLine;
5774 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5775 exit;
5776 end;
5777 end;
5779 procedure GameCommands(P: SSArray);
5780 var
5781 a, b: Integer;
5782 s, pw: String;
5783 chstr: string;
5784 cmd: string;
5785 pl: pTNetClient = nil;
5786 plr: TPlayer;
5787 prt: Word;
5788 nm: Boolean;
5789 listen: LongWord;
5790 begin
5791 // Îáùèå êîìàíäû:
5792 cmd := LowerCase(P[0]);
5793 chstr := '';
5794 if (cmd = 'quit') or
5795 (cmd = 'exit') then
5796 begin
5797 g_Game_Free();
5798 g_Game_Quit();
5799 Exit;
5800 end
5801 else if cmd = 'pause' then
5802 begin
5803 if (g_ActiveWindow = nil) then
5804 g_Game_Pause(not gPauseMain);
5805 end
5806 else if cmd = 'endgame' then
5807 gExit := EXIT_SIMPLE
5808 else if cmd = 'restart' then
5809 begin
5810 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5811 begin
5812 if g_Game_IsClient then
5813 begin
5814 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5815 Exit;
5816 end;
5817 g_Game_Restart();
5818 end else
5819 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5820 end
5821 else if cmd = 'kick' then
5822 begin
5823 if g_Game_IsServer then
5824 begin
5825 if Length(P) < 2 then
5826 begin
5827 g_Console_Add('kick <name>');
5828 Exit;
5829 end;
5830 if P[1] = '' then
5831 begin
5832 g_Console_Add('kick <name>');
5833 Exit;
5834 end;
5836 if g_Game_IsNet then
5837 pl := g_Net_Client_ByName(P[1]);
5838 if (pl <> nil) then
5839 begin
5840 s := g_Net_ClientName_ByID(pl^.ID);
5841 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5842 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5843 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5844 if NetUseMaster then
5845 g_Net_Slist_Update;
5846 end else if gPlayers <> nil then
5847 for a := Low(gPlayers) to High(gPlayers) do
5848 if gPlayers[a] <> nil then
5849 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5850 begin
5851 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5852 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5853 continue;
5854 gPlayers[a].Lives := 0;
5855 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5856 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5857 g_Player_Remove(gPlayers[a].UID);
5858 if NetUseMaster then
5859 g_Net_Slist_Update;
5860 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5861 g_Bot_MixNames();
5862 end;
5863 end else
5864 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5865 end
5866 else if cmd = 'kick_id' then
5867 begin
5868 if g_Game_IsServer and g_Game_IsNet then
5869 begin
5870 if Length(P) < 2 then
5871 begin
5872 g_Console_Add('kick_id <client ID>');
5873 Exit;
5874 end;
5875 if P[1] = '' then
5876 begin
5877 g_Console_Add('kick_id <client ID>');
5878 Exit;
5879 end;
5881 a := StrToIntDef(P[1], 0);
5882 if (NetClients <> nil) and (a <= High(NetClients)) then
5883 begin
5884 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5885 begin
5886 s := g_Net_ClientName_ByID(NetClients[a].ID);
5887 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5888 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5889 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5890 if NetUseMaster then
5891 g_Net_Slist_Update;
5892 end;
5893 end;
5894 end else
5895 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5896 end
5897 else if cmd = 'ban' then
5898 begin
5899 if g_Game_IsServer and g_Game_IsNet then
5900 begin
5901 if Length(P) < 2 then
5902 begin
5903 g_Console_Add('ban <name>');
5904 Exit;
5905 end;
5906 if P[1] = '' then
5907 begin
5908 g_Console_Add('ban <name>');
5909 Exit;
5910 end;
5912 pl := g_Net_Client_ByName(P[1]);
5913 if (pl <> nil) then
5914 begin
5915 s := g_Net_ClientName_ByID(pl^.ID);
5916 g_Net_BanHost(pl^.Peer^.address.host, False);
5917 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5918 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5919 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5920 if NetUseMaster then
5921 g_Net_Slist_Update;
5922 end else
5923 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5924 end else
5925 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5926 end
5927 else if cmd = 'ban_id' then
5928 begin
5929 if g_Game_IsServer and g_Game_IsNet then
5930 begin
5931 if Length(P) < 2 then
5932 begin
5933 g_Console_Add('ban_id <client ID>');
5934 Exit;
5935 end;
5936 if P[1] = '' then
5937 begin
5938 g_Console_Add('ban_id <client ID>');
5939 Exit;
5940 end;
5942 a := StrToIntDef(P[1], 0);
5943 if (NetClients <> nil) and (a <= High(NetClients)) then
5944 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5945 begin
5946 s := g_Net_ClientName_ByID(NetClients[a].ID);
5947 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5948 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5949 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5950 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5951 if NetUseMaster then
5952 g_Net_Slist_Update;
5953 end;
5954 end else
5955 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5956 end
5957 else if cmd = 'permban' then
5958 begin
5959 if g_Game_IsServer and g_Game_IsNet then
5960 begin
5961 if Length(P) < 2 then
5962 begin
5963 g_Console_Add('permban <name>');
5964 Exit;
5965 end;
5966 if P[1] = '' then
5967 begin
5968 g_Console_Add('permban <name>');
5969 Exit;
5970 end;
5972 pl := g_Net_Client_ByName(P[1]);
5973 if (pl <> nil) then
5974 begin
5975 s := g_Net_ClientName_ByID(pl^.ID);
5976 g_Net_BanHost(pl^.Peer^.address.host);
5977 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5978 g_Net_SaveBanList();
5979 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5980 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5981 if NetUseMaster then
5982 g_Net_Slist_Update;
5983 end else
5984 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5985 end else
5986 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5987 end
5988 else if cmd = 'permban_id' then
5989 begin
5990 if g_Game_IsServer and g_Game_IsNet then
5991 begin
5992 if Length(P) < 2 then
5993 begin
5994 g_Console_Add('permban_id <client ID>');
5995 Exit;
5996 end;
5997 if P[1] = '' then
5998 begin
5999 g_Console_Add('permban_id <client ID>');
6000 Exit;
6001 end;
6003 a := StrToIntDef(P[1], 0);
6004 if (NetClients <> nil) and (a <= High(NetClients)) then
6005 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6006 begin
6007 s := g_Net_ClientName_ByID(NetClients[a].ID);
6008 g_Net_BanHost(NetClients[a].Peer^.address.host);
6009 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
6010 g_Net_SaveBanList();
6011 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6012 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6013 if NetUseMaster then
6014 g_Net_Slist_Update;
6015 end;
6016 end else
6017 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6018 end
6019 else if cmd = 'unban' then
6020 begin
6021 if g_Game_IsServer and g_Game_IsNet then
6022 begin
6023 if Length(P) < 2 then
6024 begin
6025 g_Console_Add('unban <IP Address>');
6026 Exit;
6027 end;
6028 if P[1] = '' then
6029 begin
6030 g_Console_Add('unban <IP Address>');
6031 Exit;
6032 end;
6034 if g_Net_UnbanHost(P[1]) then
6035 begin
6036 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
6037 g_Net_SaveBanList();
6038 end else
6039 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
6040 end else
6041 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6042 end
6043 else if cmd = 'clientlist' then
6044 begin
6045 if g_Game_IsServer and g_Game_IsNet then
6046 begin
6047 b := 0;
6048 if NetClients <> nil then
6049 for a := Low(NetClients) to High(NetClients) do
6050 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6051 begin
6052 plr := g_Player_Get(NetClients[a].Player);
6053 if plr = nil then continue;
6054 Inc(b);
6055 g_Console_Add(Format('#%2d: %-15s | %s', [a,
6056 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
6057 end;
6058 if b = 0 then
6059 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
6060 end else
6061 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6062 end
6063 else if cmd = 'connect' then
6064 begin
6065 if (NetMode = NET_NONE) then
6066 begin
6067 if Length(P) < 2 then
6068 begin
6069 g_Console_Add('connect <IP> [port] [password]');
6070 Exit;
6071 end;
6072 if P[1] = '' then
6073 begin
6074 g_Console_Add('connect <IP> [port] [password]');
6075 Exit;
6076 end;
6078 if Length(P) > 2 then
6079 prt := StrToIntDef(P[2], 25666)
6080 else
6081 prt := 25666;
6083 if Length(P) > 3 then
6084 pw := P[3]
6085 else
6086 pw := '';
6088 g_Game_StartClient(P[1], prt, pw);
6089 end;
6090 end
6091 else if cmd = 'disconnect' then
6092 begin
6093 if (NetMode = NET_CLIENT) then
6094 g_Net_Disconnect();
6095 end
6096 else if cmd = 'reconnect' then
6097 begin
6098 if (NetMode = NET_SERVER) then
6099 Exit;
6101 if (NetMode = NET_CLIENT) then
6102 begin
6103 g_Net_Disconnect();
6104 gExit := EXIT_SIMPLE;
6105 EndGame;
6106 end;
6108 //TODO: Use last successful password to reconnect, instead of ''
6109 g_Game_StartClient(NetClientIP, NetClientPort, '');
6110 end
6111 else if (cmd = 'addbot') or
6112 (cmd = 'bot_add') then
6113 begin
6114 if Length(P) > 1 then
6115 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
6116 else
6117 g_Bot_Add(TEAM_NONE, 2);
6118 end
6119 else if cmd = 'bot_addlist' then
6120 begin
6121 if Length(P) > 1 then
6122 if Length(P) = 2 then
6123 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
6124 else
6125 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
6126 end
6127 else if cmd = 'bot_removeall' then
6128 g_Bot_RemoveAll()
6129 else if cmd = 'chat' then
6130 begin
6131 if g_Game_IsNet then
6132 begin
6133 if Length(P) > 1 then
6134 begin
6135 for a := 1 to High(P) do
6136 chstr := chstr + P[a] + ' ';
6138 if Length(chstr) > 200 then SetLength(chstr, 200);
6140 if Length(chstr) < 1 then
6141 begin
6142 g_Console_Add('chat <text>');
6143 Exit;
6144 end;
6146 chstr := b_Text_Format(chstr);
6147 if g_Game_IsClient then
6148 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
6149 else
6150 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
6151 end
6152 else
6153 g_Console_Add('chat <text>');
6154 end else
6155 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6156 end
6157 else if cmd = 'teamchat' then
6158 begin
6159 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
6160 begin
6161 if Length(P) > 1 then
6162 begin
6163 for a := 1 to High(P) do
6164 chstr := chstr + P[a] + ' ';
6166 if Length(chstr) > 200 then SetLength(chstr, 200);
6168 if Length(chstr) < 1 then
6169 begin
6170 g_Console_Add('teamchat <text>');
6171 Exit;
6172 end;
6174 chstr := b_Text_Format(chstr);
6175 if g_Game_IsClient then
6176 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
6177 else
6178 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
6179 gPlayer1Settings.Team);
6180 end
6181 else
6182 g_Console_Add('teamchat <text>');
6183 end else
6184 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6185 end
6186 else if cmd = 'game' then
6187 begin
6188 if gGameSettings.GameType <> GT_NONE then
6189 begin
6190 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6191 Exit;
6192 end;
6193 if Length(P) = 1 then
6194 begin
6195 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
6196 Exit;
6197 end;
6198 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
6199 P[1] := addWadExtension(P[1]);
6200 if FileExists(MapsDir + P[1]) then
6201 begin
6202 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6203 if Length(P) < 3 then
6204 begin
6205 SetLength(P, 3);
6206 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6207 end;
6209 s := P[1] + ':\' + UpperCase(P[2]);
6211 if g_Map_Exist(MapsDir + s) then
6212 begin
6213 // Çàïóñêàåì ñâîþ èãðó
6214 g_Game_Free();
6215 with gGameSettings do
6216 begin
6217 GameMode := g_Game_TextToMode(gcGameMode);
6218 if gSwitchGameMode <> GM_NONE then
6219 GameMode := gSwitchGameMode;
6220 if GameMode = GM_NONE then GameMode := GM_DM;
6221 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6222 b := 1;
6223 if Length(P) >= 4 then
6224 b := StrToIntDef(P[3], 1);
6225 g_Game_StartCustom(s, GameMode, TimeLimit,
6226 GoalLimit, MaxLives, Options, b);
6227 end;
6228 end
6229 else
6230 if P[2] = '' then
6231 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6232 else
6233 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[2]), P[1]]));
6234 end else
6235 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6236 end
6237 else if cmd = 'host' then
6238 begin
6239 if gGameSettings.GameType <> GT_NONE then
6240 begin
6241 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6242 Exit;
6243 end;
6244 if Length(P) < 4 then
6245 begin
6246 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
6247 Exit;
6248 end;
6249 if not StrToIp(P[1], listen) then
6250 Exit;
6251 prt := StrToIntDef(P[2], 25666);
6253 P[3] := addWadExtension(P[3]);
6254 if FileExists(MapsDir + P[3]) then
6255 begin
6256 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6257 if Length(P) < 5 then
6258 begin
6259 SetLength(P, 5);
6260 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
6261 end;
6263 s := P[3] + ':\' + UpperCase(P[4]);
6265 if g_Map_Exist(MapsDir + s) then
6266 begin
6267 // Çàïóñêàåì ñâîþ èãðó
6268 g_Game_Free();
6269 with gGameSettings do
6270 begin
6271 GameMode := g_Game_TextToMode(gcGameMode);
6272 if gSwitchGameMode <> GM_NONE then
6273 GameMode := gSwitchGameMode;
6274 if GameMode = GM_NONE then GameMode := GM_DM;
6275 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6276 b := 0;
6277 if Length(P) >= 6 then
6278 b := StrToIntDef(P[5], 0);
6279 g_Game_StartServer(s, GameMode, TimeLimit,
6280 GoalLimit, MaxLives, Options, b, listen, prt);
6281 end;
6282 end
6283 else
6284 if P[4] = '' then
6285 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6286 else
6287 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]));
6288 end else
6289 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6290 end
6291 else if cmd = 'map' then
6292 begin
6293 if Length(P) = 1 then
6294 begin
6295 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6296 begin
6297 g_Console_Add(cmd + ' <MAP>');
6298 g_Console_Add(cmd + ' <WAD> [MAP]');
6299 end else
6300 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6301 end else
6302 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6303 begin
6304 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6305 if Length(P) < 3 then
6306 begin
6307 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6308 s := UpperCase(P[1]);
6309 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6310 begin // Êàðòà íàøëàñü
6311 gExitByTrigger := False;
6312 if gGameOn then
6313 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6314 gNextMap := s;
6315 gExit := EXIT_ENDLEVELCUSTOM;
6316 end
6317 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6318 g_Game_ChangeMap(s);
6319 end else
6320 begin
6321 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6322 P[1] := addWadExtension(P[1]);
6323 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6324 if FileExists(MapsDir + P[1]) then
6325 begin
6326 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6327 SetLength(P, 3);
6328 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6330 s := P[1] + ':\' + P[2];
6332 if g_Map_Exist(MapsDir + s) then
6333 begin
6334 gExitByTrigger := False;
6335 if gGameOn then
6336 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6337 gNextMap := s;
6338 gExit := EXIT_ENDLEVELCUSTOM;
6339 end
6340 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6341 g_Game_ChangeMap(s);
6342 end else
6343 if P[2] = '' then
6344 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6345 else
6346 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6347 end else
6348 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6349 end;
6350 end else
6351 begin
6352 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6353 P[1] := addWadExtension(P[1]);
6354 if FileExists(MapsDir + P[1]) then
6355 begin
6356 // Íàøëè WAD ôàéë
6357 P[2] := UpperCase(P[2]);
6358 s := P[1] + ':\' + P[2];
6360 if g_Map_Exist(MapsDir + s) then
6361 begin // Íàøëè êàðòó
6362 gExitByTrigger := False;
6363 if gGameOn then
6364 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6365 gNextMap := s;
6366 gExit := EXIT_ENDLEVELCUSTOM;
6367 end
6368 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6369 g_Game_ChangeMap(s);
6370 end else
6371 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6372 end else
6373 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6374 end;
6375 end else
6376 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6377 end
6378 else if cmd = 'nextmap' then
6379 begin
6380 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6381 g_Console_Add(_lc[I_MSG_NOT_GAME])
6382 else begin
6383 nm := True;
6384 if Length(P) = 1 then
6385 begin
6386 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6387 begin
6388 g_Console_Add(cmd + ' <MAP>');
6389 g_Console_Add(cmd + ' <WAD> [MAP]');
6390 end else begin
6391 nm := False;
6392 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6393 end;
6394 end else
6395 begin
6396 nm := False;
6397 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6398 begin
6399 if Length(P) < 3 then
6400 begin
6401 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6402 s := UpperCase(P[1]);
6403 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6404 begin // Êàðòà íàøëàñü
6405 gExitByTrigger := False;
6406 gNextMap := s;
6407 nm := True;
6408 end else
6409 begin
6410 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6411 P[1] := addWadExtension(P[1]);
6412 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6413 if FileExists(MapsDir + P[1]) then
6414 begin
6415 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6416 SetLength(P, 3);
6417 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6419 s := P[1] + ':\' + P[2];
6421 if g_Map_Exist(MapsDir + s) then
6422 begin // Óñòàíàâëèâàåì êàðòó
6423 gExitByTrigger := False;
6424 gNextMap := s;
6425 nm := True;
6426 end else
6427 if P[2] = '' then
6428 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6429 else
6430 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6431 end else
6432 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6433 end;
6434 end else
6435 begin
6436 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6437 P[1] := addWadExtension(P[1]);
6438 if FileExists(MapsDir + P[1]) then
6439 begin
6440 // Íàøëè WAD ôàéë
6441 P[2] := UpperCase(P[2]);
6442 s := P[1] + ':\' + P[2];
6444 if g_Map_Exist(MapsDir + s) then
6445 begin // Íàøëè êàðòó
6446 gExitByTrigger := False;
6447 gNextMap := s;
6448 nm := True;
6449 end else
6450 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6451 end else
6452 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6453 end;
6454 end else
6455 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6456 end;
6457 if nm then
6458 if gNextMap = '' then
6459 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6460 else
6461 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6462 end;
6463 end
6464 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6465 begin
6466 if not gGameOn then
6467 g_Console_Add(_lc[I_MSG_NOT_GAME])
6468 else
6469 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6470 begin
6471 gExitByTrigger := False;
6472 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6473 if (gNextMap = '') and (gTriggers <> nil) then
6474 for a := 0 to High(gTriggers) do
6475 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6476 begin
6477 gExitByTrigger := True;
6478 //gNextMap := gTriggers[a].Data.MapName;
6479 gNextMap := gTriggers[a].tgcMap;
6480 Break;
6481 end;
6482 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6483 if gNextMap = '' then
6484 gNextMap := g_Game_GetNextMap();
6485 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6486 if not isWadPath(gNextMap) then
6487 s := gGameSettings.WAD + ':\' + gNextMap
6488 else
6489 s := gNextMap;
6490 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6491 if g_Map_Exist(MapsDir + s) then
6492 gExit := EXIT_ENDLEVELCUSTOM
6493 else
6494 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6495 end else
6496 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6497 end
6498 else if (cmd = 'event') then
6499 begin
6500 if (Length(P) <= 1) then
6501 begin
6502 for a := 0 to High(gEvents) do
6503 if gEvents[a].Command = '' then
6504 g_Console_Add(gEvents[a].Name + ' <none>')
6505 else
6506 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6507 Exit;
6508 end;
6509 if (Length(P) = 2) then
6510 begin
6511 for a := 0 to High(gEvents) do
6512 if gEvents[a].Name = P[1] then
6513 if gEvents[a].Command = '' then
6514 g_Console_Add(gEvents[a].Name + ' <none>')
6515 else
6516 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6517 Exit;
6518 end;
6519 for a := 0 to High(gEvents) do
6520 if gEvents[a].Name = P[1] then
6521 begin
6522 gEvents[a].Command := '';
6523 for b := 2 to High(P) do
6524 if Pos(' ', P[b]) = 0 then
6525 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6526 else
6527 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6528 gEvents[a].Command := Trim(gEvents[a].Command);
6529 Exit;
6530 end;
6531 end
6532 else if cmd = 'suicide' then
6533 begin
6534 if gGameOn then
6535 begin
6536 if g_Game_IsClient then
6537 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6538 else
6539 begin
6540 if gPlayer1 <> nil then
6541 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6542 if gPlayer2 <> nil then
6543 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6544 end;
6545 end;
6546 end
6547 // Êîìàíäû Ñâîåé èãðû:
6548 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6549 begin
6550 if cmd = 'bot_addred' then
6551 begin
6552 if Length(P) > 1 then
6553 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6554 else
6555 g_Bot_Add(TEAM_RED, 2);
6556 end
6557 else if cmd = 'bot_addblue' then
6558 begin
6559 if Length(P) > 1 then
6560 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6561 else
6562 g_Bot_Add(TEAM_BLUE, 2);
6563 end
6564 else if cmd = 'spectate' then
6565 begin
6566 if not gGameOn then
6567 Exit;
6568 g_Game_Spectate();
6569 end
6570 else if cmd = 'say' then
6571 begin
6572 if g_Game_IsServer and g_Game_IsNet then
6573 begin
6574 if Length(P) > 1 then
6575 begin
6576 chstr := '';
6577 for a := 1 to High(P) do
6578 chstr := chstr + P[a] + ' ';
6580 if Length(chstr) > 200 then SetLength(chstr, 200);
6582 if Length(chstr) < 1 then
6583 begin
6584 g_Console_Add('say <text>');
6585 Exit;
6586 end;
6588 chstr := b_Text_Format(chstr);
6589 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6590 end
6591 else g_Console_Add('say <text>');
6592 end else
6593 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6594 end
6595 else if cmd = 'tell' then
6596 begin
6597 if g_Game_IsServer and g_Game_IsNet then
6598 begin
6599 if (Length(P) > 2) and (P[1] <> '') then
6600 begin
6601 chstr := '';
6602 for a := 2 to High(P) do
6603 chstr := chstr + P[a] + ' ';
6605 if Length(chstr) > 200 then SetLength(chstr, 200);
6607 if Length(chstr) < 1 then
6608 begin
6609 g_Console_Add('tell <playername> <text>');
6610 Exit;
6611 end;
6613 pl := g_Net_Client_ByName(P[1]);
6614 if pl <> nil then
6615 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6616 else
6617 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6618 end
6619 else g_Console_Add('tell <playername> <text>');
6620 end else
6621 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6622 end
6623 else if (cmd = 'overtime') and not g_Game_IsClient then
6624 begin
6625 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6626 Exit;
6627 // Äîïîëíèòåëüíîå âðåìÿ:
6628 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6630 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6631 [gGameSettings.TimeLimit div 3600,
6632 (gGameSettings.TimeLimit div 60) mod 60,
6633 gGameSettings.TimeLimit mod 60]));
6634 if g_Game_IsNet then MH_SEND_GameSettings;
6635 end
6636 else if (cmd = 'rcon_password') and g_Game_IsClient then
6637 begin
6638 if (Length(P) <= 1) then
6639 g_Console_Add('rcon_password <password>')
6640 else
6641 MC_SEND_RCONPassword(P[1]);
6642 end
6643 else if cmd = 'rcon' then
6644 begin
6645 if g_Game_IsClient then
6646 begin
6647 if Length(P) > 1 then
6648 begin
6649 chstr := '';
6650 for a := 1 to High(P) do
6651 chstr := chstr + P[a] + ' ';
6653 if Length(chstr) > 200 then SetLength(chstr, 200);
6655 if Length(chstr) < 1 then
6656 begin
6657 g_Console_Add('rcon <command>');
6658 Exit;
6659 end;
6661 MC_SEND_RCONCommand(chstr);
6662 end
6663 else g_Console_Add('rcon <command>');
6664 end;
6665 end
6666 else if cmd = 'ready' then
6667 begin
6668 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6669 gLMSRespawnTime := gTime + 100;
6670 end
6671 else if (cmd = 'callvote') and g_Game_IsNet then
6672 begin
6673 if Length(P) > 1 then
6674 begin
6675 chstr := '';
6676 for a := 1 to High(P) do begin
6677 if a > 1 then chstr := chstr + ' ';
6678 chstr := chstr + P[a];
6679 end;
6681 if Length(chstr) > 200 then SetLength(chstr, 200);
6683 if Length(chstr) < 1 then
6684 begin
6685 g_Console_Add('callvote <command>');
6686 Exit;
6687 end;
6689 if g_Game_IsClient then
6690 MC_SEND_Vote(True, chstr)
6691 else
6692 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6693 g_Console_Process('vote', True);
6694 end
6695 else
6696 g_Console_Add('callvote <command>');
6697 end
6698 else if (cmd = 'vote') and g_Game_IsNet then
6699 begin
6700 if g_Game_IsClient then
6701 MC_SEND_Vote(False)
6702 else if gVoteInProgress then
6703 begin
6704 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6705 a := Floor((NetClientCount+1)/2.0) + 1
6706 else
6707 a := Floor(NetClientCount/2.0) + 1;
6708 if gVoted then
6709 begin
6710 Dec(gVoteCount);
6711 gVoted := False;
6712 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6713 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6714 end
6715 else
6716 begin
6717 Inc(gVoteCount);
6718 gVoted := True;
6719 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6720 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6721 g_Game_CheckVote;
6722 end;
6723 end;
6724 end
6725 end;
6726 end;
6728 procedure g_TakeScreenShot();
6729 var
6730 a: Word;
6731 FileName: string;
6732 ssdir, t: string;
6733 st: TStream;
6734 ok: Boolean;
6735 begin
6736 if e_NoGraphics then Exit;
6737 ssdir := GameDir+'/screenshots';
6738 if not findFileCI(ssdir, true) then
6739 begin
6740 // try to create dir
6741 try
6742 CreateDir(ssdir);
6743 except
6744 end;
6745 if not findFileCI(ssdir, true) then exit; // alas
6746 end;
6747 try
6748 for a := 1 to High(Word) do
6749 begin
6750 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6751 t := FileName;
6752 if findFileCI(t, true) then continue;
6753 if not findFileCI(FileName) then
6754 begin
6755 ok := false;
6756 st := createDiskFile(FileName);
6757 try
6758 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6759 ok := true;
6760 finally
6761 st.Free();
6762 end;
6763 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6764 break;
6765 end;
6766 end;
6767 except
6768 end;
6769 end;
6771 procedure g_Game_InGameMenu(Show: Boolean);
6772 begin
6773 if (g_ActiveWindow = nil) and Show then
6774 begin
6775 if gGameSettings.GameType = GT_SINGLE then
6776 g_GUI_ShowWindow('GameSingleMenu')
6777 else
6778 begin
6779 if g_Game_IsClient then
6780 g_GUI_ShowWindow('GameClientMenu')
6781 else
6782 if g_Game_IsNet then
6783 g_GUI_ShowWindow('GameServerMenu')
6784 else
6785 g_GUI_ShowWindow('GameCustomMenu');
6786 end;
6787 g_Sound_PlayEx('MENU_OPEN');
6789 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6790 if (not g_Game_IsNet) then
6791 g_Game_Pause(True);
6792 end
6793 else
6794 if (g_ActiveWindow <> nil) and (not Show) then
6795 begin
6796 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6797 if (not g_Game_IsNet) then
6798 g_Game_Pause(False);
6799 end;
6800 end;
6802 procedure g_Game_Pause (Enable: Boolean);
6803 var
6804 oldPause: Boolean;
6805 begin
6806 if not gGameOn then exit;
6808 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6810 oldPause := gPause;
6811 gPauseMain := Enable;
6813 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6814 end;
6816 procedure g_Game_HolmesPause (Enable: Boolean);
6817 var
6818 oldPause: Boolean;
6819 begin
6820 if not gGameOn then exit;
6821 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6823 oldPause := gPause;
6824 gPauseHolmes := Enable;
6826 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6827 end;
6829 procedure g_Game_PauseAllSounds(Enable: Boolean);
6830 var
6831 i: Integer;
6832 begin
6833 // Òðèããåðû:
6834 if gTriggers <> nil then
6835 for i := 0 to High(gTriggers) do
6836 with gTriggers[i] do
6837 if (TriggerType = TRIGGER_SOUND) and
6838 (Sound <> nil) and
6839 Sound.IsPlaying() then
6840 begin
6841 Sound.Pause(Enable);
6842 end;
6844 // Çâóêè èãðîêîâ:
6845 if gPlayers <> nil then
6846 for i := 0 to High(gPlayers) do
6847 if gPlayers[i] <> nil then
6848 gPlayers[i].PauseSounds(Enable);
6850 // Ìóçûêà:
6851 if gMusic <> nil then
6852 gMusic.Pause(Enable);
6853 end;
6855 procedure g_Game_StopAllSounds(all: Boolean);
6856 var
6857 i: Integer;
6858 begin
6859 if gTriggers <> nil then
6860 for i := 0 to High(gTriggers) do
6861 with gTriggers[i] do
6862 if (TriggerType = TRIGGER_SOUND) and
6863 (Sound <> nil) then
6864 Sound.Stop();
6866 if gMusic <> nil then
6867 gMusic.Stop();
6869 if all then
6870 e_StopChannels();
6871 end;
6873 procedure g_Game_UpdateTriggerSounds();
6874 var
6875 i: Integer;
6876 begin
6877 if gTriggers <> nil then
6878 for i := 0 to High(gTriggers) do
6879 with gTriggers[i] do
6880 if (TriggerType = TRIGGER_SOUND) and
6881 (Sound <> nil) and
6882 (tgcLocal) and
6883 Sound.IsPlaying() then
6884 begin
6885 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6886 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6887 begin
6888 Sound.SetPan(0.5 - tgcPan/255.0);
6889 Sound.SetVolume(tgcVolume/255.0);
6890 end
6891 else
6892 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
6893 end;
6894 end;
6896 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6897 begin
6898 Result := False;
6899 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6900 begin
6901 Result := True;
6902 Exit;
6903 end;
6904 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6905 begin
6906 Result := True;
6907 Exit;
6908 end;
6909 if gSpectMode <> SPECT_PLAYERS then
6910 Exit;
6911 if gSpectPID1 = UID then
6912 begin
6913 Result := True;
6914 Exit;
6915 end;
6916 if gSpectViewTwo and (gSpectPID2 = UID) then
6917 begin
6918 Result := True;
6919 Exit;
6920 end;
6921 end;
6923 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6924 var
6925 Pl: TPlayer;
6926 begin
6927 Result := False;
6928 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6929 begin
6930 Result := True;
6931 Exit;
6932 end;
6933 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6934 begin
6935 Result := True;
6936 Exit;
6937 end;
6938 if gSpectMode <> SPECT_PLAYERS then
6939 Exit;
6940 Pl := g_Player_Get(gSpectPID1);
6941 if (Pl <> nil) and (Pl.Team = Team) then
6942 begin
6943 Result := True;
6944 Exit;
6945 end;
6946 if gSpectViewTwo then
6947 begin
6948 Pl := g_Player_Get(gSpectPID2);
6949 if (Pl <> nil) and (Pl.Team = Team) then
6950 begin
6951 Result := True;
6952 Exit;
6953 end;
6954 end;
6955 end;
6957 procedure g_Game_Message(Msg: string; Time: Word);
6958 begin
6959 MessageText := b_Text_Format(Msg);
6960 MessageTime := Time;
6961 end;
6963 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
6964 const
6965 punct: Array[0..13] of String =
6966 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
6967 var
6968 i, j: Integer;
6969 ok: Boolean;
6970 fpText: String;
6972 function IsPunctuation(S: String): Boolean;
6973 var
6974 i: Integer;
6975 begin
6976 Result := False;
6977 if Length(S) <> 1 then
6978 Exit;
6979 for i := Low(punct) to High(punct) do
6980 if S = punct[i] then
6981 begin
6982 Result := True;
6983 break;
6984 end;
6985 end;
6986 function FilterPunctuation(S: String): String;
6987 var
6988 i: Integer;
6989 begin
6990 for i := Low(punct) to High(punct) do
6991 S := StringReplace(S, punct[i], ' ', [rfReplaceAll]);
6992 Result := S;
6993 end;
6994 begin
6995 ok := False;
6997 if gUseChatSounds and Taunt and (gChatSounds <> nil) and (Pos(': ', Text) > 0) then
6998 begin
6999 // remove player name
7000 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
7001 // for FullWord check
7002 Text := toLowerCase1251(' ' + Text + ' ');
7003 fpText := FilterPunctuation(Text);
7005 for i := 0 to Length(gChatSounds) - 1 do
7006 begin
7007 ok := True;
7008 for j := 0 to Length(gChatSounds[i].Tags) - 1 do
7009 begin
7010 if gChatSounds[i].FullWord and (not IsPunctuation(gChatSounds[i].Tags[j])) then
7011 ok := Pos(' ' + gChatSounds[i].Tags[j] + ' ', fpText) > 0
7012 else
7013 ok := Pos(gChatSounds[i].Tags[j], Text) > 0;
7014 if not ok then
7015 break;
7016 end;
7017 if ok then
7018 begin
7019 gChatSounds[i].Sound.Play();
7020 break;
7021 end;
7022 end;
7023 end;
7024 if not ok then
7025 g_Sound_PlayEx('SOUND_GAME_RADIO');
7026 end;
7028 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
7029 var
7030 a: Integer;
7031 begin
7032 case gAnnouncer of
7033 ANNOUNCE_NONE:
7034 Exit;
7035 ANNOUNCE_ME,
7036 ANNOUNCE_MEPLUS:
7037 if not g_Game_IsWatchedPlayer(SpawnerUID) then
7038 Exit;
7039 end;
7040 for a := 0 to 3 do
7041 if goodsnd[a].IsPlaying() then
7042 Exit;
7044 goodsnd[Random(4)].Play();
7045 end;
7047 procedure g_Game_Announce_KillCombo(Param: Integer);
7048 var
7049 UID: Word;
7050 c, n: Byte;
7051 Pl: TPlayer;
7052 Name: String;
7053 begin
7054 UID := Param and $FFFF;
7055 c := Param shr 16;
7056 if c < 2 then
7057 Exit;
7059 Pl := g_Player_Get(UID);
7060 if Pl = nil then
7061 Name := '?'
7062 else
7063 Name := Pl.Name;
7065 case c of
7066 2: begin
7067 n := 0;
7068 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
7069 end;
7070 3: begin
7071 n := 1;
7072 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
7073 end;
7074 4: begin
7075 n := 2;
7076 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
7077 end;
7078 else begin
7079 n := 3;
7080 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
7081 end;
7082 end;
7084 case gAnnouncer of
7085 ANNOUNCE_NONE:
7086 Exit;
7087 ANNOUNCE_ME:
7088 if not g_Game_IsWatchedPlayer(UID) then
7089 Exit;
7090 ANNOUNCE_MEPLUS:
7091 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
7092 Exit;
7093 end;
7095 if killsnd[n].IsPlaying() then
7096 killsnd[n].Stop();
7097 killsnd[n].Play();
7098 end;
7100 procedure g_Game_StartVote(Command, Initiator: string);
7101 var
7102 Need: Integer;
7103 begin
7104 if not gVotesEnabled then Exit;
7105 if gGameSettings.GameType <> GT_SERVER then Exit;
7106 if gVoteInProgress or gVotePassed then
7107 begin
7108 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
7109 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
7110 Exit;
7111 end;
7112 gVoteInProgress := True;
7113 gVotePassed := False;
7114 gVoteTimer := gTime + gVoteTimeout * 1000;
7115 gVoteCount := 0;
7116 gVoted := False;
7117 gVoteCommand := Command;
7119 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7120 Need := Floor((NetClientCount+1)/2.0)+1
7121 else
7122 Need := Floor(NetClientCount/2.0)+1;
7123 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
7124 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
7125 end;
7127 procedure g_Game_CheckVote;
7128 var
7129 I, Need: Integer;
7130 begin
7131 if gGameSettings.GameType <> GT_SERVER then Exit;
7132 if not gVoteInProgress then Exit;
7134 if (gTime >= gVoteTimer) then
7135 begin
7136 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7137 Need := Floor((NetClientCount+1)/2.0) + 1
7138 else
7139 Need := Floor(NetClientCount/2.0) + 1;
7140 if gVoteCount >= Need then
7141 begin
7142 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7143 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7144 gVotePassed := True;
7145 gVoteCmdTimer := gTime + 5000;
7146 end
7147 else
7148 begin
7149 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
7150 MH_SEND_VoteEvent(NET_VE_FAILED);
7151 end;
7152 if NetClients <> nil then
7153 for I := Low(NetClients) to High(NetClients) do
7154 if NetClients[i].Used then
7155 NetClients[i].Voted := False;
7156 gVoteInProgress := False;
7157 gVoted := False;
7158 gVoteCount := 0;
7159 end
7160 else
7161 begin
7162 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7163 Need := Floor((NetClientCount+1)/2.0) + 1
7164 else
7165 Need := Floor(NetClientCount/2.0) + 1;
7166 if gVoteCount >= Need then
7167 begin
7168 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7169 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7170 gVoteInProgress := False;
7171 gVotePassed := True;
7172 gVoteCmdTimer := gTime + 5000;
7173 gVoted := False;
7174 gVoteCount := 0;
7175 if NetClients <> nil then
7176 for I := Low(NetClients) to High(NetClients) do
7177 if NetClients[i].Used then
7178 NetClients[i].Voted := False;
7179 end;
7180 end;
7181 end;
7183 procedure g_Game_LoadMapList(FileName: string);
7184 var
7185 ListFile: TextFile;
7186 s: string;
7187 begin
7188 MapList := nil;
7189 MapIndex := -1;
7191 if not FileExists(FileName) then Exit;
7193 AssignFile(ListFile, FileName);
7194 Reset(ListFile);
7195 while not EOF(ListFile) do
7196 begin
7197 ReadLn(ListFile, s);
7199 s := Trim(s);
7200 if s = '' then Continue;
7202 SetLength(MapList, Length(MapList)+1);
7203 MapList[High(MapList)] := s;
7204 end;
7205 CloseFile(ListFile);
7206 end;
7208 procedure g_Game_SetDebugMode();
7209 begin
7210 gDebugMode := True;
7211 // ×èòû (äàæå â ñâîåé èãðå):
7212 gCheats := True;
7213 end;
7215 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
7216 var
7217 i: Word;
7218 begin
7219 if Length(LoadingStat.Msgs) = 0 then
7220 Exit;
7222 with LoadingStat do
7223 begin
7224 if not reWrite then
7225 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
7226 if NextMsg = Length(Msgs) then
7227 begin // scroll
7228 for i := 0 to High(Msgs)-1 do
7229 Msgs[i] := Msgs[i+1];
7230 end
7231 else
7232 Inc(NextMsg);
7233 end else
7234 if NextMsg = 0 then
7235 Inc(NextMsg);
7237 Msgs[NextMsg-1] := Text;
7238 CurValue := 0;
7239 MaxValue := Max;
7240 ShowCount := 0;
7241 PBarWasHere := false;
7242 end;
7244 g_ActiveWindow := nil;
7246 ProcessLoading(true);
7247 end;
7249 procedure g_Game_StepLoading();
7250 begin
7251 with LoadingStat do
7252 begin
7253 Inc(CurValue);
7254 Inc(ShowCount);
7255 if (ShowCount > LOADING_SHOW_STEP) then
7256 begin
7257 ShowCount := 0;
7258 ProcessLoading();
7259 end;
7260 end;
7261 end;
7263 procedure g_Game_ClearLoading();
7264 var
7265 len: Word;
7266 begin
7267 with LoadingStat do
7268 begin
7269 CurValue := 0;
7270 MaxValue := 0;
7271 ShowCount := 0;
7272 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
7273 if len < 1 then len := 1;
7274 SetLength(Msgs, len);
7275 for len := Low(Msgs) to High(Msgs) do
7276 Msgs[len] := '';
7277 NextMsg := 0;
7278 PBarWasHere := false;
7279 end;
7280 end;
7282 procedure Parse_Params(var pars: TParamStrValues);
7283 var
7284 i: Integer;
7285 s: String;
7286 begin
7287 SetLength(pars, 0);
7288 i := 1;
7289 while i <= ParamCount do
7290 begin
7291 s := ParamStr(i);
7292 if (s[1] = '-') and (Length(s) > 1) then
7293 begin
7294 if (s[2] = '-') and (Length(s) > 2) then
7295 begin // Îäèíî÷íûé ïàðàìåòð
7296 SetLength(pars, Length(pars) + 1);
7297 with pars[High(pars)] do
7298 begin
7299 Name := LowerCase(s);
7300 Value := '+';
7301 end;
7302 end
7303 else
7304 if (i < ParamCount) then
7305 begin // Ïàðàìåòð ñî çíà÷åíèåì
7306 Inc(i);
7307 SetLength(pars, Length(pars) + 1);
7308 with pars[High(pars)] do
7309 begin
7310 Name := LowerCase(s);
7311 Value := LowerCase(ParamStr(i));
7312 end;
7313 end;
7314 end;
7316 Inc(i);
7317 end;
7318 end;
7320 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
7321 var
7322 i: Integer;
7323 begin
7324 Result := '';
7325 for i := 0 to High(pars) do
7326 if pars[i].Name = aName then
7327 begin
7328 Result := pars[i].Value;
7329 Break;
7330 end;
7331 end;
7333 procedure g_Game_Process_Params();
7334 var
7335 pars: TParamStrValues;
7336 map: String;
7337 GMode, n: Byte;
7338 LimT, LimS: Integer;
7339 Opt: LongWord;
7340 Lives: Integer;
7341 s: String;
7342 Port: Integer;
7343 ip: String;
7344 F: TextFile;
7345 begin
7346 Parse_Params(pars);
7348 // Debug mode:
7349 s := Find_Param_Value(pars, '--debug');
7350 if (s <> '') then
7351 begin
7352 g_Game_SetDebugMode();
7353 s := Find_Param_Value(pars, '--netdump');
7354 if (s <> '') then
7355 NetDump := True;
7356 end;
7358 // Connect when game loads
7359 ip := Find_Param_Value(pars, '-connect');
7361 if ip <> '' then
7362 begin
7363 s := Find_Param_Value(pars, '-port');
7364 if (s = '') or not TryStrToInt(s, Port) then
7365 Port := 25666;
7367 s := Find_Param_Value(pars, '-pw');
7369 g_Game_StartClient(ip, Port, s);
7370 Exit;
7371 end;
7373 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7374 if (s <> '') then
7375 begin
7376 gDefaultMegawadStart := s;
7377 end;
7379 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7380 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7381 begin
7382 gDefaultMegawadStart := DF_Default_Megawad_Start;
7383 end;
7385 // Start map when game loads:
7386 map := LowerCase(Find_Param_Value(pars, '-map'));
7387 if isWadPath(map) then
7388 begin
7389 // Game mode:
7390 s := Find_Param_Value(pars, '-gm');
7391 GMode := g_Game_TextToMode(s);
7392 if GMode = GM_NONE then GMode := GM_DM;
7393 if GMode = GM_SINGLE then GMode := GM_COOP;
7395 // Time limit:
7396 s := Find_Param_Value(pars, '-limt');
7397 if (s = '') or (not TryStrToInt(s, LimT)) then
7398 LimT := 0;
7399 if LimT < 0 then
7400 LimT := 0;
7402 // Goal limit:
7403 s := Find_Param_Value(pars, '-lims');
7404 if (s = '') or (not TryStrToInt(s, LimS)) then
7405 LimS := 0;
7406 if LimS < 0 then
7407 LimS := 0;
7409 // Lives limit:
7410 s := Find_Param_Value(pars, '-lives');
7411 if (s = '') or (not TryStrToInt(s, Lives)) then
7412 Lives := 0;
7413 if Lives < 0 then
7414 Lives := 0;
7416 // Options:
7417 s := Find_Param_Value(pars, '-opt');
7418 if (s = '') then
7419 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7420 else
7421 Opt := StrToIntDef(s, 0);
7422 if Opt = 0 then
7423 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7425 // Close after map:
7426 s := Find_Param_Value(pars, '--close');
7427 if (s <> '') then
7428 gMapOnce := True;
7430 // Override map to test:
7431 s := LowerCase(Find_Param_Value(pars, '-testmap'));
7432 if s <> '' then
7433 gTestMap := MapsDir + s;
7435 // Delete test map after play:
7436 s := Find_Param_Value(pars, '--testdelete');
7437 if (s <> '') then
7438 begin
7439 gMapToDelete := MapsDir + map;
7440 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
7441 Halt(1);
7442 end;
7444 // Delete temporary WAD after play:
7445 s := Find_Param_Value(pars, '--tempdelete');
7446 if (s <> '') and (gTestMap <> '') then
7447 begin
7448 gMapToDelete := gTestMap;
7449 gTempDelete := True;
7450 end;
7452 // Number of players:
7453 s := Find_Param_Value(pars, '-pl');
7454 if (s = '') then
7455 n := 1
7456 else
7457 n := StrToIntDef(s, 1);
7459 // Start:
7460 s := Find_Param_Value(pars, '-port');
7461 if (s = '') or not TryStrToInt(s, Port) then
7462 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7463 else
7464 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7465 end;
7467 // Execute script when game loads:
7468 s := Find_Param_Value(pars, '-exec');
7469 if s <> '' then
7470 begin
7471 if not isWadPath(s) then
7472 s := GameDir + '/' + s;
7474 {$I-}
7475 AssignFile(F, s);
7476 Reset(F);
7477 if IOResult <> 0 then
7478 begin
7479 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7480 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7481 CloseFile(F);
7482 Exit;
7483 end;
7484 e_WriteLog('Executing script: ' + s, TMsgType.Notify);
7485 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7487 while not EOF(F) do
7488 begin
7489 ReadLn(F, s);
7490 if IOResult <> 0 then
7491 begin
7492 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7493 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7494 CloseFile(F);
7495 Exit;
7496 end;
7497 if Pos('#', s) <> 1 then // script comment
7498 g_Console_Process(s, True);
7499 end;
7501 CloseFile(F);
7502 {$I+}
7503 end;
7505 SetLength(pars, 0);
7506 end;
7508 begin
7509 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7510 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7511 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7512 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7514 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7515 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7516 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7517 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7519 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7520 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7522 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7523 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7525 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7527 conRegVar('dbg_scale', @g_dbg_scale, 0.01, 100.0, 'experimental deBUG scale mode', '', false);
7529 conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
7530 conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
7531 end.