DEADSOFTWARE

turned on "SCOPEDENUMS" fpc option
[d2df-sdl.git] / src / game / g_game.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit g_game;
19 interface
21 uses
22 SysUtils, Classes,
23 MAPDEF,
24 g_basic, g_player, e_graphics, g_res_downloader,
25 g_sound, g_gui, 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) then g_Holmes_plrViewSize(sWidth, sHeight);
3106 fixViewportForScale();
3107 p.viewPortX := sX;
3108 p.viewPortY := sY;
3109 p.viewPortW := sWidth;
3110 p.viewPortH := sHeight;
3112 if (p = gPlayer1) then g_Holmes_plrViewPos(sX, sY);
3114 renderMapInternal(-c, -d, true);
3116 if p.FSpectator then
3117 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3118 p.GameY + PLAYER_RECT_CY - 4,
3119 'X', gStdFont, 255, 255, 255, 1, True);
3121 for a := 0 to High(gCollideMap) do
3122 for b := 0 to High(gCollideMap[a]) do
3123 begin
3124 d := 0;
3125 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3126 d := d + 1;
3127 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3128 d := d + 2;
3130 case d of
3131 1: e_DrawPoint(1, b, a, 200, 200, 200);
3132 2: e_DrawPoint(1, b, a, 64, 64, 255);
3133 3: e_DrawPoint(1, b, a, 255, 0, 255);
3134 end;
3135 end;
3138 glPopMatrix();
3140 p.DrawPain();
3141 p.DrawPickup();
3142 p.DrawRulez();
3143 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3144 if g_Debug_Player then
3145 g_Player_DrawDebug(p);
3146 p.DrawGUI();
3147 end;
3149 procedure drawProfilers ();
3150 var
3151 px: Integer = -1;
3152 py: Integer = -1;
3153 begin
3154 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
3155 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3156 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3157 end;
3159 procedure g_Game_Draw();
3160 var
3161 ID: DWORD;
3162 w, h: Word;
3163 ww, hh: Byte;
3164 Time: Int64;
3165 back: string;
3166 plView1, plView2: TPlayer;
3167 Split: Boolean;
3168 begin
3169 if gExit = EXIT_QUIT then Exit;
3171 Time := GetTimer() {div 1000};
3172 FPSCounter := FPSCounter+1;
3173 if Time - FPSTime >= 1000 then
3174 begin
3175 FPS := FPSCounter;
3176 FPSCounter := 0;
3177 FPSTime := Time;
3178 end;
3180 if gGameOn or (gState = STATE_FOLD) then
3181 begin
3182 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3183 begin
3184 gSpectMode := SPECT_NONE;
3185 if not gRevertPlayers then
3186 begin
3187 plView1 := gPlayer1;
3188 plView2 := gPlayer2;
3189 end
3190 else
3191 begin
3192 plView1 := gPlayer2;
3193 plView2 := gPlayer1;
3194 end;
3195 end
3196 else
3197 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3198 begin
3199 gSpectMode := SPECT_NONE;
3200 if gPlayer2 = nil then
3201 plView1 := gPlayer1
3202 else
3203 plView1 := gPlayer2;
3204 plView2 := nil;
3205 end
3206 else
3207 begin
3208 plView1 := nil;
3209 plView2 := nil;
3210 end;
3212 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3213 gSpectMode := SPECT_STATS;
3215 if gSpectMode = SPECT_PLAYERS then
3216 if gPlayers <> nil then
3217 begin
3218 plView1 := GetActivePlayer_ByID(gSpectPID1);
3219 if plView1 = nil then
3220 begin
3221 gSpectPID1 := GetActivePlayerID_Next();
3222 plView1 := GetActivePlayer_ByID(gSpectPID1);
3223 end;
3224 if gSpectViewTwo then
3225 begin
3226 plView2 := GetActivePlayer_ByID(gSpectPID2);
3227 if plView2 = nil then
3228 begin
3229 gSpectPID2 := GetActivePlayerID_Next();
3230 plView2 := GetActivePlayer_ByID(gSpectPID2);
3231 end;
3232 end;
3233 end;
3235 if gSpectMode = SPECT_MAPVIEW then
3236 begin
3237 // Ðåæèì ïðîñìîòðà êàðòû
3238 Split := False;
3239 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3240 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3241 gHearPoint1.Active := True;
3242 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3243 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3244 gHearPoint2.Active := False;
3245 end
3246 else
3247 begin
3248 Split := (plView1 <> nil) and (plView2 <> nil);
3250 // Òî÷êè ñëóõà èãðîêîâ
3251 if plView1 <> nil then
3252 begin
3253 gHearPoint1.Active := True;
3254 gHearPoint1.Coords.X := plView1.GameX;
3255 gHearPoint1.Coords.Y := plView1.GameY;
3256 end else
3257 gHearPoint1.Active := False;
3258 if plView2 <> nil then
3259 begin
3260 gHearPoint2.Active := True;
3261 gHearPoint2.Coords.X := plView2.GameX;
3262 gHearPoint2.Coords.Y := plView2.GameY;
3263 end else
3264 gHearPoint2.Active := False;
3266 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3267 gPlayerScreenSize.X := gScreenWidth-196;
3268 if Split then
3269 begin
3270 gPlayerScreenSize.Y := gScreenHeight div 2;
3271 if gScreenHeight mod 2 = 0 then
3272 Dec(gPlayerScreenSize.Y);
3273 end
3274 else
3275 gPlayerScreenSize.Y := gScreenHeight;
3277 if Split then
3278 if gScreenHeight mod 2 = 0 then
3279 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3280 else
3281 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3283 DrawPlayer(plView1);
3284 gPlayer1ScreenCoord.X := sX;
3285 gPlayer1ScreenCoord.Y := sY;
3287 if Split then
3288 begin
3289 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3291 DrawPlayer(plView2);
3292 gPlayer2ScreenCoord.X := sX;
3293 gPlayer2ScreenCoord.Y := sY;
3294 end;
3296 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3298 if Split then
3299 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3300 end;
3302 if MessageText <> '' then
3303 begin
3304 w := 0;
3305 h := 0;
3306 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3307 if Split then
3308 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3309 (gScreenHeight div 2)-(h div 2), MessageText)
3310 else
3311 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3312 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3313 end;
3315 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3317 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3318 begin
3319 // Draw spectator GUI
3320 ww := 0;
3321 hh := 0;
3322 e_TextureFontGetSize(gStdFont, ww, hh);
3323 case gSpectMode of
3324 SPECT_STATS:
3325 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3326 SPECT_MAPVIEW:
3327 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3328 SPECT_PLAYERS:
3329 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3330 end;
3331 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3332 if gSpectMode = SPECT_MAPVIEW then
3333 begin
3334 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3335 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3336 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3337 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3338 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3339 end;
3340 if gSpectMode = SPECT_PLAYERS then
3341 begin
3342 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3343 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3344 if gSpectViewTwo then
3345 begin
3346 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3347 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3348 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3349 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3350 end
3351 else
3352 begin
3353 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3354 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3355 end;
3356 end;
3357 end;
3358 end;
3360 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
3361 begin
3362 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3363 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3365 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3366 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3367 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3368 end;
3370 if not gGameOn then
3371 begin
3372 if (gState = STATE_MENU) then
3373 begin
3374 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3375 begin
3376 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3377 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3378 end;
3379 // F3 at menu will show game loading dialog
3380 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3381 if (g_ActiveWindow <> nil) then
3382 begin
3383 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3384 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3385 end
3386 else
3387 begin
3388 // F3 at titlepic will show game loading dialog
3389 if e_KeyPressed(IK_F3) then
3390 begin
3391 g_Menu_Show_LoadMenu(true);
3392 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3393 end;
3394 end;
3395 end;
3397 if gState = STATE_FOLD then
3398 begin
3399 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3400 end;
3402 if gState = STATE_INTERCUSTOM then
3403 begin
3404 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3405 begin
3406 back := 'TEXTURE_endpic';
3407 if not g_Texture_Get(back, ID) then
3408 back := _lc[I_TEXTURE_ENDPIC];
3409 end
3410 else
3411 back := 'INTER';
3413 if g_Texture_Get(back, ID) then
3414 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3415 else
3416 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3418 DrawCustomStat();
3420 if g_ActiveWindow <> nil then
3421 begin
3422 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3423 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3424 end;
3425 end;
3427 if gState = STATE_INTERSINGLE then
3428 begin
3429 if EndingGameCounter > 0 then
3430 begin
3431 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3432 end
3433 else
3434 begin
3435 back := 'INTER';
3437 if g_Texture_Get(back, ID) then
3438 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3439 else
3440 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3442 DrawSingleStat();
3444 if g_ActiveWindow <> nil then
3445 begin
3446 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3447 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3448 end;
3449 end;
3450 end;
3452 if gState = STATE_ENDPIC then
3453 begin
3454 ID := DWORD(-1);
3455 if not g_Texture_Get('TEXTURE_endpic', ID) then
3456 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3458 if ID <> DWORD(-1) then
3459 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3460 else
3461 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3463 if g_ActiveWindow <> nil then
3464 begin
3465 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3466 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3467 end;
3468 end;
3470 if gState = STATE_SLIST then
3471 begin
3472 if g_Texture_Get('MENU_BACKGROUND', ID) then
3473 begin
3474 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3475 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3476 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3477 end;
3478 g_Serverlist_Draw(slCurrent);
3479 end;
3480 end;
3482 if g_ActiveWindow <> nil then
3483 begin
3484 if gGameOn then
3485 begin
3486 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3487 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3488 end;
3489 g_ActiveWindow.Draw();
3490 end;
3492 // draw inspector
3493 if (g_holmes_enabled) then g_Holmes_Draw();
3495 g_Console_Draw();
3497 if g_debug_Sounds and gGameOn then
3498 begin
3499 for w := 0 to High(e_SoundsArray) do
3500 for h := 0 to e_SoundsArray[w].nRefs do
3501 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3502 end;
3504 if gShowFPS then
3505 begin
3506 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3507 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3508 end;
3510 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3511 drawTime(gScreenWidth-72, gScreenHeight-16);
3513 if gGameOn then drawProfilers();
3515 g_Holmes_DrawUI();
3516 end;
3518 procedure g_Game_Quit();
3519 begin
3520 g_Game_StopAllSounds(True);
3521 gMusic.Free();
3522 g_Game_SaveOptions();
3523 g_Game_FreeData();
3524 g_PlayerModel_FreeData();
3525 g_Texture_DeleteAll();
3526 g_Frames_DeleteAll();
3527 g_Menu_Free();
3529 if NetInitDone then g_Net_Free;
3531 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3532 if gMapToDelete <> '' then
3533 g_Game_DeleteTestMap();
3535 gExit := EXIT_QUIT;
3536 PushExitEvent();
3537 end;
3539 procedure g_FatalError(Text: String);
3540 begin
3541 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3542 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning);
3544 gExit := EXIT_SIMPLE;
3545 end;
3547 procedure g_SimpleError(Text: String);
3548 begin
3549 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3550 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
3551 end;
3553 procedure g_Game_SetupScreenSize();
3554 const
3555 RES_FACTOR = 4.0 / 3.0;
3556 var
3557 s: Single;
3558 rf: Single;
3559 bw, bh: Word;
3560 begin
3561 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3562 gPlayerScreenSize.X := gScreenWidth-196;
3563 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3564 gPlayerScreenSize.Y := gScreenHeight div 2
3565 else
3566 gPlayerScreenSize.Y := gScreenHeight;
3568 // Ðàçìåð çàäíåãî ïëàíà:
3569 if BackID <> DWORD(-1) then
3570 begin
3571 s := SKY_STRETCH;
3572 if (gScreenWidth*s > gMapInfo.Width) or
3573 (gScreenHeight*s > gMapInfo.Height) then
3574 begin
3575 gBackSize.X := gScreenWidth;
3576 gBackSize.Y := gScreenHeight;
3577 end
3578 else
3579 begin
3580 e_GetTextureSize(BackID, @bw, @bh);
3581 rf := Single(bw) / Single(bh);
3582 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3583 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3584 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3585 if (s < 1.0) then s := 1.0;
3586 gBackSize.X := Round(bw*s);
3587 gBackSize.Y := Round(bh*s);
3588 end;
3589 end;
3590 end;
3592 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3593 begin
3594 g_Window_SetSize(newWidth, newHeight, nowFull);
3595 end;
3597 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3598 begin
3599 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3600 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3601 Exit;
3602 if gPlayer1 = nil then
3603 begin
3604 if g_Game_IsClient then
3605 begin
3606 if NetPlrUID1 > -1 then
3607 begin
3608 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3609 gPlayer1 := g_Player_Get(NetPlrUID1);
3610 end;
3611 Exit;
3612 end;
3614 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3615 Team := gPlayer1Settings.Team;
3617 // Ñîçäàíèå ïåðâîãî èãðîêà:
3618 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3619 gPlayer1Settings.Color,
3620 Team, False));
3621 if gPlayer1 = nil then
3622 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3623 else
3624 begin
3625 gPlayer1.Name := gPlayer1Settings.Name;
3626 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3627 if g_Game_IsServer and g_Game_IsNet then
3628 MH_SEND_PlayerCreate(gPlayer1.UID);
3629 gPlayer1.Respawn(False, True);
3631 if g_Game_IsNet and NetUseMaster then
3632 g_Net_Slist_Update;
3633 end;
3635 Exit;
3636 end;
3637 if gPlayer2 = nil then
3638 begin
3639 if g_Game_IsClient then
3640 begin
3641 if NetPlrUID2 > -1 then
3642 gPlayer2 := g_Player_Get(NetPlrUID2);
3643 Exit;
3644 end;
3646 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3647 Team := gPlayer2Settings.Team;
3649 // Ñîçäàíèå âòîðîãî èãðîêà:
3650 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3651 gPlayer2Settings.Color,
3652 Team, False));
3653 if gPlayer2 = nil then
3654 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3655 else
3656 begin
3657 gPlayer2.Name := gPlayer2Settings.Name;
3658 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3659 if g_Game_IsServer and g_Game_IsNet then
3660 MH_SEND_PlayerCreate(gPlayer2.UID);
3661 gPlayer2.Respawn(False, True);
3663 if g_Game_IsNet and NetUseMaster then
3664 g_Net_Slist_Update;
3665 end;
3667 Exit;
3668 end;
3669 end;
3671 procedure g_Game_RemovePlayer();
3672 var
3673 Pl: TPlayer;
3674 begin
3675 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3676 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3677 Exit;
3678 Pl := gPlayer2;
3679 if Pl <> nil then
3680 begin
3681 if g_Game_IsServer then
3682 begin
3683 Pl.Lives := 0;
3684 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3685 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3686 g_Player_Remove(Pl.UID);
3688 if g_Game_IsNet and NetUseMaster then
3689 g_Net_Slist_Update;
3690 end else
3691 gPlayer2 := nil;
3692 Exit;
3693 end;
3694 Pl := gPlayer1;
3695 if Pl <> nil then
3696 begin
3697 if g_Game_IsServer then
3698 begin
3699 Pl.Lives := 0;
3700 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3701 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3702 g_Player_Remove(Pl.UID);
3704 if g_Game_IsNet and NetUseMaster then
3705 g_Net_Slist_Update;
3706 end else
3707 begin
3708 gPlayer1 := nil;
3709 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3710 end;
3711 Exit;
3712 end;
3713 end;
3715 procedure g_Game_Spectate();
3716 begin
3717 g_Game_RemovePlayer();
3718 if gPlayer1 <> nil then
3719 g_Game_RemovePlayer();
3720 end;
3722 procedure g_Game_SpectateCenterView();
3723 begin
3724 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3725 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3726 end;
3728 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3729 var
3730 i, nPl: Integer;
3731 begin
3732 g_Game_Free();
3734 e_WriteLog('Starting singleplayer game...', TMsgType.Notify);
3736 g_Game_ClearLoading();
3738 // Íàñòðîéêè èãðû:
3739 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3740 gAimLine := False;
3741 gShowMap := False;
3742 gGameSettings.GameType := GT_SINGLE;
3743 gGameSettings.MaxLives := 0;
3744 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3745 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3746 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3747 gSwitchGameMode := GM_SINGLE;
3749 g_Game_ExecuteEvent('ongamestart');
3751 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3752 g_Game_SetupScreenSize();
3754 // Ñîçäàíèå ïåðâîãî èãðîêà:
3755 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3756 gPlayer1Settings.Color,
3757 gPlayer1Settings.Team, False));
3758 if gPlayer1 = nil then
3759 begin
3760 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3761 Exit;
3762 end;
3764 gPlayer1.Name := gPlayer1Settings.Name;
3765 nPl := 1;
3767 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3768 if TwoPlayers then
3769 begin
3770 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3771 gPlayer2Settings.Color,
3772 gPlayer2Settings.Team, False));
3773 if gPlayer2 = nil then
3774 begin
3775 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3776 Exit;
3777 end;
3779 gPlayer2.Name := gPlayer2Settings.Name;
3780 Inc(nPl);
3781 end;
3783 // Çàãðóçêà è çàïóñê êàðòû:
3784 if not g_Game_StartMap(MAP, True) then
3785 begin
3786 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3787 Exit;
3788 end;
3790 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3791 g_Player_Init();
3793 // Ñîçäàåì áîòîâ:
3794 for i := nPl+1 to nPlayers do
3795 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3796 end;
3798 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3799 TimeLimit, GoalLimit: Word;
3800 MaxLives: Byte;
3801 Options: LongWord; nPlayers: Byte);
3802 var
3803 i, nPl: Integer;
3804 begin
3805 g_Game_Free();
3807 e_WriteLog('Starting custom game...', TMsgType.Notify);
3809 g_Game_ClearLoading();
3811 // Íàñòðîéêè èãðû:
3812 gGameSettings.GameType := GT_CUSTOM;
3813 gGameSettings.GameMode := GameMode;
3814 gSwitchGameMode := GameMode;
3815 gGameSettings.TimeLimit := TimeLimit;
3816 gGameSettings.GoalLimit := GoalLimit;
3817 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3818 gGameSettings.Options := Options;
3820 gCoopTotalMonstersKilled := 0;
3821 gCoopTotalSecretsFound := 0;
3822 gCoopTotalMonsters := 0;
3823 gCoopTotalSecrets := 0;
3824 gAimLine := False;
3825 gShowMap := False;
3827 g_Game_ExecuteEvent('ongamestart');
3829 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3830 g_Game_SetupScreenSize();
3832 // Ðåæèì íàáëþäàòåëÿ:
3833 if nPlayers = 0 then
3834 begin
3835 gPlayer1 := nil;
3836 gPlayer2 := nil;
3837 end;
3839 nPl := 0;
3840 if nPlayers >= 1 then
3841 begin
3842 // Ñîçäàíèå ïåðâîãî èãðîêà:
3843 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3844 gPlayer1Settings.Color,
3845 gPlayer1Settings.Team, False));
3846 if gPlayer1 = nil then
3847 begin
3848 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3849 Exit;
3850 end;
3852 gPlayer1.Name := gPlayer1Settings.Name;
3853 Inc(nPl);
3854 end;
3856 if nPlayers >= 2 then
3857 begin
3858 // Ñîçäàíèå âòîðîãî èãðîêà:
3859 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3860 gPlayer2Settings.Color,
3861 gPlayer2Settings.Team, False));
3862 if gPlayer2 = nil then
3863 begin
3864 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3865 Exit;
3866 end;
3868 gPlayer2.Name := gPlayer2Settings.Name;
3869 Inc(nPl);
3870 end;
3872 // Çàãðóçêà è çàïóñê êàðòû:
3873 if not g_Game_StartMap(Map, True) then
3874 begin
3875 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3876 Exit;
3877 end;
3879 // Íåò òî÷åê ïîÿâëåíèÿ:
3880 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3881 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3882 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3883 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3884 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3885 begin
3886 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3887 Exit;
3888 end;
3890 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3891 g_Player_Init();
3893 // Ñîçäàåì áîòîâ:
3894 for i := nPl+1 to nPlayers do
3895 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3896 end;
3898 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3899 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3900 Options: LongWord; nPlayers: Byte;
3901 IPAddr: LongWord; Port: Word);
3902 begin
3903 g_Game_Free();
3905 e_WriteLog('Starting net game (server)...', TMsgType.Notify);
3907 g_Game_ClearLoading();
3909 // Íàñòðîéêè èãðû:
3910 gGameSettings.GameType := GT_SERVER;
3911 gGameSettings.GameMode := GameMode;
3912 gSwitchGameMode := GameMode;
3913 gGameSettings.TimeLimit := TimeLimit;
3914 gGameSettings.GoalLimit := GoalLimit;
3915 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3916 gGameSettings.Options := Options;
3918 gCoopTotalMonstersKilled := 0;
3919 gCoopTotalSecretsFound := 0;
3920 gCoopTotalMonsters := 0;
3921 gCoopTotalSecrets := 0;
3922 gAimLine := False;
3923 gShowMap := False;
3925 g_Game_ExecuteEvent('ongamestart');
3927 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3928 g_Game_SetupScreenSize();
3930 // Ðåæèì íàáëþäàòåëÿ:
3931 if nPlayers = 0 then
3932 begin
3933 gPlayer1 := nil;
3934 gPlayer2 := nil;
3935 end;
3937 if nPlayers >= 1 then
3938 begin
3939 // Ñîçäàíèå ïåðâîãî èãðîêà:
3940 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3941 gPlayer1Settings.Color,
3942 gPlayer1Settings.Team, False));
3943 if gPlayer1 = nil then
3944 begin
3945 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3946 Exit;
3947 end;
3949 gPlayer1.Name := gPlayer1Settings.Name;
3950 end;
3952 if nPlayers >= 2 then
3953 begin
3954 // Ñîçäàíèå âòîðîãî èãðîêà:
3955 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3956 gPlayer2Settings.Color,
3957 gPlayer2Settings.Team, False));
3958 if gPlayer2 = nil then
3959 begin
3960 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3961 Exit;
3962 end;
3964 gPlayer2.Name := gPlayer2Settings.Name;
3965 end;
3967 // Ñòàðòóåì ñåðâåð
3968 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3969 begin
3970 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3971 Exit;
3972 end;
3974 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3976 // Çàãðóçêà è çàïóñê êàðòû:
3977 if not g_Game_StartMap(Map, True) then
3978 begin
3979 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3980 Exit;
3981 end;
3983 // Íåò òî÷åê ïîÿâëåíèÿ:
3984 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3985 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3986 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3987 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3988 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3989 begin
3990 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3991 Exit;
3992 end;
3994 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3995 g_Player_Init();
3997 NetState := NET_STATE_GAME;
3998 end;
4000 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
4001 var
4002 Map: String;
4003 WadName: string;
4004 Ptr: Pointer;
4005 T: Cardinal;
4006 MID: Byte;
4007 State: Byte;
4008 OuterLoop: Boolean;
4009 newResPath: string;
4010 InMsg: TMsg;
4011 begin
4012 g_Game_Free();
4014 State := 0;
4015 e_WriteLog('Starting net game (client)...', TMsgType.Notify);
4016 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', TMsgType.Notify);
4018 g_Game_ClearLoading();
4020 // Íàñòðîéêè èãðû:
4021 gGameSettings.GameType := GT_CLIENT;
4023 gCoopTotalMonstersKilled := 0;
4024 gCoopTotalSecretsFound := 0;
4025 gCoopTotalMonsters := 0;
4026 gCoopTotalSecrets := 0;
4027 gAimLine := False;
4028 gShowMap := False;
4030 g_Game_ExecuteEvent('ongamestart');
4032 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4033 g_Game_SetupScreenSize();
4035 NetState := NET_STATE_AUTH;
4037 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4038 // Ñòàðòóåì êëèåíò
4039 if not g_Net_Connect(Addr, Port) then
4040 begin
4041 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4042 NetState := NET_STATE_NONE;
4043 Exit;
4044 end;
4046 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4047 MC_SEND_Info(PW);
4048 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4050 OuterLoop := True;
4051 while OuterLoop do
4052 begin
4053 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4054 begin
4055 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4056 begin
4057 Ptr := NetEvent.packet^.data;
4058 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4059 continue;
4061 MID := InMsg.ReadByte();
4063 if (MID = NET_MSG_INFO) and (State = 0) then
4064 begin
4065 NetMyID := InMsg.ReadByte();
4066 NetPlrUID1 := InMsg.ReadWord();
4068 WadName := InMsg.ReadString();
4069 Map := InMsg.ReadString();
4071 gWADHash := InMsg.ReadMD5();
4073 gGameSettings.GameMode := InMsg.ReadByte();
4074 gSwitchGameMode := gGameSettings.GameMode;
4075 gGameSettings.GoalLimit := InMsg.ReadWord();
4076 gGameSettings.TimeLimit := InMsg.ReadWord();
4077 gGameSettings.MaxLives := InMsg.ReadByte();
4078 gGameSettings.Options := InMsg.ReadLongWord();
4079 T := InMsg.ReadLongWord();
4081 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4082 if newResPath = '' then
4083 begin
4084 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4085 newResPath := g_Res_DownloadWAD(WadName);
4086 if newResPath = '' then
4087 begin
4088 g_FatalError(_lc[I_NET_ERR_HASH]);
4089 enet_packet_destroy(NetEvent.packet);
4090 NetState := NET_STATE_NONE;
4091 Exit;
4092 end;
4093 end;
4094 newResPath := ExtractRelativePath(MapsDir, newResPath);
4096 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4097 gPlayer1Settings.Color,
4098 gPlayer1Settings.Team, False));
4100 if gPlayer1 = nil then
4101 begin
4102 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4104 enet_packet_destroy(NetEvent.packet);
4105 NetState := NET_STATE_NONE;
4106 Exit;
4107 end;
4109 gPlayer1.Name := gPlayer1Settings.Name;
4110 gPlayer1.UID := NetPlrUID1;
4111 gPlayer1.Reset(True);
4113 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4114 begin
4115 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4117 enet_packet_destroy(NetEvent.packet);
4118 NetState := NET_STATE_NONE;
4119 Exit;
4120 end;
4122 gTime := T;
4124 State := 1;
4125 OuterLoop := False;
4126 enet_packet_destroy(NetEvent.packet);
4127 break;
4128 end
4129 else
4130 enet_packet_destroy(NetEvent.packet);
4131 end
4132 else
4133 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4134 begin
4135 State := 0;
4136 if (NetEvent.data <= NET_DISC_MAX) then
4137 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4138 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4139 OuterLoop := False;
4140 Break;
4141 end;
4142 end;
4144 ProcessLoading(true);
4146 e_PollInput();
4148 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
4149 begin
4150 State := 0;
4151 break;
4152 end;
4153 end;
4155 if State <> 1 then
4156 begin
4157 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4158 NetState := NET_STATE_NONE;
4159 Exit;
4160 end;
4162 gLMSRespawn := LMS_RESPAWN_NONE;
4163 gLMSRespawnTime := 0;
4165 g_Player_Init();
4166 NetState := NET_STATE_GAME;
4167 MC_SEND_FullStateRequest;
4168 e_WriteLog('NET: Connection successful.', TMsgType.Notify);
4169 end;
4171 procedure g_Game_SaveOptions();
4172 begin
4173 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4174 end;
4176 procedure g_Game_ChangeMap(const MapPath: String);
4177 var
4178 Force: Boolean;
4179 begin
4180 g_Game_ClearLoading();
4182 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4183 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4184 if gExitByTrigger then
4185 begin
4186 Force := False;
4187 gExitByTrigger := False;
4188 end;
4189 if not g_Game_StartMap(MapPath, Force) then
4190 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4191 end;
4193 procedure g_Game_Restart();
4194 var
4195 Map: string;
4196 begin
4197 if g_Game_IsClient then
4198 Exit;
4199 map := g_ExtractFileName(gMapInfo.Map);
4201 MessageTime := 0;
4202 gGameOn := False;
4203 g_Game_ClearLoading();
4204 g_Game_StartMap(Map, True, gCurrentMapFileName);
4205 end;
4207 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4208 var
4209 NewWAD, ResName: String;
4210 I: Integer;
4211 begin
4212 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4213 g_Player_RemoveAllCorpses();
4215 if (not g_Game_IsClient) and
4216 (gSwitchGameMode <> gGameSettings.GameMode) and
4217 (gGameSettings.GameMode <> GM_SINGLE) then
4218 begin
4219 if gSwitchGameMode = GM_CTF then
4220 gGameSettings.MaxLives := 0;
4221 gGameSettings.GameMode := gSwitchGameMode;
4222 Force := True;
4223 end else
4224 gSwitchGameMode := gGameSettings.GameMode;
4226 g_Player_ResetTeams();
4228 if isWadPath(Map) then
4229 begin
4230 NewWAD := g_ExtractWadName(Map);
4231 ResName := g_ExtractFileName(Map);
4232 if g_Game_IsServer then
4233 begin
4234 gWADHash := MD5File(MapsDir + NewWAD);
4235 g_Game_LoadWAD(NewWAD);
4236 end else
4237 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4238 g_Game_ClientWAD(NewWAD, gWADHash);
4239 end else
4240 ResName := Map;
4242 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4243 if Result then
4244 begin
4245 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4247 gState := STATE_NONE;
4248 g_ActiveWindow := nil;
4249 gGameOn := True;
4251 DisableCheats();
4252 ResetTimer();
4254 if gGameSettings.GameMode = GM_CTF then
4255 begin
4256 g_Map_ResetFlag(FLAG_RED);
4257 g_Map_ResetFlag(FLAG_BLUE);
4258 // CTF, à ôëàãîâ íåò:
4259 if not g_Map_HaveFlagPoints() then
4260 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4261 end;
4262 end
4263 else
4264 begin
4265 gState := STATE_MENU;
4266 gGameOn := False;
4267 end;
4269 gExit := 0;
4270 gPauseMain := false;
4271 gPauseHolmes := false;
4272 gTime := 0;
4273 NetTimeToUpdate := 1;
4274 NetTimeToReliable := 0;
4275 NetTimeToMaster := NetMasterRate;
4276 gLMSRespawn := LMS_RESPAWN_NONE;
4277 gLMSRespawnTime := 0;
4278 gMissionFailed := False;
4279 gNextMap := '';
4281 gCoopMonstersKilled := 0;
4282 gCoopSecretsFound := 0;
4284 gVoteInProgress := False;
4285 gVotePassed := False;
4286 gVoteCount := 0;
4287 gVoted := False;
4289 gStatsOff := False;
4291 if not gGameOn then Exit;
4293 g_Game_SpectateCenterView();
4295 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4296 begin
4297 gLMSRespawn := LMS_RESPAWN_WARMUP;
4298 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4299 gLMSSoftSpawn := True;
4300 if NetMode = NET_SERVER then
4301 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4302 else
4303 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4304 end;
4306 if NetMode = NET_SERVER then
4307 begin
4308 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4310 // Ìàñòåðñåðâåð
4311 if NetUseMaster then
4312 begin
4313 if (NetMHost = nil) or (NetMPeer = nil) then
4314 if not g_Net_Slist_Connect then
4315 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4317 g_Net_Slist_Update;
4318 end;
4320 if NetClients <> nil then
4321 for I := 0 to High(NetClients) do
4322 if NetClients[I].Used then
4323 begin
4324 NetClients[I].Voted := False;
4325 if NetClients[I].RequestedFullUpdate then
4326 begin
4327 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4328 NetClients[I].RequestedFullUpdate := False;
4329 end;
4330 end;
4332 g_Net_UnbanNonPermHosts();
4333 end;
4335 if gLastMap then
4336 begin
4337 gCoopTotalMonstersKilled := 0;
4338 gCoopTotalSecretsFound := 0;
4339 gCoopTotalMonsters := 0;
4340 gCoopTotalSecrets := 0;
4341 gLastMap := False;
4342 end;
4344 g_Game_ExecuteEvent('onmapstart');
4345 end;
4347 procedure SetFirstLevel();
4348 begin
4349 gNextMap := '';
4351 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4352 if MapList = nil then
4353 Exit;
4355 SortSArray(MapList);
4356 gNextMap := MapList[Low(MapList)];
4358 MapList := nil;
4359 end;
4361 procedure g_Game_ExitLevel(const Map: AnsiString);
4362 begin
4363 gNextMap := Map;
4365 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4366 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4367 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4368 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4370 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4371 if gGameSettings.GameType = GT_SINGLE then
4372 gExit := EXIT_ENDLEVELSINGLE
4373 else // Âûøëè â âûõîä â Ñâîåé èãðå
4374 begin
4375 gExit := EXIT_ENDLEVELCUSTOM;
4376 if gGameSettings.GameMode = GM_COOP then
4377 g_Player_RememberAll;
4379 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4380 begin
4381 gLastMap := True;
4382 if gGameSettings.GameMode = GM_COOP then
4383 gStatsOff := True;
4385 gStatsPressed := True;
4386 gNextMap := 'MAP01';
4388 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4389 g_Game_NextLevel;
4391 if g_Game_IsNet then
4392 begin
4393 MH_SEND_GameStats();
4394 MH_SEND_CoopStats();
4395 end;
4396 end;
4397 end;
4398 end;
4400 procedure g_Game_RestartLevel();
4401 var
4402 Map: string;
4403 begin
4404 if gGameSettings.GameMode = GM_SINGLE then
4405 begin
4406 g_Game_Restart();
4407 Exit;
4408 end;
4409 gExit := EXIT_ENDLEVELCUSTOM;
4410 Map := g_ExtractFileName(gMapInfo.Map);
4411 gNextMap := Map;
4412 end;
4414 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4415 var
4416 gWAD: String;
4417 begin
4418 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4419 Exit;
4420 if not g_Game_IsClient then
4421 Exit;
4422 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4423 if gWAD = '' then
4424 begin
4425 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4426 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4427 if gWAD = '' then
4428 begin
4429 g_Game_Free();
4430 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4431 Exit;
4432 end;
4433 end;
4434 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4435 g_Game_LoadWAD(NewWAD);
4436 end;
4438 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4439 var
4440 i, n, nb, nr: Integer;
4442 function monRespawn (mon: TMonster): Boolean;
4443 begin
4444 result := false; // don't stop
4445 if not mon.FNoRespawn then mon.Respawn();
4446 end;
4448 begin
4449 if not g_Game_IsServer then Exit;
4450 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4451 gLMSRespawn := LMS_RESPAWN_NONE;
4452 gLMSRespawnTime := 0;
4453 MessageTime := 0;
4455 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4456 begin
4457 gMissionFailed := True;
4458 g_Game_RestartLevel;
4459 Exit;
4460 end;
4462 n := 0; nb := 0; nr := 0;
4463 for i := Low(gPlayers) to High(gPlayers) do
4464 if (gPlayers[i] <> nil) and
4465 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4466 (gPlayers[i] is TBot)) then
4467 begin
4468 Inc(n);
4469 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4470 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4471 end;
4473 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4474 begin
4475 // wait a second until the fuckers finally decide to join
4476 gLMSRespawn := LMS_RESPAWN_WARMUP;
4477 gLMSRespawnTime := gTime + 1000;
4478 gLMSSoftSpawn := NoMapRestart;
4479 Exit;
4480 end;
4482 g_Player_RemoveAllCorpses;
4483 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4484 if g_Game_IsNet then
4485 MH_SEND_GameEvent(NET_EV_LMS_START);
4487 for i := Low(gPlayers) to High(gPlayers) do
4488 begin
4489 if gPlayers[i] = nil then continue;
4490 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4491 // don't touch normal spectators
4492 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4493 begin
4494 gPlayers[i].FNoRespawn := True;
4495 gPlayers[i].Lives := 0;
4496 if g_Game_IsNet then
4497 MH_SEND_PlayerStats(gPlayers[I].UID);
4498 continue;
4499 end;
4500 gPlayers[i].FNoRespawn := False;
4501 gPlayers[i].Lives := gGameSettings.MaxLives;
4502 gPlayers[i].Respawn(False, True);
4503 if gGameSettings.GameMode = GM_COOP then
4504 begin
4505 gPlayers[i].Frags := 0;
4506 gPlayers[i].RecallState;
4507 end;
4508 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4509 gPlayer1 := g_Player_Get(gLMSPID1);
4510 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4511 gPlayer2 := g_Player_Get(gLMSPID2);
4512 end;
4514 g_Items_RestartRound();
4517 g_Mons_ForEach(monRespawn);
4519 gLMSSoftSpawn := False;
4520 end;
4522 function g_Game_GetFirstMap(WAD: String): String;
4523 begin
4524 Result := '';
4526 MapList := g_Map_GetMapsList(WAD);
4527 if MapList = nil then
4528 Exit;
4530 SortSArray(MapList);
4531 Result := MapList[Low(MapList)];
4533 if not g_Map_Exist(WAD + ':\' + Result) then
4534 Result := '';
4536 MapList := nil;
4537 end;
4539 function g_Game_GetNextMap(): String;
4540 var
4541 I: Integer;
4542 Map: string;
4543 begin
4544 Result := '';
4546 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4547 if MapList = nil then
4548 Exit;
4550 Map := g_ExtractFileName(gMapInfo.Map);
4552 SortSArray(MapList);
4553 MapIndex := -255;
4554 for I := Low(MapList) to High(MapList) do
4555 if Map = MapList[I] then
4556 begin
4557 MapIndex := I;
4558 Break;
4559 end;
4561 if MapIndex <> -255 then
4562 begin
4563 if MapIndex = High(MapList) then
4564 Result := MapList[Low(MapList)]
4565 else
4566 Result := MapList[MapIndex + 1];
4568 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4569 end;
4571 MapList := nil;
4572 end;
4574 procedure g_Game_NextLevel();
4575 begin
4576 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4577 gExit := EXIT_ENDLEVELCUSTOM
4578 else
4579 begin
4580 gExit := EXIT_ENDLEVELSINGLE;
4581 Exit;
4582 end;
4584 if gNextMap <> '' then Exit;
4585 gNextMap := g_Game_GetNextMap();
4586 end;
4588 function g_Game_IsTestMap(): Boolean;
4589 begin
4590 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4591 end;
4593 procedure g_Game_DeleteTestMap();
4594 var
4595 a: Integer;
4596 //MapName: AnsiString;
4597 WadName: string;
4599 WAD: TWADFile;
4600 MapList: SArray;
4601 time: Integer;
4603 begin
4604 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
4605 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
4606 if (a = 0) then exit;
4608 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
4609 WadName := Copy(gMapToDelete, 1, a+3);
4610 Delete(gMapToDelete, 1, a+5);
4611 gMapToDelete := UpperCase(gMapToDelete);
4612 //MapName := '';
4613 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4616 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4617 if MapName <> TEST_MAP_NAME then
4618 Exit;
4620 if not gTempDelete then
4621 begin
4622 time := g_GetFileTime(WadName);
4623 WAD := TWADFile.Create();
4625 // ×èòàåì Wad-ôàéë:
4626 if not WAD.ReadFile(WadName) then
4627 begin // Íåò òàêîãî WAD-ôàéëà
4628 WAD.Free();
4629 Exit;
4630 end;
4632 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4633 WAD.CreateImage();
4634 MapList := WAD.GetResourcesList('');
4636 if MapList <> nil then
4637 for a := 0 to High(MapList) do
4638 if MapList[a] = MapName then
4639 begin
4640 // Óäàëÿåì è ñîõðàíÿåì:
4641 WAD.RemoveResource('', MapName);
4642 WAD.SaveTo(WadName);
4643 Break;
4644 end;
4646 WAD.Free();
4647 g_SetFileTime(WadName, time);
4648 end else
4650 if gTempDelete then DeleteFile(WadName);
4651 end;
4653 procedure GameCVars(P: SArray);
4654 var
4655 a, b: Integer;
4656 stat: TPlayerStatArray;
4657 cmd, s: string;
4658 config: TConfig;
4659 begin
4660 stat := nil;
4661 cmd := LowerCase(P[0]);
4662 if cmd = 'r_showfps' then
4663 begin
4664 if (Length(P) > 1) and
4665 ((P[1] = '1') or (P[1] = '0')) then
4666 gShowFPS := (P[1][1] = '1');
4668 if gShowFPS then
4669 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4670 else
4671 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4672 end
4673 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4674 begin
4675 with gGameSettings do
4676 begin
4677 if (Length(P) > 1) and
4678 ((P[1] = '1') or (P[1] = '0')) then
4679 begin
4680 if (P[1][1] = '1') then
4681 Options := Options or GAME_OPTION_TEAMDAMAGE
4682 else
4683 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4684 end;
4686 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4687 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4688 else
4689 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4691 if g_Game_IsNet then MH_SEND_GameSettings;
4692 end;
4693 end
4694 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4695 begin
4696 with gGameSettings do
4697 begin
4698 if (Length(P) > 1) and
4699 ((P[1] = '1') or (P[1] = '0')) then
4700 begin
4701 if (P[1][1] = '1') then
4702 Options := Options or GAME_OPTION_WEAPONSTAY
4703 else
4704 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4705 end;
4707 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4708 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4709 else
4710 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4712 if g_Game_IsNet then MH_SEND_GameSettings;
4713 end;
4714 end
4715 else if cmd = 'g_gamemode' then
4716 begin
4717 a := g_Game_TextToMode(P[1]);
4718 if a = GM_SINGLE then a := GM_COOP;
4719 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4720 begin
4721 gSwitchGameMode := a;
4722 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4723 (gState = STATE_INTERSINGLE) then
4724 gSwitchGameMode := GM_SINGLE;
4725 if not gGameOn then
4726 gGameSettings.GameMode := gSwitchGameMode;
4727 end;
4728 if gSwitchGameMode = gGameSettings.GameMode then
4729 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4730 [g_Game_ModeToText(gGameSettings.GameMode)]))
4731 else
4732 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4733 [g_Game_ModeToText(gGameSettings.GameMode),
4734 g_Game_ModeToText(gSwitchGameMode)]));
4735 end
4736 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4737 begin
4738 with gGameSettings do
4739 begin
4740 if (Length(P) > 1) and
4741 ((P[1] = '1') or (P[1] = '0')) then
4742 begin
4743 if (P[1][1] = '1') then
4744 Options := Options or GAME_OPTION_ALLOWEXIT
4745 else
4746 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4747 end;
4749 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4750 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4751 else
4752 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4753 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4755 if g_Game_IsNet then MH_SEND_GameSettings;
4756 end;
4757 end
4758 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4759 begin
4760 with gGameSettings do
4761 begin
4762 if (Length(P) > 1) and
4763 ((P[1] = '1') or (P[1] = '0')) then
4764 begin
4765 if (P[1][1] = '1') then
4766 Options := Options or GAME_OPTION_MONSTERS
4767 else
4768 Options := Options and (not GAME_OPTION_MONSTERS);
4769 end;
4771 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4772 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4773 else
4774 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4775 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4777 if g_Game_IsNet then MH_SEND_GameSettings;
4778 end;
4779 end
4780 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4781 begin
4782 with gGameSettings do
4783 begin
4784 if (Length(P) > 1) and
4785 ((P[1] = '1') or (P[1] = '0')) then
4786 begin
4787 if (P[1][1] = '1') then
4788 Options := Options or GAME_OPTION_BOTVSPLAYER
4789 else
4790 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4791 end;
4793 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4794 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4795 else
4796 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4798 if g_Game_IsNet then MH_SEND_GameSettings;
4799 end;
4800 end
4801 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4802 begin
4803 with gGameSettings do
4804 begin
4805 if (Length(P) > 1) and
4806 ((P[1] = '1') or (P[1] = '0')) then
4807 begin
4808 if (P[1][1] = '1') then
4809 Options := Options or GAME_OPTION_BOTVSMONSTER
4810 else
4811 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4812 end;
4814 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4815 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4816 else
4817 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4819 if g_Game_IsNet then MH_SEND_GameSettings;
4820 end;
4821 end
4822 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4823 begin
4824 if Length(P) > 1 then
4825 begin
4826 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4827 gGameSettings.WarmupTime := 30
4828 else
4829 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4830 end;
4832 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4833 [gGameSettings.WarmupTime]));
4834 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4835 end
4836 else if cmd = 'net_interp' then
4837 begin
4838 if (Length(P) > 1) then
4839 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4841 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4842 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4843 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4844 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4845 config.Free();
4846 end
4847 else if cmd = 'net_forceplayerupdate' then
4848 begin
4849 if (Length(P) > 1) and
4850 ((P[1] = '1') or (P[1] = '0')) then
4851 NetForcePlayerUpdate := (P[1][1] = '1');
4853 if NetForcePlayerUpdate then
4854 g_Console_Add('net_forceplayerupdate = 1')
4855 else
4856 g_Console_Add('net_forceplayerupdate = 0');
4857 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4858 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4859 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4860 config.Free();
4861 end
4862 else if cmd = 'net_predictself' then
4863 begin
4864 if (Length(P) > 1) and
4865 ((P[1] = '1') or (P[1] = '0')) then
4866 NetPredictSelf := (P[1][1] = '1');
4868 if NetPredictSelf then
4869 g_Console_Add('net_predictself = 1')
4870 else
4871 g_Console_Add('net_predictself = 0');
4872 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4873 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4874 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4875 config.Free();
4876 end
4877 else if cmd = 'sv_name' then
4878 begin
4879 if (Length(P) > 1) and (Length(P[1]) > 0) then
4880 begin
4881 NetServerName := P[1];
4882 if Length(NetServerName) > 64 then
4883 SetLength(NetServerName, 64);
4884 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4885 g_Net_Slist_Update;
4886 end;
4888 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4889 end
4890 else if cmd = 'sv_passwd' then
4891 begin
4892 if (Length(P) > 1) and (Length(P[1]) > 0) then
4893 begin
4894 NetPassword := P[1];
4895 if Length(NetPassword) > 24 then
4896 SetLength(NetPassword, 24);
4897 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4898 g_Net_Slist_Update;
4899 end;
4901 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4902 end
4903 else if cmd = 'sv_maxplrs' then
4904 begin
4905 if (Length(P) > 1) then
4906 begin
4907 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4908 if g_Game_IsServer and g_Game_IsNet then
4909 begin
4910 b := 0;
4911 for a := 0 to High(NetClients) do
4912 if NetClients[a].Used then
4913 begin
4914 Inc(b);
4915 if b > NetMaxClients then
4916 begin
4917 s := g_Player_Get(NetClients[a].Player).Name;
4918 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4919 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4920 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4921 end;
4922 end;
4923 if NetUseMaster then
4924 g_Net_Slist_Update;
4925 end;
4926 end;
4928 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4929 end
4930 else if cmd = 'sv_public' then
4931 begin
4932 if (Length(P) > 1) then
4933 begin
4934 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4935 if g_Game_IsServer and g_Game_IsNet then
4936 if NetUseMaster then
4937 begin
4938 if NetMPeer = nil then
4939 if not g_Net_Slist_Connect() then
4940 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4941 g_Net_Slist_Update();
4942 end
4943 else
4944 if NetMPeer <> nil then
4945 g_Net_Slist_Disconnect();
4946 end;
4948 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4949 end
4950 else if cmd = 'sv_intertime' then
4951 begin
4952 if (Length(P) > 1) then
4953 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4955 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4956 end
4957 else if cmd = 'p1_name' then
4958 begin
4959 if (Length(P) > 1) and gGameOn then
4960 begin
4961 if g_Game_IsClient then
4962 begin
4963 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4964 MC_SEND_PlayerSettings;
4965 end
4966 else
4967 if gPlayer1 <> nil then
4968 begin
4969 gPlayer1.Name := b_Text_Unformat(P[1]);
4970 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4971 end
4972 else
4973 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4974 end;
4975 end
4976 else if cmd = 'p2_name' then
4977 begin
4978 if (Length(P) > 1) and gGameOn then
4979 begin
4980 if g_Game_IsClient then
4981 begin
4982 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4983 MC_SEND_PlayerSettings;
4984 end
4985 else
4986 if gPlayer2 <> nil then
4987 begin
4988 gPlayer2.Name := b_Text_Unformat(P[1]);
4989 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4990 end
4991 else
4992 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4993 end;
4994 end
4995 else if cmd = 'p1_color' then
4996 begin
4997 if Length(P) > 3 then
4998 if g_Game_IsClient then
4999 begin
5000 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5001 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5002 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5003 MC_SEND_PlayerSettings;
5004 end
5005 else
5006 if gPlayer1 <> nil then
5007 begin
5008 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5009 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5010 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5011 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5012 end
5013 else
5014 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5015 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5016 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5017 end
5018 else if (cmd = 'p2_color') and not g_Game_IsNet then
5019 begin
5020 if Length(P) > 3 then
5021 if g_Game_IsClient then
5022 begin
5023 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5024 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5025 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5026 MC_SEND_PlayerSettings;
5027 end
5028 else
5029 if gPlayer2 <> nil then
5030 begin
5031 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5032 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5033 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5034 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5035 end
5036 else
5037 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5038 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5039 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5040 end
5041 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5042 begin
5043 if cmd = 'r_showtime' then
5044 begin
5045 if (Length(P) > 1) and
5046 ((P[1] = '1') or (P[1] = '0')) then
5047 gShowTime := (P[1][1] = '1');
5049 if gShowTime then
5050 g_Console_Add(_lc[I_MSG_TIME_ON])
5051 else
5052 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5053 end
5054 else if cmd = 'r_showscore' then
5055 begin
5056 if (Length(P) > 1) and
5057 ((P[1] = '1') or (P[1] = '0')) then
5058 gShowGoals := (P[1][1] = '1');
5060 if gShowGoals then
5061 g_Console_Add(_lc[I_MSG_SCORE_ON])
5062 else
5063 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5064 end
5065 else if cmd = 'r_showstat' then
5066 begin
5067 if (Length(P) > 1) and
5068 ((P[1] = '1') or (P[1] = '0')) then
5069 gShowStat := (P[1][1] = '1');
5071 if gShowStat then
5072 g_Console_Add(_lc[I_MSG_STATS_ON])
5073 else
5074 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5075 end
5076 else if cmd = 'r_showkillmsg' then
5077 begin
5078 if (Length(P) > 1) and
5079 ((P[1] = '1') or (P[1] = '0')) then
5080 gShowKillMsg := (P[1][1] = '1');
5082 if gShowKillMsg then
5083 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5084 else
5085 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5086 end
5087 else if cmd = 'r_showlives' then
5088 begin
5089 if (Length(P) > 1) and
5090 ((P[1] = '1') or (P[1] = '0')) then
5091 gShowLives := (P[1][1] = '1');
5093 if gShowLives then
5094 g_Console_Add(_lc[I_MSG_LIVES_ON])
5095 else
5096 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5097 end
5098 else if cmd = 'r_showspect' then
5099 begin
5100 if (Length(P) > 1) and
5101 ((P[1] = '1') or (P[1] = '0')) then
5102 gSpectHUD := (P[1][1] = '1');
5104 if gSpectHUD then
5105 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5106 else
5107 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5108 end
5109 else if cmd = 'r_showping' then
5110 begin
5111 if (Length(P) > 1) and
5112 ((P[1] = '1') or (P[1] = '0')) then
5113 gShowPing := (P[1][1] = '1');
5115 if gShowPing then
5116 g_Console_Add(_lc[I_MSG_PING_ON])
5117 else
5118 g_Console_Add(_lc[I_MSG_PING_OFF]);
5119 end
5120 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5121 begin
5122 if Length(P) > 1 then
5123 begin
5124 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5125 gGameSettings.GoalLimit := 0
5126 else
5127 begin
5128 b := 0;
5130 if gGameSettings.GameMode = GM_DM then
5131 begin // DM
5132 stat := g_Player_GetStats();
5133 if stat <> nil then
5134 for a := 0 to High(stat) do
5135 if stat[a].Frags > b then
5136 b := stat[a].Frags;
5137 end
5138 else // TDM/CTF
5139 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5141 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5142 end;
5144 if g_Game_IsNet then MH_SEND_GameSettings;
5145 end;
5147 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5148 end
5149 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5150 begin
5151 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5152 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5154 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5155 [gGameSettings.TimeLimit div 3600,
5156 (gGameSettings.TimeLimit div 60) mod 60,
5157 gGameSettings.TimeLimit mod 60]));
5158 if g_Game_IsNet then MH_SEND_GameSettings;
5159 end
5160 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5161 begin
5162 if Length(P) > 1 then
5163 begin
5164 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5165 gGameSettings.MaxLives := 0
5166 else
5167 begin
5168 b := 0;
5169 stat := g_Player_GetStats();
5170 if stat <> nil then
5171 for a := 0 to High(stat) do
5172 if stat[a].Lives > b then
5173 b := stat[a].Lives;
5174 gGameSettings.MaxLives :=
5175 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5176 end;
5177 end;
5179 g_Console_Add(Format(_lc[I_MSG_LIVES],
5180 [gGameSettings.MaxLives]));
5181 if g_Game_IsNet then MH_SEND_GameSettings;
5182 end;
5183 end;
5184 end;
5186 procedure PrintHeapStats();
5187 var
5188 hs: TFPCHeapStatus;
5189 begin
5190 hs := GetFPCHeapStatus();
5191 e_LogWriteLn ('v===== heap status =====v');
5192 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
5193 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
5194 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
5195 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
5196 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
5197 e_LogWriteLn ('^=======================^');
5198 end;
5200 procedure DebugCommands(P: SArray);
5201 var
5202 a, b: Integer;
5203 cmd: string;
5204 //pt: TDFPoint;
5205 mon: TMonster;
5206 begin
5207 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5208 if {gDebugMode}conIsCheatsEnabled then
5209 begin
5210 cmd := LowerCase(P[0]);
5211 if cmd = 'd_window' then
5212 begin
5213 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5214 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5215 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5216 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5217 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5218 end
5219 else if cmd = 'd_sounds' then
5220 begin
5221 if (Length(P) > 1) and
5222 ((P[1] = '1') or (P[1] = '0')) then
5223 g_Debug_Sounds := (P[1][1] = '1');
5225 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5226 end
5227 else if cmd = 'd_frames' then
5228 begin
5229 if (Length(P) > 1) and
5230 ((P[1] = '1') or (P[1] = '0')) then
5231 g_Debug_Frames := (P[1][1] = '1');
5233 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5234 end
5235 else if cmd = 'd_winmsg' then
5236 begin
5237 if (Length(P) > 1) and
5238 ((P[1] = '1') or (P[1] = '0')) then
5239 g_Debug_WinMsgs := (P[1][1] = '1');
5241 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5242 end
5243 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5244 begin
5245 if (Length(P) > 1) and
5246 ((P[1] = '1') or (P[1] = '0')) then
5247 g_Debug_MonsterOff := (P[1][1] = '1');
5249 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5250 end
5251 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5252 begin
5253 if Length(P) > 1 then
5254 case P[1][1] of
5255 '0': g_debug_BotAIOff := 0;
5256 '1': g_debug_BotAIOff := 1;
5257 '2': g_debug_BotAIOff := 2;
5258 '3': g_debug_BotAIOff := 3;
5259 end;
5261 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5262 end
5263 else if cmd = 'd_monster' then
5264 begin
5265 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5266 if Length(P) < 2 then
5267 begin
5268 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5269 g_Console_Add('ID | Name');
5270 for b := MONSTER_DEMON to MONSTER_MAN do
5271 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5272 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
5273 end else
5274 begin
5275 a := StrToIntDef(P[1], 0);
5276 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5277 a := g_Mons_TypeIdByName(P[1]);
5279 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5280 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5281 else
5282 begin
5283 with gPlayer1.Obj do
5284 begin
5285 mon := g_Monsters_Create(a,
5286 X + Rect.X + (Rect.Width div 2),
5287 Y + Rect.Y + Rect.Height,
5288 gPlayer1.Direction, True);
5289 end;
5290 if (Length(P) > 2) and (mon <> nil) then
5291 begin
5292 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
5293 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
5294 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
5295 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
5296 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
5297 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
5298 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
5299 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
5300 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5301 end;
5302 end;
5303 end;
5304 end
5305 else if (cmd = 'd_health') then
5306 begin
5307 if (Length(P) > 1) and
5308 ((P[1] = '1') or (P[1] = '0')) then
5309 g_debug_HealthBar := (P[1][1] = '1');
5311 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5312 end
5313 else if (cmd = 'd_player') then
5314 begin
5315 if (Length(P) > 1) and
5316 ((P[1] = '1') or (P[1] = '0')) then
5317 g_debug_Player := (P[1][1] = '1');
5319 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5320 end
5321 else if (cmd = 'd_joy') then
5322 begin
5323 for a := 1 to 8 do
5324 g_Console_Add(e_JoystickStateToString(a));
5325 end
5326 else if (cmd = 'd_mem') then
5327 begin
5328 PrintHeapStats();
5329 end;
5330 end
5331 else
5332 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5333 end;
5336 procedure GameCheats(P: SArray);
5337 var
5338 cmd: string;
5339 f, a: Integer;
5340 plr: TPlayer;
5341 begin
5342 if (not gGameOn) or (not conIsCheatsEnabled) then
5343 begin
5344 g_Console_Add('not available');
5345 exit;
5346 end;
5347 plr := gPlayer1;
5348 if plr = nil then
5349 begin
5350 g_Console_Add('where is the player?!');
5351 exit;
5352 end;
5353 cmd := LowerCase(P[0]);
5354 // god
5355 if cmd = 'god' then
5356 begin
5357 plr.GodMode := not plr.GodMode;
5358 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5359 exit;
5360 end;
5361 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5362 if cmd = 'give' then
5363 begin
5364 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5365 for f := 1 to High(P) do
5366 begin
5367 cmd := LowerCase(P[f]);
5368 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5369 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5370 if cmd = 'exit' then
5371 begin
5372 if gTriggers <> nil then
5373 begin
5374 for a := 0 to High(gTriggers) do
5375 begin
5376 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5377 begin
5378 g_Console_Add('player left the map');
5379 gExitByTrigger := True;
5380 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5381 g_Game_ExitLevel(gTriggers[a].tgcMap);
5382 break;
5383 end;
5384 end;
5385 end;
5386 continue;
5387 end;
5389 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5390 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5391 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5392 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5393 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5395 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5396 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5398 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5399 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;
5401 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5402 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5404 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5405 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5407 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5408 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5410 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5411 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5412 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5414 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5415 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5416 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5417 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;
5418 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5419 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5421 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;
5422 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;
5423 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;
5424 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;
5425 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;
5426 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;
5428 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5429 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;
5431 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;
5432 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;
5434 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5436 if cmd = 'ammo' then
5437 begin
5438 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5439 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5440 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5441 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5442 plr.GiveItem(ITEM_AMMO_FUELCAN);
5443 g_Console_Add('player got some ammo');
5444 continue;
5445 end;
5447 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5448 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5450 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5451 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5453 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5454 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5456 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5457 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5459 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5461 if cmd = 'weapons' then
5462 begin
5463 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5464 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5465 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5466 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5467 plr.GiveItem(ITEM_WEAPON_PLASMA);
5468 plr.GiveItem(ITEM_WEAPON_BFG);
5469 g_Console_Add('player got weapons');
5470 continue;
5471 end;
5473 if cmd = 'keys' then
5474 begin
5475 plr.GiveItem(ITEM_KEY_RED);
5476 plr.GiveItem(ITEM_KEY_GREEN);
5477 plr.GiveItem(ITEM_KEY_BLUE);
5478 g_Console_Add('player got all keys');
5479 continue;
5480 end;
5482 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5483 end;
5484 exit;
5485 end;
5486 // open
5487 if cmd = 'open' then
5488 begin
5489 g_Console_Add('player activated sesame');
5490 g_Triggers_OpenAll();
5491 exit;
5492 end;
5493 // fly
5494 if cmd = 'fly' then
5495 begin
5496 gFly := not gFly;
5497 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5498 exit;
5499 end;
5500 // noclip
5501 if cmd = 'noclip' then
5502 begin
5503 plr.SwitchNoClip;
5504 g_Console_Add('wall hardeness adjusted');
5505 exit;
5506 end;
5507 // notarget
5508 if cmd = 'notarget' then
5509 begin
5510 plr.NoTarget := not plr.NoTarget;
5511 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5512 exit;
5513 end;
5514 // noreload
5515 if cmd = 'noreload' then
5516 begin
5517 plr.NoReload := not plr.NoReload;
5518 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5519 exit;
5520 end;
5521 // speedy
5522 if cmd = 'speedy' then
5523 begin
5524 MAX_RUNVEL := 32-MAX_RUNVEL;
5525 g_Console_Add('speed adjusted');
5526 exit;
5527 end;
5528 // jumpy
5529 if cmd = 'jumpy' then
5530 begin
5531 VEL_JUMP := 30-VEL_JUMP;
5532 g_Console_Add('jump height adjusted');
5533 exit;
5534 end;
5535 // automap
5536 if cmd = 'automap' then
5537 begin
5538 gShowMap := not gShowMap;
5539 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5540 exit;
5541 end;
5542 // aimline
5543 if cmd = 'aimline' then
5544 begin
5545 gAimLine := not gAimLine;
5546 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5547 exit;
5548 end;
5549 end;
5551 procedure GameCommands(P: SArray);
5552 var
5553 a, b: Integer;
5554 s, pw: String;
5555 chstr: string;
5556 cmd: string;
5557 pl: pTNetClient = nil;
5558 plr: TPlayer;
5559 prt: Word;
5560 nm: Boolean;
5561 listen: LongWord;
5562 begin
5563 // Îáùèå êîìàíäû:
5564 cmd := LowerCase(P[0]);
5565 chstr := '';
5566 if (cmd = 'quit') or
5567 (cmd = 'exit') then
5568 begin
5569 g_Game_Free();
5570 g_Game_Quit();
5571 Exit;
5572 end
5573 else if cmd = 'pause' then
5574 begin
5575 if (g_ActiveWindow = nil) then
5576 g_Game_Pause(not gPauseMain);
5577 end
5578 else if cmd = 'endgame' then
5579 gExit := EXIT_SIMPLE
5580 else if cmd = 'restart' then
5581 begin
5582 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5583 begin
5584 if g_Game_IsClient then
5585 begin
5586 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5587 Exit;
5588 end;
5589 g_Game_Restart();
5590 end else
5591 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5592 end
5593 else if cmd = 'kick' then
5594 begin
5595 if g_Game_IsServer then
5596 begin
5597 if Length(P) < 2 then
5598 begin
5599 g_Console_Add('kick <name>');
5600 Exit;
5601 end;
5602 if P[1] = '' then
5603 begin
5604 g_Console_Add('kick <name>');
5605 Exit;
5606 end;
5608 if g_Game_IsNet then
5609 pl := g_Net_Client_ByName(P[1]);
5610 if (pl <> nil) then
5611 begin
5612 s := g_Net_ClientName_ByID(pl^.ID);
5613 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5614 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5615 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5616 if NetUseMaster then
5617 g_Net_Slist_Update;
5618 end else if gPlayers <> nil then
5619 for a := Low(gPlayers) to High(gPlayers) do
5620 if gPlayers[a] <> nil then
5621 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5622 begin
5623 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5624 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5625 continue;
5626 gPlayers[a].Lives := 0;
5627 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5628 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5629 g_Player_Remove(gPlayers[a].UID);
5630 if NetUseMaster then
5631 g_Net_Slist_Update;
5632 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5633 g_Bot_MixNames();
5634 end;
5635 end else
5636 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5637 end
5638 else if cmd = 'kick_id' then
5639 begin
5640 if g_Game_IsServer and g_Game_IsNet then
5641 begin
5642 if Length(P) < 2 then
5643 begin
5644 g_Console_Add('kick_id <client ID>');
5645 Exit;
5646 end;
5647 if P[1] = '' then
5648 begin
5649 g_Console_Add('kick_id <client ID>');
5650 Exit;
5651 end;
5653 a := StrToIntDef(P[1], 0);
5654 if (NetClients <> nil) and (a <= High(NetClients)) then
5655 begin
5656 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5657 begin
5658 s := g_Net_ClientName_ByID(NetClients[a].ID);
5659 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5660 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5661 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5662 if NetUseMaster then
5663 g_Net_Slist_Update;
5664 end;
5665 end;
5666 end else
5667 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5668 end
5669 else if cmd = 'ban' then
5670 begin
5671 if g_Game_IsServer and g_Game_IsNet then
5672 begin
5673 if Length(P) < 2 then
5674 begin
5675 g_Console_Add('ban <name>');
5676 Exit;
5677 end;
5678 if P[1] = '' then
5679 begin
5680 g_Console_Add('ban <name>');
5681 Exit;
5682 end;
5684 pl := g_Net_Client_ByName(P[1]);
5685 if (pl <> nil) then
5686 begin
5687 s := g_Net_ClientName_ByID(pl^.ID);
5688 g_Net_BanHost(pl^.Peer^.address.host, False);
5689 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5690 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5691 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5692 if NetUseMaster then
5693 g_Net_Slist_Update;
5694 end else
5695 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5696 end else
5697 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5698 end
5699 else if cmd = 'ban_id' then
5700 begin
5701 if g_Game_IsServer and g_Game_IsNet then
5702 begin
5703 if Length(P) < 2 then
5704 begin
5705 g_Console_Add('ban_id <client ID>');
5706 Exit;
5707 end;
5708 if P[1] = '' then
5709 begin
5710 g_Console_Add('ban_id <client ID>');
5711 Exit;
5712 end;
5714 a := StrToIntDef(P[1], 0);
5715 if (NetClients <> nil) and (a <= High(NetClients)) then
5716 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5717 begin
5718 s := g_Net_ClientName_ByID(NetClients[a].ID);
5719 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5720 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5721 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5722 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5723 if NetUseMaster then
5724 g_Net_Slist_Update;
5725 end;
5726 end else
5727 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5728 end
5729 else if cmd = 'permban' then
5730 begin
5731 if g_Game_IsServer and g_Game_IsNet then
5732 begin
5733 if Length(P) < 2 then
5734 begin
5735 g_Console_Add('permban <name>');
5736 Exit;
5737 end;
5738 if P[1] = '' then
5739 begin
5740 g_Console_Add('permban <name>');
5741 Exit;
5742 end;
5744 pl := g_Net_Client_ByName(P[1]);
5745 if (pl <> nil) then
5746 begin
5747 s := g_Net_ClientName_ByID(pl^.ID);
5748 g_Net_BanHost(pl^.Peer^.address.host);
5749 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5750 g_Net_SaveBanList();
5751 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5752 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5753 if NetUseMaster then
5754 g_Net_Slist_Update;
5755 end else
5756 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5757 end else
5758 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5759 end
5760 else if cmd = 'permban_id' then
5761 begin
5762 if g_Game_IsServer and g_Game_IsNet then
5763 begin
5764 if Length(P) < 2 then
5765 begin
5766 g_Console_Add('permban_id <client ID>');
5767 Exit;
5768 end;
5769 if P[1] = '' then
5770 begin
5771 g_Console_Add('permban_id <client ID>');
5772 Exit;
5773 end;
5775 a := StrToIntDef(P[1], 0);
5776 if (NetClients <> nil) and (a <= High(NetClients)) then
5777 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5778 begin
5779 s := g_Net_ClientName_ByID(NetClients[a].ID);
5780 g_Net_BanHost(NetClients[a].Peer^.address.host);
5781 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5782 g_Net_SaveBanList();
5783 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5784 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5785 if NetUseMaster then
5786 g_Net_Slist_Update;
5787 end;
5788 end else
5789 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5790 end
5791 else if cmd = 'unban' then
5792 begin
5793 if g_Game_IsServer and g_Game_IsNet then
5794 begin
5795 if Length(P) < 2 then
5796 begin
5797 g_Console_Add('unban <IP Address>');
5798 Exit;
5799 end;
5800 if P[1] = '' then
5801 begin
5802 g_Console_Add('unban <IP Address>');
5803 Exit;
5804 end;
5806 if g_Net_UnbanHost(P[1]) then
5807 begin
5808 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5809 g_Net_SaveBanList();
5810 end else
5811 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5812 end else
5813 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5814 end
5815 else if cmd = 'clientlist' then
5816 begin
5817 if g_Game_IsServer and g_Game_IsNet then
5818 begin
5819 b := 0;
5820 if NetClients <> nil then
5821 for a := Low(NetClients) to High(NetClients) do
5822 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5823 begin
5824 plr := g_Player_Get(NetClients[a].Player);
5825 if plr = nil then continue;
5826 Inc(b);
5827 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5828 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5829 end;
5830 if b = 0 then
5831 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5832 end else
5833 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5834 end
5835 else if cmd = 'connect' then
5836 begin
5837 if (NetMode = NET_NONE) then
5838 begin
5839 if Length(P) < 2 then
5840 begin
5841 g_Console_Add('connect <IP> [port] [password]');
5842 Exit;
5843 end;
5844 if P[1] = '' then
5845 begin
5846 g_Console_Add('connect <IP> [port] [password]');
5847 Exit;
5848 end;
5850 if Length(P) > 2 then
5851 prt := StrToIntDef(P[2], 25666)
5852 else
5853 prt := 25666;
5855 if Length(P) > 3 then
5856 pw := P[3]
5857 else
5858 pw := '';
5860 g_Game_StartClient(P[1], prt, pw);
5861 end;
5862 end
5863 else if cmd = 'disconnect' then
5864 begin
5865 if (NetMode = NET_CLIENT) then
5866 g_Net_Disconnect();
5867 end
5868 else if cmd = 'reconnect' then
5869 begin
5870 if (NetMode = NET_SERVER) then
5871 Exit;
5873 if (NetMode = NET_CLIENT) then
5874 begin
5875 g_Net_Disconnect();
5876 gExit := EXIT_SIMPLE;
5877 EndGame;
5878 end;
5880 //TODO: Use last successful password to reconnect, instead of ''
5881 g_Game_StartClient(NetClientIP, NetClientPort, '');
5882 end
5883 else if (cmd = 'addbot') or
5884 (cmd = 'bot_add') then
5885 begin
5886 if Length(P) > 1 then
5887 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5888 else
5889 g_Bot_Add(TEAM_NONE, 2);
5890 end
5891 else if cmd = 'bot_addlist' then
5892 begin
5893 if Length(P) > 1 then
5894 if Length(P) = 2 then
5895 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5896 else
5897 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5898 end
5899 else if cmd = 'bot_removeall' then
5900 g_Bot_RemoveAll()
5901 else if cmd = 'chat' then
5902 begin
5903 if g_Game_IsNet then
5904 begin
5905 if Length(P) > 1 then
5906 begin
5907 for a := 1 to High(P) do
5908 chstr := chstr + P[a] + ' ';
5910 if Length(chstr) > 200 then SetLength(chstr, 200);
5912 if Length(chstr) < 1 then
5913 begin
5914 g_Console_Add('chat <text>');
5915 Exit;
5916 end;
5918 chstr := b_Text_Format(chstr);
5919 if g_Game_IsClient then
5920 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5921 else
5922 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5923 end
5924 else
5925 g_Console_Add('chat <text>');
5926 end else
5927 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5928 end
5929 else if cmd = 'teamchat' then
5930 begin
5931 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5932 begin
5933 if Length(P) > 1 then
5934 begin
5935 for a := 1 to High(P) do
5936 chstr := chstr + P[a] + ' ';
5938 if Length(chstr) > 200 then SetLength(chstr, 200);
5940 if Length(chstr) < 1 then
5941 begin
5942 g_Console_Add('teamchat <text>');
5943 Exit;
5944 end;
5946 chstr := b_Text_Format(chstr);
5947 if g_Game_IsClient then
5948 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5949 else
5950 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5951 gPlayer1Settings.Team);
5952 end
5953 else
5954 g_Console_Add('teamchat <text>');
5955 end else
5956 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5957 end
5958 else if cmd = 'game' then
5959 begin
5960 if gGameSettings.GameType <> GT_NONE then
5961 begin
5962 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5963 Exit;
5964 end;
5965 if Length(P) = 1 then
5966 begin
5967 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5968 Exit;
5969 end;
5970 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5971 P[1] := addWadExtension(P[1]);
5972 if FileExists(MapsDir + P[1]) then
5973 begin
5974 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5975 if Length(P) < 3 then
5976 begin
5977 SetLength(P, 3);
5978 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5979 end;
5981 s := P[1] + ':\' + UpperCase(P[2]);
5983 if g_Map_Exist(MapsDir + s) then
5984 begin
5985 // Çàïóñêàåì ñâîþ èãðó
5986 g_Game_Free();
5987 with gGameSettings do
5988 begin
5989 GameMode := g_Game_TextToMode(gcGameMode);
5990 if gSwitchGameMode <> GM_NONE then
5991 GameMode := gSwitchGameMode;
5992 if GameMode = GM_NONE then GameMode := GM_DM;
5993 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5994 b := 1;
5995 if Length(P) >= 4 then
5996 b := StrToIntDef(P[3], 1);
5997 g_Game_StartCustom(s, GameMode, TimeLimit,
5998 GoalLimit, MaxLives, Options, b);
5999 end;
6000 end
6001 else
6002 if P[2] = '' then
6003 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6004 else
6005 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
6006 end else
6007 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6008 end
6009 else if cmd = 'host' then
6010 begin
6011 if gGameSettings.GameType <> GT_NONE then
6012 begin
6013 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6014 Exit;
6015 end;
6016 if Length(P) < 4 then
6017 begin
6018 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
6019 Exit;
6020 end;
6021 if not StrToIp(P[1], listen) then
6022 Exit;
6023 prt := StrToIntDef(P[2], 25666);
6025 P[3] := addWadExtension(P[3]);
6026 if FileExists(MapsDir + P[3]) then
6027 begin
6028 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6029 if Length(P) < 5 then
6030 begin
6031 SetLength(P, 5);
6032 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
6033 end;
6035 s := P[3] + ':\' + UpperCase(P[4]);
6037 if g_Map_Exist(MapsDir + s) then
6038 begin
6039 // Çàïóñêàåì ñâîþ èãðó
6040 g_Game_Free();
6041 with gGameSettings do
6042 begin
6043 GameMode := g_Game_TextToMode(gcGameMode);
6044 if gSwitchGameMode <> GM_NONE then
6045 GameMode := gSwitchGameMode;
6046 if GameMode = GM_NONE then GameMode := GM_DM;
6047 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6048 b := 0;
6049 if Length(P) >= 6 then
6050 b := StrToIntDef(P[5], 0);
6051 g_Game_StartServer(s, GameMode, TimeLimit,
6052 GoalLimit, MaxLives, Options, b, listen, prt);
6053 end;
6054 end
6055 else
6056 if P[4] = '' then
6057 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6058 else
6059 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
6060 end else
6061 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6062 end
6063 else if cmd = 'map' then
6064 begin
6065 if Length(P) = 1 then
6066 begin
6067 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6068 begin
6069 g_Console_Add(cmd + ' <MAP>');
6070 g_Console_Add(cmd + ' <WAD> [MAP]');
6071 end else
6072 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6073 end else
6074 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6075 begin
6076 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6077 if Length(P) < 3 then
6078 begin
6079 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6080 s := UpperCase(P[1]);
6081 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6082 begin // Êàðòà íàøëàñü
6083 gExitByTrigger := False;
6084 if gGameOn then
6085 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6086 gNextMap := s;
6087 gExit := EXIT_ENDLEVELCUSTOM;
6088 end
6089 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6090 g_Game_ChangeMap(s);
6091 end else
6092 begin
6093 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6094 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6095 P[1] := addWadExtension(P[1]);
6096 if FileExists(MapsDir + P[1]) then
6097 begin
6098 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6099 SetLength(P, 3);
6100 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6102 s := P[1] + ':\' + P[2];
6104 if g_Map_Exist(MapsDir + s) then
6105 begin
6106 gExitByTrigger := False;
6107 if gGameOn then
6108 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6109 gNextMap := s;
6110 gExit := EXIT_ENDLEVELCUSTOM;
6111 end
6112 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6113 g_Game_ChangeMap(s);
6114 end else
6115 if P[2] = '' then
6116 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6117 else
6118 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6119 end else
6120 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6121 end;
6122 end else
6123 begin
6124 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6125 P[1] := addWadExtension(P[1]);
6126 if FileExists(MapsDir + P[1]) then
6127 begin
6128 // Íàøëè WAD ôàéë
6129 P[2] := UpperCase(P[2]);
6130 s := P[1] + ':\' + P[2];
6132 if g_Map_Exist(MapsDir + s) then
6133 begin // Íàøëè êàðòó
6134 gExitByTrigger := False;
6135 if gGameOn then
6136 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6137 gNextMap := s;
6138 gExit := EXIT_ENDLEVELCUSTOM;
6139 end
6140 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6141 g_Game_ChangeMap(s);
6142 end else
6143 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6144 end else
6145 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6146 end;
6147 end else
6148 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6149 end
6150 else if cmd = 'nextmap' then
6151 begin
6152 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6153 g_Console_Add(_lc[I_MSG_NOT_GAME])
6154 else begin
6155 nm := True;
6156 if Length(P) = 1 then
6157 begin
6158 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6159 begin
6160 g_Console_Add(cmd + ' <MAP>');
6161 g_Console_Add(cmd + ' <WAD> [MAP]');
6162 end else begin
6163 nm := False;
6164 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6165 end;
6166 end else
6167 begin
6168 nm := False;
6169 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6170 begin
6171 if Length(P) < 3 then
6172 begin
6173 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6174 s := UpperCase(P[1]);
6175 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6176 begin // Êàðòà íàøëàñü
6177 gExitByTrigger := False;
6178 gNextMap := s;
6179 nm := True;
6180 end else
6181 begin
6182 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6183 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6184 P[1] := addWadExtension(P[1]);
6185 if FileExists(MapsDir + P[1]) then
6186 begin
6187 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6188 SetLength(P, 3);
6189 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6191 s := P[1] + ':\' + P[2];
6193 if g_Map_Exist(MapsDir + s) then
6194 begin // Óñòàíàâëèâàåì êàðòó
6195 gExitByTrigger := False;
6196 gNextMap := s;
6197 nm := True;
6198 end else
6199 if P[2] = '' then
6200 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6201 else
6202 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6203 end else
6204 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6205 end;
6206 end else
6207 begin
6208 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6209 P[1] := addWadExtension(P[1]);
6210 if FileExists(MapsDir + P[1]) then
6211 begin
6212 // Íàøëè WAD ôàéë
6213 P[2] := UpperCase(P[2]);
6214 s := P[1] + ':\' + P[2];
6216 if g_Map_Exist(MapsDir + s) then
6217 begin // Íàøëè êàðòó
6218 gExitByTrigger := False;
6219 gNextMap := s;
6220 nm := True;
6221 end else
6222 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6223 end else
6224 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6225 end;
6226 end else
6227 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6228 end;
6229 if nm then
6230 if gNextMap = '' then
6231 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6232 else
6233 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6234 end;
6235 end
6236 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6237 begin
6238 if not gGameOn then
6239 g_Console_Add(_lc[I_MSG_NOT_GAME])
6240 else
6241 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6242 begin
6243 gExitByTrigger := False;
6244 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6245 if (gNextMap = '') and (gTriggers <> nil) then
6246 for a := 0 to High(gTriggers) do
6247 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6248 begin
6249 gExitByTrigger := True;
6250 //gNextMap := gTriggers[a].Data.MapName;
6251 gNextMap := gTriggers[a].tgcMap;
6252 Break;
6253 end;
6254 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6255 if gNextMap = '' then
6256 gNextMap := g_Game_GetNextMap();
6257 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6258 if not isWadPath(gNextMap) then
6259 s := gGameSettings.WAD + ':\' + gNextMap
6260 else
6261 s := gNextMap;
6262 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6263 if g_Map_Exist(MapsDir + s) then
6264 gExit := EXIT_ENDLEVELCUSTOM
6265 else
6266 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6267 end else
6268 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6269 end
6270 else if (cmd = 'event') then
6271 begin
6272 if (Length(P) <= 1) then
6273 begin
6274 for a := 0 to High(gEvents) do
6275 if gEvents[a].Command = '' then
6276 g_Console_Add(gEvents[a].Name + ' <none>')
6277 else
6278 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6279 Exit;
6280 end;
6281 if (Length(P) = 2) then
6282 begin
6283 for a := 0 to High(gEvents) do
6284 if gEvents[a].Name = P[1] then
6285 if gEvents[a].Command = '' then
6286 g_Console_Add(gEvents[a].Name + ' <none>')
6287 else
6288 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6289 Exit;
6290 end;
6291 for a := 0 to High(gEvents) do
6292 if gEvents[a].Name = P[1] then
6293 begin
6294 gEvents[a].Command := '';
6295 for b := 2 to High(P) do
6296 if Pos(' ', P[b]) = 0 then
6297 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6298 else
6299 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6300 gEvents[a].Command := Trim(gEvents[a].Command);
6301 Exit;
6302 end;
6303 end
6304 else if cmd = 'suicide' then
6305 begin
6306 if gGameOn then
6307 begin
6308 if g_Game_IsClient then
6309 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6310 else
6311 begin
6312 if gPlayer1 <> nil then
6313 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6314 if gPlayer2 <> nil then
6315 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6316 end;
6317 end;
6318 end
6319 // Êîìàíäû Ñâîåé èãðû:
6320 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6321 begin
6322 if cmd = 'bot_addred' then
6323 begin
6324 if Length(P) > 1 then
6325 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6326 else
6327 g_Bot_Add(TEAM_RED, 2);
6328 end
6329 else if cmd = 'bot_addblue' then
6330 begin
6331 if Length(P) > 1 then
6332 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6333 else
6334 g_Bot_Add(TEAM_BLUE, 2);
6335 end
6336 else if cmd = 'spectate' then
6337 begin
6338 if not gGameOn then
6339 Exit;
6340 g_Game_Spectate();
6341 end
6342 else if cmd = 'say' then
6343 begin
6344 if g_Game_IsServer and g_Game_IsNet then
6345 begin
6346 if Length(P) > 1 then
6347 begin
6348 chstr := '';
6349 for a := 1 to High(P) do
6350 chstr := chstr + P[a] + ' ';
6352 if Length(chstr) > 200 then SetLength(chstr, 200);
6354 if Length(chstr) < 1 then
6355 begin
6356 g_Console_Add('say <text>');
6357 Exit;
6358 end;
6360 chstr := b_Text_Format(chstr);
6361 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6362 end
6363 else g_Console_Add('say <text>');
6364 end else
6365 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6366 end
6367 else if cmd = 'tell' then
6368 begin
6369 if g_Game_IsServer and g_Game_IsNet then
6370 begin
6371 if (Length(P) > 2) and (P[1] <> '') then
6372 begin
6373 chstr := '';
6374 for a := 2 to High(P) do
6375 chstr := chstr + P[a] + ' ';
6377 if Length(chstr) > 200 then SetLength(chstr, 200);
6379 if Length(chstr) < 1 then
6380 begin
6381 g_Console_Add('tell <playername> <text>');
6382 Exit;
6383 end;
6385 pl := g_Net_Client_ByName(P[1]);
6386 if pl <> nil then
6387 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6388 else
6389 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6390 end
6391 else g_Console_Add('tell <playername> <text>');
6392 end else
6393 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6394 end
6395 else if (cmd = 'overtime') and not g_Game_IsClient then
6396 begin
6397 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6398 Exit;
6399 // Äîïîëíèòåëüíîå âðåìÿ:
6400 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6402 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6403 [gGameSettings.TimeLimit div 3600,
6404 (gGameSettings.TimeLimit div 60) mod 60,
6405 gGameSettings.TimeLimit mod 60]));
6406 if g_Game_IsNet then MH_SEND_GameSettings;
6407 end
6408 else if (cmd = 'rcon_password') and g_Game_IsClient then
6409 begin
6410 if (Length(P) <= 1) then
6411 g_Console_Add('rcon_password <password>')
6412 else
6413 MC_SEND_RCONPassword(P[1]);
6414 end
6415 else if cmd = 'rcon' then
6416 begin
6417 if g_Game_IsClient then
6418 begin
6419 if Length(P) > 1 then
6420 begin
6421 chstr := '';
6422 for a := 1 to High(P) do
6423 chstr := chstr + P[a] + ' ';
6425 if Length(chstr) > 200 then SetLength(chstr, 200);
6427 if Length(chstr) < 1 then
6428 begin
6429 g_Console_Add('rcon <command>');
6430 Exit;
6431 end;
6433 MC_SEND_RCONCommand(chstr);
6434 end
6435 else g_Console_Add('rcon <command>');
6436 end;
6437 end
6438 else if cmd = 'ready' then
6439 begin
6440 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6441 gLMSRespawnTime := gTime + 100;
6442 end
6443 else if (cmd = 'callvote') and g_Game_IsNet then
6444 begin
6445 if Length(P) > 1 then
6446 begin
6447 chstr := '';
6448 for a := 1 to High(P) do begin
6449 if a > 1 then chstr := chstr + ' ';
6450 chstr := chstr + P[a];
6451 end;
6453 if Length(chstr) > 200 then SetLength(chstr, 200);
6455 if Length(chstr) < 1 then
6456 begin
6457 g_Console_Add('callvote <command>');
6458 Exit;
6459 end;
6461 if g_Game_IsClient then
6462 MC_SEND_Vote(True, chstr)
6463 else
6464 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6465 g_Console_Process('vote', True);
6466 end
6467 else
6468 g_Console_Add('callvote <command>');
6469 end
6470 else if (cmd = 'vote') and g_Game_IsNet then
6471 begin
6472 if g_Game_IsClient then
6473 MC_SEND_Vote(False)
6474 else if gVoteInProgress then
6475 begin
6476 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6477 a := Floor((NetClientCount+1)/2.0) + 1
6478 else
6479 a := Floor(NetClientCount/2.0) + 1;
6480 if gVoted then
6481 begin
6482 Dec(gVoteCount);
6483 gVoted := False;
6484 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6485 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6486 end
6487 else
6488 begin
6489 Inc(gVoteCount);
6490 gVoted := True;
6491 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6492 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6493 g_Game_CheckVote;
6494 end;
6495 end;
6496 end
6497 end;
6498 end;
6500 procedure g_TakeScreenShot();
6501 var
6502 a: Word;
6503 FileName: string;
6504 ssdir, t: string;
6505 st: TStream;
6506 ok: Boolean;
6507 begin
6508 if e_NoGraphics then Exit;
6509 ssdir := GameDir+'/screenshots';
6510 if not findFileCI(ssdir, true) then
6511 begin
6512 // try to create dir
6513 try
6514 CreateDir(ssdir);
6515 except
6516 end;
6517 if not findFileCI(ssdir, true) then exit; // alas
6518 end;
6519 try
6520 for a := 1 to High(Word) do
6521 begin
6522 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6523 t := FileName;
6524 if findFileCI(t, true) then continue;
6525 if not findFileCI(FileName) then
6526 begin
6527 ok := false;
6528 st := createDiskFile(FileName);
6529 try
6530 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6531 ok := true;
6532 finally
6533 st.Free();
6534 end;
6535 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6536 break;
6537 end;
6538 end;
6539 except
6540 end;
6541 end;
6543 procedure g_Game_InGameMenu(Show: Boolean);
6544 begin
6545 if (g_ActiveWindow = nil) and Show then
6546 begin
6547 if gGameSettings.GameType = GT_SINGLE then
6548 g_GUI_ShowWindow('GameSingleMenu')
6549 else
6550 begin
6551 if g_Game_IsClient then
6552 g_GUI_ShowWindow('GameClientMenu')
6553 else
6554 if g_Game_IsNet then
6555 g_GUI_ShowWindow('GameServerMenu')
6556 else
6557 g_GUI_ShowWindow('GameCustomMenu');
6558 end;
6559 g_Sound_PlayEx('MENU_OPEN');
6561 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6562 if (not g_Game_IsNet) then
6563 g_Game_Pause(True);
6564 end
6565 else
6566 if (g_ActiveWindow <> nil) and (not Show) then
6567 begin
6568 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6569 if (not g_Game_IsNet) then
6570 g_Game_Pause(False);
6571 end;
6572 end;
6574 procedure g_Game_Pause (Enable: Boolean);
6575 var
6576 oldPause: Boolean;
6577 begin
6578 if not gGameOn then exit;
6580 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6582 oldPause := gPause;
6583 gPauseMain := Enable;
6585 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6586 end;
6588 procedure g_Game_HolmesPause (Enable: Boolean);
6589 var
6590 oldPause: Boolean;
6591 begin
6592 if not gGameOn then exit;
6593 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6595 oldPause := gPause;
6596 gPauseHolmes := Enable;
6598 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6599 end;
6601 procedure g_Game_PauseAllSounds(Enable: Boolean);
6602 var
6603 i: Integer;
6604 begin
6605 // Òðèããåðû:
6606 if gTriggers <> nil then
6607 for i := 0 to High(gTriggers) do
6608 with gTriggers[i] do
6609 if (TriggerType = TRIGGER_SOUND) and
6610 (Sound <> nil) and
6611 Sound.IsPlaying() then
6612 begin
6613 Sound.Pause(Enable);
6614 end;
6616 // Çâóêè èãðîêîâ:
6617 if gPlayers <> nil then
6618 for i := 0 to High(gPlayers) do
6619 if gPlayers[i] <> nil then
6620 gPlayers[i].PauseSounds(Enable);
6622 // Ìóçûêà:
6623 if gMusic <> nil then
6624 gMusic.Pause(Enable);
6625 end;
6627 procedure g_Game_StopAllSounds(all: Boolean);
6628 var
6629 i: Integer;
6630 begin
6631 if gTriggers <> nil then
6632 for i := 0 to High(gTriggers) do
6633 with gTriggers[i] do
6634 if (TriggerType = TRIGGER_SOUND) and
6635 (Sound <> nil) then
6636 Sound.Stop();
6638 if gMusic <> nil then
6639 gMusic.Stop();
6641 if all then
6642 e_StopChannels();
6643 end;
6645 procedure g_Game_UpdateTriggerSounds();
6646 var
6647 i: Integer;
6648 begin
6649 if gTriggers <> nil then
6650 for i := 0 to High(gTriggers) do
6651 with gTriggers[i] do
6652 if (TriggerType = TRIGGER_SOUND) and
6653 (Sound <> nil) and
6654 (tgcLocal) and
6655 Sound.IsPlaying() then
6656 begin
6657 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6658 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6659 begin
6660 Sound.SetPan(0.5 - tgcPan/255.0);
6661 Sound.SetVolume(tgcVolume/255.0);
6662 end
6663 else
6664 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
6665 end;
6666 end;
6668 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6669 begin
6670 Result := False;
6671 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6672 begin
6673 Result := True;
6674 Exit;
6675 end;
6676 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6677 begin
6678 Result := True;
6679 Exit;
6680 end;
6681 if gSpectMode <> SPECT_PLAYERS then
6682 Exit;
6683 if gSpectPID1 = UID then
6684 begin
6685 Result := True;
6686 Exit;
6687 end;
6688 if gSpectViewTwo and (gSpectPID2 = UID) then
6689 begin
6690 Result := True;
6691 Exit;
6692 end;
6693 end;
6695 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6696 var
6697 Pl: TPlayer;
6698 begin
6699 Result := False;
6700 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6701 begin
6702 Result := True;
6703 Exit;
6704 end;
6705 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6706 begin
6707 Result := True;
6708 Exit;
6709 end;
6710 if gSpectMode <> SPECT_PLAYERS then
6711 Exit;
6712 Pl := g_Player_Get(gSpectPID1);
6713 if (Pl <> nil) and (Pl.Team = Team) then
6714 begin
6715 Result := True;
6716 Exit;
6717 end;
6718 if gSpectViewTwo then
6719 begin
6720 Pl := g_Player_Get(gSpectPID2);
6721 if (Pl <> nil) and (Pl.Team = Team) then
6722 begin
6723 Result := True;
6724 Exit;
6725 end;
6726 end;
6727 end;
6729 procedure g_Game_Message(Msg: string; Time: Word);
6730 begin
6731 MessageText := b_Text_Format(Msg);
6732 MessageTime := Time;
6733 end;
6735 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6736 var
6737 a: Integer;
6738 begin
6739 case gAnnouncer of
6740 ANNOUNCE_NONE:
6741 Exit;
6742 ANNOUNCE_ME,
6743 ANNOUNCE_MEPLUS:
6744 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6745 Exit;
6746 end;
6747 for a := 0 to 3 do
6748 if goodsnd[a].IsPlaying() then
6749 Exit;
6751 goodsnd[Random(4)].Play();
6752 end;
6754 procedure g_Game_Announce_KillCombo(Param: Integer);
6755 var
6756 UID: Word;
6757 c, n: Byte;
6758 Pl: TPlayer;
6759 Name: String;
6760 begin
6761 UID := Param and $FFFF;
6762 c := Param shr 16;
6763 if c < 2 then
6764 Exit;
6766 Pl := g_Player_Get(UID);
6767 if Pl = nil then
6768 Name := '?'
6769 else
6770 Name := Pl.Name;
6772 case c of
6773 2: begin
6774 n := 0;
6775 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6776 end;
6777 3: begin
6778 n := 1;
6779 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6780 end;
6781 4: begin
6782 n := 2;
6783 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6784 end;
6785 else begin
6786 n := 3;
6787 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6788 end;
6789 end;
6791 case gAnnouncer of
6792 ANNOUNCE_NONE:
6793 Exit;
6794 ANNOUNCE_ME:
6795 if not g_Game_IsWatchedPlayer(UID) then
6796 Exit;
6797 ANNOUNCE_MEPLUS:
6798 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6799 Exit;
6800 end;
6802 if killsnd[n].IsPlaying() then
6803 killsnd[n].Stop();
6804 killsnd[n].Play();
6805 end;
6807 procedure g_Game_StartVote(Command, Initiator: string);
6808 var
6809 Need: Integer;
6810 begin
6811 if not gVotesEnabled then Exit;
6812 if gGameSettings.GameType <> GT_SERVER then Exit;
6813 if gVoteInProgress or gVotePassed then
6814 begin
6815 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6816 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6817 Exit;
6818 end;
6819 gVoteInProgress := True;
6820 gVotePassed := False;
6821 gVoteTimer := gTime + gVoteTimeout * 1000;
6822 gVoteCount := 0;
6823 gVoted := False;
6824 gVoteCommand := Command;
6826 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6827 Need := Floor((NetClientCount+1)/2.0)+1
6828 else
6829 Need := Floor(NetClientCount/2.0)+1;
6830 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6831 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6832 end;
6834 procedure g_Game_CheckVote;
6835 var
6836 I, Need: Integer;
6837 begin
6838 if gGameSettings.GameType <> GT_SERVER then Exit;
6839 if not gVoteInProgress then Exit;
6841 if (gTime >= gVoteTimer) then
6842 begin
6843 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6844 Need := Floor((NetClientCount+1)/2.0) + 1
6845 else
6846 Need := Floor(NetClientCount/2.0) + 1;
6847 if gVoteCount >= Need then
6848 begin
6849 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6850 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6851 gVotePassed := True;
6852 gVoteCmdTimer := gTime + 5000;
6853 end
6854 else
6855 begin
6856 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6857 MH_SEND_VoteEvent(NET_VE_FAILED);
6858 end;
6859 if NetClients <> nil then
6860 for I := Low(NetClients) to High(NetClients) do
6861 if NetClients[i].Used then
6862 NetClients[i].Voted := False;
6863 gVoteInProgress := False;
6864 gVoted := False;
6865 gVoteCount := 0;
6866 end
6867 else
6868 begin
6869 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6870 Need := Floor((NetClientCount+1)/2.0) + 1
6871 else
6872 Need := Floor(NetClientCount/2.0) + 1;
6873 if gVoteCount >= Need then
6874 begin
6875 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6876 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6877 gVoteInProgress := False;
6878 gVotePassed := True;
6879 gVoteCmdTimer := gTime + 5000;
6880 gVoted := False;
6881 gVoteCount := 0;
6882 if NetClients <> nil then
6883 for I := Low(NetClients) to High(NetClients) do
6884 if NetClients[i].Used then
6885 NetClients[i].Voted := False;
6886 end;
6887 end;
6888 end;
6890 procedure g_Game_LoadMapList(FileName: string);
6891 var
6892 ListFile: TextFile;
6893 s: string;
6894 begin
6895 MapList := nil;
6896 MapIndex := -1;
6898 if not FileExists(FileName) then Exit;
6900 AssignFile(ListFile, FileName);
6901 Reset(ListFile);
6902 while not EOF(ListFile) do
6903 begin
6904 ReadLn(ListFile, s);
6906 s := Trim(s);
6907 if s = '' then Continue;
6909 SetLength(MapList, Length(MapList)+1);
6910 MapList[High(MapList)] := s;
6911 end;
6912 CloseFile(ListFile);
6913 end;
6915 procedure g_Game_SetDebugMode();
6916 begin
6917 gDebugMode := True;
6918 // ×èòû (äàæå â ñâîåé èãðå):
6919 gCheats := True;
6920 end;
6922 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6923 var
6924 i: Word;
6925 begin
6926 if Length(LoadingStat.Msgs) = 0 then
6927 Exit;
6929 with LoadingStat do
6930 begin
6931 if not reWrite then
6932 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6933 if NextMsg = Length(Msgs) then
6934 begin // scroll
6935 for i := 0 to High(Msgs)-1 do
6936 Msgs[i] := Msgs[i+1];
6937 end
6938 else
6939 Inc(NextMsg);
6940 end else
6941 if NextMsg = 0 then
6942 Inc(NextMsg);
6944 Msgs[NextMsg-1] := Text;
6945 CurValue := 0;
6946 MaxValue := Max;
6947 ShowCount := 0;
6948 end;
6950 g_ActiveWindow := nil;
6952 ProcessLoading(true);
6953 end;
6955 procedure g_Game_StepLoading();
6956 begin
6957 with LoadingStat do
6958 begin
6959 Inc(CurValue);
6960 Inc(ShowCount);
6961 if (ShowCount > LOADING_SHOW_STEP) then
6962 begin
6963 ShowCount := 0;
6964 ProcessLoading();
6965 end;
6966 end;
6967 end;
6969 procedure g_Game_ClearLoading();
6970 var
6971 len: Word;
6972 begin
6973 with LoadingStat do
6974 begin
6975 CurValue := 0;
6976 MaxValue := 0;
6977 ShowCount := 0;
6978 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6979 if len < 1 then len := 1;
6980 SetLength(Msgs, len);
6981 for len := Low(Msgs) to High(Msgs) do
6982 Msgs[len] := '';
6983 NextMsg := 0;
6984 end;
6985 end;
6987 procedure Parse_Params(var pars: TParamStrValues);
6988 var
6989 i: Integer;
6990 s: String;
6991 begin
6992 SetLength(pars, 0);
6993 i := 1;
6994 while i <= ParamCount do
6995 begin
6996 s := ParamStr(i);
6997 if (s[1] = '-') and (Length(s) > 1) then
6998 begin
6999 if (s[2] = '-') and (Length(s) > 2) then
7000 begin // Îäèíî÷íûé ïàðàìåòð
7001 SetLength(pars, Length(pars) + 1);
7002 with pars[High(pars)] do
7003 begin
7004 Name := LowerCase(s);
7005 Value := '+';
7006 end;
7007 end
7008 else
7009 if (i < ParamCount) then
7010 begin // Ïàðàìåòð ñî çíà÷åíèåì
7011 Inc(i);
7012 SetLength(pars, Length(pars) + 1);
7013 with pars[High(pars)] do
7014 begin
7015 Name := LowerCase(s);
7016 Value := LowerCase(ParamStr(i));
7017 end;
7018 end;
7019 end;
7021 Inc(i);
7022 end;
7023 end;
7025 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
7026 var
7027 i: Integer;
7028 begin
7029 Result := '';
7030 for i := 0 to High(pars) do
7031 if pars[i].Name = aName then
7032 begin
7033 Result := pars[i].Value;
7034 Break;
7035 end;
7036 end;
7038 procedure g_Game_Process_Params();
7039 var
7040 pars: TParamStrValues;
7041 map: String;
7042 GMode, n: Byte;
7043 LimT, LimS: Integer;
7044 Opt: LongWord;
7045 Lives: Integer;
7046 s: String;
7047 Port: Integer;
7048 ip: String;
7049 F: TextFile;
7050 begin
7051 Parse_Params(pars);
7053 // Debug mode:
7054 s := Find_Param_Value(pars, '--debug');
7055 if (s <> '') then
7056 begin
7057 g_Game_SetDebugMode();
7058 s := Find_Param_Value(pars, '--netdump');
7059 if (s <> '') then
7060 NetDump := True;
7061 end;
7063 // Connect when game loads
7064 ip := Find_Param_Value(pars, '-connect');
7066 if ip <> '' then
7067 begin
7068 s := Find_Param_Value(pars, '-port');
7069 if (s = '') or not TryStrToInt(s, Port) then
7070 Port := 25666;
7072 s := Find_Param_Value(pars, '-pw');
7074 g_Game_StartClient(ip, Port, s);
7075 Exit;
7076 end;
7078 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7079 if (s <> '') then
7080 begin
7081 gDefaultMegawadStart := s;
7082 end;
7084 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7085 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7086 begin
7087 gDefaultMegawadStart := DF_Default_Megawad_Start;
7088 end;
7090 // Start map when game loads:
7091 map := LowerCase(Find_Param_Value(pars, '-map'));
7092 if isWadPath(map) then
7093 begin
7094 // Game mode:
7095 s := Find_Param_Value(pars, '-gm');
7096 GMode := g_Game_TextToMode(s);
7097 if GMode = GM_NONE then GMode := GM_DM;
7098 if GMode = GM_SINGLE then GMode := GM_COOP;
7100 // Time limit:
7101 s := Find_Param_Value(pars, '-limt');
7102 if (s = '') or (not TryStrToInt(s, LimT)) then
7103 LimT := 0;
7104 if LimT < 0 then
7105 LimT := 0;
7107 // Goal limit:
7108 s := Find_Param_Value(pars, '-lims');
7109 if (s = '') or (not TryStrToInt(s, LimS)) then
7110 LimS := 0;
7111 if LimS < 0 then
7112 LimS := 0;
7114 // Lives limit:
7115 s := Find_Param_Value(pars, '-lives');
7116 if (s = '') or (not TryStrToInt(s, Lives)) then
7117 Lives := 0;
7118 if Lives < 0 then
7119 Lives := 0;
7121 // Options:
7122 s := Find_Param_Value(pars, '-opt');
7123 if (s = '') then
7124 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7125 else
7126 Opt := StrToIntDef(s, 0);
7127 if Opt = 0 then
7128 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7130 // Close after map:
7131 s := Find_Param_Value(pars, '--close');
7132 if (s <> '') then
7133 gMapOnce := True;
7135 // Delete test map after play:
7136 s := Find_Param_Value(pars, '--testdelete');
7137 if (s <> '') then
7138 begin
7139 gMapToDelete := MapsDir + map;
7140 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
7141 Halt(1);
7142 end;
7144 // Delete temporary WAD after play:
7145 s := Find_Param_Value(pars, '--tempdelete');
7146 if (s <> '') then
7147 begin
7148 gMapToDelete := MapsDir + map;
7149 gTempDelete := True;
7150 end;
7152 // Number of players:
7153 s := Find_Param_Value(pars, '-pl');
7154 if (s = '') then
7155 n := 1
7156 else
7157 n := StrToIntDef(s, 1);
7159 // Start:
7160 s := Find_Param_Value(pars, '-port');
7161 if (s = '') or not TryStrToInt(s, Port) then
7162 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7163 else
7164 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7165 end;
7167 // Execute script when game loads:
7168 s := Find_Param_Value(pars, '-exec');
7169 if s <> '' then
7170 begin
7171 if not isWadPath(s) then
7172 s := GameDir + '/' + s;
7174 {$I-}
7175 AssignFile(F, s);
7176 Reset(F);
7177 if IOResult <> 0 then
7178 begin
7179 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7180 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7181 CloseFile(F);
7182 Exit;
7183 end;
7184 e_WriteLog('Executing script: ' + s, TMsgType.Notify);
7185 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7187 while not EOF(F) do
7188 begin
7189 ReadLn(F, s);
7190 if IOResult <> 0 then
7191 begin
7192 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7193 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7194 CloseFile(F);
7195 Exit;
7196 end;
7197 if Pos('#', s) <> 1 then // script comment
7198 g_Console_Process(s, True);
7199 end;
7201 CloseFile(F);
7202 {$I+}
7203 end;
7205 SetLength(pars, 0);
7206 end;
7208 begin
7209 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7210 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7211 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7212 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7214 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7215 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7216 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7217 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7219 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7220 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7222 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7223 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7225 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7227 conRegVar('dbg_scale', @g_dbg_scale, 0.01, 100.0, 'experimental deBUG scale mode', '', false);
7229 conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
7230 conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
7231 end.