DEADSOFTWARE

added some alternative texture names ==> simple zipping "game.wad" from resource...
[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 g_basic, g_player, e_graphics, Classes, g_res_downloader,
23 SysUtils, g_sound, g_gui, MAPDEF, wadreader, md5, xprofiler;
25 type
26 TGameSettings = record
27 GameType: Byte;
28 GameMode: Byte;
29 TimeLimit: Word;
30 GoalLimit: Word;
31 WarmupTime: Word;
32 MaxLives: Byte;
33 Options: LongWord;
34 WAD: String;
35 end;
37 TGameEvent = record
38 Name: String;
39 Command: String;
40 end;
42 TDelayedEvent = record
43 Pending: Boolean;
44 Time: LongWord;
45 DEType: Byte;
46 DENum: Integer;
47 DEStr: String;
48 end;
50 TPlayerSettings = record
51 Name: String;
52 Model: String;
53 Color: TRGB;
54 Team: Byte;
55 end;
57 TMegaWADInfo = record
58 Name: String;
59 Description: String;
60 Author: String;
61 Pic: String;
62 end;
64 THearPoint = record
65 Active: Boolean;
66 Coords: TDFPoint;
67 end;
69 function g_Game_IsNet(): Boolean;
70 function g_Game_IsServer(): Boolean;
71 function g_Game_IsClient(): Boolean;
72 procedure g_Game_Init();
73 procedure g_Game_Free();
74 procedure g_Game_LoadData();
75 procedure g_Game_FreeData();
76 procedure g_Game_Update();
77 procedure g_Game_Draw();
78 procedure g_Game_Quit();
79 procedure g_Game_SetupScreenSize();
80 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
81 function g_Game_ModeToText(Mode: Byte): string;
82 function g_Game_TextToMode(Mode: string): Byte;
83 procedure g_Game_ExecuteEvent(Name: String);
84 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
85 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
86 procedure g_Game_RemovePlayer();
87 procedure g_Game_Spectate();
88 procedure g_Game_SpectateCenterView();
89 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
90 procedure g_Game_StartCustom(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte);
91 procedure g_Game_StartServer(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte; IPAddr: LongWord; Port: Word);
92 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
93 procedure g_Game_Restart();
94 procedure g_Game_RestartLevel();
95 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
96 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
97 procedure g_Game_SaveOptions();
98 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
99 procedure g_Game_ChangeMap(MapPath: String);
100 procedure g_Game_ExitLevel(Map: Char16);
101 function g_Game_GetFirstMap(WAD: String): String;
102 function g_Game_GetNextMap(): String;
103 procedure g_Game_NextLevel();
104 procedure g_Game_Pause(Enable: Boolean);
105 procedure g_Game_InGameMenu(Show: Boolean);
106 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
107 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
108 procedure g_Game_Message(Msg: String; Time: Word);
109 procedure g_Game_LoadMapList(FileName: String);
110 procedure g_Game_PauseAllSounds(Enable: Boolean);
111 procedure g_Game_StopAllSounds(all: Boolean);
112 procedure g_Game_UpdateTriggerSounds();
113 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
114 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
115 procedure g_Game_Announce_KillCombo(Param: Integer);
116 procedure g_Game_StartVote(Command, Initiator: string);
117 procedure g_Game_CheckVote;
118 procedure g_TakeScreenShot();
119 procedure g_FatalError(Text: String);
120 procedure g_SimpleError(Text: String);
121 function g_Game_IsTestMap(): Boolean;
122 procedure g_Game_DeleteTestMap();
123 procedure GameCVars(P: SArray);
124 procedure GameCommands(P: SArray);
125 procedure GameCheats(P: SArray);
126 procedure DebugCommands(P: SArray);
127 procedure g_Game_Process_Params;
128 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
129 procedure g_Game_StepLoading();
130 procedure g_Game_ClearLoading();
131 procedure g_Game_SetDebugMode();
132 procedure DrawLoadingStat();
134 { procedure SetWinPause(Enable: Boolean); }
136 const
137 GAME_TICK = 28;
139 LOADING_SHOW_STEP = 100;
140 LOADING_INTERLINE = 20;
142 GT_NONE = 0;
143 GT_SINGLE = 1;
144 GT_CUSTOM = 2;
145 GT_SERVER = 3;
146 GT_CLIENT = 4;
148 GM_NONE = 0;
149 GM_DM = 1;
150 GM_TDM = 2;
151 GM_CTF = 3;
152 GM_COOP = 4;
153 GM_SINGLE = 5;
155 MESSAGE_DIKEY = WM_USER + 1;
157 EXIT_QUIT = 1;
158 EXIT_SIMPLE = 2;
159 EXIT_RESTART = 3;
160 EXIT_ENDLEVELSINGLE = 4;
161 EXIT_ENDLEVELCUSTOM = 5;
163 GAME_OPTION_RESERVED = 1;
164 GAME_OPTION_TEAMDAMAGE = 2;
165 GAME_OPTION_ALLOWEXIT = 4;
166 GAME_OPTION_WEAPONSTAY = 8;
167 GAME_OPTION_MONSTERS = 16;
168 GAME_OPTION_BOTVSPLAYER = 32;
169 GAME_OPTION_BOTVSMONSTER = 64;
171 STATE_NONE = 0;
172 STATE_MENU = 1;
173 STATE_FOLD = 2;
174 STATE_INTERCUSTOM = 3;
175 STATE_INTERSINGLE = 4;
176 STATE_INTERTEXT = 5;
177 STATE_INTERPIC = 6;
178 STATE_ENDPIC = 7;
179 STATE_SLIST = 8;
181 LMS_RESPAWN_NONE = 0;
182 LMS_RESPAWN_WARMUP = 1;
183 LMS_RESPAWN_FINAL = 2;
185 SPECT_NONE = 0;
186 SPECT_STATS = 1;
187 SPECT_MAPVIEW = 2;
188 SPECT_PLAYERS = 3;
190 DE_GLOBEVENT = 0;
191 DE_BFGHIT = 1;
192 DE_KILLCOMBO = 2;
194 ANNOUNCE_NONE = 0;
195 ANNOUNCE_ME = 1;
196 ANNOUNCE_MEPLUS = 2;
197 ANNOUNCE_ALL = 3;
199 CONFIG_FILENAME = 'Doom2DF.cfg';
200 LOG_FILENAME = 'Doom2DF.log';
202 TEST_MAP_NAME = '$$$_TEST_$$$';
204 STD_PLAYER_MODEL = 'Doomer';
206 var
207 gStdFont: DWORD;
208 gGameSettings: TGameSettings;
209 gPlayer1Settings: TPlayerSettings;
210 gPlayer2Settings: TPlayerSettings;
211 gGameOn: Boolean;
212 gPlayerScreenSize: TDFPoint;
213 gPlayer1ScreenCoord: TDFPoint;
214 gPlayer2ScreenCoord: TDFPoint;
215 gPlayer1: TPlayer = nil;
216 gPlayer2: TPlayer = nil;
217 gPlayerDrawn: TPlayer = nil;
218 gTime: LongWord;
219 gSwitchGameMode: Byte = GM_DM;
220 gHearPoint1, gHearPoint2: THearPoint;
221 gSoundEffectsDF: Boolean = False;
222 gSoundTriggerTime: Word = 0;
223 gAnnouncer: Byte = ANNOUNCE_NONE;
224 goodsnd: array[0..3] of TPlayableSound;
225 killsnd: array[0..3] of TPlayableSound;
226 gDefInterTime: ShortInt = -1;
227 gInterEndTime: LongWord = 0;
228 gInterTime: LongWord = 0;
229 gServInterTime: Byte = 0;
230 gGameStartTime: LongWord = 0;
231 gTotalMonsters: Integer = 0;
232 gPause: Boolean;
233 gShowTime: Boolean = True;
234 gShowFPS: Boolean = False;
235 gShowGoals: Boolean = True;
236 gShowStat: Boolean = True;
237 gShowKillMsg: Boolean = True;
238 gShowLives: Boolean = True;
239 gShowPing: Boolean = False;
240 gShowMap: Boolean = False;
241 gExit: Byte = 0;
242 gState: Byte = STATE_NONE;
243 sX, sY: Integer;
244 sWidth, sHeight: Word;
245 gSpectMode: Byte = SPECT_NONE;
246 gSpectHUD: Boolean = True;
247 gSpectKeyPress: Boolean = False;
248 gSpectX: Integer = 0;
249 gSpectY: Integer = 0;
250 gSpectStep: Byte = 8;
251 gSpectViewTwo: Boolean = False;
252 gSpectPID1: Integer = -1;
253 gSpectPID2: Integer = -1;
254 gMusic: TMusic = nil;
255 gLoadGameMode: Boolean;
256 gCheats: Boolean = False;
257 gMapOnce: Boolean = False;
258 gMapToDelete: String;
259 gTempDelete: Boolean = False;
260 gLastMap: Boolean = False;
261 gWinPosX, gWinPosY: Integer;
262 gWinSizeX, gWinSizeY: Integer;
263 gWinFrameX, gWinFrameY, gWinCaption: Integer;
264 gWinActive: Boolean = True; // by default window is active, lol
265 gResolutionChange: Boolean = False;
266 gRC_Width, gRC_Height: Word;
267 gRC_FullScreen, gRC_Maximized: Boolean;
268 gLanguageChange: Boolean = False;
269 gDebugMode: Boolean = False;
270 g_debug_Sounds: Boolean = False;
271 g_debug_Frames: Boolean = False;
272 g_debug_WinMsgs: Boolean = False;
273 g_debug_MonsterOff: Boolean = False;
274 g_debug_BotAIOff: Byte = 0;
275 g_debug_HealthBar: Boolean = False;
276 g_Debug_Player: Boolean = False;
277 gCoopMonstersKilled: Word = 0;
278 gCoopSecretsFound: Word = 0;
279 gCoopTotalMonstersKilled: Word = 0;
280 gCoopTotalSecretsFound: Word = 0;
281 gCoopTotalMonsters: Word = 0;
282 gCoopTotalSecrets: Word = 0;
283 gStatsOff: Boolean = False;
284 gStatsPressed: Boolean = False;
285 gExitByTrigger: Boolean = False;
286 gNextMap: String = '';
287 gLMSRespawn: Byte = LMS_RESPAWN_NONE;
288 gLMSRespawnTime: Cardinal = 0;
289 gLMSSoftSpawn: Boolean = False;
290 gMissionFailed: Boolean = False;
291 gVoteInProgress: Boolean = False;
292 gVotePassed: Boolean = False;
293 gVoteCommand: string = '';
294 gVoteTimer: Cardinal = 0;
295 gVoteCmdTimer: Cardinal = 0;
296 gVoteCount: Integer = 0;
297 gVoteTimeout: Cardinal = 30;
298 gVoted: Boolean = False;
299 gVotesEnabled: Boolean = True;
300 gEvents: Array of TGameEvent;
301 gDelayedEvents: Array of TDelayedEvent;
303 // move button values:
304 // bits 0-1: l/r state:
305 // 0: neither left, nor right pressed
306 // 1: left pressed
307 // 2: right pressed
308 // bits 4-5: l/r state when strafe was pressed
309 P1MoveButton: Byte = 0;
310 P2MoveButton: Byte = 0;
312 g_profile_frame_update: Boolean = false;
313 g_profile_frame_draw: Boolean = false;
314 g_profile_collision: Boolean = false;
315 g_profile_los: Boolean = false;
316 g_profile_history_size: Integer = 1000;
318 g_rlayer_back: Boolean = true;
319 g_rlayer_step: Boolean = true;
320 g_rlayer_wall: Boolean = true;
321 g_rlayer_door: Boolean = true;
322 g_rlayer_acid1: Boolean = true;
323 g_rlayer_acid2: Boolean = true;
324 g_rlayer_water: Boolean = true;
325 g_rlayer_fore: Boolean = true;
328 procedure g_ResetDynlights ();
329 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
330 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
332 function conIsCheatsEnabled (): Boolean;
335 implementation
337 uses
338 g_textures, g_main, g_window, g_menu,
339 e_input, e_log, g_console, g_items, g_map, g_panel,
340 g_playermodel, g_gfx, g_options, g_weapons, Math,
341 g_triggers, g_monsters, e_sound, CONFIG,
342 BinEditor, g_language, g_net, SDL,
343 ENet, e_msg, g_netmsg, g_netmaster, GL, GLExt,
344 utils, sfs, g_holmes;
347 // ////////////////////////////////////////////////////////////////////////// //
348 function conIsCheatsEnabled (): Boolean;
349 begin
350 result := false;
351 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
352 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then exit;
353 result := true;
354 end;
357 // ////////////////////////////////////////////////////////////////////////// //
358 var
359 profileFrameDraw: TProfiler = nil;
362 // ////////////////////////////////////////////////////////////////////////// //
363 type
364 TDynLight = record
365 x, y, radius: Integer;
366 r, g, b, a: Single;
367 exploCount: Integer;
368 exploRadius: Integer;
369 end;
371 var
372 g_dynLights: array of TDynLight = nil;
373 g_dynLightCount: Integer = 0;
374 g_playerLight: Boolean = false;
376 procedure g_ResetDynlights ();
377 var
378 lnum, idx: Integer;
379 begin
380 if not gwin_has_stencil then begin g_dynLightCount := 0; exit; end;
381 lnum := 0;
382 for idx := 0 to g_dynLightCount-1 do
383 begin
384 if g_dynLights[idx].exploCount = -666 then
385 begin
386 // skip it
387 end
388 else
389 begin
390 // explosion
391 Inc(g_dynLights[idx].exploCount);
392 if (g_dynLights[idx].exploCount < 10) then
393 begin
394 g_dynLights[idx].radius := g_dynLights[idx].exploRadius+g_dynLights[idx].exploCount*8;
395 g_dynLights[idx].a := 0.4+g_dynLights[idx].exploCount/10;
396 if (g_dynLights[idx].a > 0.8) then g_dynLights[idx].a := 0.8;
397 if lnum <> idx then g_dynLights[lnum] := g_dynLights[idx];
398 Inc(lnum);
399 end;
400 end;
401 end;
402 g_dynLightCount := lnum;
403 end;
405 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
406 begin
407 if not gwin_has_stencil then exit;
408 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
409 g_dynLights[g_dynLightCount].x := x;
410 g_dynLights[g_dynLightCount].y := y;
411 g_dynLights[g_dynLightCount].radius := radius;
412 g_dynLights[g_dynLightCount].r := r;
413 g_dynLights[g_dynLightCount].g := g;
414 g_dynLights[g_dynLightCount].b := b;
415 g_dynLights[g_dynLightCount].a := a;
416 g_dynLights[g_dynLightCount].exploCount := -666;
417 Inc(g_dynLightCount);
418 end;
420 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
421 begin
422 if not gwin_has_stencil then exit;
423 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
424 g_dynLights[g_dynLightCount].x := x;
425 g_dynLights[g_dynLightCount].y := y;
426 g_dynLights[g_dynLightCount].radius := 0;
427 g_dynLights[g_dynLightCount].exploRadius := radius;
428 g_dynLights[g_dynLightCount].r := r;
429 g_dynLights[g_dynLightCount].g := g;
430 g_dynLights[g_dynLightCount].b := b;
431 g_dynLights[g_dynLightCount].a := 0;
432 g_dynLights[g_dynLightCount].exploCount := 0;
433 Inc(g_dynLightCount);
434 end;
437 // ////////////////////////////////////////////////////////////////////////// //
438 function calcProfilesHeight (prof: TProfiler): Integer;
439 begin
440 result := 0;
441 if (prof = nil) then exit;
442 if (length(prof.bars) = 0) then exit;
443 result := length(prof.bars)*(16+2);
444 end;
446 // returns width
447 function drawProfiles (x, y: Integer; prof: TProfiler): Integer;
448 var
449 wdt, hgt: Integer;
450 yy: Integer;
451 ii: Integer;
452 begin
453 result := 0;
454 if (prof = nil) then exit;
455 // gScreenWidth
456 if (length(prof.bars) = 0) then exit;
457 wdt := 192;
458 hgt := calcProfilesHeight(prof);
459 if (x < 0) then x := gScreenWidth-(wdt-1)+x;
460 if (y < 0) then y := gScreenHeight-(hgt-1)+y;
461 // background
462 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
463 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
464 e_DarkenQuadWH(x, y, wdt, hgt, 150);
465 // title
466 yy := y+2;
467 for ii := 0 to High(prof.bars) do
468 begin
469 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);
470 Inc(yy, 16+2);
471 end;
472 result := wdt;
473 end;
476 // ////////////////////////////////////////////////////////////////////////// //
477 type
478 TEndCustomGameStat = record
479 PlayerStat: TPlayerStatArray;
480 TeamStat: TTeamStat;
481 GameTime: LongWord;
482 GameMode: Byte;
483 Map, MapName: String;
484 end;
486 TEndSingleGameStat = record
487 PlayerStat: Array [0..1] of record
488 Kills: Integer;
489 Secrets: Integer;
490 end;
491 GameTime: LongWord;
492 TwoPlayers: Boolean;
493 TotalSecrets: Integer;
494 end;
496 TLoadingStat = record
497 CurValue: Integer;
498 MaxValue: Integer;
499 ShowCount: Integer;
500 Msgs: Array of String;
501 NextMsg: Word;
502 end;
504 TParamStrValue = record
505 Name: String;
506 Value: String;
507 end;
509 TParamStrValues = Array of TParamStrValue;
511 const
512 INTER_ACTION_TEXT = 1;
513 INTER_ACTION_PIC = 2;
514 INTER_ACTION_MUSIC = 3;
516 var
517 FPS, UPS: Word;
518 FPSCounter, UPSCounter: Word;
519 FPSTime, UPSTime: LongWord;
520 DataLoaded: Boolean = False;
521 LastScreenShot: Int64;
522 IsDrawStat: Boolean = False;
523 CustomStat: TEndCustomGameStat;
524 SingleStat: TEndSingleGameStat;
525 LoadingStat: TLoadingStat;
526 EndingGameCounter: Byte = 0;
527 MessageText: String;
528 MessageTime: Word;
529 MapList: SArray = nil;
530 MapIndex: Integer = -1;
531 MegaWAD: record
532 info: TMegaWADInfo;
533 endpic: String;
534 endmus: String;
535 res: record
536 text: Array of ShortString;
537 anim: Array of ShortString;
538 pic: Array of ShortString;
539 mus: Array of ShortString;
540 end;
541 triggers: Array of record
542 event: ShortString;
543 actions: Array of record
544 action, p1, p2: Integer;
545 end;
546 end;
547 cur_trigger: Integer;
548 cur_action: Integer;
549 end;
550 //InterPic: String;
551 InterText: record
552 lines: SArray;
553 img: String;
554 cur_line: Integer;
555 cur_char: Integer;
556 counter: Integer;
557 endtext: Boolean;
558 end;
560 function Compare(a, b: TPlayerStat): Integer;
561 begin
562 if a.Spectator then Result := 1
563 else if b.Spectator then Result := -1
564 else if a.Frags < b.Frags then Result := 1
565 else if a.Frags > b.Frags then Result := -1
566 else if a.Deaths < b.Deaths then Result := -1
567 else if a.Deaths > b.Deaths then Result := 1
568 else if a.Kills < b.Kills then Result := -1
569 else Result := 1;
570 end;
572 procedure SortGameStat(var stat: TPlayerStatArray);
573 var
574 I, J: Integer;
575 T: TPlayerStat;
576 begin
577 if stat = nil then Exit;
579 for I := High(stat) downto Low(stat) do
580 for J := Low(stat) to High(stat) - 1 do
581 if Compare(stat[J], stat[J + 1]) = 1 then
582 begin
583 T := stat[J];
584 stat[J] := stat[J + 1];
585 stat[J + 1] := T;
586 end;
587 end;
589 function g_Game_ModeToText(Mode: Byte): string;
590 begin
591 Result := '';
592 case Mode of
593 GM_DM: Result := _lc[I_MENU_GAME_TYPE_DM];
594 GM_TDM: Result := _lc[I_MENU_GAME_TYPE_TDM];
595 GM_CTF: Result := _lc[I_MENU_GAME_TYPE_CTF];
596 GM_COOP: Result := _lc[I_MENU_GAME_TYPE_COOP];
597 GM_SINGLE: Result := _lc[I_MENU_GAME_TYPE_SINGLE];
598 end;
599 end;
601 function g_Game_TextToMode(Mode: string): Byte;
602 begin
603 Result := GM_NONE;
604 Mode := UpperCase(Mode);
605 if Mode = _lc[I_MENU_GAME_TYPE_DM] then
606 begin
607 Result := GM_DM;
608 Exit;
609 end;
610 if Mode = _lc[I_MENU_GAME_TYPE_TDM] then
611 begin
612 Result := GM_TDM;
613 Exit;
614 end;
615 if Mode = _lc[I_MENU_GAME_TYPE_CTF] then
616 begin
617 Result := GM_CTF;
618 Exit;
619 end;
620 if Mode = _lc[I_MENU_GAME_TYPE_COOP] then
621 begin
622 Result := GM_COOP;
623 Exit;
624 end;
625 if Mode = _lc[I_MENU_GAME_TYPE_SINGLE] then
626 begin
627 Result := GM_SINGLE;
628 Exit;
629 end;
630 end;
632 function g_Game_IsNet(): Boolean;
633 begin
634 Result := (gGameSettings.GameType in [GT_SERVER, GT_CLIENT]);
635 end;
637 function g_Game_IsServer(): Boolean;
638 begin
639 Result := (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM, GT_SERVER]);
640 end;
642 function g_Game_IsClient(): Boolean;
643 begin
644 Result := (gGameSettings.GameType = GT_CLIENT);
645 end;
647 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
648 var
649 w: TWADFile;
650 cfg: TConfig;
651 p: Pointer;
652 len: Integer;
653 begin
654 Result.name := ExtractFileName(WAD);
655 Result.description := '';
656 Result.author := '';
658 w := TWADFile.Create();
659 w.ReadFile(WAD);
661 if not w.GetResource('INTERSCRIPT', p, len) then
662 begin
663 w.Free();
664 Exit;
665 end;
667 cfg := TConfig.CreateMem(p, len);
668 Result.name := cfg.ReadStr('megawad', 'name', ExtractFileName(WAD));
669 Result.description := cfg.ReadStr('megawad', 'description', '');
670 Result.author := cfg.ReadStr('megawad', 'author', '');
671 Result.pic := cfg.ReadStr('megawad', 'pic', '');
672 cfg.Free();
674 FreeMem(p);
675 end;
677 procedure g_Game_FreeWAD();
678 var
679 a: Integer;
680 begin
681 for a := 0 to High(MegaWAD.res.pic) do
682 if MegaWAD.res.pic[a] <> '' then
683 g_Texture_Delete(MegaWAD.res.pic[a]);
685 for a := 0 to High(MegaWAD.res.mus) do
686 if MegaWAD.res.mus[a] <> '' then
687 g_Sound_Delete(MegaWAD.res.mus[a]);
689 MegaWAD.res.pic := nil;
690 MegaWAD.res.text := nil;
691 MegaWAD.res.anim := nil;
692 MegaWAD.res.mus := nil;
693 MegaWAD.triggers := nil;
695 g_Texture_Delete('TEXTURE_endpic');
696 g_Sound_Delete('MUSIC_endmus');
698 ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
699 gGameSettings.WAD := '';
700 end;
702 procedure g_Game_LoadWAD(WAD: string);
703 var
704 w: TWADFile;
705 cfg: TConfig;
706 p: Pointer;
707 {b, }len: Integer;
708 s: string;
709 begin
710 g_Game_FreeWAD();
711 gGameSettings.WAD := WAD;
712 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then
713 Exit;
715 MegaWAD.info := g_Game_GetMegaWADInfo(MapsDir + WAD);
717 w := TWADFile.Create();
718 w.ReadFile(MapsDir + WAD);
720 if not w.GetResource('INTERSCRIPT', p, len) then
721 begin
722 w.Free();
723 Exit;
724 end;
726 cfg := TConfig.CreateMem(p, len);
728 {b := 1;
729 while True do
730 begin
731 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
732 if s = '' then Break;
733 b := b+1;
735 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
736 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
738 g_Texture_CreateWADEx(s, s);
739 end;
741 b := 1;
742 while True do
743 begin
744 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
745 if s = '' then Break;
746 b := b+1;
748 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
749 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
751 g_Music_CreateWADEx(s, s);
752 end;}
754 MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
755 if MegaWAD.endpic <> '' then
756 begin
757 s := g_ExtractWadName(MegaWAD.endpic);
758 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
759 g_Texture_CreateWADEx('TEXTURE_endpic', s+MegaWAD.endpic);
760 end;
761 MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
762 if MegaWAD.endmus <> '' then
763 begin
764 s := g_ExtractWadName(MegaWAD.endmus);
765 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
766 g_Sound_CreateWADEx('MUSIC_endmus', s+MegaWAD.endmus, True);
767 end;
769 cfg.Free();
770 FreeMem(p);
771 w.Free();
772 end;
774 {procedure start_trigger(t: string);
775 begin
776 end;
778 function next_trigger(): Boolean;
779 begin
780 end;}
782 procedure DisableCheats();
783 begin
784 MAX_RUNVEL := 8;
785 VEL_JUMP := 10;
786 gFly := False;
788 if gPlayer1 <> nil then gPlayer1.GodMode := False;
789 if gPlayer2 <> nil then gPlayer2.GodMode := False;
790 if gPlayer1 <> nil then gPlayer1.NoTarget := False;
791 if gPlayer2 <> nil then gPlayer2.NoTarget := False;
793 {$IF DEFINED(D2F_DEBUG)}
794 if gPlayer1 <> nil then gPlayer1.NoTarget := True;
795 gAimLine := true;
796 {$ENDIF}
797 end;
799 procedure g_Game_ExecuteEvent(Name: String);
800 var
801 a: Integer;
802 begin
803 if Name = '' then
804 Exit;
805 if gEvents = nil then
806 Exit;
807 for a := 0 to High(gEvents) do
808 if gEvents[a].Name = Name then
809 begin
810 if gEvents[a].Command <> '' then
811 g_Console_Process(gEvents[a].Command, True);
812 break;
813 end;
814 end;
816 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
817 var
818 a, n: Integer;
819 begin
820 n := -1;
821 if gDelayedEvents <> nil then
822 for a := 0 to High(gDelayedEvents) do
823 if not gDelayedEvents[a].Pending then
824 begin
825 n := a;
826 break;
827 end;
828 if n = -1 then
829 begin
830 SetLength(gDelayedEvents, Length(gDelayedEvents) + 1);
831 n := High(gDelayedEvents);
832 end;
833 gDelayedEvents[n].Pending := True;
834 gDelayedEvents[n].DEType := DEType;
835 gDelayedEvents[n].DENum := Num;
836 gDelayedEvents[n].DEStr := Str;
837 if DEType = DE_GLOBEVENT then
838 gDelayedEvents[n].Time := (GetTimer() {div 1000}) + Time
839 else
840 gDelayedEvents[n].Time := gTime + Time;
841 Result := n;
842 end;
844 procedure EndGame();
845 var
846 a: Integer;
847 FileName: string;
848 begin
849 if g_Game_IsNet and g_Game_IsServer then
850 MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed));
852 // Ñòîï èãðà:
853 gPause := False;
854 gGameOn := False;
856 g_Game_StopAllSounds(False);
858 MessageTime := 0;
859 MessageText := '';
861 EndingGameCounter := 0;
862 g_ActiveWindow := nil;
864 gLMSRespawn := LMS_RESPAWN_NONE;
865 gLMSRespawnTime := 0;
867 case gExit of
868 EXIT_SIMPLE: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
869 begin
870 g_Game_Free();
872 if gMapOnce then
873 begin // Ýòî áûë òåñò
874 g_Game_Quit();
875 end
876 else
877 begin // Âûõîä â ãëàâíîå ìåíþ
878 gMusic.SetByName('MUSIC_MENU');
879 gMusic.Play();
880 if gState <> STATE_SLIST then
881 begin
882 g_GUI_ShowWindow('MainMenu');
883 gState := STATE_MENU;
884 end else
885 begin
886 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
887 slReturnPressed := True;
888 if g_Net_Slist_Fetch(slCurrent) then
889 begin
890 if slCurrent = nil then
891 slWaitStr := _lc[I_NET_SLIST_NOSERVERS];
892 end
893 else
894 slWaitStr := _lc[I_NET_SLIST_ERROR];
895 end;
897 g_Game_ExecuteEvent('ongameend');
898 end;
899 end;
901 EXIT_RESTART: // Íà÷àòü óðîâåíü ñíà÷àëà
902 begin
903 if not g_Game_IsClient then g_Game_Restart();
904 end;
906 EXIT_ENDLEVELCUSTOM: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
907 begin
908 // Ñòàòèñòèêà Ñâîåé èãðû:
909 FileName := g_ExtractWadName(gMapInfo.Map);
911 CustomStat.GameTime := gTime;
912 CustomStat.Map := ExtractFileName(FileName)+':'+g_ExtractFileName(gMapInfo.Map); //ResName;
913 CustomStat.MapName := gMapInfo.Name;
914 CustomStat.GameMode := gGameSettings.GameMode;
915 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
916 CustomStat.TeamStat := gTeamStat;
918 CustomStat.PlayerStat := nil;
920 // Ñòàòèñòèêà èãðîêîâ:
921 if gPlayers <> nil then
922 begin
923 for a := 0 to High(gPlayers) do
924 if gPlayers[a] <> nil then
925 begin
926 SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1);
927 with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do
928 begin
929 Name := gPlayers[a].Name;
930 Frags := gPlayers[a].Frags;
931 Deaths := gPlayers[a].Death;
932 Kills := gPlayers[a].Kills;
933 Team := gPlayers[a].Team;
934 Color := gPlayers[a].Model.Color;
935 Spectator := gPlayers[a].FSpectator;
936 end;
937 end;
939 SortGameStat(CustomStat.PlayerStat);
940 end;
942 g_Game_ExecuteEvent('onmapend');
944 // Çàòóõàþùèé ýêðàí:
945 EndingGameCounter := 255;
946 gState := STATE_FOLD;
947 gInterTime := 0;
948 if gDefInterTime < 0 then
949 gInterEndTime := IfThen((gGameSettings.GameType = GT_SERVER) and (gPlayer1 = nil), 15000, 25000)
950 else
951 gInterEndTime := gDefInterTime * 1000;
952 end;
954 EXIT_ENDLEVELSINGLE: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
955 begin
956 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
957 SingleStat.GameTime := gTime;
958 SingleStat.TwoPlayers := gPlayer2 <> nil;
959 SingleStat.TotalSecrets := gSecretsCount;
960 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
961 SingleStat.PlayerStat[0].Kills := gPlayer1.MonsterKills;
962 SingleStat.PlayerStat[0].Secrets := gPlayer1.Secrets;
963 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
964 if SingleStat.TwoPlayers then
965 begin
966 SingleStat.PlayerStat[1].Kills := gPlayer2.MonsterKills;
967 SingleStat.PlayerStat[1].Secrets := gPlayer2.Secrets;
968 end;
970 g_Game_ExecuteEvent('onmapend');
972 // Åñòü åùå êàðòû:
973 if gNextMap <> '' then
974 begin
975 gMusic.SetByName('MUSIC_INTERMUS');
976 gMusic.Play();
977 gState := STATE_INTERSINGLE;
979 g_Game_ExecuteEvent('oninter');
980 end
981 else // Áîëüøå íåò êàðò
982 begin
983 // Çàòóõàþùèé ýêðàí:
984 EndingGameCounter := 255;
985 gState := STATE_FOLD;
986 end;
987 end;
988 end;
990 // Îêîí÷àíèå îáðàáîòàíî:
991 if gExit <> EXIT_QUIT then
992 gExit := 0;
993 end;
995 procedure drawTime(X, Y: Integer); inline;
996 begin
997 e_TextureFontPrint(x, y,
998 Format('%d:%.2d:%.2d', [
999 gTime div 1000 div 3600,
1000 (gTime div 1000 div 60) mod 60,
1001 gTime div 1000 mod 60
1002 ]),
1003 gStdFont);
1004 end;
1006 procedure DrawStat();
1007 var
1008 pc, x, y, w, h: Integer;
1009 w1, w2, w3, w4: Integer;
1010 a, aa: Integer;
1011 cw, ch, r, g, b, rr, gg, bb: Byte;
1012 s1, s2, s3: String;
1013 _y: Integer;
1014 stat: TPlayerStatArray;
1015 wad, map: string;
1016 mapstr: string;
1017 begin
1018 s1 := '';
1019 s2 := '';
1020 s3 := '';
1021 pc := g_Player_GetCount;
1022 e_TextureFontGetSize(gStdFont, cw, ch);
1024 w := gScreenWidth-(gScreenWidth div 5);
1025 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1026 h := 32+ch*(11+pc)
1027 else
1028 h := 40+ch*5+(ch+8)*pc;
1029 x := (gScreenWidth div 2)-(w div 2);
1030 y := (gScreenHeight div 2)-(h div 2);
1032 e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32);
1033 e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0);
1035 drawTime(x+w-78, y+8);
1037 wad := g_ExtractWadNameNoPath(gMapInfo.Map);
1038 map := g_ExtractFileName(gMapInfo.Map);
1039 mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name;
1041 case gGameSettings.GameMode of
1042 GM_DM:
1043 begin
1044 if gGameSettings.MaxLives = 0 then
1045 s1 := _lc[I_GAME_DM]
1046 else
1047 s1 := _lc[I_GAME_LMS];
1048 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
1049 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1050 end;
1052 GM_TDM:
1053 begin
1054 if gGameSettings.MaxLives = 0 then
1055 s1 := _lc[I_GAME_TDM]
1056 else
1057 s1 := _lc[I_GAME_TLMS];
1058 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
1059 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1060 end;
1062 GM_CTF:
1063 begin
1064 s1 := _lc[I_GAME_CTF];
1065 s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.GoalLimit]);
1066 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1067 end;
1069 GM_COOP:
1070 begin
1071 if gGameSettings.MaxLives = 0 then
1072 s1 := _lc[I_GAME_COOP]
1073 else
1074 s1 := _lc[I_GAME_SURV];
1075 s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters);
1076 s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount);
1077 end;
1079 else
1080 begin
1081 s1 := '';
1082 s2 := '';
1083 end;
1084 end;
1086 _y := y+8;
1087 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1);
1088 _y := _y+ch+8;
1089 e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1);
1090 _y := _y+ch+8;
1091 e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1);
1093 e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3,
1094 gStdFont, 200, 200, 200, 1);
1096 if NetMode = NET_SERVER then
1097 e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1)
1098 else
1099 if NetMode = NET_CLIENT then
1100 e_TextureFontPrintEx(x+8, y + 8,
1101 NetClientIP + ':' + IntToStr(NetClientPort), gStdFont, 255, 255, 255, 1);
1103 if pc = 0 then
1104 Exit;
1105 stat := g_Player_GetStats();
1106 SortGameStat(stat);
1108 w2 := (w-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1109 w3 := (w-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1110 w4 := w3;
1111 w1 := w-16-w2-w3-w4; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1113 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1114 begin
1115 _y := _y+ch+ch;
1117 for a := TEAM_RED to TEAM_BLUE do
1118 begin
1119 if a = TEAM_RED then
1120 begin
1121 s1 := _lc[I_GAME_TEAM_RED];
1122 r := 255;
1123 g := 0;
1124 b := 0;
1125 end
1126 else
1127 begin
1128 s1 := _lc[I_GAME_TEAM_BLUE];
1129 r := 0;
1130 g := 0;
1131 b := 255;
1132 end;
1134 e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1);
1135 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Goals),
1136 gStdFont, r, g, b, 1);
1138 _y := _y+ch+(ch div 4);
1139 e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b);
1140 _y := _y+(ch div 4);
1142 for aa := 0 to High(stat) do
1143 if stat[aa].Team = a then
1144 with stat[aa] do
1145 begin
1146 if Spectator then
1147 begin
1148 rr := r div 2;
1149 gg := g div 2;
1150 bb := b div 2;
1151 end
1152 else
1153 begin
1154 rr := r;
1155 gg := g;
1156 bb := b;
1157 end;
1158 // Èìÿ
1159 e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
1160 // Ïèíã/ïîòåðè
1161 e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1);
1162 // Ôðàãè
1163 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
1164 // Ñìåðòè
1165 e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
1166 _y := _y+ch;
1167 end;
1169 _y := _y+ch;
1170 end;
1171 end
1172 else if gGameSettings.GameMode in [GM_DM, GM_COOP] then
1173 begin
1174 _y := _y+ch+ch;
1175 e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
1176 e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1);
1177 e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
1178 e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
1180 _y := _y+ch+8;
1181 for aa := 0 to High(stat) do
1182 with stat[aa] do
1183 begin
1184 if Spectator then
1185 begin
1186 r := 127;
1187 g := 64;
1188 end
1189 else
1190 begin
1191 r := 255;
1192 g := 127;
1193 end;
1194 // Öâåò èãðîêà
1195 e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
1196 e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192);
1197 // Èìÿ
1198 e_TextureFontPrintEx(x+16+16+8, _y+4, Name, gStdFont, r, g, 0, 1);
1199 // Ïèíã/ïîòåðè
1200 e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1);
1201 // Ôðàãè
1202 e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1);
1203 // Ñìåðòè
1204 e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1);
1205 _y := _y+ch+8;
1206 end;
1207 end
1208 end;
1210 procedure g_Game_Init();
1211 var
1212 SR: TSearchRec;
1213 begin
1214 gExit := 0;
1215 gMapToDelete := '';
1216 gTempDelete := False;
1218 sfsGCDisable(); // temporary disable removing of temporary volumes
1220 try
1221 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE');
1222 g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER');
1223 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN');
1224 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU');
1226 LoadStdFont('STDTXT', 'STDFONT', gStdFont);
1227 LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
1228 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
1230 g_Game_ClearLoading();
1231 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
1232 g_Game_SetLoadingText('', 0, False);
1234 g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
1235 g_Console_Init();
1237 g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
1238 g_PlayerModel_LoadData();
1240 if FindFirst(ModelsDir+'*.wad', faAnyFile, SR) = 0 then
1241 repeat
1242 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1243 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1244 until FindNext(SR) <> 0;
1245 FindClose(SR);
1247 if FindFirst(ModelsDir+'*.pk3', faAnyFile, SR) = 0 then
1248 repeat
1249 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1250 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1251 until FindNext(SR) <> 0;
1252 FindClose(SR);
1254 if FindFirst(ModelsDir+'*.zip', faAnyFile, SR) = 0 then
1255 repeat
1256 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1257 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1258 until FindNext(SR) <> 0;
1259 FindClose(SR);
1261 gGameOn := False;
1262 gPause := False;
1263 gTime := 0;
1264 LastScreenShot := 0;
1266 {e_MouseInfo.Accel := 1.0;}
1268 g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
1269 g_Game_LoadData();
1271 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1272 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
1273 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
1274 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True);
1275 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
1277 g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
1278 g_Menu_Init();
1280 gMusic := TMusic.Create();
1281 gMusic.SetByName('MUSIC_MENU');
1282 gMusic.Play();
1284 gGameSettings.WarmupTime := 30;
1286 gState := STATE_MENU;
1288 SetLength(gEvents, 6);
1289 gEvents[0].Name := 'ongamestart';
1290 gEvents[1].Name := 'ongameend';
1291 gEvents[2].Name := 'onmapstart';
1292 gEvents[3].Name := 'onmapend';
1293 gEvents[4].Name := 'oninter';
1294 gEvents[5].Name := 'onwadend';
1295 finally
1296 sfsGCEnable(); // enable releasing unused volumes
1297 end;
1298 end;
1300 procedure g_Game_Free();
1301 begin
1302 if NetMode = NET_CLIENT then g_Net_Disconnect();
1303 if NetMode = NET_SERVER then g_Net_Host_Die();
1305 g_Map_Free();
1306 g_Player_Free();
1307 g_Player_RemoveAllCorpses();
1309 gGameSettings.GameType := GT_NONE;
1310 if gGameSettings.GameMode = GM_SINGLE then
1311 gGameSettings.GameMode := GM_DM;
1312 gSwitchGameMode := gGameSettings.GameMode;
1314 gChatShow := False;
1315 gExitByTrigger := False;
1316 end;
1318 function IsActivePlayer(p: TPlayer): Boolean;
1319 begin
1320 Result := False;
1321 if p = nil then
1322 Exit;
1323 Result := (not p.FDummy) and (not p.FSpectator);
1324 end;
1326 function GetActivePlayer_ByID(ID: Integer): TPlayer;
1327 var
1328 a: Integer;
1329 begin
1330 Result := nil;
1331 if ID < 0 then
1332 Exit;
1333 if gPlayers = nil then
1334 Exit;
1335 for a := Low(gPlayers) to High(gPlayers) do
1336 if IsActivePlayer(gPlayers[a]) then
1337 begin
1338 if gPlayers[a].UID <> ID then
1339 continue;
1340 Result := gPlayers[a];
1341 break;
1342 end;
1343 end;
1345 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
1346 var
1347 a, idx: Integer;
1348 ids: Array of Word;
1349 begin
1350 Result := -1;
1351 if gPlayers = nil then
1352 Exit;
1353 SetLength(ids, 0);
1354 idx := -1;
1355 for a := Low(gPlayers) to High(gPlayers) do
1356 if IsActivePlayer(gPlayers[a]) then
1357 begin
1358 SetLength(ids, Length(ids) + 1);
1359 ids[High(ids)] := gPlayers[a].UID;
1360 if gPlayers[a].UID = Skip then
1361 idx := High(ids);
1362 end;
1363 if Length(ids) = 0 then
1364 Exit;
1365 if idx = -1 then
1366 Result := ids[0]
1367 else
1368 Result := ids[(idx + 1) mod Length(ids)];
1369 end;
1371 function GetActivePlayerID_Prev(Skip: Integer = -1): Integer;
1372 var
1373 a, idx: Integer;
1374 ids: Array of Word;
1375 begin
1376 Result := -1;
1377 if gPlayers = nil then
1378 Exit;
1379 SetLength(ids, 0);
1380 idx := -1;
1381 for a := Low(gPlayers) to High(gPlayers) do
1382 if IsActivePlayer(gPlayers[a]) then
1383 begin
1384 SetLength(ids, Length(ids) + 1);
1385 ids[High(ids)] := gPlayers[a].UID;
1386 if gPlayers[a].UID = Skip then
1387 idx := High(ids);
1388 end;
1389 if Length(ids) = 0 then
1390 Exit;
1391 if idx = -1 then
1392 Result := ids[Length(ids) - 1]
1393 else
1394 Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
1395 end;
1397 function isKeyPressed (key1: Word; key2: Word): Boolean;
1398 begin
1399 if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
1400 if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
1401 result := false;
1402 end;
1404 procedure processPlayerControls (plr: TPlayer; var ctrl: TPlayerControl; var MoveButton: Byte; p2hack: Boolean=false);
1405 var
1406 time: Word;
1407 strafeDir: Byte;
1408 i: Integer;
1409 begin
1410 if (plr = nil) then exit;
1411 if (p2hack) then time := 1000 else time := 1;
1412 strafeDir := MoveButton shr 4;
1413 MoveButton := MoveButton and $0F;
1414 with ctrl do
1415 begin
1416 if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1417 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1418 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1420 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1421 if MoveButton = 1 then plr.PressKey(KEY_LEFT, time)
1422 else if MoveButton = 2 then plr.PressKey(KEY_RIGHT, time);
1424 // if we have "strafe" key, turn off old strafe mechanics
1425 if isKeyPressed(KeyStrafe, KeyStrafe2) then
1426 begin
1427 // new strafe mechanics
1428 if (strafeDir = 0) then strafeDir := MoveButton; // start strafing
1429 // now set direction according to strafe (reversed)
1430 if (strafeDir = 2) then plr.SetDirection(D_LEFT)
1431 else if (strafeDir = 1) then plr.SetDirection(D_RIGHT);
1432 end
1433 else
1434 begin
1435 strafeDir := 0; // not strafing anymore
1436 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1437 if (MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then plr.SetDirection(D_LEFT)
1438 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1439 else if (MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then plr.SetDirection(D_RIGHT)
1440 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1441 else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1));
1442 end;
1444 // fix movebutton state
1445 MoveButton := MoveButton or (strafeDir shl 4);
1447 // Îñòàëüíûå êëàâèøè:
1448 if isKeyPressed(KeyJump, KeyJump2) then plr.PressKey(KEY_JUMP, time);
1449 if isKeyPressed(KeyUp, KeyUp2) then plr.PressKey(KEY_UP, time);
1450 if isKeyPressed(KeyDown, KeyDown2) then plr.PressKey(KEY_DOWN, time);
1451 if isKeyPressed(KeyFire, KeyFire2) then plr.PressKey(KEY_FIRE);
1452 if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then plr.PressKey(KEY_NEXTWEAPON);
1453 if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then plr.PressKey(KEY_PREVWEAPON);
1454 if isKeyPressed(KeyOpen, KeyOpen2) then plr.PressKey(KEY_OPEN);
1456 for i := 0 to High(KeyWeapon) do
1457 if isKeyPressed(KeyWeapon[i], KeyWeapon2[i]) then
1458 plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
1459 end;
1461 // HACK: add dynlight here
1462 if gwin_k8_enable_light_experiments then
1463 begin
1464 if e_KeyPressed(IK_F8) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1465 begin
1466 g_playerLight := true;
1467 end;
1468 if e_KeyPressed(IK_F9) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1469 begin
1470 g_playerLight := false;
1471 end;
1472 end;
1474 if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
1475 end;
1477 procedure g_Game_Update();
1478 var
1479 Msg: g_gui.TMessage;
1480 Time: Int64;
1481 a: Byte;
1482 w: Word;
1483 i, b: Integer;
1485 function sendMonsPos (mon: TMonster): Boolean;
1486 begin
1487 result := false; // don't stop
1488 if (mon.MonsterType = MONSTER_BARREL) then
1489 begin
1490 if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1491 end
1492 else
1493 if (mon.MonsterState <> MONSTATE_SLEEP) then
1494 begin
1495 if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then
1496 begin
1497 MH_SEND_MonsterPos(mon.UID);
1498 end;
1499 end;
1500 end;
1502 begin
1503 g_ResetDynlights();
1504 // Ïîðà âûêëþ÷àòü èãðó:
1505 if gExit = EXIT_QUIT then
1506 Exit;
1507 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1508 if gExit <> 0 then
1509 begin
1510 EndGame();
1511 if gExit = EXIT_QUIT then
1512 Exit;
1513 end;
1515 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî:
1516 e_PollInput();
1518 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1519 g_Console_Update();
1521 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1522 begin
1523 gExit := EXIT_SIMPLE;
1524 EndGame();
1525 Exit;
1526 end;
1528 case gState of
1529 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1530 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1531 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1532 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1533 begin
1534 if g_Game_IsNet and g_Game_IsServer then
1535 begin
1536 gInterTime := gInterTime + GAME_TICK;
1537 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1538 if a <> gServInterTime then
1539 begin
1540 gServInterTime := a;
1541 MH_SEND_TimeSync(gServInterTime);
1542 end;
1543 end;
1545 if (not g_Game_IsClient) and
1548 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1549 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1550 and (g_ActiveWindow = nil)
1552 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1554 then
1555 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1556 g_Game_StopAllSounds(True);
1558 if gMapOnce then // Ýòî áûë òåñò
1559 gExit := EXIT_SIMPLE
1560 else
1561 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1562 g_Game_ChangeMap(gNextMap)
1563 else // Ñëåäóþùåé êàðòû íåò
1564 begin
1565 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1566 begin
1567 // Âûõîä â ãëàâíîå ìåíþ:
1568 g_Game_Free;
1569 g_GUI_ShowWindow('MainMenu');
1570 gMusic.SetByName('MUSIC_MENU');
1571 gMusic.Play();
1572 gState := STATE_MENU;
1573 end else
1574 begin
1575 // Ôèíàëüíàÿ êàðòèíêà:
1576 g_Game_ExecuteEvent('onwadend');
1577 g_Game_Free();
1578 if not gMusic.SetByName('MUSIC_endmus') then
1579 gMusic.SetByName('MUSIC_STDENDMUS');
1580 gMusic.Play();
1581 gState := STATE_ENDPIC;
1582 end;
1583 g_Game_ExecuteEvent('ongameend');
1584 end;
1586 Exit;
1587 end;
1589 if gState = STATE_INTERTEXT then
1590 if InterText.counter > 0 then
1591 InterText.counter := InterText.counter - 1;
1592 end;
1594 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1595 begin
1596 if EndingGameCounter = 0 then
1597 begin
1598 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1599 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1600 begin
1601 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1602 begin
1603 g_Game_ExecuteEvent('onwadend');
1604 if not gMusic.SetByName('MUSIC_endmus') then
1605 gMusic.SetByName('MUSIC_STDENDMUS');
1606 end
1607 else
1608 gMusic.SetByName('MUSIC_ROUNDMUS');
1610 gMusic.Play();
1611 gState := STATE_INTERCUSTOM;
1612 end
1613 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1614 begin
1615 gMusic.SetByName('MUSIC_INTERMUS');
1616 gMusic.Play();
1617 gState := STATE_INTERSINGLE;
1618 end;
1619 g_Game_ExecuteEvent('oninter');
1620 end
1621 else
1622 DecMin(EndingGameCounter, 6, 0);
1623 end;
1625 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1626 begin
1627 if gMapOnce then // Ýòî áûë òåñò
1628 begin
1629 gExit := EXIT_SIMPLE;
1630 Exit;
1631 end;
1632 end;
1634 STATE_SLIST:
1635 g_Serverlist_Control(slCurrent);
1636 end;
1638 if g_Game_IsNet then
1639 if not gConsoleShow then
1640 if not gChatShow then
1641 begin
1642 if g_ActiveWindow = nil then
1643 begin
1644 if e_KeyPressed(gGameControls.GameControls.Chat) then
1645 g_Console_Chat_Switch(False)
1646 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1647 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1648 g_Console_Chat_Switch(True);
1649 end;
1650 end else
1651 if not gChatEnter then
1652 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1653 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1654 gChatEnter := True;
1656 // Ñòàòèñòèêà ïî Tab:
1657 if gGameOn then
1658 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1659 (gGameSettings.GameType <> GT_SINGLE) and
1660 e_KeyPressed(gGameControls.GameControls.Stat);
1662 // Èãðà èäåò:
1663 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1664 begin
1665 // Âðåìÿ += 28 ìèëëèñåêóíä:
1666 gTime := gTime + GAME_TICK;
1668 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1669 if MessageTime = 0 then
1670 MessageText := '';
1671 if MessageTime > 0 then
1672 MessageTime := MessageTime - 1;
1674 if (g_Game_IsServer) then
1675 begin
1676 // Áûë çàäàí ëèìèò âðåìåíè:
1677 if (gGameSettings.TimeLimit > 0) then
1678 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1679 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1680 g_Game_NextLevel();
1681 Exit;
1682 end;
1684 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1685 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1686 g_Game_RestartRound(gLMSSoftSpawn);
1688 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1689 if gVoteInProgress and (gVoteTimer < gTime) then
1690 g_Game_CheckVote
1691 else if gVotePassed and (gVoteCmdTimer < gTime) then
1692 begin
1693 g_Console_Process(gVoteCommand);
1694 gVoteCommand := '';
1695 gVotePassed := False;
1696 end;
1698 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1699 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1700 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1701 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1702 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1704 // Áûë çàäàí ëèìèò ïîáåä:
1705 if (gGameSettings.GoalLimit > 0) then
1706 begin
1707 b := 0;
1709 if gGameSettings.GameMode = GM_DM then
1710 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1711 for i := 0 to High(gPlayers) do
1712 if gPlayers[i] <> nil then
1713 if gPlayers[i].Frags > b then
1714 b := gPlayers[i].Frags;
1715 end
1716 else
1717 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1718 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1719 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1720 end;
1722 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1723 if b >= gGameSettings.GoalLimit then
1724 begin
1725 g_Game_NextLevel();
1726 Exit;
1727 end;
1728 end;
1730 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1731 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1732 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1733 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1734 begin
1735 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1736 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1737 end // if not console
1738 else
1739 begin
1740 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1741 end;
1742 // process weapon switch queue
1743 end; // if server
1745 // Íàáëþäàòåëü
1746 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1747 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1748 begin
1749 if not gSpectKeyPress then
1750 begin
1751 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
1752 begin
1753 // switch spect mode
1754 case gSpectMode of
1755 SPECT_NONE: ; // not spectator
1756 SPECT_STATS,
1757 SPECT_MAPVIEW: Inc(gSpectMode);
1758 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1759 end;
1760 gSpectKeyPress := True;
1761 end;
1762 if gSpectMode = SPECT_MAPVIEW then
1763 begin
1764 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1765 gSpectX := Max(gSpectX - gSpectStep, 0);
1766 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1767 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1768 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1769 gSpectY := Max(gSpectY - gSpectStep, 0);
1770 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1771 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1772 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1773 begin
1774 // decrease step
1775 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1776 gSpectKeyPress := True;
1777 end;
1778 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1779 begin
1780 // increase step
1781 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1782 gSpectKeyPress := True;
1783 end;
1784 end;
1785 if gSpectMode = SPECT_PLAYERS then
1786 begin
1787 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1788 begin
1789 // add second view
1790 gSpectViewTwo := True;
1791 gSpectKeyPress := True;
1792 end;
1793 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1794 begin
1795 // remove second view
1796 gSpectViewTwo := False;
1797 gSpectKeyPress := True;
1798 end;
1799 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1800 begin
1801 // prev player (view 1)
1802 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1803 gSpectKeyPress := True;
1804 end;
1805 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1806 begin
1807 // next player (view 1)
1808 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1809 gSpectKeyPress := True;
1810 end;
1811 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1812 begin
1813 // prev player (view 2)
1814 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1815 gSpectKeyPress := True;
1816 end;
1817 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1818 begin
1819 // next player (view 2)
1820 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1821 gSpectKeyPress := True;
1822 end;
1823 end;
1824 end
1825 else
1826 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1827 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1828 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1829 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1830 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1831 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1832 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1833 gSpectKeyPress := False;
1834 end;
1836 // Îáíîâëÿåì âñå îñòàëüíîå:
1837 g_Map_Update();
1838 g_Items_Update();
1839 g_Triggers_Update();
1840 g_Weapon_Update();
1841 g_Monsters_Update();
1842 g_GFX_Update();
1843 g_Player_UpdateAll();
1844 g_Player_UpdatePhysicalObjects();
1845 if gGameSettings.GameType = GT_SERVER then
1846 if Length(gMonstersSpawned) > 0 then
1847 begin
1848 for I := 0 to High(gMonstersSpawned) do
1849 MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
1850 SetLength(gMonstersSpawned, 0);
1851 end;
1853 if (gSoundTriggerTime > 8) then
1854 begin
1855 g_Game_UpdateTriggerSounds();
1856 gSoundTriggerTime := 0;
1857 end
1858 else
1859 Inc(gSoundTriggerTime);
1861 if (NetMode = NET_SERVER) then
1862 begin
1863 Inc(NetTimeToUpdate);
1864 Inc(NetTimeToReliable);
1865 if NetTimeToReliable >= NetRelupdRate then
1866 begin
1867 for I := 0 to High(gPlayers) do
1868 if gPlayers[I] <> nil then
1869 MH_SEND_PlayerPos(True, gPlayers[I].UID);
1871 g_Mons_ForEach(sendMonsPos);
1873 NetTimeToReliable := 0;
1874 NetTimeToUpdate := NetUpdateRate;
1875 end
1876 else if NetTimeToUpdate >= NetUpdateRate then
1877 begin
1878 if gPlayers <> nil then
1879 for I := 0 to High(gPlayers) do
1880 if gPlayers[I] <> nil then
1881 MH_SEND_PlayerPos(False, gPlayers[I].UID);
1883 g_Mons_ForEach(sendMonsPos);
1885 NetTimeToUpdate := 0;
1886 end;
1888 if NetUseMaster then
1889 if gTime >= NetTimeToMaster then
1890 begin
1891 if (NetMHost = nil) or (NetMPeer = nil) then
1892 if not g_Net_Slist_Connect then
1893 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
1895 g_Net_Slist_Update;
1896 NetTimeToMaster := gTime + NetMasterRate;
1897 end;
1898 end
1899 else
1900 if NetMode = NET_CLIENT then
1901 MC_SEND_PlayerPos();
1902 end; // if gameOn ...
1904 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
1905 if g_ActiveWindow <> nil then
1906 begin
1907 w := e_GetFirstKeyPressed();
1909 if (w <> IK_INVALID) then
1910 begin
1911 Msg.Msg := MESSAGE_DIKEY;
1912 Msg.wParam := w;
1913 g_ActiveWindow.OnMessage(Msg);
1914 end;
1916 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
1917 if g_ActiveWindow <> nil then
1918 g_ActiveWindow.Update();
1920 // Íóæíî ñìåíèòü ðàçðåøåíèå:
1921 if gResolutionChange then
1922 begin
1923 e_WriteLog('Changing resolution', MSG_NOTIFY);
1924 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
1925 gResolutionChange := False;
1926 end;
1928 // Íóæíî ñìåíèòü ÿçûê:
1929 if gLanguageChange then
1930 begin
1931 //e_WriteLog('Read language file', MSG_NOTIFY);
1932 //g_Language_Load(DataDir + gLanguage + '.txt');
1933 g_Language_Set(gLanguage);
1934 g_Menu_Reset();
1935 gLanguageChange := False;
1936 end;
1937 end;
1939 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
1940 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
1941 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
1942 begin
1943 g_TakeScreenShot();
1944 LastScreenShot := GetTimer();
1945 end;
1947 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
1948 if e_KeyPressed(IK_F10) and
1949 gGameOn and
1950 (not gConsoleShow) and
1951 (g_ActiveWindow = nil) then
1952 begin
1953 KeyPress(IK_F10);
1954 end;
1956 Time := GetTimer() {div 1000};
1958 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
1959 if gDelayedEvents <> nil then
1960 for a := 0 to High(gDelayedEvents) do
1961 if gDelayedEvents[a].Pending and
1963 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
1964 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
1965 ) then
1966 begin
1967 case gDelayedEvents[a].DEType of
1968 DE_GLOBEVENT:
1969 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
1970 DE_BFGHIT:
1971 if gGameOn then
1972 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
1973 DE_KILLCOMBO:
1974 if gGameOn then
1975 begin
1976 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
1977 if g_Game_IsNet and g_Game_IsServer then
1978 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
1979 end;
1980 end;
1981 gDelayedEvents[a].Pending := False;
1982 end;
1984 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
1985 UPSCounter := UPSCounter + 1;
1986 if Time - UPSTime >= 1000 then
1987 begin
1988 UPS := UPSCounter;
1989 UPSCounter := 0;
1990 UPSTime := Time;
1991 end;
1993 if gGameOn then
1994 begin
1995 g_Weapon_AddDynLights();
1996 g_Items_AddDynLights();
1997 end;
1998 end;
2000 procedure g_Game_LoadData();
2001 begin
2002 if DataLoaded then Exit;
2004 e_WriteLog('Loading game data...', MSG_NOTIFY);
2006 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2007 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2008 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2009 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2010 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2011 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2012 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_RB', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
2013 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
2014 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
2015 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
2016 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
2017 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
2018 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2019 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2020 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2021 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2022 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2023 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2024 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2025 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2026 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2027 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2028 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2029 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2030 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2031 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2032 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2033 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2034 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2035 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2036 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2037 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2038 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2039 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2041 goodsnd[0] := TPlayableSound.Create();
2042 goodsnd[1] := TPlayableSound.Create();
2043 goodsnd[2] := TPlayableSound.Create();
2044 goodsnd[3] := TPlayableSound.Create();
2046 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2047 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2048 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2049 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2051 killsnd[0] := TPlayableSound.Create();
2052 killsnd[1] := TPlayableSound.Create();
2053 killsnd[2] := TPlayableSound.Create();
2054 killsnd[3] := TPlayableSound.Create();
2056 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2057 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2058 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2059 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2061 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2062 g_Items_LoadData();
2064 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2065 g_Weapon_LoadData();
2067 g_Monsters_LoadData();
2069 DataLoaded := True;
2070 end;
2072 procedure g_Game_FreeData();
2073 begin
2074 if not DataLoaded then Exit;
2076 g_Items_FreeData();
2077 g_Weapon_FreeData();
2078 g_Monsters_FreeData();
2080 e_WriteLog('Releasing game data...', MSG_NOTIFY);
2082 g_Texture_Delete('NOTEXTURE');
2083 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2084 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2085 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2086 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2087 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2088 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2089 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2090 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2091 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2092 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2093 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2094 g_Frames_DeleteByName('FRAMES_TELEPORT');
2095 g_Sound_Delete('SOUND_GAME_TELEPORT');
2096 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2097 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2098 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2099 g_Sound_Delete('SOUND_GAME_BULK1');
2100 g_Sound_Delete('SOUND_GAME_BULK2');
2101 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2102 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2103 g_Sound_Delete('SOUND_GAME_SWITCH1');
2104 g_Sound_Delete('SOUND_GAME_SWITCH0');
2106 goodsnd[0].Free();
2107 goodsnd[1].Free();
2108 goodsnd[2].Free();
2109 goodsnd[3].Free();
2111 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2112 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2113 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2114 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2116 killsnd[0].Free();
2117 killsnd[1].Free();
2118 killsnd[2].Free();
2119 killsnd[3].Free();
2121 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2122 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2123 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2124 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2126 DataLoaded := False;
2127 end;
2129 procedure DrawCustomStat();
2130 var
2131 pc, x, y, w, _y,
2132 w1, w2, w3,
2133 t, p, m: Integer;
2134 ww1, hh1: Word;
2135 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2136 s1, s2, topstr: String;
2137 begin
2138 e_TextureFontGetSize(gStdFont, ww2, hh2);
2140 e_PollInput();
2141 if e_KeyPressed(IK_TAB) then
2142 begin
2143 if not gStatsPressed then
2144 begin
2145 gStatsOff := not gStatsOff;
2146 gStatsPressed := True;
2147 end;
2148 end
2149 else
2150 gStatsPressed := False;
2152 if gStatsOff then
2153 begin
2154 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2155 w := (Length(s1) * ww2) div 2;
2156 x := gScreenWidth div 2 - w;
2157 y := 8;
2158 e_TextureFontPrint(x, y, s1, gStdFont);
2159 Exit;
2160 end;
2162 if (gGameSettings.GameMode = GM_COOP) then
2163 begin
2164 if gMissionFailed then
2165 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2166 else
2167 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2168 end
2169 else
2170 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2172 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2173 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2175 if g_Game_IsNet then
2176 begin
2177 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2178 if not gChatShow then
2179 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2180 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2181 end;
2183 if g_Game_IsClient then
2184 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2185 else
2186 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2187 if not gChatShow then
2188 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2189 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2191 x := 32;
2192 y := 16+hh1+16;
2194 w := gScreenWidth-x*2;
2196 w2 := (w-16) div 6;
2197 w3 := w2;
2198 w1 := w-16-w2-w3;
2200 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2201 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2203 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2205 case CustomStat.GameMode of
2206 GM_DM:
2207 begin
2208 if gGameSettings.MaxLives = 0 then
2209 s1 := _lc[I_GAME_DM]
2210 else
2211 s1 := _lc[I_GAME_LMS];
2212 end;
2213 GM_TDM:
2214 begin
2215 if gGameSettings.MaxLives = 0 then
2216 s1 := _lc[I_GAME_TDM]
2217 else
2218 s1 := _lc[I_GAME_TLMS];
2219 end;
2220 GM_CTF: s1 := _lc[I_GAME_CTF];
2221 GM_COOP:
2222 begin
2223 if gGameSettings.MaxLives = 0 then
2224 s1 := _lc[I_GAME_COOP]
2225 else
2226 s1 := _lc[I_GAME_SURV];
2227 end;
2228 else s1 := '';
2229 end;
2231 _y := y+16;
2232 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2233 _y := _y+8;
2235 _y := _y+16;
2236 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2237 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2239 _y := _y+16;
2240 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2241 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2242 (CustomStat.GameTime div 1000 div 60) mod 60,
2243 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2245 pc := Length(CustomStat.PlayerStat);
2246 if pc = 0 then Exit;
2248 if CustomStat.GameMode = GM_COOP then
2249 begin
2250 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2251 _y := _y+32;
2252 s2 := _lc[I_GAME_MONSTERS];
2253 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2254 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2255 _y := _y+16;
2256 s2 := _lc[I_GAME_SECRETS];
2257 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2258 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2259 if gLastMap then
2260 begin
2261 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2262 _y := _y-16;
2263 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2264 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2265 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2266 _y := _y+16;
2267 s2 := _lc[I_GAME_SECRETS_TOTAL];
2268 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2269 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2270 end;
2271 end;
2273 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2274 begin
2275 _y := _y+16+16;
2277 with CustomStat do
2278 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2279 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2280 else s1 := _lc[I_GAME_WIN_DRAW];
2282 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2283 _y := _y+40;
2285 for t := TEAM_RED to TEAM_BLUE do
2286 begin
2287 if t = TEAM_RED then
2288 begin
2289 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2290 gStdFont, 255, 0, 0, 1);
2291 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2292 gStdFont, 255, 0, 0, 1);
2293 r := 255;
2294 g := 0;
2295 b := 0;
2296 end
2297 else
2298 begin
2299 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2300 gStdFont, 0, 0, 255, 1);
2301 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2302 gStdFont, 0, 0, 255, 1);
2303 r := 0;
2304 g := 0;
2305 b := 255;
2306 end;
2308 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2309 _y := _y+24;
2311 for p := 0 to High(CustomStat.PlayerStat) do
2312 if CustomStat.PlayerStat[p].Team = t then
2313 with CustomStat.PlayerStat[p] do
2314 begin
2315 if Spectator then
2316 begin
2317 rr := r div 2;
2318 gg := g div 2;
2319 bb := b div 2;
2320 end
2321 else
2322 begin
2323 rr := r;
2324 gg := g;
2325 bb := b;
2326 end;
2327 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2328 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2329 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2330 _y := _y+24;
2331 end;
2333 _y := _y+16+16;
2334 end;
2335 end
2336 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2337 begin
2338 _y := _y+40;
2339 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2340 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2341 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2343 _y := _y+24;
2344 for p := 0 to High(CustomStat.PlayerStat) do
2345 with CustomStat.PlayerStat[p] do
2346 begin
2347 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2349 if Spectator then
2350 r := 127
2351 else
2352 r := 255;
2354 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2355 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2356 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2357 _y := _y+24;
2358 end;
2359 end;
2360 end;
2362 procedure DrawSingleStat();
2363 var
2364 tm, key_x, val_x, y: Integer;
2365 w1, w2, h: Word;
2366 s1, s2: String;
2368 procedure player_stat(n: Integer);
2369 var
2370 kpm: Real;
2372 begin
2373 // "Kills: # / #":
2374 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2375 s2 := Format(' %d', [gTotalMonsters]);
2377 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2378 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2379 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2380 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2381 s1 := s1 + '/';
2382 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2383 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2385 // "Kills-per-minute: ##.#":
2386 s1 := _lc[I_MENU_INTER_KPM];
2387 if tm > 0 then
2388 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2389 else
2390 kpm := SingleStat.PlayerStat[n].Kills;
2391 s2 := Format(' %.1f', [kpm]);
2393 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2394 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2396 // "Secrets found: # / #":
2397 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2398 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2400 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2401 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2402 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2403 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2404 s1 := s1 + '/';
2405 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2406 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2407 end;
2409 begin
2410 // "Level Complete":
2411 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2412 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2414 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2415 s1 := _lc[I_MENU_INTER_KPM];
2416 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2417 Inc(w1, 16);
2418 s1 := ' 9999.9';
2419 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2421 key_x := (gScreenWidth-w1-w2) div 2;
2422 val_x := key_x + w1;
2424 // "Time: #:##:##":
2425 tm := SingleStat.GameTime div 1000;
2426 s1 := _lc[I_MENU_INTER_TIME];
2427 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2429 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2430 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2432 if SingleStat.TwoPlayers then
2433 begin
2434 // "Player 1":
2435 s1 := _lc[I_MENU_PLAYER_1];
2436 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2437 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2439 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2440 y := 176;
2441 player_stat(0);
2443 // "Player 2":
2444 s1 := _lc[I_MENU_PLAYER_2];
2445 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2446 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2448 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2449 y := 336;
2450 player_stat(1);
2451 end
2452 else
2453 begin
2454 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2455 y := 128;
2456 player_stat(0);
2457 end;
2458 end;
2460 procedure DrawLoadingStat();
2461 var
2462 ww, hh: Word;
2463 xx, yy, i: Integer;
2464 s: String;
2465 begin
2466 if Length(LoadingStat.Msgs) = 0 then
2467 Exit;
2469 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2470 yy := (gScreenHeight div 3);
2471 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2472 xx := (gScreenWidth div 3);
2474 with LoadingStat do
2475 for i := 0 to NextMsg-1 do
2476 begin
2477 if (i = (NextMsg-1)) and (MaxValue > 0) then
2478 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2479 else
2480 s := Msgs[i];
2482 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2483 yy := yy + LOADING_INTERLINE;
2484 end;
2485 end;
2487 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2488 var
2489 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2491 function monDraw (mon: TMonster): Boolean;
2492 begin
2493 result := false; // don't stop
2494 with mon do
2495 begin
2496 if Live then
2497 begin
2498 // Ëåâûé âåðõíèé óãîë
2499 aX := Obj.X div ScaleSz + 1;
2500 aY := Obj.Y div ScaleSz + 1;
2501 // Ðàçìåðû
2502 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2503 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2504 // Ïðàâûé íèæíèé óãîë
2505 aX2 := aX + aX2 - 1;
2506 aY2 := aY + aY2 - 1;
2507 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2508 end;
2509 end;
2510 end;
2512 begin
2513 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2514 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2515 begin
2516 Scale := 1;
2517 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2518 ScaleSz := 16 div Scale;
2519 // Ðàçìåðû ìèíè-êàðòû:
2520 aX := max(gMapInfo.Width div ScaleSz, 1);
2521 aY := max(gMapInfo.Height div ScaleSz, 1);
2522 // Ðàìêà êàðòû:
2523 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2525 if gWalls <> nil then
2526 begin
2527 // Ðèñóåì ñòåíû:
2528 for a := 0 to High(gWalls) do
2529 with gWalls[a] do
2530 if PanelType <> 0 then
2531 begin
2532 // Ëåâûé âåðõíèé óãîë:
2533 aX := X div ScaleSz;
2534 aY := Y div ScaleSz;
2535 // Ðàçìåðû:
2536 aX2 := max(Width div ScaleSz, 1);
2537 aY2 := max(Height div ScaleSz, 1);
2538 // Ïðàâûé íèæíèé óãîë:
2539 aX2 := aX + aX2 - 1;
2540 aY2 := aY + aY2 - 1;
2542 case PanelType of
2543 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2544 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2545 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2546 end;
2547 end;
2548 end;
2549 if gSteps <> nil then
2550 begin
2551 // Ðèñóåì ñòóïåíè:
2552 for a := 0 to High(gSteps) do
2553 with gSteps[a] do
2554 if PanelType <> 0 then
2555 begin
2556 // Ëåâûé âåðõíèé óãîë:
2557 aX := X div ScaleSz;
2558 aY := Y div ScaleSz;
2559 // Ðàçìåðû:
2560 aX2 := max(Width div ScaleSz, 1);
2561 aY2 := max(Height div ScaleSz, 1);
2562 // Ïðàâûé íèæíèé óãîë:
2563 aX2 := aX + aX2 - 1;
2564 aY2 := aY + aY2 - 1;
2566 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2567 end;
2568 end;
2569 if gLifts <> nil then
2570 begin
2571 // Ðèñóåì ëèôòû:
2572 for a := 0 to High(gLifts) do
2573 with gLifts[a] do
2574 if PanelType <> 0 then
2575 begin
2576 // Ëåâûé âåðõíèé óãîë:
2577 aX := X div ScaleSz;
2578 aY := Y div ScaleSz;
2579 // Ðàçìåðû:
2580 aX2 := max(Width div ScaleSz, 1);
2581 aY2 := max(Height div ScaleSz, 1);
2582 // Ïðàâûé íèæíèé óãîë:
2583 aX2 := aX + aX2 - 1;
2584 aY2 := aY + aY2 - 1;
2586 case LiftType of
2587 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2588 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2589 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2590 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2591 end;
2592 end;
2593 end;
2594 if gWater <> nil then
2595 begin
2596 // Ðèñóåì âîäó:
2597 for a := 0 to High(gWater) do
2598 with gWater[a] do
2599 if PanelType <> 0 then
2600 begin
2601 // Ëåâûé âåðõíèé óãîë:
2602 aX := X div ScaleSz;
2603 aY := Y div ScaleSz;
2604 // Ðàçìåðû:
2605 aX2 := max(Width div ScaleSz, 1);
2606 aY2 := max(Height div ScaleSz, 1);
2607 // Ïðàâûé íèæíèé óãîë:
2608 aX2 := aX + aX2 - 1;
2609 aY2 := aY + aY2 - 1;
2611 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2612 end;
2613 end;
2614 if gAcid1 <> nil then
2615 begin
2616 // Ðèñóåì êèñëîòó 1:
2617 for a := 0 to High(gAcid1) do
2618 with gAcid1[a] do
2619 if PanelType <> 0 then
2620 begin
2621 // Ëåâûé âåðõíèé óãîë:
2622 aX := X div ScaleSz;
2623 aY := Y div ScaleSz;
2624 // Ðàçìåðû:
2625 aX2 := max(Width div ScaleSz, 1);
2626 aY2 := max(Height div ScaleSz, 1);
2627 // Ïðàâûé íèæíèé óãîë:
2628 aX2 := aX + aX2 - 1;
2629 aY2 := aY + aY2 - 1;
2631 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2632 end;
2633 end;
2634 if gAcid2 <> nil then
2635 begin
2636 // Ðèñóåì êèñëîòó 2:
2637 for a := 0 to High(gAcid2) do
2638 with gAcid2[a] do
2639 if PanelType <> 0 then
2640 begin
2641 // Ëåâûé âåðõíèé óãîë:
2642 aX := X div ScaleSz;
2643 aY := Y div ScaleSz;
2644 // Ðàçìåðû:
2645 aX2 := max(Width div ScaleSz, 1);
2646 aY2 := max(Height div ScaleSz, 1);
2647 // Ïðàâûé íèæíèé óãîë:
2648 aX2 := aX + aX2 - 1;
2649 aY2 := aY + aY2 - 1;
2651 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2652 end;
2653 end;
2654 if gPlayers <> nil then
2655 begin
2656 // Ðèñóåì èãðîêîâ:
2657 for a := 0 to High(gPlayers) do
2658 if gPlayers[a] <> nil then with gPlayers[a] do
2659 if Live then begin
2660 // Ëåâûé âåðõíèé óãîë:
2661 aX := Obj.X div ScaleSz + 1;
2662 aY := Obj.Y div ScaleSz + 1;
2663 // Ðàçìåðû:
2664 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2665 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2666 // Ïðàâûé íèæíèé óãîë:
2667 aX2 := aX + aX2 - 1;
2668 aY2 := aY + aY2 - 1;
2670 if gPlayers[a] = p then
2671 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2672 else
2673 case Team of
2674 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2675 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2676 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2677 end;
2678 end;
2679 end;
2680 // Ðèñóåì ìîíñòðîâ
2681 g_Mons_ForEach(monDraw);
2682 end;
2683 end;
2686 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2687 procedure renderDynLightsInternal ();
2688 var
2689 lln: Integer;
2690 lx, ly, lrad: Integer;
2691 begin
2692 //TODO: lights should be in separate grid, i think
2693 // but on the other side: grid may be slower for dynlights, as their lifetime is short
2694 if not gwin_has_stencil or (g_dynLightCount < 1) then exit;
2696 // setup OpenGL parameters
2697 glStencilMask($FFFFFFFF);
2698 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
2699 glEnable(GL_STENCIL_TEST);
2700 glEnable(GL_SCISSOR_TEST);
2701 glClear(GL_STENCIL_BUFFER_BIT);
2702 glStencilFunc(GL_EQUAL, 0, $ff);
2704 for lln := 0 to g_dynLightCount-1 do
2705 begin
2706 lx := g_dynLights[lln].x;
2707 ly := g_dynLights[lln].y;
2708 lrad := g_dynLights[lln].radius;
2709 if lrad < 3 then continue;
2711 if lx-sX+lrad < 0 then continue;
2712 if ly-sY+lrad < 0 then continue;
2713 if lx-sX-lrad >= gPlayerScreenSize.X then continue;
2714 if ly-sY-lrad >= gPlayerScreenSize.Y then continue;
2716 // set scissor to optimize drawing
2717 //FIXME: broken for splitscreen mode
2718 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
2719 // no need to clear stencil buffer, light blitting will do it for us
2720 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2721 // draw extruded panels
2722 glDisable(GL_TEXTURE_2D);
2723 glDisable(GL_BLEND);
2724 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
2725 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
2726 // render light texture
2727 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2728 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
2729 // blend it
2730 glEnable(GL_BLEND);
2731 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2732 glEnable(GL_TEXTURE_2D);
2733 // color and opacity
2734 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
2735 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
2736 glBegin(GL_QUADS);
2737 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
2738 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
2739 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
2740 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
2741 glEnd();
2742 end;
2744 // done
2745 glDisable(GL_STENCIL_TEST);
2746 glDisable(GL_BLEND);
2747 glDisable(GL_SCISSOR_TEST);
2748 glScissor(0, 0, sWidth, sHeight);
2749 end;
2752 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2753 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
2754 procedure renderMapInternal (backXOfs, backYOfs: Integer; transX, transY: Integer; setTransMatrix: Boolean);
2755 type
2756 TDrawCB = procedure ();
2758 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
2759 var
2760 tagmask: Integer;
2761 pan: TPanel;
2762 begin
2763 profileFrameDraw.sectionBegin(profname);
2764 if gdbg_map_use_accel_render then
2765 begin
2766 tagmask := panelTypeToTag(panType);
2767 while (gDrawPanelList.count > 0) do
2768 begin
2769 pan := TPanel(gDrawPanelList.front());
2770 if ((pan.tag and tagmask) = 0) then break;
2771 if doDraw then pan.Draw();
2772 gDrawPanelList.popFront();
2773 end;
2774 end
2775 else
2776 begin
2777 if doDraw then g_Map_DrawPanels(panType);
2778 end;
2779 profileFrameDraw.sectionEnd();
2780 end;
2782 procedure drawOther (profname: AnsiString; cb: TDrawCB);
2783 begin
2784 profileFrameDraw.sectionBegin(profname);
2785 if assigned(cb) then cb();
2786 profileFrameDraw.sectionEnd();
2787 end;
2789 begin
2790 profileFrameDraw.sectionBegin('total');
2792 // our accelerated renderer will collect all panels to gDrawPanelList
2793 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
2794 profileFrameDraw.sectionBegin('collect');
2795 if gdbg_map_use_accel_render then
2796 begin
2797 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
2798 end;
2799 profileFrameDraw.sectionEnd();
2801 profileFrameDraw.sectionBegin('skyback');
2802 g_Map_DrawBack(backXOfs, backYOfs);
2803 profileFrameDraw.sectionEnd();
2805 if (setTransMatrix) then glTranslatef(transX, transY, 0);
2807 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
2808 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
2809 drawOther('items', @g_Items_Draw);
2810 drawOther('weapons', @g_Weapon_Draw);
2811 drawOther('shells', @g_Player_DrawShells);
2812 drawOther('drawall', @g_Player_DrawAll);
2813 drawOther('corpses', @g_Player_DrawCorpses);
2814 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
2815 drawOther('monsters', @g_Monsters_Draw);
2816 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
2817 drawOther('gfx', @g_GFX_Draw);
2818 drawOther('flags', @g_Map_DrawFlags);
2819 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
2820 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
2821 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
2822 drawOther('dynlights', @renderDynLightsInternal);
2823 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
2825 if g_debug_HealthBar then
2826 begin
2827 g_Monsters_DrawHealth();
2828 g_Player_DrawHealth();
2829 end;
2831 profileFrameDraw.mainEnd(); // map rendering
2832 end;
2835 procedure DrawMapView(x, y, w, h: Integer);
2837 var
2838 bx, by: Integer;
2839 begin
2840 glPushMatrix();
2842 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2843 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2845 sX := x;
2846 sY := y;
2847 sWidth := w;
2848 sHeight := h;
2850 renderMapInternal(-bx, -by, -x, -y, true);
2852 glPopMatrix();
2853 end;
2856 procedure DrawPlayer(p: TPlayer);
2857 var
2858 px, py, a, b, c, d: Integer;
2859 //R: TRect;
2860 begin
2861 if (p = nil) or (p.FDummy) then
2862 begin
2863 glPushMatrix();
2864 g_Map_DrawBack(0, 0);
2865 glPopMatrix();
2866 Exit;
2867 end;
2869 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
2870 profileFrameDraw.mainBegin(g_profile_frame_draw);
2872 gPlayerDrawn := p;
2874 glPushMatrix();
2876 px := p.GameX + PLAYER_RECT_CX;
2877 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
2879 if px > (gPlayerScreenSize.X div 2) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
2880 if py > (gPlayerScreenSize.Y div 2) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
2882 if px > gMapInfo.Width-(gPlayerScreenSize.X div 2) then a := -gMapInfo.Width+gPlayerScreenSize.X;
2883 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
2885 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
2886 else if (gMapInfo.Width < gPlayerScreenSize.X) then
2887 begin
2888 // hcenter
2889 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
2890 end;
2892 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
2893 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
2894 begin
2895 // vcenter
2896 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
2897 end;
2899 if p.IncCam <> 0 then
2900 begin
2901 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
2902 begin
2903 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
2904 begin
2905 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
2906 end;
2907 end;
2909 if py < gPlayerScreenSize.Y div 2 then
2910 begin
2911 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
2912 begin
2913 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
2914 end;
2915 end;
2917 if p.IncCam < 0 then
2918 begin
2919 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
2920 end;
2922 if p.IncCam > 0 then
2923 begin
2924 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
2925 end;
2926 end;
2928 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
2929 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
2930 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
2932 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
2933 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
2934 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
2936 sX := -a;
2937 sY := -(b+p.IncCam);
2938 sWidth := gPlayerScreenSize.X;
2939 sHeight := gPlayerScreenSize.Y;
2941 //glTranslatef(a, b+p.IncCam, 0);
2943 p.viewPortX := sX;
2944 p.viewPortY := sY;
2945 p.viewPortW := sWidth;
2946 p.viewPortH := sHeight;
2948 if (p = gPlayer1) then
2949 begin
2950 g_Holmes_plrView(p.viewPortX, p.viewPortY, p.viewPortW, p.viewPortH);
2951 end;
2953 renderMapInternal(-c, -d, a, b+p.IncCam, true);
2955 if p.FSpectator then
2956 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2957 p.GameY + PLAYER_RECT_CY - 4,
2958 'X', gStdFont, 255, 255, 255, 1, True);
2960 for a := 0 to High(gCollideMap) do
2961 for b := 0 to High(gCollideMap[a]) do
2962 begin
2963 d := 0;
2964 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
2965 d := d + 1;
2966 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
2967 d := d + 2;
2969 case d of
2970 1: e_DrawPoint(1, b, a, 200, 200, 200);
2971 2: e_DrawPoint(1, b, a, 64, 64, 255);
2972 3: e_DrawPoint(1, b, a, 255, 0, 255);
2973 end;
2974 end;
2977 glPopMatrix();
2979 p.DrawPain();
2980 p.DrawPickup();
2981 p.DrawRulez();
2982 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
2983 if g_Debug_Player then
2984 g_Player_DrawDebug(p);
2985 p.DrawGUI();
2986 end;
2988 procedure drawProfilers ();
2989 var
2990 px: Integer = -1;
2991 py: Integer = -1;
2992 begin
2993 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
2994 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
2995 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
2996 end;
2998 procedure g_Game_Draw();
2999 var
3000 ID: DWORD;
3001 w, h: Word;
3002 ww, hh: Byte;
3003 Time: Int64;
3004 back: string;
3005 plView1, plView2: TPlayer;
3006 Split: Boolean;
3007 begin
3008 if gExit = EXIT_QUIT then Exit;
3010 Time := GetTimer() {div 1000};
3011 FPSCounter := FPSCounter+1;
3012 if Time - FPSTime >= 1000 then
3013 begin
3014 FPS := FPSCounter;
3015 FPSCounter := 0;
3016 FPSTime := Time;
3017 end;
3019 if gGameOn or (gState = STATE_FOLD) then
3020 begin
3021 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3022 begin
3023 gSpectMode := SPECT_NONE;
3024 if not gRevertPlayers then
3025 begin
3026 plView1 := gPlayer1;
3027 plView2 := gPlayer2;
3028 end
3029 else
3030 begin
3031 plView1 := gPlayer2;
3032 plView2 := gPlayer1;
3033 end;
3034 end
3035 else
3036 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3037 begin
3038 gSpectMode := SPECT_NONE;
3039 if gPlayer2 = nil then
3040 plView1 := gPlayer1
3041 else
3042 plView1 := gPlayer2;
3043 plView2 := nil;
3044 end
3045 else
3046 begin
3047 plView1 := nil;
3048 plView2 := nil;
3049 end;
3051 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3052 gSpectMode := SPECT_STATS;
3054 if gSpectMode = SPECT_PLAYERS then
3055 if gPlayers <> nil then
3056 begin
3057 plView1 := GetActivePlayer_ByID(gSpectPID1);
3058 if plView1 = nil then
3059 begin
3060 gSpectPID1 := GetActivePlayerID_Next();
3061 plView1 := GetActivePlayer_ByID(gSpectPID1);
3062 end;
3063 if gSpectViewTwo then
3064 begin
3065 plView2 := GetActivePlayer_ByID(gSpectPID2);
3066 if plView2 = nil then
3067 begin
3068 gSpectPID2 := GetActivePlayerID_Next();
3069 plView2 := GetActivePlayer_ByID(gSpectPID2);
3070 end;
3071 end;
3072 end;
3074 if gSpectMode = SPECT_MAPVIEW then
3075 begin
3076 // Ðåæèì ïðîñìîòðà êàðòû
3077 Split := False;
3078 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3079 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3080 gHearPoint1.Active := True;
3081 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3082 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3083 gHearPoint2.Active := False;
3084 end
3085 else
3086 begin
3087 Split := (plView1 <> nil) and (plView2 <> nil);
3089 // Òî÷êè ñëóõà èãðîêîâ
3090 if plView1 <> nil then
3091 begin
3092 gHearPoint1.Active := True;
3093 gHearPoint1.Coords.X := plView1.GameX;
3094 gHearPoint1.Coords.Y := plView1.GameY;
3095 end else
3096 gHearPoint1.Active := False;
3097 if plView2 <> nil then
3098 begin
3099 gHearPoint2.Active := True;
3100 gHearPoint2.Coords.X := plView2.GameX;
3101 gHearPoint2.Coords.Y := plView2.GameY;
3102 end else
3103 gHearPoint2.Active := False;
3105 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3106 gPlayerScreenSize.X := gScreenWidth-196;
3107 if Split then
3108 begin
3109 gPlayerScreenSize.Y := gScreenHeight div 2;
3110 if gScreenHeight mod 2 = 0 then
3111 Dec(gPlayerScreenSize.Y);
3112 end
3113 else
3114 gPlayerScreenSize.Y := gScreenHeight;
3116 if Split then
3117 if gScreenHeight mod 2 = 0 then
3118 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3119 else
3120 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3122 DrawPlayer(plView1);
3123 gPlayer1ScreenCoord.X := sX;
3124 gPlayer1ScreenCoord.Y := sY;
3126 if Split then
3127 begin
3128 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3130 DrawPlayer(plView2);
3131 gPlayer2ScreenCoord.X := sX;
3132 gPlayer2ScreenCoord.Y := sY;
3133 end;
3135 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3137 if Split then
3138 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3139 end;
3141 if MessageText <> '' then
3142 begin
3143 w := 0;
3144 h := 0;
3145 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3146 if Split then
3147 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3148 (gScreenHeight div 2)-(h div 2), MessageText)
3149 else
3150 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3151 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3152 end;
3154 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3156 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3157 begin
3158 // Draw spectator GUI
3159 ww := 0;
3160 hh := 0;
3161 e_TextureFontGetSize(gStdFont, ww, hh);
3162 case gSpectMode of
3163 SPECT_STATS:
3164 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3165 SPECT_MAPVIEW:
3166 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3167 SPECT_PLAYERS:
3168 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3169 end;
3170 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3171 if gSpectMode = SPECT_MAPVIEW then
3172 begin
3173 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3174 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3175 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3176 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3177 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3178 end;
3179 if gSpectMode = SPECT_PLAYERS then
3180 begin
3181 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3182 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3183 if gSpectViewTwo then
3184 begin
3185 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3186 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3187 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3188 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3189 end
3190 else
3191 begin
3192 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3193 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3194 end;
3195 end;
3196 end;
3197 end;
3199 if gPause and gGameOn and (g_ActiveWindow = nil) then
3200 begin
3201 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3202 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3204 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3205 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3206 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3207 end;
3209 if not gGameOn then
3210 begin
3211 if (gState = STATE_MENU) then
3212 begin
3213 if ((g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '')) then
3214 begin
3215 if g_Texture_Get('MENU_BACKGROUND', ID) then
3216 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3217 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3218 end;
3219 if g_ActiveWindow <> nil then
3220 begin
3221 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3222 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3223 end;
3224 end;
3226 if gState = STATE_FOLD then
3227 begin
3228 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3229 end;
3231 if gState = STATE_INTERCUSTOM then
3232 begin
3233 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3234 begin
3235 back := 'TEXTURE_endpic';
3236 if not g_Texture_Get(back, ID) then
3237 back := _lc[I_TEXTURE_ENDPIC];
3238 end
3239 else
3240 back := 'INTER';
3242 if g_Texture_Get(back, ID) then
3243 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3244 else
3245 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3247 DrawCustomStat();
3249 if g_ActiveWindow <> nil then
3250 begin
3251 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3252 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3253 end;
3254 end;
3256 if gState = STATE_INTERSINGLE then
3257 begin
3258 if EndingGameCounter > 0 then
3259 begin
3260 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3261 end
3262 else
3263 begin
3264 back := 'INTER';
3266 if g_Texture_Get(back, ID) then
3267 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3268 else
3269 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3271 DrawSingleStat();
3273 if g_ActiveWindow <> nil then
3274 begin
3275 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3276 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3277 end;
3278 end;
3279 end;
3281 if gState = STATE_ENDPIC then
3282 begin
3283 ID := DWORD(-1);
3284 if not g_Texture_Get('TEXTURE_endpic', ID) then
3285 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3287 if ID <> DWORD(-1) then
3288 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3289 else
3290 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3292 if g_ActiveWindow <> nil then
3293 begin
3294 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3295 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3296 end;
3297 end;
3299 if gState = STATE_SLIST then
3300 begin
3301 if g_Texture_Get('MENU_BACKGROUND', ID) then
3302 begin
3303 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3304 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3305 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3306 end;
3307 g_Serverlist_Draw(slCurrent);
3308 end;
3309 end;
3311 if g_ActiveWindow <> nil then
3312 begin
3313 if gGameOn then
3314 begin
3315 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3316 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3317 end;
3318 g_ActiveWindow.Draw();
3319 end;
3321 // draw inspector
3322 if (g_holmes_enabled) then g_Holmes_Draw();
3324 g_Console_Draw();
3326 if g_debug_Sounds and gGameOn then
3327 begin
3328 for w := 0 to High(e_SoundsArray) do
3329 for h := 0 to e_SoundsArray[w].nRefs do
3330 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3331 end;
3333 if gShowFPS then
3334 begin
3335 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3336 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3337 end;
3339 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3340 drawTime(gScreenWidth-72, gScreenHeight-16);
3342 if gGameOn then drawProfilers();
3344 g_Holmes_DrawUI();
3345 end;
3347 procedure g_Game_Quit();
3348 begin
3349 g_Game_StopAllSounds(True);
3350 gMusic.Free();
3351 g_Game_SaveOptions();
3352 g_Game_FreeData();
3353 g_PlayerModel_FreeData();
3354 g_Texture_DeleteAll();
3355 g_Frames_DeleteAll();
3356 g_Menu_Free();
3358 if NetInitDone then g_Net_Free;
3360 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3361 if gMapToDelete <> '' then
3362 g_Game_DeleteTestMap();
3364 gExit := EXIT_QUIT;
3365 PushExitEvent();
3366 end;
3368 procedure g_FatalError(Text: String);
3369 begin
3370 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3371 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3373 gExit := EXIT_SIMPLE;
3374 end;
3376 procedure g_SimpleError(Text: String);
3377 begin
3378 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3379 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3380 end;
3382 procedure g_Game_SetupScreenSize();
3383 const
3384 RES_FACTOR = 4.0 / 3.0;
3385 var
3386 s: Single;
3387 rf: Single;
3388 bw, bh: Word;
3389 begin
3390 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3391 gPlayerScreenSize.X := gScreenWidth-196;
3392 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3393 gPlayerScreenSize.Y := gScreenHeight div 2
3394 else
3395 gPlayerScreenSize.Y := gScreenHeight;
3397 // Ðàçìåð çàäíåãî ïëàíà:
3398 if BackID <> DWORD(-1) then
3399 begin
3400 s := SKY_STRETCH;
3401 if (gScreenWidth*s > gMapInfo.Width) or
3402 (gScreenHeight*s > gMapInfo.Height) then
3403 begin
3404 gBackSize.X := gScreenWidth;
3405 gBackSize.Y := gScreenHeight;
3406 end
3407 else
3408 begin
3409 e_GetTextureSize(BackID, @bw, @bh);
3410 rf := Single(bw) / Single(bh);
3411 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3412 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3413 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3414 if (s < 1.0) then s := 1.0;
3415 gBackSize.X := Round(bw*s);
3416 gBackSize.Y := Round(bh*s);
3417 end;
3418 end;
3419 end;
3421 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3422 begin
3423 g_Window_SetSize(newWidth, newHeight, nowFull);
3424 end;
3426 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3427 begin
3428 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3429 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3430 Exit;
3431 if gPlayer1 = nil then
3432 begin
3433 if g_Game_IsClient then
3434 begin
3435 if NetPlrUID1 > -1 then
3436 begin
3437 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3438 gPlayer1 := g_Player_Get(NetPlrUID1);
3439 end;
3440 Exit;
3441 end;
3443 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3444 Team := gPlayer1Settings.Team;
3446 // Ñîçäàíèå ïåðâîãî èãðîêà:
3447 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3448 gPlayer1Settings.Color,
3449 Team, False));
3450 if gPlayer1 = nil then
3451 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3452 else
3453 begin
3454 gPlayer1.Name := gPlayer1Settings.Name;
3455 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3456 if g_Game_IsServer and g_Game_IsNet then
3457 MH_SEND_PlayerCreate(gPlayer1.UID);
3458 gPlayer1.Respawn(False, True);
3460 if g_Game_IsNet and NetUseMaster then
3461 g_Net_Slist_Update;
3462 end;
3464 Exit;
3465 end;
3466 if gPlayer2 = nil then
3467 begin
3468 if g_Game_IsClient then
3469 begin
3470 if NetPlrUID2 > -1 then
3471 gPlayer2 := g_Player_Get(NetPlrUID2);
3472 Exit;
3473 end;
3475 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3476 Team := gPlayer2Settings.Team;
3478 // Ñîçäàíèå âòîðîãî èãðîêà:
3479 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3480 gPlayer2Settings.Color,
3481 Team, False));
3482 if gPlayer2 = nil then
3483 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3484 else
3485 begin
3486 gPlayer2.Name := gPlayer2Settings.Name;
3487 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3488 if g_Game_IsServer and g_Game_IsNet then
3489 MH_SEND_PlayerCreate(gPlayer2.UID);
3490 gPlayer2.Respawn(False, True);
3492 if g_Game_IsNet and NetUseMaster then
3493 g_Net_Slist_Update;
3494 end;
3496 Exit;
3497 end;
3498 end;
3500 procedure g_Game_RemovePlayer();
3501 var
3502 Pl: TPlayer;
3503 begin
3504 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3505 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3506 Exit;
3507 Pl := gPlayer2;
3508 if Pl <> nil then
3509 begin
3510 if g_Game_IsServer then
3511 begin
3512 Pl.Lives := 0;
3513 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3514 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3515 g_Player_Remove(Pl.UID);
3517 if g_Game_IsNet and NetUseMaster then
3518 g_Net_Slist_Update;
3519 end else
3520 gPlayer2 := nil;
3521 Exit;
3522 end;
3523 Pl := gPlayer1;
3524 if Pl <> nil then
3525 begin
3526 if g_Game_IsServer then
3527 begin
3528 Pl.Lives := 0;
3529 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3530 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3531 g_Player_Remove(Pl.UID);
3533 if g_Game_IsNet and NetUseMaster then
3534 g_Net_Slist_Update;
3535 end else
3536 begin
3537 gPlayer1 := nil;
3538 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3539 end;
3540 Exit;
3541 end;
3542 end;
3544 procedure g_Game_Spectate();
3545 begin
3546 g_Game_RemovePlayer();
3547 if gPlayer1 <> nil then
3548 g_Game_RemovePlayer();
3549 end;
3551 procedure g_Game_SpectateCenterView();
3552 begin
3553 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3554 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3555 end;
3557 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3558 var
3559 i, nPl: Integer;
3560 begin
3561 g_Game_Free();
3563 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3565 g_Game_ClearLoading();
3567 // Íàñòðîéêè èãðû:
3568 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3569 gAimLine := False;
3570 gShowMap := False;
3571 gGameSettings.GameType := GT_SINGLE;
3572 gGameSettings.MaxLives := 0;
3573 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3574 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3575 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3576 gSwitchGameMode := GM_SINGLE;
3578 g_Game_ExecuteEvent('ongamestart');
3580 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3581 g_Game_SetupScreenSize();
3583 // Ñîçäàíèå ïåðâîãî èãðîêà:
3584 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3585 gPlayer1Settings.Color,
3586 gPlayer1Settings.Team, False));
3587 if gPlayer1 = nil then
3588 begin
3589 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3590 Exit;
3591 end;
3593 gPlayer1.Name := gPlayer1Settings.Name;
3594 nPl := 1;
3596 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3597 if TwoPlayers then
3598 begin
3599 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3600 gPlayer2Settings.Color,
3601 gPlayer2Settings.Team, False));
3602 if gPlayer2 = nil then
3603 begin
3604 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3605 Exit;
3606 end;
3608 gPlayer2.Name := gPlayer2Settings.Name;
3609 Inc(nPl);
3610 end;
3612 // Çàãðóçêà è çàïóñê êàðòû:
3613 if not g_Game_StartMap(MAP, True) then
3614 begin
3615 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3616 Exit;
3617 end;
3619 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3620 g_Player_Init();
3622 // Ñîçäàåì áîòîâ:
3623 for i := nPl+1 to nPlayers do
3624 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3625 end;
3627 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3628 TimeLimit, GoalLimit: Word;
3629 MaxLives: Byte;
3630 Options: LongWord; nPlayers: Byte);
3631 var
3632 i, nPl: Integer;
3633 begin
3634 g_Game_Free();
3636 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3638 g_Game_ClearLoading();
3640 // Íàñòðîéêè èãðû:
3641 gGameSettings.GameType := GT_CUSTOM;
3642 gGameSettings.GameMode := GameMode;
3643 gSwitchGameMode := GameMode;
3644 gGameSettings.TimeLimit := TimeLimit;
3645 gGameSettings.GoalLimit := GoalLimit;
3646 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3647 gGameSettings.Options := Options;
3649 gCoopTotalMonstersKilled := 0;
3650 gCoopTotalSecretsFound := 0;
3651 gCoopTotalMonsters := 0;
3652 gCoopTotalSecrets := 0;
3653 gAimLine := False;
3654 gShowMap := False;
3656 g_Game_ExecuteEvent('ongamestart');
3658 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3659 g_Game_SetupScreenSize();
3661 // Ðåæèì íàáëþäàòåëÿ:
3662 if nPlayers = 0 then
3663 begin
3664 gPlayer1 := nil;
3665 gPlayer2 := nil;
3666 end;
3668 nPl := 0;
3669 if nPlayers >= 1 then
3670 begin
3671 // Ñîçäàíèå ïåðâîãî èãðîêà:
3672 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3673 gPlayer1Settings.Color,
3674 gPlayer1Settings.Team, False));
3675 if gPlayer1 = nil then
3676 begin
3677 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3678 Exit;
3679 end;
3681 gPlayer1.Name := gPlayer1Settings.Name;
3682 Inc(nPl);
3683 end;
3685 if nPlayers >= 2 then
3686 begin
3687 // Ñîçäàíèå âòîðîãî èãðîêà:
3688 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3689 gPlayer2Settings.Color,
3690 gPlayer2Settings.Team, False));
3691 if gPlayer2 = nil then
3692 begin
3693 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3694 Exit;
3695 end;
3697 gPlayer2.Name := gPlayer2Settings.Name;
3698 Inc(nPl);
3699 end;
3701 // Çàãðóçêà è çàïóñê êàðòû:
3702 if not g_Game_StartMap(Map, True) then
3703 begin
3704 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3705 Exit;
3706 end;
3708 // Íåò òî÷åê ïîÿâëåíèÿ:
3709 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3710 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3711 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3712 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3713 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3714 begin
3715 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3716 Exit;
3717 end;
3719 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3720 g_Player_Init();
3722 // Ñîçäàåì áîòîâ:
3723 for i := nPl+1 to nPlayers do
3724 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3725 end;
3727 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3728 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3729 Options: LongWord; nPlayers: Byte;
3730 IPAddr: LongWord; Port: Word);
3731 begin
3732 g_Game_Free();
3734 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3736 g_Game_ClearLoading();
3738 // Íàñòðîéêè èãðû:
3739 gGameSettings.GameType := GT_SERVER;
3740 gGameSettings.GameMode := GameMode;
3741 gSwitchGameMode := GameMode;
3742 gGameSettings.TimeLimit := TimeLimit;
3743 gGameSettings.GoalLimit := GoalLimit;
3744 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3745 gGameSettings.Options := Options;
3747 gCoopTotalMonstersKilled := 0;
3748 gCoopTotalSecretsFound := 0;
3749 gCoopTotalMonsters := 0;
3750 gCoopTotalSecrets := 0;
3751 gAimLine := False;
3752 gShowMap := False;
3754 g_Game_ExecuteEvent('ongamestart');
3756 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3757 g_Game_SetupScreenSize();
3759 // Ðåæèì íàáëþäàòåëÿ:
3760 if nPlayers = 0 then
3761 begin
3762 gPlayer1 := nil;
3763 gPlayer2 := nil;
3764 end;
3766 if nPlayers >= 1 then
3767 begin
3768 // Ñîçäàíèå ïåðâîãî èãðîêà:
3769 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3770 gPlayer1Settings.Color,
3771 gPlayer1Settings.Team, False));
3772 if gPlayer1 = nil then
3773 begin
3774 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3775 Exit;
3776 end;
3778 gPlayer1.Name := gPlayer1Settings.Name;
3779 end;
3781 if nPlayers >= 2 then
3782 begin
3783 // Ñîçäàíèå âòîðîãî èãðîêà:
3784 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3785 gPlayer2Settings.Color,
3786 gPlayer2Settings.Team, False));
3787 if gPlayer2 = nil then
3788 begin
3789 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3790 Exit;
3791 end;
3793 gPlayer2.Name := gPlayer2Settings.Name;
3794 end;
3796 // Ñòàðòóåì ñåðâåð
3797 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3798 begin
3799 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3800 Exit;
3801 end;
3803 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3805 // Çàãðóçêà è çàïóñê êàðòû:
3806 if not g_Game_StartMap(Map, True) then
3807 begin
3808 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3809 Exit;
3810 end;
3812 // Íåò òî÷åê ïîÿâëåíèÿ:
3813 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3814 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3815 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3816 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3817 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3818 begin
3819 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3820 Exit;
3821 end;
3823 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3824 g_Player_Init();
3826 NetState := NET_STATE_GAME;
3827 end;
3829 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3830 var
3831 Map: String;
3832 WadName: string;
3833 Ptr: Pointer;
3834 T: Cardinal;
3835 MID: Byte;
3836 State: Byte;
3837 OuterLoop: Boolean;
3838 newResPath: string;
3839 InMsg: TMsg;
3840 begin
3841 g_Game_Free();
3843 State := 0;
3844 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3845 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3847 g_Game_ClearLoading();
3849 // Íàñòðîéêè èãðû:
3850 gGameSettings.GameType := GT_CLIENT;
3852 gCoopTotalMonstersKilled := 0;
3853 gCoopTotalSecretsFound := 0;
3854 gCoopTotalMonsters := 0;
3855 gCoopTotalSecrets := 0;
3856 gAimLine := False;
3857 gShowMap := False;
3859 g_Game_ExecuteEvent('ongamestart');
3861 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3862 g_Game_SetupScreenSize();
3864 NetState := NET_STATE_AUTH;
3866 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3867 // Ñòàðòóåì êëèåíò
3868 if not g_Net_Connect(Addr, Port) then
3869 begin
3870 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3871 NetState := NET_STATE_NONE;
3872 Exit;
3873 end;
3875 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3876 MC_SEND_Info(PW);
3877 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3879 OuterLoop := True;
3880 while OuterLoop do
3881 begin
3882 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3883 begin
3884 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3885 begin
3886 Ptr := NetEvent.packet^.data;
3887 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
3888 continue;
3890 MID := InMsg.ReadByte();
3892 if (MID = NET_MSG_INFO) and (State = 0) then
3893 begin
3894 NetMyID := InMsg.ReadByte();
3895 NetPlrUID1 := InMsg.ReadWord();
3897 WadName := InMsg.ReadString();
3898 Map := InMsg.ReadString();
3900 gWADHash := InMsg.ReadMD5();
3902 gGameSettings.GameMode := InMsg.ReadByte();
3903 gSwitchGameMode := gGameSettings.GameMode;
3904 gGameSettings.GoalLimit := InMsg.ReadWord();
3905 gGameSettings.TimeLimit := InMsg.ReadWord();
3906 gGameSettings.MaxLives := InMsg.ReadByte();
3907 gGameSettings.Options := InMsg.ReadLongWord();
3908 T := InMsg.ReadLongWord();
3910 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3911 if newResPath = '' then
3912 begin
3913 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3914 newResPath := g_Res_DownloadWAD(WadName);
3915 if newResPath = '' then
3916 begin
3917 g_FatalError(_lc[I_NET_ERR_HASH]);
3918 enet_packet_destroy(NetEvent.packet);
3919 NetState := NET_STATE_NONE;
3920 Exit;
3921 end;
3922 end;
3923 newResPath := ExtractRelativePath(MapsDir, newResPath);
3925 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3926 gPlayer1Settings.Color,
3927 gPlayer1Settings.Team, False));
3929 if gPlayer1 = nil then
3930 begin
3931 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3933 enet_packet_destroy(NetEvent.packet);
3934 NetState := NET_STATE_NONE;
3935 Exit;
3936 end;
3938 gPlayer1.Name := gPlayer1Settings.Name;
3939 gPlayer1.UID := NetPlrUID1;
3940 gPlayer1.Reset(True);
3942 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3943 begin
3944 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3946 enet_packet_destroy(NetEvent.packet);
3947 NetState := NET_STATE_NONE;
3948 Exit;
3949 end;
3951 gTime := T;
3953 State := 1;
3954 OuterLoop := False;
3955 enet_packet_destroy(NetEvent.packet);
3956 break;
3957 end
3958 else
3959 enet_packet_destroy(NetEvent.packet);
3960 end
3961 else
3962 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3963 begin
3964 State := 0;
3965 if (NetEvent.data <= NET_DISC_MAX) then
3966 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3967 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3968 OuterLoop := False;
3969 Break;
3970 end;
3971 end;
3973 ProcessLoading();
3975 e_PollInput();
3977 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3978 begin
3979 State := 0;
3980 break;
3981 end;
3982 end;
3984 if State <> 1 then
3985 begin
3986 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3987 NetState := NET_STATE_NONE;
3988 Exit;
3989 end;
3991 gLMSRespawn := LMS_RESPAWN_NONE;
3992 gLMSRespawnTime := 0;
3994 g_Player_Init();
3995 NetState := NET_STATE_GAME;
3996 MC_SEND_FullStateRequest;
3997 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3998 end;
4000 procedure g_Game_SaveOptions();
4001 begin
4002 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4003 end;
4005 procedure g_Game_ChangeMap(MapPath: String);
4006 var
4007 Force: Boolean;
4008 begin
4009 g_Game_ClearLoading();
4011 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4012 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4013 if gExitByTrigger then
4014 begin
4015 Force := False;
4016 gExitByTrigger := False;
4017 end;
4018 if not g_Game_StartMap(MapPath, Force) then
4019 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4020 end;
4022 procedure g_Game_Restart();
4023 var
4024 Map: string;
4025 begin
4026 if g_Game_IsClient then
4027 Exit;
4028 map := g_ExtractFileName(gMapInfo.Map);
4030 MessageTime := 0;
4031 gGameOn := False;
4032 g_Game_ClearLoading();
4033 g_Game_StartMap(Map, True);
4034 end;
4036 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
4037 var
4038 NewWAD, ResName: String;
4039 I: Integer;
4040 begin
4041 g_Map_Free();
4042 g_Player_RemoveAllCorpses();
4044 if (not g_Game_IsClient) and
4045 (gSwitchGameMode <> gGameSettings.GameMode) and
4046 (gGameSettings.GameMode <> GM_SINGLE) then
4047 begin
4048 if gSwitchGameMode = GM_CTF then
4049 gGameSettings.MaxLives := 0;
4050 gGameSettings.GameMode := gSwitchGameMode;
4051 Force := True;
4052 end else
4053 gSwitchGameMode := gGameSettings.GameMode;
4055 g_Player_ResetTeams();
4057 if Pos(':\', Map) > 0 then
4058 begin
4059 NewWAD := g_ExtractWadName(Map);
4060 ResName := g_ExtractFileName(Map);
4061 if g_Game_IsServer then
4062 begin
4063 gWADHash := MD5File(MapsDir + NewWAD);
4064 g_Game_LoadWAD(NewWAD);
4065 end else
4066 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4067 g_Game_ClientWAD(NewWAD, gWADHash);
4068 end else
4069 ResName := Map;
4071 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4072 if Result then
4073 begin
4074 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4076 gState := STATE_NONE;
4077 g_ActiveWindow := nil;
4078 gGameOn := True;
4080 DisableCheats();
4081 ResetTimer();
4083 if gGameSettings.GameMode = GM_CTF then
4084 begin
4085 g_Map_ResetFlag(FLAG_RED);
4086 g_Map_ResetFlag(FLAG_BLUE);
4087 // CTF, à ôëàãîâ íåò:
4088 if not g_Map_HaveFlagPoints() then
4089 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4090 end;
4091 end
4092 else
4093 begin
4094 gState := STATE_MENU;
4095 gGameOn := False;
4096 end;
4098 gExit := 0;
4099 gPause := False;
4100 gTime := 0;
4101 NetTimeToUpdate := 1;
4102 NetTimeToReliable := 0;
4103 NetTimeToMaster := NetMasterRate;
4104 gLMSRespawn := LMS_RESPAWN_NONE;
4105 gLMSRespawnTime := 0;
4106 gMissionFailed := False;
4107 gNextMap := '';
4109 gCoopMonstersKilled := 0;
4110 gCoopSecretsFound := 0;
4112 gVoteInProgress := False;
4113 gVotePassed := False;
4114 gVoteCount := 0;
4115 gVoted := False;
4117 gStatsOff := False;
4119 if not gGameOn then Exit;
4121 g_Game_SpectateCenterView();
4123 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4124 begin
4125 gLMSRespawn := LMS_RESPAWN_WARMUP;
4126 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4127 gLMSSoftSpawn := True;
4128 if NetMode = NET_SERVER then
4129 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4130 else
4131 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4132 end;
4134 if NetMode = NET_SERVER then
4135 begin
4136 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4138 // Ìàñòåðñåðâåð
4139 if NetUseMaster then
4140 begin
4141 if (NetMHost = nil) or (NetMPeer = nil) then
4142 if not g_Net_Slist_Connect then
4143 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4145 g_Net_Slist_Update;
4146 end;
4148 if NetClients <> nil then
4149 for I := 0 to High(NetClients) do
4150 if NetClients[I].Used then
4151 begin
4152 NetClients[I].Voted := False;
4153 if NetClients[I].RequestedFullUpdate then
4154 begin
4155 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4156 NetClients[I].RequestedFullUpdate := False;
4157 end;
4158 end;
4160 g_Net_UnbanNonPermHosts();
4161 end;
4163 if gLastMap then
4164 begin
4165 gCoopTotalMonstersKilled := 0;
4166 gCoopTotalSecretsFound := 0;
4167 gCoopTotalMonsters := 0;
4168 gCoopTotalSecrets := 0;
4169 gLastMap := False;
4170 end;
4172 g_Game_ExecuteEvent('onmapstart');
4173 end;
4175 procedure SetFirstLevel();
4176 begin
4177 gNextMap := '';
4179 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4180 if MapList = nil then
4181 Exit;
4183 SortSArray(MapList);
4184 gNextMap := MapList[Low(MapList)];
4186 MapList := nil;
4187 end;
4189 procedure g_Game_ExitLevel(Map: Char16);
4190 begin
4191 gNextMap := Map;
4193 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4194 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4195 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4196 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4198 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4199 if gGameSettings.GameType = GT_SINGLE then
4200 gExit := EXIT_ENDLEVELSINGLE
4201 else // Âûøëè â âûõîä â Ñâîåé èãðå
4202 begin
4203 gExit := EXIT_ENDLEVELCUSTOM;
4204 if gGameSettings.GameMode = GM_COOP then
4205 g_Player_RememberAll;
4207 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4208 begin
4209 gLastMap := True;
4210 if gGameSettings.GameMode = GM_COOP then
4211 gStatsOff := True;
4213 gStatsPressed := True;
4214 gNextMap := 'MAP01';
4216 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4217 g_Game_NextLevel;
4219 if g_Game_IsNet then
4220 begin
4221 MH_SEND_GameStats();
4222 MH_SEND_CoopStats();
4223 end;
4224 end;
4225 end;
4226 end;
4228 procedure g_Game_RestartLevel();
4229 var
4230 Map: string;
4231 begin
4232 if gGameSettings.GameMode = GM_SINGLE then
4233 begin
4234 g_Game_Restart();
4235 Exit;
4236 end;
4237 gExit := EXIT_ENDLEVELCUSTOM;
4238 Map := g_ExtractFileName(gMapInfo.Map);
4239 gNextMap := Map;
4240 end;
4242 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4243 var
4244 gWAD: String;
4245 begin
4246 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4247 Exit;
4248 if not g_Game_IsClient then
4249 Exit;
4250 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4251 if gWAD = '' then
4252 begin
4253 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4254 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4255 if gWAD = '' then
4256 begin
4257 g_Game_Free();
4258 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4259 Exit;
4260 end;
4261 end;
4262 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4263 g_Game_LoadWAD(NewWAD);
4264 end;
4266 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4267 var
4268 i, n, nb, nr: Integer;
4270 function monRespawn (mon: TMonster): Boolean;
4271 begin
4272 result := false; // don't stop
4273 if not mon.FNoRespawn then mon.Respawn();
4274 end;
4276 begin
4277 if not g_Game_IsServer then Exit;
4278 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4279 gLMSRespawn := LMS_RESPAWN_NONE;
4280 gLMSRespawnTime := 0;
4281 MessageTime := 0;
4283 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4284 begin
4285 gMissionFailed := True;
4286 g_Game_RestartLevel;
4287 Exit;
4288 end;
4290 n := 0; nb := 0; nr := 0;
4291 for i := Low(gPlayers) to High(gPlayers) do
4292 if (gPlayers[i] <> nil) and
4293 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4294 (gPlayers[i] is TBot)) then
4295 begin
4296 Inc(n);
4297 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4298 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4299 end;
4301 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4302 begin
4303 // wait a second until the fuckers finally decide to join
4304 gLMSRespawn := LMS_RESPAWN_WARMUP;
4305 gLMSRespawnTime := gTime + 1000;
4306 gLMSSoftSpawn := NoMapRestart;
4307 Exit;
4308 end;
4310 g_Player_RemoveAllCorpses;
4311 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4312 if g_Game_IsNet then
4313 MH_SEND_GameEvent(NET_EV_LMS_START);
4315 for i := Low(gPlayers) to High(gPlayers) do
4316 begin
4317 if gPlayers[i] = nil then continue;
4318 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4319 // don't touch normal spectators
4320 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4321 begin
4322 gPlayers[i].FNoRespawn := True;
4323 gPlayers[i].Lives := 0;
4324 if g_Game_IsNet then
4325 MH_SEND_PlayerStats(gPlayers[I].UID);
4326 continue;
4327 end;
4328 gPlayers[i].FNoRespawn := False;
4329 gPlayers[i].Lives := gGameSettings.MaxLives;
4330 gPlayers[i].Respawn(False, True);
4331 if gGameSettings.GameMode = GM_COOP then
4332 begin
4333 gPlayers[i].Frags := 0;
4334 gPlayers[i].RecallState;
4335 end;
4336 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4337 gPlayer1 := g_Player_Get(gLMSPID1);
4338 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4339 gPlayer2 := g_Player_Get(gLMSPID2);
4340 end;
4342 g_Items_RestartRound();
4345 g_Mons_ForEach(monRespawn);
4347 gLMSSoftSpawn := False;
4348 end;
4350 function g_Game_GetFirstMap(WAD: String): String;
4351 begin
4352 Result := '';
4354 MapList := g_Map_GetMapsList(WAD);
4355 if MapList = nil then
4356 Exit;
4358 SortSArray(MapList);
4359 Result := MapList[Low(MapList)];
4361 if not g_Map_Exist(WAD + ':\' + Result) then
4362 Result := '';
4364 MapList := nil;
4365 end;
4367 function g_Game_GetNextMap(): String;
4368 var
4369 I: Integer;
4370 Map: string;
4371 begin
4372 Result := '';
4374 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4375 if MapList = nil then
4376 Exit;
4378 Map := g_ExtractFileName(gMapInfo.Map);
4380 SortSArray(MapList);
4381 MapIndex := -255;
4382 for I := Low(MapList) to High(MapList) do
4383 if Map = MapList[I] then
4384 begin
4385 MapIndex := I;
4386 Break;
4387 end;
4389 if MapIndex <> -255 then
4390 begin
4391 if MapIndex = High(MapList) then
4392 Result := MapList[Low(MapList)]
4393 else
4394 Result := MapList[MapIndex + 1];
4396 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4397 end;
4399 MapList := nil;
4400 end;
4402 procedure g_Game_NextLevel();
4403 begin
4404 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4405 gExit := EXIT_ENDLEVELCUSTOM
4406 else
4407 begin
4408 gExit := EXIT_ENDLEVELSINGLE;
4409 Exit;
4410 end;
4412 if gNextMap <> '' then Exit;
4413 gNextMap := g_Game_GetNextMap();
4414 end;
4416 function g_Game_IsTestMap(): Boolean;
4417 begin
4418 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4419 end;
4421 procedure g_Game_DeleteTestMap();
4422 var
4423 a: Integer;
4424 MapName: Char16;
4425 WadName: string;
4427 WAD: TWADFile;
4428 MapList: SArray;
4429 time: Integer;
4431 begin
4432 a := Pos('.wad:\', gMapToDelete);
4433 if a = 0 then
4434 Exit;
4436 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4437 WadName := Copy(gMapToDelete, 1, a + 3);
4438 Delete(gMapToDelete, 1, a + 5);
4439 gMapToDelete := UpperCase(gMapToDelete);
4440 MapName := '';
4441 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4444 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4445 if MapName <> TEST_MAP_NAME then
4446 Exit;
4448 if not gTempDelete then
4449 begin
4450 time := g_GetFileTime(WadName);
4451 WAD := TWADFile.Create();
4453 // ×èòàåì Wad-ôàéë:
4454 if not WAD.ReadFile(WadName) then
4455 begin // Íåò òàêîãî WAD-ôàéëà
4456 WAD.Free();
4457 Exit;
4458 end;
4460 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4461 WAD.CreateImage();
4462 MapList := WAD.GetResourcesList('');
4464 if MapList <> nil then
4465 for a := 0 to High(MapList) do
4466 if MapList[a] = MapName then
4467 begin
4468 // Óäàëÿåì è ñîõðàíÿåì:
4469 WAD.RemoveResource('', MapName);
4470 WAD.SaveTo(WadName);
4471 Break;
4472 end;
4474 WAD.Free();
4475 g_SetFileTime(WadName, time);
4476 end else
4478 if gTempDelete then DeleteFile(WadName);
4479 end;
4481 procedure GameCVars(P: SArray);
4482 var
4483 a, b: Integer;
4484 stat: TPlayerStatArray;
4485 cmd, s: string;
4486 config: TConfig;
4487 begin
4488 stat := nil;
4489 cmd := LowerCase(P[0]);
4490 if cmd = 'r_showfps' then
4491 begin
4492 if (Length(P) > 1) and
4493 ((P[1] = '1') or (P[1] = '0')) then
4494 gShowFPS := (P[1][1] = '1');
4496 if gShowFPS then
4497 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4498 else
4499 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4500 end
4501 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4502 begin
4503 with gGameSettings do
4504 begin
4505 if (Length(P) > 1) and
4506 ((P[1] = '1') or (P[1] = '0')) then
4507 begin
4508 if (P[1][1] = '1') then
4509 Options := Options or GAME_OPTION_TEAMDAMAGE
4510 else
4511 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4512 end;
4514 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4515 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4516 else
4517 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4519 if g_Game_IsNet then MH_SEND_GameSettings;
4520 end;
4521 end
4522 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4523 begin
4524 with gGameSettings do
4525 begin
4526 if (Length(P) > 1) and
4527 ((P[1] = '1') or (P[1] = '0')) then
4528 begin
4529 if (P[1][1] = '1') then
4530 Options := Options or GAME_OPTION_WEAPONSTAY
4531 else
4532 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4533 end;
4535 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4536 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4537 else
4538 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4540 if g_Game_IsNet then MH_SEND_GameSettings;
4541 end;
4542 end
4543 else if cmd = 'g_gamemode' then
4544 begin
4545 a := g_Game_TextToMode(P[1]);
4546 if a = GM_SINGLE then a := GM_COOP;
4547 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4548 begin
4549 gSwitchGameMode := a;
4550 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4551 (gState = STATE_INTERSINGLE) then
4552 gSwitchGameMode := GM_SINGLE;
4553 if not gGameOn then
4554 gGameSettings.GameMode := gSwitchGameMode;
4555 end;
4556 if gSwitchGameMode = gGameSettings.GameMode then
4557 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4558 [g_Game_ModeToText(gGameSettings.GameMode)]))
4559 else
4560 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4561 [g_Game_ModeToText(gGameSettings.GameMode),
4562 g_Game_ModeToText(gSwitchGameMode)]));
4563 end
4564 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4565 begin
4566 with gGameSettings do
4567 begin
4568 if (Length(P) > 1) and
4569 ((P[1] = '1') or (P[1] = '0')) then
4570 begin
4571 if (P[1][1] = '1') then
4572 Options := Options or GAME_OPTION_ALLOWEXIT
4573 else
4574 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4575 end;
4577 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4578 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4579 else
4580 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4581 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4583 if g_Game_IsNet then MH_SEND_GameSettings;
4584 end;
4585 end
4586 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4587 begin
4588 with gGameSettings do
4589 begin
4590 if (Length(P) > 1) and
4591 ((P[1] = '1') or (P[1] = '0')) then
4592 begin
4593 if (P[1][1] = '1') then
4594 Options := Options or GAME_OPTION_MONSTERS
4595 else
4596 Options := Options and (not GAME_OPTION_MONSTERS);
4597 end;
4599 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4600 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4601 else
4602 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4603 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4605 if g_Game_IsNet then MH_SEND_GameSettings;
4606 end;
4607 end
4608 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4609 begin
4610 with gGameSettings do
4611 begin
4612 if (Length(P) > 1) and
4613 ((P[1] = '1') or (P[1] = '0')) then
4614 begin
4615 if (P[1][1] = '1') then
4616 Options := Options or GAME_OPTION_BOTVSPLAYER
4617 else
4618 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4619 end;
4621 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4622 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4623 else
4624 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4626 if g_Game_IsNet then MH_SEND_GameSettings;
4627 end;
4628 end
4629 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4630 begin
4631 with gGameSettings do
4632 begin
4633 if (Length(P) > 1) and
4634 ((P[1] = '1') or (P[1] = '0')) then
4635 begin
4636 if (P[1][1] = '1') then
4637 Options := Options or GAME_OPTION_BOTVSMONSTER
4638 else
4639 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4640 end;
4642 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4643 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4644 else
4645 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4647 if g_Game_IsNet then MH_SEND_GameSettings;
4648 end;
4649 end
4650 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4651 begin
4652 if Length(P) > 1 then
4653 begin
4654 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4655 gGameSettings.WarmupTime := 30
4656 else
4657 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4658 end;
4660 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4661 [gGameSettings.WarmupTime]));
4662 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4663 end
4664 else if cmd = 'net_interp' then
4665 begin
4666 if (Length(P) > 1) then
4667 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4669 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4670 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4671 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4672 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4673 config.Free();
4674 end
4675 else if cmd = 'net_forceplayerupdate' then
4676 begin
4677 if (Length(P) > 1) and
4678 ((P[1] = '1') or (P[1] = '0')) then
4679 NetForcePlayerUpdate := (P[1][1] = '1');
4681 if NetForcePlayerUpdate then
4682 g_Console_Add('net_forceplayerupdate = 1')
4683 else
4684 g_Console_Add('net_forceplayerupdate = 0');
4685 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4686 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4687 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4688 config.Free();
4689 end
4690 else if cmd = 'net_predictself' then
4691 begin
4692 if (Length(P) > 1) and
4693 ((P[1] = '1') or (P[1] = '0')) then
4694 NetPredictSelf := (P[1][1] = '1');
4696 if NetPredictSelf then
4697 g_Console_Add('net_predictself = 1')
4698 else
4699 g_Console_Add('net_predictself = 0');
4700 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4701 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4702 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4703 config.Free();
4704 end
4705 else if cmd = 'sv_name' then
4706 begin
4707 if (Length(P) > 1) and (Length(P[1]) > 0) then
4708 begin
4709 NetServerName := P[1];
4710 if Length(NetServerName) > 64 then
4711 SetLength(NetServerName, 64);
4712 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4713 g_Net_Slist_Update;
4714 end;
4716 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4717 end
4718 else if cmd = 'sv_passwd' then
4719 begin
4720 if (Length(P) > 1) and (Length(P[1]) > 0) then
4721 begin
4722 NetPassword := P[1];
4723 if Length(NetPassword) > 24 then
4724 SetLength(NetPassword, 24);
4725 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4726 g_Net_Slist_Update;
4727 end;
4729 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4730 end
4731 else if cmd = 'sv_maxplrs' then
4732 begin
4733 if (Length(P) > 1) then
4734 begin
4735 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4736 if g_Game_IsServer and g_Game_IsNet then
4737 begin
4738 b := 0;
4739 for a := 0 to High(NetClients) do
4740 if NetClients[a].Used then
4741 begin
4742 Inc(b);
4743 if b > NetMaxClients then
4744 begin
4745 s := g_Player_Get(NetClients[a].Player).Name;
4746 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4747 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4748 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4749 end;
4750 end;
4751 if NetUseMaster then
4752 g_Net_Slist_Update;
4753 end;
4754 end;
4756 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4757 end
4758 else if cmd = 'sv_public' then
4759 begin
4760 if (Length(P) > 1) then
4761 begin
4762 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4763 if g_Game_IsServer and g_Game_IsNet then
4764 if NetUseMaster then
4765 begin
4766 if NetMPeer = nil then
4767 if not g_Net_Slist_Connect() then
4768 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4769 g_Net_Slist_Update();
4770 end
4771 else
4772 if NetMPeer <> nil then
4773 g_Net_Slist_Disconnect();
4774 end;
4776 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4777 end
4778 else if cmd = 'sv_intertime' then
4779 begin
4780 if (Length(P) > 1) then
4781 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4783 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4784 end
4785 else if cmd = 'p1_name' then
4786 begin
4787 if (Length(P) > 1) and gGameOn then
4788 begin
4789 if g_Game_IsClient then
4790 begin
4791 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4792 MC_SEND_PlayerSettings;
4793 end
4794 else
4795 if gPlayer1 <> nil then
4796 begin
4797 gPlayer1.Name := b_Text_Unformat(P[1]);
4798 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4799 end
4800 else
4801 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4802 end;
4803 end
4804 else if cmd = 'p2_name' then
4805 begin
4806 if (Length(P) > 1) and gGameOn then
4807 begin
4808 if g_Game_IsClient then
4809 begin
4810 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4811 MC_SEND_PlayerSettings;
4812 end
4813 else
4814 if gPlayer2 <> nil then
4815 begin
4816 gPlayer2.Name := b_Text_Unformat(P[1]);
4817 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4818 end
4819 else
4820 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4821 end;
4822 end
4823 else if cmd = 'p1_color' then
4824 begin
4825 if Length(P) > 3 then
4826 if g_Game_IsClient then
4827 begin
4828 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4829 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4830 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4831 MC_SEND_PlayerSettings;
4832 end
4833 else
4834 if gPlayer1 <> nil then
4835 begin
4836 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4837 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4838 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4839 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4840 end
4841 else
4842 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4843 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4844 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4845 end
4846 else if (cmd = 'p2_color') and not g_Game_IsNet then
4847 begin
4848 if Length(P) > 3 then
4849 if g_Game_IsClient then
4850 begin
4851 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4852 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4853 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4854 MC_SEND_PlayerSettings;
4855 end
4856 else
4857 if gPlayer2 <> nil then
4858 begin
4859 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4860 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4861 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4862 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4863 end
4864 else
4865 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4866 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4867 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4868 end
4869 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4870 begin
4871 if cmd = 'r_showtime' then
4872 begin
4873 if (Length(P) > 1) and
4874 ((P[1] = '1') or (P[1] = '0')) then
4875 gShowTime := (P[1][1] = '1');
4877 if gShowTime then
4878 g_Console_Add(_lc[I_MSG_TIME_ON])
4879 else
4880 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4881 end
4882 else if cmd = 'r_showscore' then
4883 begin
4884 if (Length(P) > 1) and
4885 ((P[1] = '1') or (P[1] = '0')) then
4886 gShowGoals := (P[1][1] = '1');
4888 if gShowGoals then
4889 g_Console_Add(_lc[I_MSG_SCORE_ON])
4890 else
4891 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4892 end
4893 else if cmd = 'r_showstat' then
4894 begin
4895 if (Length(P) > 1) and
4896 ((P[1] = '1') or (P[1] = '0')) then
4897 gShowStat := (P[1][1] = '1');
4899 if gShowStat then
4900 g_Console_Add(_lc[I_MSG_STATS_ON])
4901 else
4902 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4903 end
4904 else if cmd = 'r_showkillmsg' then
4905 begin
4906 if (Length(P) > 1) and
4907 ((P[1] = '1') or (P[1] = '0')) then
4908 gShowKillMsg := (P[1][1] = '1');
4910 if gShowKillMsg then
4911 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4912 else
4913 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4914 end
4915 else if cmd = 'r_showlives' then
4916 begin
4917 if (Length(P) > 1) and
4918 ((P[1] = '1') or (P[1] = '0')) then
4919 gShowLives := (P[1][1] = '1');
4921 if gShowLives then
4922 g_Console_Add(_lc[I_MSG_LIVES_ON])
4923 else
4924 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4925 end
4926 else if cmd = 'r_showspect' then
4927 begin
4928 if (Length(P) > 1) and
4929 ((P[1] = '1') or (P[1] = '0')) then
4930 gSpectHUD := (P[1][1] = '1');
4932 if gSpectHUD then
4933 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4934 else
4935 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4936 end
4937 else if cmd = 'r_showping' then
4938 begin
4939 if (Length(P) > 1) and
4940 ((P[1] = '1') or (P[1] = '0')) then
4941 gShowPing := (P[1][1] = '1');
4943 if gShowPing then
4944 g_Console_Add(_lc[I_MSG_PING_ON])
4945 else
4946 g_Console_Add(_lc[I_MSG_PING_OFF]);
4947 end
4948 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4949 begin
4950 if Length(P) > 1 then
4951 begin
4952 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4953 gGameSettings.GoalLimit := 0
4954 else
4955 begin
4956 b := 0;
4958 if gGameSettings.GameMode = GM_DM then
4959 begin // DM
4960 stat := g_Player_GetStats();
4961 if stat <> nil then
4962 for a := 0 to High(stat) do
4963 if stat[a].Frags > b then
4964 b := stat[a].Frags;
4965 end
4966 else // TDM/CTF
4967 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4969 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4970 end;
4972 if g_Game_IsNet then MH_SEND_GameSettings;
4973 end;
4975 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4976 end
4977 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4978 begin
4979 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4980 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4982 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4983 [gGameSettings.TimeLimit div 3600,
4984 (gGameSettings.TimeLimit div 60) mod 60,
4985 gGameSettings.TimeLimit mod 60]));
4986 if g_Game_IsNet then MH_SEND_GameSettings;
4987 end
4988 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4989 begin
4990 if Length(P) > 1 then
4991 begin
4992 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4993 gGameSettings.MaxLives := 0
4994 else
4995 begin
4996 b := 0;
4997 stat := g_Player_GetStats();
4998 if stat <> nil then
4999 for a := 0 to High(stat) do
5000 if stat[a].Lives > b then
5001 b := stat[a].Lives;
5002 gGameSettings.MaxLives :=
5003 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5004 end;
5005 end;
5007 g_Console_Add(Format(_lc[I_MSG_LIVES],
5008 [gGameSettings.MaxLives]));
5009 if g_Game_IsNet then MH_SEND_GameSettings;
5010 end;
5011 end;
5012 end;
5015 procedure DebugCommands(P: SArray);
5016 var
5017 a, b: Integer;
5018 cmd: string;
5019 //pt: TDFPoint;
5020 mon: TMonster;
5021 begin
5022 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5023 if gDebugMode then
5024 begin
5025 cmd := LowerCase(P[0]);
5026 if cmd = 'd_window' then
5027 begin
5028 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5029 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5030 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5031 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5032 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5033 end
5034 else if cmd = 'd_sounds' then
5035 begin
5036 if (Length(P) > 1) and
5037 ((P[1] = '1') or (P[1] = '0')) then
5038 g_Debug_Sounds := (P[1][1] = '1');
5040 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5041 end
5042 else if cmd = 'd_frames' then
5043 begin
5044 if (Length(P) > 1) and
5045 ((P[1] = '1') or (P[1] = '0')) then
5046 g_Debug_Frames := (P[1][1] = '1');
5048 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5049 end
5050 else if cmd = 'd_winmsg' then
5051 begin
5052 if (Length(P) > 1) and
5053 ((P[1] = '1') or (P[1] = '0')) then
5054 g_Debug_WinMsgs := (P[1][1] = '1');
5056 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5057 end
5058 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5059 begin
5060 if (Length(P) > 1) and
5061 ((P[1] = '1') or (P[1] = '0')) then
5062 g_Debug_MonsterOff := (P[1][1] = '1');
5064 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5065 end
5066 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5067 begin
5068 if Length(P) > 1 then
5069 case P[1][1] of
5070 '0': g_debug_BotAIOff := 0;
5071 '1': g_debug_BotAIOff := 1;
5072 '2': g_debug_BotAIOff := 2;
5073 '3': g_debug_BotAIOff := 3;
5074 end;
5076 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5077 end
5078 else if cmd = 'd_monster' then
5079 begin
5080 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
5081 if Length(P) < 2 then
5082 begin
5083 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5084 g_Console_Add('ID | Name');
5085 for b := MONSTER_DEMON to MONSTER_MAN do
5086 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
5087 end else
5088 begin
5089 a := StrToIntDef(P[1], 0);
5090 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5091 a := g_Monsters_GetIDByName(P[1]);
5093 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5094 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5095 else
5096 begin
5097 with gPlayer1.Obj do
5098 begin
5099 mon := g_Monsters_Create(a,
5100 X + Rect.X + (Rect.Width div 2),
5101 Y + Rect.Y + Rect.Height,
5102 gPlayer1.Direction, True);
5103 end;
5104 if (Length(P) > 2) and (mon <> nil) then
5105 mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5106 end;
5107 end;
5108 end
5109 else if (cmd = 'd_health') then
5110 begin
5111 if (Length(P) > 1) and
5112 ((P[1] = '1') or (P[1] = '0')) then
5113 g_debug_HealthBar := (P[1][1] = '1');
5115 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5116 end
5117 else if (cmd = 'd_player') then
5118 begin
5119 if (Length(P) > 1) and
5120 ((P[1] = '1') or (P[1] = '0')) then
5121 g_debug_Player := (P[1][1] = '1');
5123 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5124 end
5125 else if (cmd = 'd_joy') then
5126 begin
5127 for a := 1 to 8 do
5128 g_Console_Add(e_JoystickStateToString(a));
5129 end;
5130 end
5131 else
5132 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5133 end;
5136 procedure GameCheats(P: SArray);
5137 var
5138 cmd: string;
5139 f, a: Integer;
5140 plr: TPlayer;
5141 begin
5142 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5143 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5144 begin
5145 g_Console_Add('not available');
5146 exit;
5147 end;
5148 plr := gPlayer1;
5149 if plr = nil then
5150 begin
5151 g_Console_Add('where is the player?!');
5152 exit;
5153 end;
5154 cmd := LowerCase(P[0]);
5155 // god
5156 if cmd = 'god' then
5157 begin
5158 plr.GodMode := not plr.GodMode;
5159 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5160 exit;
5161 end;
5162 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5163 if cmd = 'give' then
5164 begin
5165 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5166 for f := 1 to High(P) do
5167 begin
5168 cmd := LowerCase(P[f]);
5169 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5170 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5171 if cmd = 'exit' then
5172 begin
5173 if gTriggers <> nil then
5174 begin
5175 for a := 0 to High(gTriggers) do
5176 begin
5177 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5178 begin
5179 g_Console_Add('player left the map');
5180 gExitByTrigger := True;
5181 g_Game_ExitLevel(gTriggers[a].Data.MapName);
5182 break;
5183 end;
5184 end;
5185 end;
5186 continue;
5187 end;
5189 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5190 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5191 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5192 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5193 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5195 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5196 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5198 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5199 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;
5201 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5202 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5204 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5205 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5207 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5208 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5210 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5211 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5212 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5214 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5215 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5216 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5217 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;
5218 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5219 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5221 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;
5222 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;
5223 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;
5224 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;
5225 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;
5226 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;
5228 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5229 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;
5231 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;
5232 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;
5234 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5236 if cmd = 'ammo' then
5237 begin
5238 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5239 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5240 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5241 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5242 plr.GiveItem(ITEM_AMMO_FUELCAN);
5243 g_Console_Add('player got some ammo');
5244 continue;
5245 end;
5247 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5248 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5250 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5251 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5253 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5254 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5256 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5257 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5259 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5261 if cmd = 'weapons' then
5262 begin
5263 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5264 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5265 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5266 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5267 plr.GiveItem(ITEM_WEAPON_PLASMA);
5268 plr.GiveItem(ITEM_WEAPON_BFG);
5269 g_Console_Add('player got weapons');
5270 continue;
5271 end;
5273 if cmd = 'keys' then
5274 begin
5275 plr.GiveItem(ITEM_KEY_RED);
5276 plr.GiveItem(ITEM_KEY_GREEN);
5277 plr.GiveItem(ITEM_KEY_BLUE);
5278 g_Console_Add('player got all keys');
5279 continue;
5280 end;
5282 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5283 end;
5284 exit;
5285 end;
5286 // open
5287 if cmd = 'open' then
5288 begin
5289 g_Console_Add('player activated sesame');
5290 g_Triggers_OpenAll();
5291 exit;
5292 end;
5293 // fly
5294 if cmd = 'fly' then
5295 begin
5296 gFly := not gFly;
5297 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5298 exit;
5299 end;
5300 // noclip
5301 if cmd = 'noclip' then
5302 begin
5303 plr.SwitchNoClip;
5304 g_Console_Add('wall hardeness adjusted');
5305 exit;
5306 end;
5307 // notarget
5308 if cmd = 'notarget' then
5309 begin
5310 plr.NoTarget := not plr.NoTarget;
5311 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5312 exit;
5313 end;
5314 // noreload
5315 if cmd = 'noreload' then
5316 begin
5317 plr.NoReload := not plr.NoReload;
5318 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5319 exit;
5320 end;
5321 // speedy
5322 if cmd = 'speedy' then
5323 begin
5324 MAX_RUNVEL := 32-MAX_RUNVEL;
5325 g_Console_Add('speed adjusted');
5326 exit;
5327 end;
5328 // jumpy
5329 if cmd = 'jumpy' then
5330 begin
5331 VEL_JUMP := 30-VEL_JUMP;
5332 g_Console_Add('jump height adjusted');
5333 exit;
5334 end;
5335 // automap
5336 if cmd = 'automap' then
5337 begin
5338 gShowMap := not gShowMap;
5339 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5340 exit;
5341 end;
5342 // aimline
5343 if cmd = 'aimline' then
5344 begin
5345 gAimLine := not gAimLine;
5346 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5347 exit;
5348 end;
5349 end;
5351 procedure GameCommands(P: SArray);
5352 var
5353 a, b: Integer;
5354 s, pw: String;
5355 chstr: string;
5356 cmd: string;
5357 pl: pTNetClient = nil;
5358 plr: TPlayer;
5359 prt: Word;
5360 nm: Boolean;
5361 listen: LongWord;
5362 begin
5363 // Îáùèå êîìàíäû:
5364 cmd := LowerCase(P[0]);
5365 chstr := '';
5366 if (cmd = 'quit') or
5367 (cmd = 'exit') then
5368 begin
5369 g_Game_Free();
5370 g_Game_Quit();
5371 Exit;
5372 end
5373 else if cmd = 'pause' then
5374 begin
5375 if (g_ActiveWindow = nil) then
5376 g_Game_Pause(not gPause);
5377 end
5378 else if cmd = 'endgame' then
5379 gExit := EXIT_SIMPLE
5380 else if cmd = 'restart' then
5381 begin
5382 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5383 begin
5384 if g_Game_IsClient then
5385 begin
5386 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5387 Exit;
5388 end;
5389 g_Game_Restart();
5390 end else
5391 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5392 end
5393 else if cmd = 'kick' then
5394 begin
5395 if g_Game_IsServer then
5396 begin
5397 if Length(P) < 2 then
5398 begin
5399 g_Console_Add('kick <name>');
5400 Exit;
5401 end;
5402 if P[1] = '' then
5403 begin
5404 g_Console_Add('kick <name>');
5405 Exit;
5406 end;
5408 if g_Game_IsNet then
5409 pl := g_Net_Client_ByName(P[1]);
5410 if (pl <> nil) then
5411 begin
5412 s := g_Net_ClientName_ByID(pl^.ID);
5413 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5414 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5415 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5416 if NetUseMaster then
5417 g_Net_Slist_Update;
5418 end else if gPlayers <> nil then
5419 for a := Low(gPlayers) to High(gPlayers) do
5420 if gPlayers[a] <> nil then
5421 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5422 begin
5423 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5424 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5425 continue;
5426 gPlayers[a].Lives := 0;
5427 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5428 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5429 g_Player_Remove(gPlayers[a].UID);
5430 if NetUseMaster then
5431 g_Net_Slist_Update;
5432 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5433 g_Bot_MixNames();
5434 end;
5435 end else
5436 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5437 end
5438 else if cmd = 'kick_id' then
5439 begin
5440 if g_Game_IsServer and g_Game_IsNet then
5441 begin
5442 if Length(P) < 2 then
5443 begin
5444 g_Console_Add('kick_id <client ID>');
5445 Exit;
5446 end;
5447 if P[1] = '' then
5448 begin
5449 g_Console_Add('kick_id <client ID>');
5450 Exit;
5451 end;
5453 a := StrToIntDef(P[1], 0);
5454 if (NetClients <> nil) and (a <= High(NetClients)) then
5455 begin
5456 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5457 begin
5458 s := g_Net_ClientName_ByID(NetClients[a].ID);
5459 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5460 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5461 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5462 if NetUseMaster then
5463 g_Net_Slist_Update;
5464 end;
5465 end;
5466 end else
5467 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5468 end
5469 else if cmd = 'ban' then
5470 begin
5471 if g_Game_IsServer and g_Game_IsNet then
5472 begin
5473 if Length(P) < 2 then
5474 begin
5475 g_Console_Add('ban <name>');
5476 Exit;
5477 end;
5478 if P[1] = '' then
5479 begin
5480 g_Console_Add('ban <name>');
5481 Exit;
5482 end;
5484 pl := g_Net_Client_ByName(P[1]);
5485 if (pl <> nil) then
5486 begin
5487 s := g_Net_ClientName_ByID(pl^.ID);
5488 g_Net_BanHost(pl^.Peer^.address.host, False);
5489 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5490 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5491 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5492 if NetUseMaster then
5493 g_Net_Slist_Update;
5494 end else
5495 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5496 end else
5497 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5498 end
5499 else if cmd = 'ban_id' then
5500 begin
5501 if g_Game_IsServer and g_Game_IsNet then
5502 begin
5503 if Length(P) < 2 then
5504 begin
5505 g_Console_Add('ban_id <client ID>');
5506 Exit;
5507 end;
5508 if P[1] = '' then
5509 begin
5510 g_Console_Add('ban_id <client ID>');
5511 Exit;
5512 end;
5514 a := StrToIntDef(P[1], 0);
5515 if (NetClients <> nil) and (a <= High(NetClients)) then
5516 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5517 begin
5518 s := g_Net_ClientName_ByID(NetClients[a].ID);
5519 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5520 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5521 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5522 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5523 if NetUseMaster then
5524 g_Net_Slist_Update;
5525 end;
5526 end else
5527 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5528 end
5529 else if cmd = 'permban' then
5530 begin
5531 if g_Game_IsServer and g_Game_IsNet then
5532 begin
5533 if Length(P) < 2 then
5534 begin
5535 g_Console_Add('permban <name>');
5536 Exit;
5537 end;
5538 if P[1] = '' then
5539 begin
5540 g_Console_Add('permban <name>');
5541 Exit;
5542 end;
5544 pl := g_Net_Client_ByName(P[1]);
5545 if (pl <> nil) then
5546 begin
5547 s := g_Net_ClientName_ByID(pl^.ID);
5548 g_Net_BanHost(pl^.Peer^.address.host);
5549 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5550 g_Net_SaveBanList();
5551 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5552 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5553 if NetUseMaster then
5554 g_Net_Slist_Update;
5555 end else
5556 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5557 end else
5558 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5559 end
5560 else if cmd = 'permban_id' then
5561 begin
5562 if g_Game_IsServer and g_Game_IsNet then
5563 begin
5564 if Length(P) < 2 then
5565 begin
5566 g_Console_Add('permban_id <client ID>');
5567 Exit;
5568 end;
5569 if P[1] = '' then
5570 begin
5571 g_Console_Add('permban_id <client ID>');
5572 Exit;
5573 end;
5575 a := StrToIntDef(P[1], 0);
5576 if (NetClients <> nil) and (a <= High(NetClients)) then
5577 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5578 begin
5579 s := g_Net_ClientName_ByID(NetClients[a].ID);
5580 g_Net_BanHost(NetClients[a].Peer^.address.host);
5581 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5582 g_Net_SaveBanList();
5583 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5584 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5585 if NetUseMaster then
5586 g_Net_Slist_Update;
5587 end;
5588 end else
5589 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5590 end
5591 else if cmd = 'unban' then
5592 begin
5593 if g_Game_IsServer and g_Game_IsNet then
5594 begin
5595 if Length(P) < 2 then
5596 begin
5597 g_Console_Add('unban <IP Address>');
5598 Exit;
5599 end;
5600 if P[1] = '' then
5601 begin
5602 g_Console_Add('unban <IP Address>');
5603 Exit;
5604 end;
5606 if g_Net_UnbanHost(P[1]) then
5607 begin
5608 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5609 g_Net_SaveBanList();
5610 end else
5611 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5612 end else
5613 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5614 end
5615 else if cmd = 'clientlist' then
5616 begin
5617 if g_Game_IsServer and g_Game_IsNet then
5618 begin
5619 b := 0;
5620 if NetClients <> nil then
5621 for a := Low(NetClients) to High(NetClients) do
5622 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5623 begin
5624 plr := g_Player_Get(NetClients[a].Player);
5625 if plr = nil then continue;
5626 Inc(b);
5627 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5628 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5629 end;
5630 if b = 0 then
5631 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5632 end else
5633 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5634 end
5635 else if cmd = 'connect' then
5636 begin
5637 if (NetMode = NET_NONE) then
5638 begin
5639 if Length(P) < 2 then
5640 begin
5641 g_Console_Add('connect <IP> [port] [password]');
5642 Exit;
5643 end;
5644 if P[1] = '' then
5645 begin
5646 g_Console_Add('connect <IP> [port] [password]');
5647 Exit;
5648 end;
5650 if Length(P) > 2 then
5651 prt := StrToIntDef(P[2], 25666)
5652 else
5653 prt := 25666;
5655 if Length(P) > 3 then
5656 pw := P[3]
5657 else
5658 pw := '';
5660 g_Game_StartClient(P[1], prt, pw);
5661 end;
5662 end
5663 else if cmd = 'disconnect' then
5664 begin
5665 if (NetMode = NET_CLIENT) then
5666 g_Net_Disconnect();
5667 end
5668 else if cmd = 'reconnect' then
5669 begin
5670 if (NetMode = NET_SERVER) then
5671 Exit;
5673 if (NetMode = NET_CLIENT) then
5674 begin
5675 g_Net_Disconnect();
5676 gExit := EXIT_SIMPLE;
5677 EndGame;
5678 end;
5680 //TODO: Use last successful password to reconnect, instead of ''
5681 g_Game_StartClient(NetClientIP, NetClientPort, '');
5682 end
5683 else if (cmd = 'addbot') or
5684 (cmd = 'bot_add') then
5685 begin
5686 if Length(P) > 1 then
5687 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5688 else
5689 g_Bot_Add(TEAM_NONE, 2);
5690 end
5691 else if cmd = 'bot_addlist' then
5692 begin
5693 if Length(P) > 1 then
5694 if Length(P) = 2 then
5695 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5696 else
5697 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5698 end
5699 else if cmd = 'bot_removeall' then
5700 g_Bot_RemoveAll()
5701 else if cmd = 'chat' then
5702 begin
5703 if g_Game_IsNet then
5704 begin
5705 if Length(P) > 1 then
5706 begin
5707 for a := 1 to High(P) do
5708 chstr := chstr + P[a] + ' ';
5710 if Length(chstr) > 200 then SetLength(chstr, 200);
5712 if Length(chstr) < 1 then
5713 begin
5714 g_Console_Add('chat <text>');
5715 Exit;
5716 end;
5718 chstr := b_Text_Format(chstr);
5719 if g_Game_IsClient then
5720 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5721 else
5722 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5723 end
5724 else
5725 g_Console_Add('chat <text>');
5726 end else
5727 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5728 end
5729 else if cmd = 'teamchat' then
5730 begin
5731 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5732 begin
5733 if Length(P) > 1 then
5734 begin
5735 for a := 1 to High(P) do
5736 chstr := chstr + P[a] + ' ';
5738 if Length(chstr) > 200 then SetLength(chstr, 200);
5740 if Length(chstr) < 1 then
5741 begin
5742 g_Console_Add('teamchat <text>');
5743 Exit;
5744 end;
5746 chstr := b_Text_Format(chstr);
5747 if g_Game_IsClient then
5748 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5749 else
5750 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5751 gPlayer1Settings.Team);
5752 end
5753 else
5754 g_Console_Add('teamchat <text>');
5755 end else
5756 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5757 end
5758 else if cmd = 'game' then
5759 begin
5760 if gGameSettings.GameType <> GT_NONE then
5761 begin
5762 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5763 Exit;
5764 end;
5765 if Length(P) = 1 then
5766 begin
5767 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5768 Exit;
5769 end;
5770 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5771 P[1] := addWadExtension(P[1]);
5772 if FileExists(MapsDir + P[1]) then
5773 begin
5774 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5775 if Length(P) < 3 then
5776 begin
5777 SetLength(P, 3);
5778 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5779 end;
5781 s := P[1] + ':\' + UpperCase(P[2]);
5783 if g_Map_Exist(MapsDir + s) then
5784 begin
5785 // Çàïóñêàåì ñâîþ èãðó
5786 g_Game_Free();
5787 with gGameSettings do
5788 begin
5789 GameMode := g_Game_TextToMode(gcGameMode);
5790 if gSwitchGameMode <> GM_NONE then
5791 GameMode := gSwitchGameMode;
5792 if GameMode = GM_NONE then GameMode := GM_DM;
5793 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5794 b := 1;
5795 if Length(P) >= 4 then
5796 b := StrToIntDef(P[3], 1);
5797 g_Game_StartCustom(s, GameMode, TimeLimit,
5798 GoalLimit, MaxLives, Options, b);
5799 end;
5800 end
5801 else
5802 if P[2] = '' then
5803 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5804 else
5805 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5806 end else
5807 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5808 end
5809 else if cmd = 'host' then
5810 begin
5811 if gGameSettings.GameType <> GT_NONE then
5812 begin
5813 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5814 Exit;
5815 end;
5816 if Length(P) < 4 then
5817 begin
5818 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5819 Exit;
5820 end;
5821 if not StrToIp(P[1], listen) then
5822 Exit;
5823 prt := StrToIntDef(P[2], 25666);
5825 P[3] := addWadExtension(P[3]);
5826 if FileExists(MapsDir + P[3]) then
5827 begin
5828 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5829 if Length(P) < 5 then
5830 begin
5831 SetLength(P, 5);
5832 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5833 end;
5835 s := P[3] + ':\' + UpperCase(P[4]);
5837 if g_Map_Exist(MapsDir + s) then
5838 begin
5839 // Çàïóñêàåì ñâîþ èãðó
5840 g_Game_Free();
5841 with gGameSettings do
5842 begin
5843 GameMode := g_Game_TextToMode(gcGameMode);
5844 if gSwitchGameMode <> GM_NONE then
5845 GameMode := gSwitchGameMode;
5846 if GameMode = GM_NONE then GameMode := GM_DM;
5847 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5848 b := 0;
5849 if Length(P) >= 6 then
5850 b := StrToIntDef(P[5], 0);
5851 g_Game_StartServer(s, GameMode, TimeLimit,
5852 GoalLimit, MaxLives, Options, b, listen, prt);
5853 end;
5854 end
5855 else
5856 if P[4] = '' then
5857 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5858 else
5859 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5860 end else
5861 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5862 end
5863 else if cmd = 'map' then
5864 begin
5865 if Length(P) = 1 then
5866 begin
5867 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5868 begin
5869 g_Console_Add(cmd + ' <MAP>');
5870 g_Console_Add(cmd + ' <WAD> [MAP]');
5871 end else
5872 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5873 end else
5874 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5875 begin
5876 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5877 if Length(P) < 3 then
5878 begin
5879 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5880 s := UpperCase(P[1]);
5881 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5882 begin // Êàðòà íàøëàñü
5883 gExitByTrigger := False;
5884 if gGameOn then
5885 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5886 gNextMap := s;
5887 gExit := EXIT_ENDLEVELCUSTOM;
5888 end
5889 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5890 g_Game_ChangeMap(s);
5891 end else
5892 begin
5893 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5894 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5895 P[1] := addWadExtension(P[1]);
5896 if FileExists(MapsDir + P[1]) then
5897 begin
5898 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5899 SetLength(P, 3);
5900 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5902 s := P[1] + ':\' + P[2];
5904 if g_Map_Exist(MapsDir + s) then
5905 begin
5906 gExitByTrigger := False;
5907 if gGameOn then
5908 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5909 gNextMap := s;
5910 gExit := EXIT_ENDLEVELCUSTOM;
5911 end
5912 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5913 g_Game_ChangeMap(s);
5914 end else
5915 if P[2] = '' then
5916 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5917 else
5918 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5919 end else
5920 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5921 end;
5922 end else
5923 begin
5924 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5925 P[1] := addWadExtension(P[1]);
5926 if FileExists(MapsDir + P[1]) then
5927 begin
5928 // Íàøëè WAD ôàéë
5929 P[2] := UpperCase(P[2]);
5930 s := P[1] + ':\' + P[2];
5932 if g_Map_Exist(MapsDir + s) then
5933 begin // Íàøëè êàðòó
5934 gExitByTrigger := False;
5935 if gGameOn then
5936 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5937 gNextMap := s;
5938 gExit := EXIT_ENDLEVELCUSTOM;
5939 end
5940 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5941 g_Game_ChangeMap(s);
5942 end else
5943 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5944 end else
5945 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5946 end;
5947 end else
5948 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5949 end
5950 else if cmd = 'nextmap' then
5951 begin
5952 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5953 g_Console_Add(_lc[I_MSG_NOT_GAME])
5954 else begin
5955 nm := True;
5956 if Length(P) = 1 then
5957 begin
5958 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5959 begin
5960 g_Console_Add(cmd + ' <MAP>');
5961 g_Console_Add(cmd + ' <WAD> [MAP]');
5962 end else begin
5963 nm := False;
5964 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5965 end;
5966 end else
5967 begin
5968 nm := False;
5969 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5970 begin
5971 if Length(P) < 3 then
5972 begin
5973 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5974 s := UpperCase(P[1]);
5975 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5976 begin // Êàðòà íàøëàñü
5977 gExitByTrigger := False;
5978 gNextMap := s;
5979 nm := True;
5980 end else
5981 begin
5982 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5983 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5984 P[1] := addWadExtension(P[1]);
5985 if FileExists(MapsDir + P[1]) then
5986 begin
5987 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5988 SetLength(P, 3);
5989 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5991 s := P[1] + ':\' + P[2];
5993 if g_Map_Exist(MapsDir + s) then
5994 begin // Óñòàíàâëèâàåì êàðòó
5995 gExitByTrigger := False;
5996 gNextMap := s;
5997 nm := True;
5998 end else
5999 if P[2] = '' then
6000 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6001 else
6002 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6003 end else
6004 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6005 end;
6006 end else
6007 begin
6008 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6009 P[1] := addWadExtension(P[1]);
6010 if FileExists(MapsDir + P[1]) then
6011 begin
6012 // Íàøëè WAD ôàéë
6013 P[2] := UpperCase(P[2]);
6014 s := P[1] + ':\' + P[2];
6016 if g_Map_Exist(MapsDir + s) then
6017 begin // Íàøëè êàðòó
6018 gExitByTrigger := False;
6019 gNextMap := s;
6020 nm := True;
6021 end else
6022 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6023 end else
6024 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6025 end;
6026 end else
6027 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6028 end;
6029 if nm then
6030 if gNextMap = '' then
6031 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6032 else
6033 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6034 end;
6035 end
6036 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6037 begin
6038 if not gGameOn then
6039 g_Console_Add(_lc[I_MSG_NOT_GAME])
6040 else
6041 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6042 begin
6043 gExitByTrigger := False;
6044 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6045 if (gNextMap = '') and (gTriggers <> nil) then
6046 for a := 0 to High(gTriggers) do
6047 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6048 begin
6049 gExitByTrigger := True;
6050 gNextMap := gTriggers[a].Data.MapName;
6051 Break;
6052 end;
6053 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6054 if gNextMap = '' then
6055 gNextMap := g_Game_GetNextMap();
6056 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6057 if Pos(':\', gNextMap) = 0 then
6058 s := gGameSettings.WAD + ':\' + gNextMap
6059 else
6060 s := gNextMap;
6061 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6062 if g_Map_Exist(MapsDir + s) then
6063 gExit := EXIT_ENDLEVELCUSTOM
6064 else
6065 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6066 end else
6067 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6068 end
6069 else if (cmd = 'event') then
6070 begin
6071 if (Length(P) <= 1) then
6072 begin
6073 for a := 0 to High(gEvents) do
6074 if gEvents[a].Command = '' then
6075 g_Console_Add(gEvents[a].Name + ' <none>')
6076 else
6077 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6078 Exit;
6079 end;
6080 if (Length(P) = 2) then
6081 begin
6082 for a := 0 to High(gEvents) do
6083 if gEvents[a].Name = P[1] then
6084 if gEvents[a].Command = '' then
6085 g_Console_Add(gEvents[a].Name + ' <none>')
6086 else
6087 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6088 Exit;
6089 end;
6090 for a := 0 to High(gEvents) do
6091 if gEvents[a].Name = P[1] then
6092 begin
6093 gEvents[a].Command := '';
6094 for b := 2 to High(P) do
6095 if Pos(' ', P[b]) = 0 then
6096 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6097 else
6098 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6099 gEvents[a].Command := Trim(gEvents[a].Command);
6100 Exit;
6101 end;
6102 end
6103 // Êîìàíäû Ñâîåé èãðû:
6104 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6105 begin
6106 if cmd = 'bot_addred' then
6107 begin
6108 if Length(P) > 1 then
6109 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6110 else
6111 g_Bot_Add(TEAM_RED, 2);
6112 end
6113 else if cmd = 'bot_addblue' then
6114 begin
6115 if Length(P) > 1 then
6116 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6117 else
6118 g_Bot_Add(TEAM_BLUE, 2);
6119 end
6120 else if cmd = 'suicide' then
6121 begin
6122 if gGameOn then
6123 begin
6124 if g_Game_IsClient then
6125 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6126 else
6127 begin
6128 if gPlayer1 <> nil then
6129 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6130 if gPlayer2 <> nil then
6131 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6132 end;
6133 end;
6134 end
6135 else if cmd = 'spectate' then
6136 begin
6137 if not gGameOn then
6138 Exit;
6139 g_Game_Spectate();
6140 end
6141 else if cmd = 'say' then
6142 begin
6143 if g_Game_IsServer and g_Game_IsNet then
6144 begin
6145 if Length(P) > 1 then
6146 begin
6147 chstr := '';
6148 for a := 1 to High(P) do
6149 chstr := chstr + P[a] + ' ';
6151 if Length(chstr) > 200 then SetLength(chstr, 200);
6153 if Length(chstr) < 1 then
6154 begin
6155 g_Console_Add('say <text>');
6156 Exit;
6157 end;
6159 chstr := b_Text_Format(chstr);
6160 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6161 end
6162 else g_Console_Add('say <text>');
6163 end else
6164 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6165 end
6166 else if cmd = 'tell' then
6167 begin
6168 if g_Game_IsServer and g_Game_IsNet then
6169 begin
6170 if (Length(P) > 2) and (P[1] <> '') then
6171 begin
6172 chstr := '';
6173 for a := 2 to High(P) do
6174 chstr := chstr + P[a] + ' ';
6176 if Length(chstr) > 200 then SetLength(chstr, 200);
6178 if Length(chstr) < 1 then
6179 begin
6180 g_Console_Add('tell <playername> <text>');
6181 Exit;
6182 end;
6184 pl := g_Net_Client_ByName(P[1]);
6185 if pl <> nil then
6186 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6187 else
6188 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6189 end
6190 else g_Console_Add('tell <playername> <text>');
6191 end else
6192 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6193 end
6194 else if (cmd = 'overtime') and not g_Game_IsClient then
6195 begin
6196 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6197 Exit;
6198 // Äîïîëíèòåëüíîå âðåìÿ:
6199 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6201 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6202 [gGameSettings.TimeLimit div 3600,
6203 (gGameSettings.TimeLimit div 60) mod 60,
6204 gGameSettings.TimeLimit mod 60]));
6205 if g_Game_IsNet then MH_SEND_GameSettings;
6206 end
6207 else if (cmd = 'rcon_password') and g_Game_IsClient then
6208 begin
6209 if (Length(P) <= 1) then
6210 g_Console_Add('rcon_password <password>')
6211 else
6212 MC_SEND_RCONPassword(P[1]);
6213 end
6214 else if cmd = 'rcon' then
6215 begin
6216 if g_Game_IsClient then
6217 begin
6218 if Length(P) > 1 then
6219 begin
6220 chstr := '';
6221 for a := 1 to High(P) do
6222 chstr := chstr + P[a] + ' ';
6224 if Length(chstr) > 200 then SetLength(chstr, 200);
6226 if Length(chstr) < 1 then
6227 begin
6228 g_Console_Add('rcon <command>');
6229 Exit;
6230 end;
6232 MC_SEND_RCONCommand(chstr);
6233 end
6234 else g_Console_Add('rcon <command>');
6235 end;
6236 end
6237 else if cmd = 'ready' then
6238 begin
6239 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6240 gLMSRespawnTime := gTime + 100;
6241 end
6242 else if (cmd = 'callvote') and g_Game_IsNet then
6243 begin
6244 if Length(P) > 1 then
6245 begin
6246 chstr := '';
6247 for a := 1 to High(P) do begin
6248 if a > 1 then chstr := chstr + ' ';
6249 chstr := chstr + P[a];
6250 end;
6252 if Length(chstr) > 200 then SetLength(chstr, 200);
6254 if Length(chstr) < 1 then
6255 begin
6256 g_Console_Add('callvote <command>');
6257 Exit;
6258 end;
6260 if g_Game_IsClient then
6261 MC_SEND_Vote(True, chstr)
6262 else
6263 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6264 g_Console_Process('vote', True);
6265 end
6266 else
6267 g_Console_Add('callvote <command>');
6268 end
6269 else if (cmd = 'vote') and g_Game_IsNet then
6270 begin
6271 if g_Game_IsClient then
6272 MC_SEND_Vote(False)
6273 else if gVoteInProgress then
6274 begin
6275 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6276 a := Floor((NetClientCount+1)/2.0) + 1
6277 else
6278 a := Floor(NetClientCount/2.0) + 1;
6279 if gVoted then
6280 begin
6281 Dec(gVoteCount);
6282 gVoted := False;
6283 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6284 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6285 end
6286 else
6287 begin
6288 Inc(gVoteCount);
6289 gVoted := True;
6290 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6291 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6292 g_Game_CheckVote;
6293 end;
6294 end;
6295 end
6296 end;
6297 end;
6299 procedure g_TakeScreenShot();
6300 var
6301 a: Word;
6302 FileName: string;
6303 ssdir, t: string;
6304 st: TStream;
6305 ok: Boolean;
6306 begin
6307 if e_NoGraphics then Exit;
6308 ssdir := GameDir+'/screenshots';
6309 if not findFileCI(ssdir, true) then
6310 begin
6311 // try to create dir
6312 try
6313 CreateDir(ssdir);
6314 except
6315 end;
6316 if not findFileCI(ssdir, true) then exit; // alas
6317 end;
6318 try
6319 for a := 1 to High(Word) do
6320 begin
6321 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6322 t := FileName;
6323 if findFileCI(t, true) then continue;
6324 if not findFileCI(FileName) then
6325 begin
6326 ok := false;
6327 st := createDiskFile(FileName);
6328 try
6329 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6330 ok := true;
6331 finally
6332 st.Free();
6333 end;
6334 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6335 break;
6336 end;
6337 end;
6338 except
6339 end;
6340 end;
6342 procedure g_Game_InGameMenu(Show: Boolean);
6343 begin
6344 if (g_ActiveWindow = nil) and Show then
6345 begin
6346 if gGameSettings.GameType = GT_SINGLE then
6347 g_GUI_ShowWindow('GameSingleMenu')
6348 else
6349 begin
6350 if g_Game_IsClient then
6351 g_GUI_ShowWindow('GameClientMenu')
6352 else
6353 if g_Game_IsNet then
6354 g_GUI_ShowWindow('GameServerMenu')
6355 else
6356 g_GUI_ShowWindow('GameCustomMenu');
6357 end;
6358 g_Sound_PlayEx('MENU_OPEN');
6360 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6361 if (not g_Game_IsNet) then
6362 g_Game_Pause(True);
6363 end
6364 else
6365 if (g_ActiveWindow <> nil) and (not Show) then
6366 begin
6367 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6368 if (not g_Game_IsNet) then
6369 g_Game_Pause(False);
6370 end;
6371 end;
6373 procedure g_Game_Pause(Enable: Boolean);
6374 begin
6375 if not gGameOn then
6376 Exit;
6378 if gPause = Enable then
6379 Exit;
6381 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6382 Exit;
6384 gPause := Enable;
6385 g_Game_PauseAllSounds(Enable);
6386 end;
6388 procedure g_Game_PauseAllSounds(Enable: Boolean);
6389 var
6390 i: Integer;
6391 begin
6392 // Òðèããåðû:
6393 if gTriggers <> nil then
6394 for i := 0 to High(gTriggers) do
6395 with gTriggers[i] do
6396 if (TriggerType = TRIGGER_SOUND) and
6397 (Sound <> nil) and
6398 Sound.IsPlaying() then
6399 begin
6400 Sound.Pause(Enable);
6401 end;
6403 // Çâóêè èãðîêîâ:
6404 if gPlayers <> nil then
6405 for i := 0 to High(gPlayers) do
6406 if gPlayers[i] <> nil then
6407 gPlayers[i].PauseSounds(Enable);
6409 // Ìóçûêà:
6410 if gMusic <> nil then
6411 gMusic.Pause(Enable);
6412 end;
6414 procedure g_Game_StopAllSounds(all: Boolean);
6415 var
6416 i: Integer;
6417 begin
6418 if gTriggers <> nil then
6419 for i := 0 to High(gTriggers) do
6420 with gTriggers[i] do
6421 if (TriggerType = TRIGGER_SOUND) and
6422 (Sound <> nil) then
6423 Sound.Stop();
6425 if gMusic <> nil then
6426 gMusic.Stop();
6428 if all then
6429 e_StopChannels();
6430 end;
6432 procedure g_Game_UpdateTriggerSounds();
6433 var
6434 i: Integer;
6435 begin
6436 if gTriggers <> nil then
6437 for i := 0 to High(gTriggers) do
6438 with gTriggers[i] do
6439 if (TriggerType = TRIGGER_SOUND) and
6440 (Sound <> nil) and
6441 (Data.Local) and
6442 Sound.IsPlaying() then
6443 begin
6444 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6445 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6446 begin
6447 Sound.SetPan(0.5 - Data.Pan/255.0);
6448 Sound.SetVolume(Data.Volume/255.0);
6449 end
6450 else
6451 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6452 end;
6453 end;
6455 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6456 begin
6457 Result := False;
6458 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6459 begin
6460 Result := True;
6461 Exit;
6462 end;
6463 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6464 begin
6465 Result := True;
6466 Exit;
6467 end;
6468 if gSpectMode <> SPECT_PLAYERS then
6469 Exit;
6470 if gSpectPID1 = UID then
6471 begin
6472 Result := True;
6473 Exit;
6474 end;
6475 if gSpectViewTwo and (gSpectPID2 = UID) then
6476 begin
6477 Result := True;
6478 Exit;
6479 end;
6480 end;
6482 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6483 var
6484 Pl: TPlayer;
6485 begin
6486 Result := False;
6487 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6488 begin
6489 Result := True;
6490 Exit;
6491 end;
6492 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6493 begin
6494 Result := True;
6495 Exit;
6496 end;
6497 if gSpectMode <> SPECT_PLAYERS then
6498 Exit;
6499 Pl := g_Player_Get(gSpectPID1);
6500 if (Pl <> nil) and (Pl.Team = Team) then
6501 begin
6502 Result := True;
6503 Exit;
6504 end;
6505 if gSpectViewTwo then
6506 begin
6507 Pl := g_Player_Get(gSpectPID2);
6508 if (Pl <> nil) and (Pl.Team = Team) then
6509 begin
6510 Result := True;
6511 Exit;
6512 end;
6513 end;
6514 end;
6516 procedure g_Game_Message(Msg: string; Time: Word);
6517 begin
6518 MessageText := b_Text_Format(Msg);
6519 MessageTime := Time;
6520 end;
6522 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6523 var
6524 a: Integer;
6525 begin
6526 case gAnnouncer of
6527 ANNOUNCE_NONE:
6528 Exit;
6529 ANNOUNCE_ME,
6530 ANNOUNCE_MEPLUS:
6531 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6532 Exit;
6533 end;
6534 for a := 0 to 3 do
6535 if goodsnd[a].IsPlaying() then
6536 Exit;
6538 goodsnd[Random(4)].Play();
6539 end;
6541 procedure g_Game_Announce_KillCombo(Param: Integer);
6542 var
6543 UID: Word;
6544 c, n: Byte;
6545 Pl: TPlayer;
6546 Name: String;
6547 begin
6548 UID := Param and $FFFF;
6549 c := Param shr 16;
6550 if c < 2 then
6551 Exit;
6553 Pl := g_Player_Get(UID);
6554 if Pl = nil then
6555 Name := '?'
6556 else
6557 Name := Pl.Name;
6559 case c of
6560 2: begin
6561 n := 0;
6562 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6563 end;
6564 3: begin
6565 n := 1;
6566 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6567 end;
6568 4: begin
6569 n := 2;
6570 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6571 end;
6572 else begin
6573 n := 3;
6574 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6575 end;
6576 end;
6578 case gAnnouncer of
6579 ANNOUNCE_NONE:
6580 Exit;
6581 ANNOUNCE_ME:
6582 if not g_Game_IsWatchedPlayer(UID) then
6583 Exit;
6584 ANNOUNCE_MEPLUS:
6585 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6586 Exit;
6587 end;
6589 if killsnd[n].IsPlaying() then
6590 killsnd[n].Stop();
6591 killsnd[n].Play();
6592 end;
6594 procedure g_Game_StartVote(Command, Initiator: string);
6595 var
6596 Need: Integer;
6597 begin
6598 if not gVotesEnabled then Exit;
6599 if gGameSettings.GameType <> GT_SERVER then Exit;
6600 if gVoteInProgress or gVotePassed then
6601 begin
6602 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6603 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6604 Exit;
6605 end;
6606 gVoteInProgress := True;
6607 gVotePassed := False;
6608 gVoteTimer := gTime + gVoteTimeout * 1000;
6609 gVoteCount := 0;
6610 gVoted := False;
6611 gVoteCommand := Command;
6613 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6614 Need := Floor((NetClientCount+1)/2.0)+1
6615 else
6616 Need := Floor(NetClientCount/2.0)+1;
6617 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6618 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6619 end;
6621 procedure g_Game_CheckVote;
6622 var
6623 I, Need: Integer;
6624 begin
6625 if gGameSettings.GameType <> GT_SERVER then Exit;
6626 if not gVoteInProgress then Exit;
6628 if (gTime >= gVoteTimer) then
6629 begin
6630 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6631 Need := Floor((NetClientCount+1)/2.0) + 1
6632 else
6633 Need := Floor(NetClientCount/2.0) + 1;
6634 if gVoteCount >= Need then
6635 begin
6636 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6637 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6638 gVotePassed := True;
6639 gVoteCmdTimer := gTime + 5000;
6640 end
6641 else
6642 begin
6643 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6644 MH_SEND_VoteEvent(NET_VE_FAILED);
6645 end;
6646 if NetClients <> nil then
6647 for I := Low(NetClients) to High(NetClients) do
6648 if NetClients[i].Used then
6649 NetClients[i].Voted := False;
6650 gVoteInProgress := False;
6651 gVoted := False;
6652 gVoteCount := 0;
6653 end
6654 else
6655 begin
6656 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6657 Need := Floor((NetClientCount+1)/2.0) + 1
6658 else
6659 Need := Floor(NetClientCount/2.0) + 1;
6660 if gVoteCount >= Need then
6661 begin
6662 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6663 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6664 gVoteInProgress := False;
6665 gVotePassed := True;
6666 gVoteCmdTimer := gTime + 5000;
6667 gVoted := False;
6668 gVoteCount := 0;
6669 if NetClients <> nil then
6670 for I := Low(NetClients) to High(NetClients) do
6671 if NetClients[i].Used then
6672 NetClients[i].Voted := False;
6673 end;
6674 end;
6675 end;
6677 procedure g_Game_LoadMapList(FileName: string);
6678 var
6679 ListFile: TextFile;
6680 s: string;
6681 begin
6682 MapList := nil;
6683 MapIndex := -1;
6685 if not FileExists(FileName) then Exit;
6687 AssignFile(ListFile, FileName);
6688 Reset(ListFile);
6689 while not EOF(ListFile) do
6690 begin
6691 ReadLn(ListFile, s);
6693 s := Trim(s);
6694 if s = '' then Continue;
6696 SetLength(MapList, Length(MapList)+1);
6697 MapList[High(MapList)] := s;
6698 end;
6699 CloseFile(ListFile);
6700 end;
6702 procedure g_Game_SetDebugMode();
6703 begin
6704 gDebugMode := True;
6705 // ×èòû (äàæå â ñâîåé èãðå):
6706 gCheats := True;
6707 end;
6709 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6710 var
6711 i: Word;
6712 begin
6713 if Length(LoadingStat.Msgs) = 0 then
6714 Exit;
6716 with LoadingStat do
6717 begin
6718 if not reWrite then
6719 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6720 if NextMsg = Length(Msgs) then
6721 begin // scroll
6722 for i := 0 to High(Msgs)-1 do
6723 Msgs[i] := Msgs[i+1];
6724 end
6725 else
6726 Inc(NextMsg);
6727 end else
6728 if NextMsg = 0 then
6729 Inc(NextMsg);
6731 Msgs[NextMsg-1] := Text;
6732 CurValue := 0;
6733 MaxValue := Max;
6734 ShowCount := 0;
6735 end;
6737 g_ActiveWindow := nil;
6739 ProcessLoading;
6740 end;
6742 procedure g_Game_StepLoading();
6743 begin
6744 with LoadingStat do
6745 begin
6746 Inc(CurValue);
6747 Inc(ShowCount);
6748 if (ShowCount > LOADING_SHOW_STEP) then
6749 begin
6750 ShowCount := 0;
6751 ProcessLoading;
6752 end;
6753 end;
6754 end;
6756 procedure g_Game_ClearLoading();
6757 var
6758 len: Word;
6759 begin
6760 with LoadingStat do
6761 begin
6762 CurValue := 0;
6763 MaxValue := 0;
6764 ShowCount := 0;
6765 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6766 if len < 1 then len := 1;
6767 SetLength(Msgs, len);
6768 for len := Low(Msgs) to High(Msgs) do
6769 Msgs[len] := '';
6770 NextMsg := 0;
6771 end;
6772 end;
6774 procedure Parse_Params(var pars: TParamStrValues);
6775 var
6776 i: Integer;
6777 s: String;
6778 begin
6779 SetLength(pars, 0);
6780 i := 1;
6781 while i <= ParamCount do
6782 begin
6783 s := ParamStr(i);
6784 if (s[1] = '-') and (Length(s) > 1) then
6785 begin
6786 if (s[2] = '-') and (Length(s) > 2) then
6787 begin // Îäèíî÷íûé ïàðàìåòð
6788 SetLength(pars, Length(pars) + 1);
6789 with pars[High(pars)] do
6790 begin
6791 Name := LowerCase(s);
6792 Value := '+';
6793 end;
6794 end
6795 else
6796 if (i < ParamCount) then
6797 begin // Ïàðàìåòð ñî çíà÷åíèåì
6798 Inc(i);
6799 SetLength(pars, Length(pars) + 1);
6800 with pars[High(pars)] do
6801 begin
6802 Name := LowerCase(s);
6803 Value := LowerCase(ParamStr(i));
6804 end;
6805 end;
6806 end;
6808 Inc(i);
6809 end;
6810 end;
6812 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6813 var
6814 i: Integer;
6815 begin
6816 Result := '';
6817 for i := 0 to High(pars) do
6818 if pars[i].Name = aName then
6819 begin
6820 Result := pars[i].Value;
6821 Break;
6822 end;
6823 end;
6825 procedure g_Game_Process_Params();
6826 var
6827 pars: TParamStrValues;
6828 map: String;
6829 GMode, n: Byte;
6830 LimT, LimS: Integer;
6831 Opt: LongWord;
6832 Lives: Integer;
6833 s: String;
6834 Port: Integer;
6835 ip: String;
6836 F: TextFile;
6837 begin
6838 Parse_Params(pars);
6840 // Debug mode:
6841 s := Find_Param_Value(pars, '--debug');
6842 if (s <> '') then
6843 begin
6844 g_Game_SetDebugMode();
6845 s := Find_Param_Value(pars, '--netdump');
6846 if (s <> '') then
6847 NetDump := True;
6848 end;
6850 // Connect when game loads
6851 ip := Find_Param_Value(pars, '-connect');
6853 if ip <> '' then
6854 begin
6855 s := Find_Param_Value(pars, '-port');
6856 if (s = '') or not TryStrToInt(s, Port) then
6857 Port := 25666;
6859 s := Find_Param_Value(pars, '-pw');
6861 g_Game_StartClient(ip, Port, s);
6862 Exit;
6863 end;
6865 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
6866 if (s <> '') then
6867 begin
6868 gDefaultMegawadStart := s;
6869 end;
6871 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
6872 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
6873 begin
6874 gDefaultMegawadStart := DF_Default_Megawad_Start;
6875 end;
6877 // Start map when game loads:
6878 map := LowerCase(Find_Param_Value(pars, '-map'));
6879 if isWadPath(map) then
6880 begin
6881 // Game mode:
6882 s := Find_Param_Value(pars, '-gm');
6883 GMode := g_Game_TextToMode(s);
6884 if GMode = GM_NONE then GMode := GM_DM;
6885 if GMode = GM_SINGLE then GMode := GM_COOP;
6887 // Time limit:
6888 s := Find_Param_Value(pars, '-limt');
6889 if (s = '') or (not TryStrToInt(s, LimT)) then
6890 LimT := 0;
6891 if LimT < 0 then
6892 LimT := 0;
6894 // Goal limit:
6895 s := Find_Param_Value(pars, '-lims');
6896 if (s = '') or (not TryStrToInt(s, LimS)) then
6897 LimS := 0;
6898 if LimS < 0 then
6899 LimS := 0;
6901 // Lives limit:
6902 s := Find_Param_Value(pars, '-lives');
6903 if (s = '') or (not TryStrToInt(s, Lives)) then
6904 Lives := 0;
6905 if Lives < 0 then
6906 Lives := 0;
6908 // Options:
6909 s := Find_Param_Value(pars, '-opt');
6910 if (s = '') then
6911 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6912 else
6913 Opt := StrToIntDef(s, 0);
6914 if Opt = 0 then
6915 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6917 // Close after map:
6918 s := Find_Param_Value(pars, '--close');
6919 if (s <> '') then
6920 gMapOnce := True;
6922 // Delete test map after play:
6923 s := Find_Param_Value(pars, '--testdelete');
6924 if (s <> '') then
6925 begin
6926 gMapToDelete := MapsDir + map;
6927 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6928 Halt(1);
6929 end;
6931 // Delete temporary WAD after play:
6932 s := Find_Param_Value(pars, '--tempdelete');
6933 if (s <> '') then
6934 begin
6935 gMapToDelete := MapsDir + map;
6936 gTempDelete := True;
6937 end;
6939 // Number of players:
6940 s := Find_Param_Value(pars, '-pl');
6941 if (s = '') then
6942 n := 1
6943 else
6944 n := StrToIntDef(s, 1);
6946 // Start:
6947 s := Find_Param_Value(pars, '-port');
6948 if (s = '') or not TryStrToInt(s, Port) then
6949 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6950 else
6951 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6952 end;
6954 // Execute script when game loads:
6955 s := Find_Param_Value(pars, '-exec');
6956 if s <> '' then
6957 begin
6958 if Pos(':\', s) = 0 then
6959 s := GameDir + '/' + s;
6961 {$I-}
6962 AssignFile(F, s);
6963 Reset(F);
6964 if IOResult <> 0 then
6965 begin
6966 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6967 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6968 CloseFile(F);
6969 Exit;
6970 end;
6971 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6972 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6974 while not EOF(F) do
6975 begin
6976 ReadLn(F, s);
6977 if IOResult <> 0 then
6978 begin
6979 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6980 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6981 CloseFile(F);
6982 Exit;
6983 end;
6984 if Pos('#', s) <> 1 then // script comment
6985 g_Console_Process(s, True);
6986 end;
6988 CloseFile(F);
6989 {$I+}
6990 end;
6992 SetLength(pars, 0);
6993 end;
6995 begin
6996 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
6997 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
6998 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
6999 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7001 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7002 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7003 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7004 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7006 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7007 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7009 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7010 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7012 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7013 end.