DEADSOFTWARE

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