DEADSOFTWARE

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