DEADSOFTWARE

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