DEADSOFTWARE

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