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