DEADSOFTWARE

Now nanoGL supports glIsEnabled, glPushAttrib and glPopAttrib
[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 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
3011 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
3013 // setup OpenGL parameters
3014 glStencilMask($FFFFFFFF);
3015 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
3016 glEnable(GL_STENCIL_TEST);
3017 glEnable(GL_SCISSOR_TEST);
3018 glClear(GL_STENCIL_BUFFER_BIT);
3019 glStencilFunc(GL_EQUAL, 0, $ff);
3021 for lln := 0 to g_dynLightCount-1 do
3022 begin
3023 lx := g_dynLights[lln].x;
3024 ly := g_dynLights[lln].y;
3025 lrad := g_dynLights[lln].radius;
3026 if (lrad < 3) then continue;
3028 if (lx-sX+lrad < 0) then continue;
3029 if (ly-sY+lrad < 0) then continue;
3030 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
3031 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
3033 // set scissor to optimize drawing
3034 if (g_dbg_scale = 1.0) then
3035 begin
3036 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
3037 end
3038 else
3039 begin
3040 glScissor(0, 0, gWinSizeX, gWinSizeY);
3041 end;
3042 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3043 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
3044 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3045 // draw extruded panels
3046 glDisable(GL_TEXTURE_2D);
3047 glDisable(GL_BLEND);
3048 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
3049 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
3050 // render light texture
3051 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3052 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
3053 // blend it
3054 glEnable(GL_BLEND);
3055 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3056 glEnable(GL_TEXTURE_2D);
3057 // color and opacity
3058 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
3059 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
3060 glBegin(GL_QUADS);
3061 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
3062 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
3063 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
3064 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
3065 glEnd();
3066 end;
3068 // done
3069 glDisable(GL_STENCIL_TEST);
3070 glDisable(GL_BLEND);
3071 glDisable(GL_SCISSOR_TEST);
3072 //glScissor(0, 0, sWidth, sHeight);
3074 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
3075 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
3076 end;
3079 function fixViewportForScale (): Boolean;
3080 var
3081 nx0, ny0, nw, nh: Integer;
3082 begin
3083 result := false;
3084 if (g_dbg_scale <> 1.0) then
3085 begin
3086 result := true;
3087 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
3088 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
3089 nw := round(sWidth/g_dbg_scale);
3090 nh := round(sHeight/g_dbg_scale);
3091 sX := nx0;
3092 sY := ny0;
3093 sWidth := nw;
3094 sHeight := nh;
3095 end;
3096 end;
3099 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3100 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3101 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
3102 type
3103 TDrawCB = procedure ();
3105 var
3106 hasAmbient: Boolean;
3107 ambColor: TDFColor;
3108 doAmbient: Boolean = false;
3110 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
3111 var
3112 tagmask: Integer;
3113 pan: TPanel;
3114 begin
3115 profileFrameDraw.sectionBegin(profname);
3116 if gdbg_map_use_accel_render then
3117 begin
3118 tagmask := panelTypeToTag(panType);
3119 while (gDrawPanelList.count > 0) do
3120 begin
3121 pan := TPanel(gDrawPanelList.front());
3122 if ((pan.tag and tagmask) = 0) then break;
3123 if doDraw then pan.Draw(doAmbient, ambColor);
3124 gDrawPanelList.popFront();
3125 end;
3126 end
3127 else
3128 begin
3129 if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor);
3130 end;
3131 profileFrameDraw.sectionEnd();
3132 end;
3134 procedure drawOther (profname: AnsiString; cb: TDrawCB);
3135 begin
3136 profileFrameDraw.sectionBegin(profname);
3137 if assigned(cb) then cb();
3138 profileFrameDraw.sectionEnd();
3139 end;
3141 begin
3142 profileFrameDraw.sectionBegin('total');
3144 // our accelerated renderer will collect all panels to gDrawPanelList
3145 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3146 profileFrameDraw.sectionBegin('collect');
3147 if gdbg_map_use_accel_render then
3148 begin
3149 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
3150 end;
3151 profileFrameDraw.sectionEnd();
3153 profileFrameDraw.sectionBegin('skyback');
3154 g_Map_DrawBack(backXOfs, backYOfs);
3155 profileFrameDraw.sectionEnd();
3157 if setTransMatrix then
3158 begin
3159 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3160 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
3161 glTranslatef(-sX, -sY, 0);
3162 end;
3164 // rendering mode
3165 ambColor := gCurrentMap['light_ambient'].rgba;
3166 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3169 if hasAmbient then
3170 begin
3171 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3172 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3173 glClear(GL_COLOR_BUFFER_BIT);
3174 end;
3176 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3179 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
3180 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
3181 drawOther('items', @g_Items_Draw);
3182 drawOther('weapons', @g_Weapon_Draw);
3183 drawOther('shells', @g_Player_DrawShells);
3184 drawOther('drawall', @g_Player_DrawAll);
3185 drawOther('corpses', @g_Player_DrawCorpses);
3186 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
3187 drawOther('monsters', @g_Monsters_Draw);
3188 drawOther('itemdrop', @g_Items_DrawDrop);
3189 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
3190 drawOther('gfx', @g_GFX_Draw);
3191 drawOther('flags', @g_Map_DrawFlags);
3192 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
3193 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
3194 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
3195 drawOther('dynlights', @renderDynLightsInternal);
3197 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3198 begin
3199 renderAmbientQuad(hasAmbient, ambColor);
3200 end;
3202 doAmbient := true;
3203 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
3206 if g_debug_HealthBar then
3207 begin
3208 g_Monsters_DrawHealth();
3209 g_Player_DrawHealth();
3210 end;
3212 profileFrameDraw.mainEnd(); // map rendering
3213 end;
3216 procedure DrawMapView(x, y, w, h: Integer);
3218 var
3219 bx, by: Integer;
3220 begin
3221 glPushMatrix();
3223 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
3224 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
3226 sX := x;
3227 sY := y;
3228 sWidth := w;
3229 sHeight := h;
3231 fixViewportForScale();
3232 renderMapInternal(-bx, -by, true);
3234 glPopMatrix();
3235 end;
3238 procedure DrawPlayer(p: TPlayer);
3239 var
3240 px, py, a, b, c, d: Integer;
3241 //R: TRect;
3242 begin
3243 if (p = nil) or (p.FDummy) then
3244 begin
3245 glPushMatrix();
3246 g_Map_DrawBack(0, 0);
3247 glPopMatrix();
3248 Exit;
3249 end;
3251 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3252 profileFrameDraw.mainBegin(g_profile_frame_draw);
3254 gPlayerDrawn := p;
3256 glPushMatrix();
3258 px := p.GameX + PLAYER_RECT_CX;
3259 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
3261 if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then
3262 begin
3263 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3264 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3266 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3267 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3269 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3270 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3271 begin
3272 // hcenter
3273 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3274 end;
3276 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3277 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3278 begin
3279 // vcenter
3280 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3281 end;
3282 end
3283 else
3284 begin
3285 // scaled, ignore level bounds
3286 a := -px+(gPlayerScreenSize.X div 2);
3287 b := -py+(gPlayerScreenSize.Y div 2);
3288 end;
3290 if p.IncCam <> 0 then
3291 begin
3292 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
3293 begin
3294 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
3295 begin
3296 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
3297 end;
3298 end;
3300 if py < gPlayerScreenSize.Y div 2 then
3301 begin
3302 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
3303 begin
3304 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
3305 end;
3306 end;
3308 if p.IncCam < 0 then
3309 begin
3310 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
3311 end;
3313 if p.IncCam > 0 then
3314 begin
3315 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
3316 end;
3317 end;
3319 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
3320 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
3321 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
3323 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
3324 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
3325 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
3327 sX := -a;
3328 sY := -(b+p.IncCam);
3329 sWidth := gPlayerScreenSize.X;
3330 sHeight := gPlayerScreenSize.Y;
3332 //glTranslatef(a, b+p.IncCam, 0);
3334 //if (p = gPlayer1) and (g_dbg_scale >= 1.0) then g_Holmes_plrViewSize(sWidth, sHeight);
3336 //conwritefln('OLD: (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3337 fixViewportForScale();
3338 //conwritefln(' (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3339 if (g_dbg_scale <> 1.0) and (not g_dbg_ignore_bounds) then
3340 begin
3341 if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth;
3342 if (sY+sHeight > gMapInfo.Height) then sY := gMapInfo.Height-sHeight;
3343 if (sX < 0) then sX := 0;
3344 if (sY < 0) then sY := 0;
3346 if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth));
3347 if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight));
3348 end;
3349 p.viewPortX := sX;
3350 p.viewPortY := sY;
3351 p.viewPortW := sWidth;
3352 p.viewPortH := sHeight;
3354 if (p = gPlayer1) then
3355 begin
3356 g_Holmes_plrViewPos(sX, sY);
3357 g_Holmes_plrViewSize(sWidth, sHeight);
3358 end;
3360 renderMapInternal(-c, -d, true);
3362 if p.FSpectator then
3363 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3364 p.GameY + PLAYER_RECT_CY - 4,
3365 'X', gStdFont, 255, 255, 255, 1, True);
3367 for a := 0 to High(gCollideMap) do
3368 for b := 0 to High(gCollideMap[a]) do
3369 begin
3370 d := 0;
3371 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3372 d := d + 1;
3373 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3374 d := d + 2;
3376 case d of
3377 1: e_DrawPoint(1, b, a, 200, 200, 200);
3378 2: e_DrawPoint(1, b, a, 64, 64, 255);
3379 3: e_DrawPoint(1, b, a, 255, 0, 255);
3380 end;
3381 end;
3384 glPopMatrix();
3386 p.DrawPain();
3387 p.DrawPickup();
3388 p.DrawRulez();
3389 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3390 if g_Debug_Player then
3391 g_Player_DrawDebug(p);
3392 p.DrawGUI();
3393 end;
3395 procedure drawProfilers ();
3396 var
3397 px: Integer = -1;
3398 py: Integer = -1;
3399 begin
3400 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
3401 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3402 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3403 end;
3405 procedure g_Game_Draw();
3406 var
3407 ID: DWORD;
3408 w, h: Word;
3409 ww, hh: Byte;
3410 Time: Int64;
3411 back: string;
3412 plView1, plView2: TPlayer;
3413 Split: Boolean;
3414 begin
3415 if gExit = EXIT_QUIT then Exit;
3417 Time := GetTimer() {div 1000};
3418 FPSCounter := FPSCounter+1;
3419 if Time - FPSTime >= 1000 then
3420 begin
3421 FPS := FPSCounter;
3422 FPSCounter := 0;
3423 FPSTime := Time;
3424 end;
3426 if gGameOn or (gState = STATE_FOLD) then
3427 begin
3428 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3429 begin
3430 gSpectMode := SPECT_NONE;
3431 if not gRevertPlayers then
3432 begin
3433 plView1 := gPlayer1;
3434 plView2 := gPlayer2;
3435 end
3436 else
3437 begin
3438 plView1 := gPlayer2;
3439 plView2 := gPlayer1;
3440 end;
3441 end
3442 else
3443 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3444 begin
3445 gSpectMode := SPECT_NONE;
3446 if gPlayer2 = nil then
3447 plView1 := gPlayer1
3448 else
3449 plView1 := gPlayer2;
3450 plView2 := nil;
3451 end
3452 else
3453 begin
3454 plView1 := nil;
3455 plView2 := nil;
3456 end;
3458 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3459 gSpectMode := SPECT_STATS;
3461 if gSpectMode = SPECT_PLAYERS then
3462 if gPlayers <> nil then
3463 begin
3464 plView1 := GetActivePlayer_ByID(gSpectPID1);
3465 if plView1 = nil then
3466 begin
3467 gSpectPID1 := GetActivePlayerID_Next();
3468 plView1 := GetActivePlayer_ByID(gSpectPID1);
3469 end;
3470 if gSpectViewTwo then
3471 begin
3472 plView2 := GetActivePlayer_ByID(gSpectPID2);
3473 if plView2 = nil then
3474 begin
3475 gSpectPID2 := GetActivePlayerID_Next();
3476 plView2 := GetActivePlayer_ByID(gSpectPID2);
3477 end;
3478 end;
3479 end;
3481 if gSpectMode = SPECT_MAPVIEW then
3482 begin
3483 // Ðåæèì ïðîñìîòðà êàðòû
3484 Split := False;
3485 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3486 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3487 gHearPoint1.Active := True;
3488 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3489 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3490 gHearPoint2.Active := False;
3491 end
3492 else
3493 begin
3494 Split := (plView1 <> nil) and (plView2 <> nil);
3496 // Òî÷êè ñëóõà èãðîêîâ
3497 if plView1 <> nil then
3498 begin
3499 gHearPoint1.Active := True;
3500 gHearPoint1.Coords.X := plView1.GameX;
3501 gHearPoint1.Coords.Y := plView1.GameY;
3502 end else
3503 gHearPoint1.Active := False;
3504 if plView2 <> nil then
3505 begin
3506 gHearPoint2.Active := True;
3507 gHearPoint2.Coords.X := plView2.GameX;
3508 gHearPoint2.Coords.Y := plView2.GameY;
3509 end else
3510 gHearPoint2.Active := False;
3512 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3513 gPlayerScreenSize.X := gScreenWidth-196;
3514 if Split then
3515 begin
3516 gPlayerScreenSize.Y := gScreenHeight div 2;
3517 if gScreenHeight mod 2 = 0 then
3518 Dec(gPlayerScreenSize.Y);
3519 end
3520 else
3521 gPlayerScreenSize.Y := gScreenHeight;
3523 if Split then
3524 if gScreenHeight mod 2 = 0 then
3525 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3526 else
3527 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3529 DrawPlayer(plView1);
3530 gPlayer1ScreenCoord.X := sX;
3531 gPlayer1ScreenCoord.Y := sY;
3533 if Split then
3534 begin
3535 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3537 DrawPlayer(plView2);
3538 gPlayer2ScreenCoord.X := sX;
3539 gPlayer2ScreenCoord.Y := sY;
3540 end;
3542 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3544 if Split then
3545 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3546 end;
3548 // draw inspector
3549 if (g_holmes_enabled) then g_Holmes_Draw();
3551 if MessageText <> '' then
3552 begin
3553 w := 0;
3554 h := 0;
3555 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3556 if Split then
3557 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3558 (gScreenHeight div 2)-(h div 2), MessageText)
3559 else
3560 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3561 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3562 end;
3564 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3566 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3567 begin
3568 // Draw spectator GUI
3569 ww := 0;
3570 hh := 0;
3571 e_TextureFontGetSize(gStdFont, ww, hh);
3572 case gSpectMode of
3573 SPECT_STATS:
3574 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3575 SPECT_MAPVIEW:
3576 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3577 SPECT_PLAYERS:
3578 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3579 end;
3580 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3581 if gSpectMode = SPECT_MAPVIEW then
3582 begin
3583 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3584 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3585 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3586 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3587 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3588 end;
3589 if gSpectMode = SPECT_PLAYERS then
3590 begin
3591 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3592 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3593 if gSpectViewTwo then
3594 begin
3595 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3596 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3597 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3598 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3599 end
3600 else
3601 begin
3602 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3603 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3604 end;
3605 end;
3606 end;
3607 end;
3609 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
3610 begin
3611 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3612 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3614 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3615 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3616 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3617 end;
3619 if not gGameOn then
3620 begin
3621 if (gState = STATE_MENU) then
3622 begin
3623 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3624 begin
3625 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3626 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3627 end;
3628 // F3 at menu will show game loading dialog
3629 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3630 if (g_ActiveWindow <> nil) then
3631 begin
3632 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3633 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3634 end
3635 else
3636 begin
3637 // F3 at titlepic will show game loading dialog
3638 if e_KeyPressed(IK_F3) then
3639 begin
3640 g_Menu_Show_LoadMenu(true);
3641 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3642 end;
3643 end;
3644 end;
3646 if gState = STATE_FOLD then
3647 begin
3648 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3649 end;
3651 if gState = STATE_INTERCUSTOM then
3652 begin
3653 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3654 begin
3655 back := 'TEXTURE_endpic';
3656 if not g_Texture_Get(back, ID) then
3657 back := _lc[I_TEXTURE_ENDPIC];
3658 end
3659 else
3660 back := 'INTER';
3662 if g_Texture_Get(back, ID) then
3663 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3664 else
3665 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3667 DrawCustomStat();
3669 if g_ActiveWindow <> nil then
3670 begin
3671 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3672 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3673 end;
3674 end;
3676 if gState = STATE_INTERSINGLE then
3677 begin
3678 if EndingGameCounter > 0 then
3679 begin
3680 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3681 end
3682 else
3683 begin
3684 back := 'INTER';
3686 if g_Texture_Get(back, ID) then
3687 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3688 else
3689 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3691 DrawSingleStat();
3693 if g_ActiveWindow <> nil then
3694 begin
3695 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3696 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3697 end;
3698 end;
3699 end;
3701 if gState = STATE_ENDPIC then
3702 begin
3703 ID := DWORD(-1);
3704 if not g_Texture_Get('TEXTURE_endpic', ID) then
3705 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3707 if ID <> DWORD(-1) then
3708 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3709 else
3710 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3712 if g_ActiveWindow <> nil then
3713 begin
3714 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3715 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3716 end;
3717 end;
3719 if gState = STATE_SLIST then
3720 begin
3721 if g_Texture_Get('MENU_BACKGROUND', ID) then
3722 begin
3723 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3724 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3725 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3726 end;
3727 g_Serverlist_Draw(slCurrent);
3728 end;
3729 end;
3731 if g_ActiveWindow <> nil then
3732 begin
3733 if gGameOn then
3734 begin
3735 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3736 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3737 end;
3738 g_ActiveWindow.Draw();
3739 end;
3741 g_Console_Draw();
3743 if g_debug_Sounds and gGameOn then
3744 begin
3745 for w := 0 to High(e_SoundsArray) do
3746 for h := 0 to e_SoundsArray[w].nRefs do
3747 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3748 end;
3750 if gShowFPS then
3751 begin
3752 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3753 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3754 end;
3756 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3757 drawTime(gScreenWidth-72, gScreenHeight-16);
3759 if gGameOn then drawProfilers();
3761 g_Holmes_DrawUI();
3762 end;
3764 procedure g_Game_Quit();
3765 begin
3766 g_Game_StopAllSounds(True);
3767 gMusic.Free();
3768 g_Game_SaveOptions();
3769 g_Game_FreeData();
3770 g_PlayerModel_FreeData();
3771 g_Texture_DeleteAll();
3772 g_Frames_DeleteAll();
3773 //g_Menu_Free(); //k8: this segfaults after resolution change; who cares?
3775 if NetInitDone then g_Net_Free;
3777 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3778 if gMapToDelete <> '' then
3779 g_Game_DeleteTestMap();
3781 gExit := EXIT_QUIT;
3782 PushExitEvent();
3783 end;
3785 procedure g_FatalError(Text: String);
3786 begin
3787 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3788 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning);
3790 gExit := EXIT_SIMPLE;
3791 end;
3793 procedure g_SimpleError(Text: String);
3794 begin
3795 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3796 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
3797 end;
3799 procedure g_Game_SetupScreenSize();
3800 const
3801 RES_FACTOR = 4.0 / 3.0;
3802 var
3803 s: Single;
3804 rf: Single;
3805 bw, bh: Word;
3806 begin
3807 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3808 gPlayerScreenSize.X := gScreenWidth-196;
3809 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3810 gPlayerScreenSize.Y := gScreenHeight div 2
3811 else
3812 gPlayerScreenSize.Y := gScreenHeight;
3814 // Ðàçìåð çàäíåãî ïëàíà:
3815 if BackID <> DWORD(-1) then
3816 begin
3817 s := SKY_STRETCH;
3818 if (gScreenWidth*s > gMapInfo.Width) or
3819 (gScreenHeight*s > gMapInfo.Height) then
3820 begin
3821 gBackSize.X := gScreenWidth;
3822 gBackSize.Y := gScreenHeight;
3823 end
3824 else
3825 begin
3826 e_GetTextureSize(BackID, @bw, @bh);
3827 rf := Single(bw) / Single(bh);
3828 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3829 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3830 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3831 if (s < 1.0) then s := 1.0;
3832 gBackSize.X := Round(bw*s);
3833 gBackSize.Y := Round(bh*s);
3834 end;
3835 end;
3836 end;
3838 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3839 begin
3840 g_Window_SetSize(newWidth, newHeight, nowFull);
3841 end;
3843 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3844 begin
3845 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3846 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3847 Exit;
3848 if gPlayer1 = nil then
3849 begin
3850 if g_Game_IsClient then
3851 begin
3852 if NetPlrUID1 > -1 then
3853 begin
3854 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3855 gPlayer1 := g_Player_Get(NetPlrUID1);
3856 end;
3857 Exit;
3858 end;
3860 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3861 Team := gPlayer1Settings.Team;
3863 // Ñîçäàíèå ïåðâîãî èãðîêà:
3864 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3865 gPlayer1Settings.Color,
3866 Team, False));
3867 if gPlayer1 = nil then
3868 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3869 else
3870 begin
3871 gPlayer1.Name := gPlayer1Settings.Name;
3872 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3873 if g_Game_IsServer and g_Game_IsNet then
3874 MH_SEND_PlayerCreate(gPlayer1.UID);
3875 gPlayer1.Respawn(False, True);
3877 if g_Game_IsNet and NetUseMaster then
3878 g_Net_Slist_Update;
3879 end;
3881 Exit;
3882 end;
3883 if gPlayer2 = nil then
3884 begin
3885 if g_Game_IsClient then
3886 begin
3887 if NetPlrUID2 > -1 then
3888 gPlayer2 := g_Player_Get(NetPlrUID2);
3889 Exit;
3890 end;
3892 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3893 Team := gPlayer2Settings.Team;
3895 // Ñîçäàíèå âòîðîãî èãðîêà:
3896 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3897 gPlayer2Settings.Color,
3898 Team, False));
3899 if gPlayer2 = nil then
3900 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3901 else
3902 begin
3903 gPlayer2.Name := gPlayer2Settings.Name;
3904 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3905 if g_Game_IsServer and g_Game_IsNet then
3906 MH_SEND_PlayerCreate(gPlayer2.UID);
3907 gPlayer2.Respawn(False, True);
3909 if g_Game_IsNet and NetUseMaster then
3910 g_Net_Slist_Update;
3911 end;
3913 Exit;
3914 end;
3915 end;
3917 procedure g_Game_RemovePlayer();
3918 var
3919 Pl: TPlayer;
3920 begin
3921 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3922 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3923 Exit;
3924 Pl := gPlayer2;
3925 if Pl <> nil then
3926 begin
3927 if g_Game_IsServer then
3928 begin
3929 Pl.Lives := 0;
3930 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3931 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3932 g_Player_Remove(Pl.UID);
3934 if g_Game_IsNet and NetUseMaster then
3935 g_Net_Slist_Update;
3936 end else
3937 gPlayer2 := nil;
3938 Exit;
3939 end;
3940 Pl := gPlayer1;
3941 if Pl <> nil then
3942 begin
3943 if g_Game_IsServer then
3944 begin
3945 Pl.Lives := 0;
3946 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3947 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3948 g_Player_Remove(Pl.UID);
3950 if g_Game_IsNet and NetUseMaster then
3951 g_Net_Slist_Update;
3952 end else
3953 begin
3954 gPlayer1 := nil;
3955 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3956 end;
3957 Exit;
3958 end;
3959 end;
3961 procedure g_Game_Spectate();
3962 begin
3963 g_Game_RemovePlayer();
3964 if gPlayer1 <> nil then
3965 g_Game_RemovePlayer();
3966 end;
3968 procedure g_Game_SpectateCenterView();
3969 begin
3970 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3971 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3972 end;
3974 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3975 var
3976 i, nPl: Integer;
3977 tmps: AnsiString;
3978 begin
3979 g_Game_Free();
3981 e_WriteLog('Starting singleplayer game...', TMsgType.Notify);
3983 g_Game_ClearLoading();
3985 // Íàñòðîéêè èãðû:
3986 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3987 gAimLine := False;
3988 gShowMap := False;
3989 gGameSettings.GameType := GT_SINGLE;
3990 gGameSettings.MaxLives := 0;
3991 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3992 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3993 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3994 gSwitchGameMode := GM_SINGLE;
3996 g_Game_ExecuteEvent('ongamestart');
3998 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3999 g_Game_SetupScreenSize();
4001 // Ñîçäàíèå ïåðâîãî èãðîêà:
4002 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4003 gPlayer1Settings.Color,
4004 gPlayer1Settings.Team, False));
4005 if gPlayer1 = nil then
4006 begin
4007 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4008 Exit;
4009 end;
4011 gPlayer1.Name := gPlayer1Settings.Name;
4012 nPl := 1;
4014 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
4015 if TwoPlayers then
4016 begin
4017 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4018 gPlayer2Settings.Color,
4019 gPlayer2Settings.Team, False));
4020 if gPlayer2 = nil then
4021 begin
4022 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4023 Exit;
4024 end;
4026 gPlayer2.Name := gPlayer2Settings.Name;
4027 Inc(nPl);
4028 end;
4030 // Çàãðóçêà è çàïóñê êàðòû:
4031 if not g_Game_StartMap(MAP, True) then
4032 begin
4033 if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP;
4034 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps]));
4035 Exit;
4036 end;
4038 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4039 g_Player_Init();
4041 // Ñîçäàåì áîòîâ:
4042 for i := nPl+1 to nPlayers do
4043 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4044 end;
4046 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
4047 TimeLimit, GoalLimit: Word;
4048 MaxLives: Byte;
4049 Options: LongWord; nPlayers: Byte);
4050 var
4051 i, nPl: Integer;
4052 begin
4053 g_Game_Free();
4055 e_WriteLog('Starting custom game...', TMsgType.Notify);
4057 g_Game_ClearLoading();
4059 // Íàñòðîéêè èãðû:
4060 gGameSettings.GameType := GT_CUSTOM;
4061 gGameSettings.GameMode := GameMode;
4062 gSwitchGameMode := GameMode;
4063 gGameSettings.TimeLimit := TimeLimit;
4064 gGameSettings.GoalLimit := GoalLimit;
4065 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4066 gGameSettings.Options := Options;
4068 gCoopTotalMonstersKilled := 0;
4069 gCoopTotalSecretsFound := 0;
4070 gCoopTotalMonsters := 0;
4071 gCoopTotalSecrets := 0;
4072 gAimLine := False;
4073 gShowMap := False;
4075 g_Game_ExecuteEvent('ongamestart');
4077 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4078 g_Game_SetupScreenSize();
4080 // Ðåæèì íàáëþäàòåëÿ:
4081 if nPlayers = 0 then
4082 begin
4083 gPlayer1 := nil;
4084 gPlayer2 := nil;
4085 end;
4087 nPl := 0;
4088 if nPlayers >= 1 then
4089 begin
4090 // Ñîçäàíèå ïåðâîãî èãðîêà:
4091 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4092 gPlayer1Settings.Color,
4093 gPlayer1Settings.Team, False));
4094 if gPlayer1 = nil then
4095 begin
4096 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4097 Exit;
4098 end;
4100 gPlayer1.Name := gPlayer1Settings.Name;
4101 Inc(nPl);
4102 end;
4104 if nPlayers >= 2 then
4105 begin
4106 // Ñîçäàíèå âòîðîãî èãðîêà:
4107 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4108 gPlayer2Settings.Color,
4109 gPlayer2Settings.Team, False));
4110 if gPlayer2 = nil then
4111 begin
4112 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4113 Exit;
4114 end;
4116 gPlayer2.Name := gPlayer2Settings.Name;
4117 Inc(nPl);
4118 end;
4120 // Çàãðóçêà è çàïóñê êàðòû:
4121 if not g_Game_StartMap(Map, True) then
4122 begin
4123 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4124 Exit;
4125 end;
4127 // Íåò òî÷åê ïîÿâëåíèÿ:
4128 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4129 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4130 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4131 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4132 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4133 begin
4134 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4135 Exit;
4136 end;
4138 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4139 g_Player_Init();
4141 // Ñîçäàåì áîòîâ:
4142 for i := nPl+1 to nPlayers do
4143 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4144 end;
4146 procedure g_Game_StartServer(Map: String; GameMode: Byte;
4147 TimeLimit, GoalLimit: Word; MaxLives: Byte;
4148 Options: LongWord; nPlayers: Byte;
4149 IPAddr: LongWord; Port: Word);
4150 begin
4151 g_Game_Free();
4153 e_WriteLog('Starting net game (server)...', TMsgType.Notify);
4155 g_Game_ClearLoading();
4157 // Íàñòðîéêè èãðû:
4158 gGameSettings.GameType := GT_SERVER;
4159 gGameSettings.GameMode := GameMode;
4160 gSwitchGameMode := GameMode;
4161 gGameSettings.TimeLimit := TimeLimit;
4162 gGameSettings.GoalLimit := GoalLimit;
4163 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4164 gGameSettings.Options := Options;
4166 gCoopTotalMonstersKilled := 0;
4167 gCoopTotalSecretsFound := 0;
4168 gCoopTotalMonsters := 0;
4169 gCoopTotalSecrets := 0;
4170 gAimLine := False;
4171 gShowMap := False;
4173 g_Game_ExecuteEvent('ongamestart');
4175 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
4176 g_Game_SetupScreenSize();
4178 // Ðåæèì íàáëþäàòåëÿ:
4179 if nPlayers = 0 then
4180 begin
4181 gPlayer1 := nil;
4182 gPlayer2 := nil;
4183 end;
4185 if nPlayers >= 1 then
4186 begin
4187 // Ñîçäàíèå ïåðâîãî èãðîêà:
4188 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4189 gPlayer1Settings.Color,
4190 gPlayer1Settings.Team, False));
4191 if gPlayer1 = nil then
4192 begin
4193 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4194 Exit;
4195 end;
4197 gPlayer1.Name := gPlayer1Settings.Name;
4198 end;
4200 if nPlayers >= 2 then
4201 begin
4202 // Ñîçäàíèå âòîðîãî èãðîêà:
4203 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4204 gPlayer2Settings.Color,
4205 gPlayer2Settings.Team, False));
4206 if gPlayer2 = nil then
4207 begin
4208 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4209 Exit;
4210 end;
4212 gPlayer2.Name := gPlayer2Settings.Name;
4213 end;
4215 g_Game_SetLoadingText(_lc[I_LOAD_HOST], 0, False);
4216 if NetForwardPorts then
4217 g_Game_SetLoadingText(_lc[I_LOAD_PORTS], 0, False);
4219 // Ñòàðòóåì ñåðâåð
4220 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
4221 begin
4222 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
4223 Exit;
4224 end;
4226 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
4228 // Çàãðóçêà è çàïóñê êàðòû:
4229 if not g_Game_StartMap(Map, True) then
4230 begin
4231 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4232 Exit;
4233 end;
4235 // Íåò òî÷åê ïîÿâëåíèÿ:
4236 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4237 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4238 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4239 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4240 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4241 begin
4242 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4243 Exit;
4244 end;
4246 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4247 g_Player_Init();
4249 NetState := NET_STATE_GAME;
4250 end;
4252 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
4253 var
4254 Map: String;
4255 WadName: string;
4256 Ptr: Pointer;
4257 T: Cardinal;
4258 MID: Byte;
4259 State: Byte;
4260 OuterLoop: Boolean;
4261 newResPath: string;
4262 InMsg: TMsg;
4263 begin
4264 g_Game_Free();
4266 State := 0;
4267 e_WriteLog('Starting net game (client)...', TMsgType.Notify);
4268 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', TMsgType.Notify);
4270 g_Game_ClearLoading();
4272 // Íàñòðîéêè èãðû:
4273 gGameSettings.GameType := GT_CLIENT;
4275 gCoopTotalMonstersKilled := 0;
4276 gCoopTotalSecretsFound := 0;
4277 gCoopTotalMonsters := 0;
4278 gCoopTotalSecrets := 0;
4279 gAimLine := False;
4280 gShowMap := False;
4282 g_Game_ExecuteEvent('ongamestart');
4284 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4285 g_Game_SetupScreenSize();
4287 NetState := NET_STATE_AUTH;
4289 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4290 // Ñòàðòóåì êëèåíò
4291 if not g_Net_Connect(Addr, Port) then
4292 begin
4293 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4294 NetState := NET_STATE_NONE;
4295 Exit;
4296 end;
4298 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4299 MC_SEND_Info(PW);
4300 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4302 OuterLoop := True;
4303 while OuterLoop do
4304 begin
4305 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4306 begin
4307 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4308 begin
4309 Ptr := NetEvent.packet^.data;
4310 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4311 continue;
4313 MID := InMsg.ReadByte();
4315 if (MID = NET_MSG_INFO) and (State = 0) then
4316 begin
4317 NetMyID := InMsg.ReadByte();
4318 NetPlrUID1 := InMsg.ReadWord();
4320 WadName := InMsg.ReadString();
4321 Map := InMsg.ReadString();
4323 gWADHash := InMsg.ReadMD5();
4325 gGameSettings.GameMode := InMsg.ReadByte();
4326 gSwitchGameMode := gGameSettings.GameMode;
4327 gGameSettings.GoalLimit := InMsg.ReadWord();
4328 gGameSettings.TimeLimit := InMsg.ReadWord();
4329 gGameSettings.MaxLives := InMsg.ReadByte();
4330 gGameSettings.Options := InMsg.ReadLongWord();
4331 T := InMsg.ReadLongWord();
4333 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4334 if newResPath = '' then
4335 begin
4336 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4337 newResPath := g_Res_DownloadWAD(WadName);
4338 if newResPath = '' then
4339 begin
4340 g_FatalError(_lc[I_NET_ERR_HASH]);
4341 enet_packet_destroy(NetEvent.packet);
4342 NetState := NET_STATE_NONE;
4343 Exit;
4344 end;
4345 end;
4346 newResPath := ExtractRelativePath(MapsDir, newResPath);
4348 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4349 gPlayer1Settings.Color,
4350 gPlayer1Settings.Team, False));
4352 if gPlayer1 = nil then
4353 begin
4354 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4356 enet_packet_destroy(NetEvent.packet);
4357 NetState := NET_STATE_NONE;
4358 Exit;
4359 end;
4361 gPlayer1.Name := gPlayer1Settings.Name;
4362 gPlayer1.UID := NetPlrUID1;
4363 gPlayer1.Reset(True);
4365 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4366 begin
4367 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4369 enet_packet_destroy(NetEvent.packet);
4370 NetState := NET_STATE_NONE;
4371 Exit;
4372 end;
4374 gTime := T;
4376 State := 1;
4377 OuterLoop := False;
4378 enet_packet_destroy(NetEvent.packet);
4379 break;
4380 end
4381 else
4382 enet_packet_destroy(NetEvent.packet);
4383 end
4384 else
4385 begin
4386 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4387 begin
4388 State := 0;
4389 if (NetEvent.data <= NET_DISC_MAX) then
4390 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4391 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4392 OuterLoop := False;
4393 Break;
4394 end;
4395 end;
4396 end;
4398 ProcessLoading(true);
4400 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
4401 begin
4402 State := 0;
4403 break;
4404 end;
4405 end;
4407 if State <> 1 then
4408 begin
4409 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4410 NetState := NET_STATE_NONE;
4411 Exit;
4412 end;
4414 gLMSRespawn := LMS_RESPAWN_NONE;
4415 gLMSRespawnTime := 0;
4417 g_Player_Init();
4418 NetState := NET_STATE_GAME;
4419 MC_SEND_FullStateRequest;
4420 e_WriteLog('NET: Connection successful.', TMsgType.Notify);
4421 end;
4423 procedure g_Game_SaveOptions();
4424 begin
4425 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4426 end;
4428 procedure g_Game_ChangeMap(const MapPath: String);
4429 var
4430 Force: Boolean;
4431 begin
4432 g_Game_ClearLoading();
4434 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4435 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4436 if gExitByTrigger then
4437 begin
4438 Force := False;
4439 gExitByTrigger := False;
4440 end;
4441 if not g_Game_StartMap(MapPath, Force) then
4442 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4443 end;
4445 procedure g_Game_Restart();
4446 var
4447 Map: string;
4448 begin
4449 if g_Game_IsClient then
4450 Exit;
4451 map := g_ExtractFileName(gMapInfo.Map);
4453 MessageTime := 0;
4454 gGameOn := False;
4455 g_Game_ClearLoading();
4456 g_Game_StartMap(Map, True, gCurrentMapFileName);
4457 end;
4459 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4460 var
4461 NewWAD, ResName: String;
4462 I: Integer;
4463 begin
4464 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4465 g_Player_RemoveAllCorpses();
4467 if (not g_Game_IsClient) and
4468 (gSwitchGameMode <> gGameSettings.GameMode) and
4469 (gGameSettings.GameMode <> GM_SINGLE) then
4470 begin
4471 if gSwitchGameMode = GM_CTF then
4472 gGameSettings.MaxLives := 0;
4473 gGameSettings.GameMode := gSwitchGameMode;
4474 Force := True;
4475 end else
4476 gSwitchGameMode := gGameSettings.GameMode;
4478 g_Player_ResetTeams();
4480 if isWadPath(Map) then
4481 begin
4482 NewWAD := g_ExtractWadName(Map);
4483 ResName := g_ExtractFileName(Map);
4484 if g_Game_IsServer then
4485 begin
4486 gWADHash := MD5File(MapsDir + NewWAD);
4487 g_Game_LoadWAD(NewWAD);
4488 end else
4489 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4490 g_Game_ClientWAD(NewWAD, gWADHash);
4491 end else
4492 ResName := Map;
4494 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4495 if Result then
4496 begin
4497 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4499 gState := STATE_NONE;
4500 g_ActiveWindow := nil;
4501 gGameOn := True;
4503 DisableCheats();
4504 ResetTimer();
4506 if gGameSettings.GameMode = GM_CTF then
4507 begin
4508 g_Map_ResetFlag(FLAG_RED);
4509 g_Map_ResetFlag(FLAG_BLUE);
4510 // CTF, à ôëàãîâ íåò:
4511 if not g_Map_HaveFlagPoints() then
4512 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4513 end;
4514 end
4515 else
4516 begin
4517 gState := STATE_MENU;
4518 gGameOn := False;
4519 end;
4521 gExit := 0;
4522 gPauseMain := false;
4523 gPauseHolmes := false;
4524 gTime := 0;
4525 NetTimeToUpdate := 1;
4526 NetTimeToReliable := 0;
4527 NetTimeToMaster := NetMasterRate;
4528 gLMSRespawn := LMS_RESPAWN_NONE;
4529 gLMSRespawnTime := 0;
4530 gMissionFailed := False;
4531 gNextMap := '';
4533 gCoopMonstersKilled := 0;
4534 gCoopSecretsFound := 0;
4536 gVoteInProgress := False;
4537 gVotePassed := False;
4538 gVoteCount := 0;
4539 gVoted := False;
4541 gStatsOff := False;
4543 if not gGameOn then Exit;
4545 g_Game_SpectateCenterView();
4547 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4548 begin
4549 gLMSRespawn := LMS_RESPAWN_WARMUP;
4550 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4551 gLMSSoftSpawn := True;
4552 if NetMode = NET_SERVER then
4553 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4554 else
4555 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4556 end;
4558 if NetMode = NET_SERVER then
4559 begin
4560 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4562 // Ìàñòåðñåðâåð
4563 if NetUseMaster then
4564 begin
4565 if (NetMHost = nil) or (NetMPeer = nil) then
4566 if not g_Net_Slist_Connect then
4567 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4569 g_Net_Slist_Update;
4570 end;
4572 if NetClients <> nil then
4573 for I := 0 to High(NetClients) do
4574 if NetClients[I].Used then
4575 begin
4576 NetClients[I].Voted := False;
4577 if NetClients[I].RequestedFullUpdate then
4578 begin
4579 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4580 NetClients[I].RequestedFullUpdate := False;
4581 end;
4582 end;
4584 g_Net_UnbanNonPermHosts();
4585 end;
4587 if gLastMap then
4588 begin
4589 gCoopTotalMonstersKilled := 0;
4590 gCoopTotalSecretsFound := 0;
4591 gCoopTotalMonsters := 0;
4592 gCoopTotalSecrets := 0;
4593 gLastMap := False;
4594 end;
4596 g_Game_ExecuteEvent('onmapstart');
4597 end;
4599 procedure SetFirstLevel();
4600 begin
4601 gNextMap := '';
4603 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4604 if MapList = nil then
4605 Exit;
4607 SortSArray(MapList);
4608 gNextMap := MapList[Low(MapList)];
4610 MapList := nil;
4611 end;
4613 procedure g_Game_ExitLevel(const Map: AnsiString);
4614 begin
4615 gNextMap := Map;
4617 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4618 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4619 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4620 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4622 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4623 if gGameSettings.GameType = GT_SINGLE then
4624 gExit := EXIT_ENDLEVELSINGLE
4625 else // Âûøëè â âûõîä â Ñâîåé èãðå
4626 begin
4627 gExit := EXIT_ENDLEVELCUSTOM;
4628 if gGameSettings.GameMode = GM_COOP then
4629 g_Player_RememberAll;
4631 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4632 begin
4633 gLastMap := True;
4634 if gGameSettings.GameMode = GM_COOP then
4635 gStatsOff := True;
4637 gStatsPressed := True;
4638 gNextMap := 'MAP01';
4640 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4641 g_Game_NextLevel;
4643 if g_Game_IsNet then
4644 begin
4645 MH_SEND_GameStats();
4646 MH_SEND_CoopStats();
4647 end;
4648 end;
4649 end;
4650 end;
4652 procedure g_Game_RestartLevel();
4653 var
4654 Map: string;
4655 begin
4656 if gGameSettings.GameMode = GM_SINGLE then
4657 begin
4658 g_Game_Restart();
4659 Exit;
4660 end;
4661 gExit := EXIT_ENDLEVELCUSTOM;
4662 Map := g_ExtractFileName(gMapInfo.Map);
4663 gNextMap := Map;
4664 end;
4666 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4667 var
4668 gWAD: String;
4669 begin
4670 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4671 Exit;
4672 if not g_Game_IsClient then
4673 Exit;
4674 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4675 if gWAD = '' then
4676 begin
4677 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4678 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4679 if gWAD = '' then
4680 begin
4681 g_Game_Free();
4682 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4683 Exit;
4684 end;
4685 end;
4686 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4687 g_Game_LoadWAD(NewWAD);
4688 end;
4690 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4691 var
4692 i, n, nb, nr: Integer;
4694 function monRespawn (mon: TMonster): Boolean;
4695 begin
4696 result := false; // don't stop
4697 if not mon.FNoRespawn then mon.Respawn();
4698 end;
4700 begin
4701 if not g_Game_IsServer then Exit;
4702 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4703 gLMSRespawn := LMS_RESPAWN_NONE;
4704 gLMSRespawnTime := 0;
4705 MessageTime := 0;
4707 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4708 begin
4709 gMissionFailed := True;
4710 g_Game_RestartLevel;
4711 Exit;
4712 end;
4714 n := 0; nb := 0; nr := 0;
4715 for i := Low(gPlayers) to High(gPlayers) do
4716 if (gPlayers[i] <> nil) and
4717 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4718 (gPlayers[i] is TBot)) then
4719 begin
4720 Inc(n);
4721 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4722 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4723 end;
4725 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4726 begin
4727 // wait a second until the fuckers finally decide to join
4728 gLMSRespawn := LMS_RESPAWN_WARMUP;
4729 gLMSRespawnTime := gTime + 1000;
4730 gLMSSoftSpawn := NoMapRestart;
4731 Exit;
4732 end;
4734 g_Player_RemoveAllCorpses;
4735 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4736 if g_Game_IsNet then
4737 MH_SEND_GameEvent(NET_EV_LMS_START);
4739 for i := Low(gPlayers) to High(gPlayers) do
4740 begin
4741 if gPlayers[i] = nil then continue;
4742 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4743 // don't touch normal spectators
4744 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4745 begin
4746 gPlayers[i].FNoRespawn := True;
4747 gPlayers[i].Lives := 0;
4748 if g_Game_IsNet then
4749 MH_SEND_PlayerStats(gPlayers[I].UID);
4750 continue;
4751 end;
4752 gPlayers[i].FNoRespawn := False;
4753 gPlayers[i].Lives := gGameSettings.MaxLives;
4754 gPlayers[i].Respawn(False, True);
4755 if gGameSettings.GameMode = GM_COOP then
4756 begin
4757 gPlayers[i].Frags := 0;
4758 gPlayers[i].RecallState;
4759 end;
4760 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4761 gPlayer1 := g_Player_Get(gLMSPID1);
4762 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4763 gPlayer2 := g_Player_Get(gLMSPID2);
4764 end;
4766 g_Items_RestartRound();
4769 g_Mons_ForEach(monRespawn);
4771 gLMSSoftSpawn := False;
4772 end;
4774 function g_Game_GetFirstMap(WAD: String): String;
4775 begin
4776 Result := '';
4778 MapList := g_Map_GetMapsList(WAD);
4779 if MapList = nil then
4780 Exit;
4782 SortSArray(MapList);
4783 Result := MapList[Low(MapList)];
4785 if not g_Map_Exist(WAD + ':\' + Result) then
4786 Result := '';
4788 MapList := nil;
4789 end;
4791 function g_Game_GetNextMap(): String;
4792 var
4793 I: Integer;
4794 Map: string;
4795 begin
4796 Result := '';
4798 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4799 if MapList = nil then
4800 Exit;
4802 Map := g_ExtractFileName(gMapInfo.Map);
4804 SortSArray(MapList);
4805 MapIndex := -255;
4806 for I := Low(MapList) to High(MapList) do
4807 if Map = MapList[I] then
4808 begin
4809 MapIndex := I;
4810 Break;
4811 end;
4813 if MapIndex <> -255 then
4814 begin
4815 if MapIndex = High(MapList) then
4816 Result := MapList[Low(MapList)]
4817 else
4818 Result := MapList[MapIndex + 1];
4820 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4821 end;
4823 MapList := nil;
4824 end;
4826 procedure g_Game_NextLevel();
4827 begin
4828 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4829 gExit := EXIT_ENDLEVELCUSTOM
4830 else
4831 begin
4832 gExit := EXIT_ENDLEVELSINGLE;
4833 Exit;
4834 end;
4836 if gNextMap <> '' then Exit;
4837 gNextMap := g_Game_GetNextMap();
4838 end;
4840 function g_Game_IsTestMap(): Boolean;
4841 begin
4842 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4843 end;
4845 procedure g_Game_DeleteTestMap();
4846 var
4847 a: Integer;
4848 //MapName: AnsiString;
4849 WadName: string;
4851 WAD: TWADFile;
4852 MapList: SSArray;
4853 time: Integer;
4855 begin
4856 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
4857 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
4858 if (a = 0) then exit;
4860 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
4861 WadName := Copy(gMapToDelete, 1, a+3);
4862 Delete(gMapToDelete, 1, a+5);
4863 gMapToDelete := UpperCase(gMapToDelete);
4864 //MapName := '';
4865 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4868 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4869 if MapName <> TEST_MAP_NAME then
4870 Exit;
4872 if not gTempDelete then
4873 begin
4874 time := g_GetFileTime(WadName);
4875 WAD := TWADFile.Create();
4877 // ×èòàåì Wad-ôàéë:
4878 if not WAD.ReadFile(WadName) then
4879 begin // Íåò òàêîãî WAD-ôàéëà
4880 WAD.Free();
4881 Exit;
4882 end;
4884 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4885 WAD.CreateImage();
4886 MapList := WAD.GetResourcesList('');
4888 if MapList <> nil then
4889 for a := 0 to High(MapList) do
4890 if MapList[a] = MapName then
4891 begin
4892 // Óäàëÿåì è ñîõðàíÿåì:
4893 WAD.RemoveResource('', MapName);
4894 WAD.SaveTo(WadName);
4895 Break;
4896 end;
4898 WAD.Free();
4899 g_SetFileTime(WadName, time);
4900 end else
4902 if gTempDelete then DeleteFile(WadName);
4903 end;
4905 procedure GameCVars(P: SSArray);
4906 var
4907 a, b: Integer;
4908 stat: TPlayerStatArray;
4909 cmd, s: string;
4910 config: TConfig;
4911 begin
4912 stat := nil;
4913 cmd := LowerCase(P[0]);
4914 if cmd = 'r_showfps' then
4915 begin
4916 if (Length(P) > 1) and
4917 ((P[1] = '1') or (P[1] = '0')) then
4918 gShowFPS := (P[1][1] = '1');
4920 if gShowFPS then
4921 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4922 else
4923 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4924 end
4925 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4926 begin
4927 with gGameSettings do
4928 begin
4929 if (Length(P) > 1) and
4930 ((P[1] = '1') or (P[1] = '0')) then
4931 begin
4932 if (P[1][1] = '1') then
4933 Options := Options or GAME_OPTION_TEAMDAMAGE
4934 else
4935 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4936 end;
4938 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4939 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4940 else
4941 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4943 if g_Game_IsNet then MH_SEND_GameSettings;
4944 end;
4945 end
4946 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4947 begin
4948 with gGameSettings do
4949 begin
4950 if (Length(P) > 1) and
4951 ((P[1] = '1') or (P[1] = '0')) then
4952 begin
4953 if (P[1][1] = '1') then
4954 Options := Options or GAME_OPTION_WEAPONSTAY
4955 else
4956 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4957 end;
4959 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4960 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4961 else
4962 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4964 if g_Game_IsNet then MH_SEND_GameSettings;
4965 end;
4966 end
4967 else if cmd = 'g_gamemode' then
4968 begin
4969 a := g_Game_TextToMode(P[1]);
4970 if a = GM_SINGLE then a := GM_COOP;
4971 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4972 begin
4973 gSwitchGameMode := a;
4974 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4975 (gState = STATE_INTERSINGLE) then
4976 gSwitchGameMode := GM_SINGLE;
4977 if not gGameOn then
4978 gGameSettings.GameMode := gSwitchGameMode;
4979 end;
4980 if gSwitchGameMode = gGameSettings.GameMode then
4981 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4982 [g_Game_ModeToText(gGameSettings.GameMode)]))
4983 else
4984 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4985 [g_Game_ModeToText(gGameSettings.GameMode),
4986 g_Game_ModeToText(gSwitchGameMode)]));
4987 end
4988 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4989 begin
4990 with gGameSettings do
4991 begin
4992 if (Length(P) > 1) and
4993 ((P[1] = '1') or (P[1] = '0')) then
4994 begin
4995 if (P[1][1] = '1') then
4996 Options := Options or GAME_OPTION_ALLOWEXIT
4997 else
4998 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4999 end;
5001 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
5002 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
5003 else
5004 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
5005 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5007 if g_Game_IsNet then MH_SEND_GameSettings;
5008 end;
5009 end
5010 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
5011 begin
5012 with gGameSettings do
5013 begin
5014 if (Length(P) > 1) and
5015 ((P[1] = '1') or (P[1] = '0')) then
5016 begin
5017 if (P[1][1] = '1') then
5018 Options := Options or GAME_OPTION_MONSTERS
5019 else
5020 Options := Options and (not GAME_OPTION_MONSTERS);
5021 end;
5023 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
5024 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
5025 else
5026 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
5027 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5029 if g_Game_IsNet then MH_SEND_GameSettings;
5030 end;
5031 end
5032 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
5033 begin
5034 with gGameSettings do
5035 begin
5036 if (Length(P) > 1) and
5037 ((P[1] = '1') or (P[1] = '0')) then
5038 begin
5039 if (P[1][1] = '1') then
5040 Options := Options or GAME_OPTION_BOTVSPLAYER
5041 else
5042 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
5043 end;
5045 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
5046 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
5047 else
5048 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
5050 if g_Game_IsNet then MH_SEND_GameSettings;
5051 end;
5052 end
5053 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
5054 begin
5055 with gGameSettings do
5056 begin
5057 if (Length(P) > 1) and
5058 ((P[1] = '1') or (P[1] = '0')) then
5059 begin
5060 if (P[1][1] = '1') then
5061 Options := Options or GAME_OPTION_BOTVSMONSTER
5062 else
5063 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
5064 end;
5066 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
5067 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
5068 else
5069 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
5071 if g_Game_IsNet then MH_SEND_GameSettings;
5072 end;
5073 end
5074 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
5075 begin
5076 if Length(P) > 1 then
5077 begin
5078 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
5079 gGameSettings.WarmupTime := 30
5080 else
5081 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
5082 end;
5084 g_Console_Add(Format(_lc[I_MSG_WARMUP],
5085 [gGameSettings.WarmupTime]));
5086 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5087 end
5088 else if cmd = 'net_interp' then
5089 begin
5090 if (Length(P) > 1) then
5091 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
5093 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
5094 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5095 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
5096 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5097 config.Free();
5098 end
5099 else if cmd = 'net_forceplayerupdate' then
5100 begin
5101 if (Length(P) > 1) and
5102 ((P[1] = '1') or (P[1] = '0')) then
5103 NetForcePlayerUpdate := (P[1][1] = '1');
5105 if NetForcePlayerUpdate then
5106 g_Console_Add('net_forceplayerupdate = 1')
5107 else
5108 g_Console_Add('net_forceplayerupdate = 0');
5109 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5110 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
5111 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5112 config.Free();
5113 end
5114 else if cmd = 'net_predictself' then
5115 begin
5116 if (Length(P) > 1) and
5117 ((P[1] = '1') or (P[1] = '0')) then
5118 NetPredictSelf := (P[1][1] = '1');
5120 if NetPredictSelf then
5121 g_Console_Add('net_predictself = 1')
5122 else
5123 g_Console_Add('net_predictself = 0');
5124 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5125 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
5126 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5127 config.Free();
5128 end
5129 else if cmd = 'sv_name' then
5130 begin
5131 if (Length(P) > 1) and (Length(P[1]) > 0) then
5132 begin
5133 NetServerName := P[1];
5134 if Length(NetServerName) > 64 then
5135 SetLength(NetServerName, 64);
5136 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5137 g_Net_Slist_Update;
5138 end;
5140 g_Console_Add(cmd + ' = "' + NetServerName + '"');
5141 end
5142 else if cmd = 'sv_passwd' then
5143 begin
5144 if (Length(P) > 1) and (Length(P[1]) > 0) then
5145 begin
5146 NetPassword := P[1];
5147 if Length(NetPassword) > 24 then
5148 SetLength(NetPassword, 24);
5149 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5150 g_Net_Slist_Update;
5151 end;
5153 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
5154 end
5155 else if cmd = 'sv_maxplrs' then
5156 begin
5157 if (Length(P) > 1) then
5158 begin
5159 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
5160 if g_Game_IsServer and g_Game_IsNet then
5161 begin
5162 b := 0;
5163 for a := 0 to High(NetClients) do
5164 if NetClients[a].Used then
5165 begin
5166 Inc(b);
5167 if b > NetMaxClients then
5168 begin
5169 s := g_Player_Get(NetClients[a].Player).Name;
5170 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
5171 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5172 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5173 end;
5174 end;
5175 if NetUseMaster then
5176 g_Net_Slist_Update;
5177 end;
5178 end;
5180 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
5181 end
5182 else if cmd = 'sv_public' then
5183 begin
5184 if (Length(P) > 1) then
5185 begin
5186 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
5187 if g_Game_IsServer and g_Game_IsNet then
5188 if NetUseMaster then
5189 begin
5190 if NetMPeer = nil then
5191 if not g_Net_Slist_Connect() then
5192 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
5193 g_Net_Slist_Update();
5194 end
5195 else
5196 if NetMPeer <> nil then
5197 g_Net_Slist_Disconnect();
5198 end;
5200 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
5201 end
5202 else if cmd = 'sv_intertime' then
5203 begin
5204 if (Length(P) > 1) then
5205 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
5207 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
5208 end
5209 else if cmd = 'p1_name' then
5210 begin
5211 if (Length(P) > 1) and gGameOn then
5212 begin
5213 if g_Game_IsClient then
5214 begin
5215 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5216 MC_SEND_PlayerSettings;
5217 end
5218 else
5219 if gPlayer1 <> nil then
5220 begin
5221 gPlayer1.Name := b_Text_Unformat(P[1]);
5222 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5223 end
5224 else
5225 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5226 end;
5227 end
5228 else if cmd = 'p2_name' then
5229 begin
5230 if (Length(P) > 1) and gGameOn then
5231 begin
5232 if g_Game_IsClient then
5233 begin
5234 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5235 MC_SEND_PlayerSettings;
5236 end
5237 else
5238 if gPlayer2 <> nil then
5239 begin
5240 gPlayer2.Name := b_Text_Unformat(P[1]);
5241 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5242 end
5243 else
5244 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5245 end;
5246 end
5247 else if cmd = 'p1_color' then
5248 begin
5249 if Length(P) > 3 then
5250 if g_Game_IsClient then
5251 begin
5252 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5253 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5254 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5255 MC_SEND_PlayerSettings;
5256 end
5257 else
5258 if gPlayer1 <> nil then
5259 begin
5260 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5261 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5262 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5263 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5264 end
5265 else
5266 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5267 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5268 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5269 end
5270 else if (cmd = 'p2_color') and not g_Game_IsNet then
5271 begin
5272 if Length(P) > 3 then
5273 if g_Game_IsClient then
5274 begin
5275 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5276 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5277 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5278 MC_SEND_PlayerSettings;
5279 end
5280 else
5281 if gPlayer2 <> nil then
5282 begin
5283 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5284 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5285 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5286 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5287 end
5288 else
5289 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5290 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5291 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5292 end
5293 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5294 begin
5295 if cmd = 'r_showtime' then
5296 begin
5297 if (Length(P) > 1) and
5298 ((P[1] = '1') or (P[1] = '0')) then
5299 gShowTime := (P[1][1] = '1');
5301 if gShowTime then
5302 g_Console_Add(_lc[I_MSG_TIME_ON])
5303 else
5304 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5305 end
5306 else if cmd = 'r_showscore' then
5307 begin
5308 if (Length(P) > 1) and
5309 ((P[1] = '1') or (P[1] = '0')) then
5310 gShowGoals := (P[1][1] = '1');
5312 if gShowGoals then
5313 g_Console_Add(_lc[I_MSG_SCORE_ON])
5314 else
5315 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5316 end
5317 else if cmd = 'r_showstat' then
5318 begin
5319 if (Length(P) > 1) and
5320 ((P[1] = '1') or (P[1] = '0')) then
5321 gShowStat := (P[1][1] = '1');
5323 if gShowStat then
5324 g_Console_Add(_lc[I_MSG_STATS_ON])
5325 else
5326 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5327 end
5328 else if cmd = 'r_showkillmsg' then
5329 begin
5330 if (Length(P) > 1) and
5331 ((P[1] = '1') or (P[1] = '0')) then
5332 gShowKillMsg := (P[1][1] = '1');
5334 if gShowKillMsg then
5335 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5336 else
5337 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5338 end
5339 else if cmd = 'r_showlives' then
5340 begin
5341 if (Length(P) > 1) and
5342 ((P[1] = '1') or (P[1] = '0')) then
5343 gShowLives := (P[1][1] = '1');
5345 if gShowLives then
5346 g_Console_Add(_lc[I_MSG_LIVES_ON])
5347 else
5348 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5349 end
5350 else if cmd = 'r_showspect' then
5351 begin
5352 if (Length(P) > 1) and
5353 ((P[1] = '1') or (P[1] = '0')) then
5354 gSpectHUD := (P[1][1] = '1');
5356 if gSpectHUD then
5357 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5358 else
5359 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5360 end
5361 else if cmd = 'r_showping' then
5362 begin
5363 if (Length(P) > 1) and
5364 ((P[1] = '1') or (P[1] = '0')) then
5365 gShowPing := (P[1][1] = '1');
5367 if gShowPing then
5368 g_Console_Add(_lc[I_MSG_PING_ON])
5369 else
5370 g_Console_Add(_lc[I_MSG_PING_OFF]);
5371 end
5372 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5373 begin
5374 if Length(P) > 1 then
5375 begin
5376 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5377 gGameSettings.GoalLimit := 0
5378 else
5379 begin
5380 b := 0;
5382 if gGameSettings.GameMode = GM_DM then
5383 begin // DM
5384 stat := g_Player_GetStats();
5385 if stat <> nil then
5386 for a := 0 to High(stat) do
5387 if stat[a].Frags > b then
5388 b := stat[a].Frags;
5389 end
5390 else // TDM/CTF
5391 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5393 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5394 end;
5396 if g_Game_IsNet then MH_SEND_GameSettings;
5397 end;
5399 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5400 end
5401 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5402 begin
5403 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5404 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5406 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5407 [gGameSettings.TimeLimit div 3600,
5408 (gGameSettings.TimeLimit div 60) mod 60,
5409 gGameSettings.TimeLimit mod 60]));
5410 if g_Game_IsNet then MH_SEND_GameSettings;
5411 end
5412 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5413 begin
5414 if Length(P) > 1 then
5415 begin
5416 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5417 gGameSettings.MaxLives := 0
5418 else
5419 begin
5420 b := 0;
5421 stat := g_Player_GetStats();
5422 if stat <> nil then
5423 for a := 0 to High(stat) do
5424 if stat[a].Lives > b then
5425 b := stat[a].Lives;
5426 gGameSettings.MaxLives :=
5427 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5428 end;
5429 end;
5431 g_Console_Add(Format(_lc[I_MSG_LIVES],
5432 [gGameSettings.MaxLives]));
5433 if g_Game_IsNet then MH_SEND_GameSettings;
5434 end;
5435 end;
5436 end;
5438 procedure PrintHeapStats();
5439 var
5440 hs: TFPCHeapStatus;
5441 begin
5442 hs := GetFPCHeapStatus();
5443 e_LogWriteLn ('v===== heap status =====v');
5444 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
5445 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
5446 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
5447 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
5448 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
5449 e_LogWriteLn ('^=======================^');
5450 end;
5452 procedure DebugCommands(P: SSArray);
5453 var
5454 a, b: Integer;
5455 cmd: string;
5456 //pt: TDFPoint;
5457 mon: TMonster;
5458 begin
5459 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5460 if {gDebugMode}conIsCheatsEnabled then
5461 begin
5462 cmd := LowerCase(P[0]);
5463 if cmd = 'd_window' then
5464 begin
5465 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5466 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5467 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5468 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5469 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5470 end
5471 else if cmd = 'd_sounds' then
5472 begin
5473 if (Length(P) > 1) and
5474 ((P[1] = '1') or (P[1] = '0')) then
5475 g_Debug_Sounds := (P[1][1] = '1');
5477 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5478 end
5479 else if cmd = 'd_frames' then
5480 begin
5481 if (Length(P) > 1) and
5482 ((P[1] = '1') or (P[1] = '0')) then
5483 g_Debug_Frames := (P[1][1] = '1');
5485 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5486 end
5487 else if cmd = 'd_winmsg' then
5488 begin
5489 if (Length(P) > 1) and
5490 ((P[1] = '1') or (P[1] = '0')) then
5491 g_Debug_WinMsgs := (P[1][1] = '1');
5493 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5494 end
5495 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5496 begin
5497 if (Length(P) > 1) and
5498 ((P[1] = '1') or (P[1] = '0')) then
5499 g_Debug_MonsterOff := (P[1][1] = '1');
5501 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5502 end
5503 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5504 begin
5505 if Length(P) > 1 then
5506 case P[1][1] of
5507 '0': g_debug_BotAIOff := 0;
5508 '1': g_debug_BotAIOff := 1;
5509 '2': g_debug_BotAIOff := 2;
5510 '3': g_debug_BotAIOff := 3;
5511 end;
5513 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5514 end
5515 else if cmd = 'd_monster' then
5516 begin
5517 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5518 if Length(P) < 2 then
5519 begin
5520 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5521 g_Console_Add('ID | Name');
5522 for b := MONSTER_DEMON to MONSTER_MAN do
5523 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5524 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
5525 end else
5526 begin
5527 a := StrToIntDef(P[1], 0);
5528 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5529 a := g_Mons_TypeIdByName(P[1]);
5531 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5532 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5533 else
5534 begin
5535 with gPlayer1.Obj do
5536 begin
5537 mon := g_Monsters_Create(a,
5538 X + Rect.X + (Rect.Width div 2),
5539 Y + Rect.Y + Rect.Height,
5540 gPlayer1.Direction, True);
5541 end;
5542 if (Length(P) > 2) and (mon <> nil) then
5543 begin
5544 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
5545 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
5546 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
5547 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
5548 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
5549 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
5550 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
5551 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
5552 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5553 end;
5554 end;
5555 end;
5556 end
5557 else if (cmd = 'd_health') then
5558 begin
5559 if (Length(P) > 1) and
5560 ((P[1] = '1') or (P[1] = '0')) then
5561 g_debug_HealthBar := (P[1][1] = '1');
5563 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5564 end
5565 else if (cmd = 'd_player') then
5566 begin
5567 if (Length(P) > 1) and
5568 ((P[1] = '1') or (P[1] = '0')) then
5569 g_debug_Player := (P[1][1] = '1');
5571 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5572 end
5573 else if (cmd = 'd_joy') then
5574 begin
5575 for a := 1 to 8 do
5576 g_Console_Add(e_JoystickStateToString(a));
5577 end
5578 else if (cmd = 'd_mem') then
5579 begin
5580 PrintHeapStats();
5581 end;
5582 end
5583 else
5584 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5585 end;
5588 procedure GameCheats(P: SSArray);
5589 var
5590 cmd: string;
5591 f, a: Integer;
5592 plr: TPlayer;
5593 begin
5594 if (not gGameOn) or (not conIsCheatsEnabled) then
5595 begin
5596 g_Console_Add('not available');
5597 exit;
5598 end;
5599 plr := gPlayer1;
5600 if plr = nil then
5601 begin
5602 g_Console_Add('where is the player?!');
5603 exit;
5604 end;
5605 cmd := LowerCase(P[0]);
5606 // god
5607 if cmd = 'god' then
5608 begin
5609 plr.GodMode := not plr.GodMode;
5610 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5611 exit;
5612 end;
5613 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5614 if cmd = 'give' then
5615 begin
5616 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5617 for f := 1 to High(P) do
5618 begin
5619 cmd := LowerCase(P[f]);
5620 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5621 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5622 if cmd = 'exit' then
5623 begin
5624 if gTriggers <> nil then
5625 begin
5626 for a := 0 to High(gTriggers) do
5627 begin
5628 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5629 begin
5630 g_Console_Add('player left the map');
5631 gExitByTrigger := True;
5632 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5633 g_Game_ExitLevel(gTriggers[a].tgcMap);
5634 break;
5635 end;
5636 end;
5637 end;
5638 continue;
5639 end;
5641 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5642 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5643 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5644 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5645 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5647 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5648 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5650 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5651 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;
5653 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5654 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5656 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5657 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5659 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5660 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5662 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5663 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5664 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5666 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5667 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5668 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5669 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;
5670 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5671 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5673 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;
5674 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;
5675 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;
5676 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;
5677 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;
5678 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;
5680 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5681 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;
5683 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;
5684 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;
5686 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5688 if cmd = 'ammo' then
5689 begin
5690 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5691 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5692 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5693 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5694 plr.GiveItem(ITEM_AMMO_FUELCAN);
5695 g_Console_Add('player got some ammo');
5696 continue;
5697 end;
5699 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5700 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5702 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5703 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5705 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5706 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5708 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5709 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5711 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5713 if cmd = 'weapons' then
5714 begin
5715 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5716 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5717 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5718 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5719 plr.GiveItem(ITEM_WEAPON_PLASMA);
5720 plr.GiveItem(ITEM_WEAPON_BFG);
5721 g_Console_Add('player got weapons');
5722 continue;
5723 end;
5725 if cmd = 'keys' then
5726 begin
5727 plr.GiveItem(ITEM_KEY_RED);
5728 plr.GiveItem(ITEM_KEY_GREEN);
5729 plr.GiveItem(ITEM_KEY_BLUE);
5730 g_Console_Add('player got all keys');
5731 continue;
5732 end;
5734 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5735 end;
5736 exit;
5737 end;
5738 // open
5739 if cmd = 'open' then
5740 begin
5741 g_Console_Add('player activated sesame');
5742 g_Triggers_OpenAll();
5743 exit;
5744 end;
5745 // fly
5746 if cmd = 'fly' then
5747 begin
5748 gFly := not gFly;
5749 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5750 exit;
5751 end;
5752 // noclip
5753 if cmd = 'noclip' then
5754 begin
5755 plr.SwitchNoClip;
5756 g_Console_Add('wall hardeness adjusted');
5757 exit;
5758 end;
5759 // notarget
5760 if cmd = 'notarget' then
5761 begin
5762 plr.NoTarget := not plr.NoTarget;
5763 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5764 exit;
5765 end;
5766 // noreload
5767 if cmd = 'noreload' then
5768 begin
5769 plr.NoReload := not plr.NoReload;
5770 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5771 exit;
5772 end;
5773 // speedy
5774 if cmd = 'speedy' then
5775 begin
5776 MAX_RUNVEL := 32-MAX_RUNVEL;
5777 g_Console_Add('speed adjusted');
5778 exit;
5779 end;
5780 // jumpy
5781 if cmd = 'jumpy' then
5782 begin
5783 VEL_JUMP := 30-VEL_JUMP;
5784 g_Console_Add('jump height adjusted');
5785 exit;
5786 end;
5787 // automap
5788 if cmd = 'automap' then
5789 begin
5790 gShowMap := not gShowMap;
5791 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5792 exit;
5793 end;
5794 // aimline
5795 if cmd = 'aimline' then
5796 begin
5797 gAimLine := not gAimLine;
5798 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5799 exit;
5800 end;
5801 end;
5803 procedure GameCommands(P: SSArray);
5804 var
5805 a, b: Integer;
5806 s, pw: String;
5807 chstr: string;
5808 cmd: string;
5809 pl: pTNetClient = nil;
5810 plr: TPlayer;
5811 prt: Word;
5812 nm: Boolean;
5813 listen: LongWord;
5814 begin
5815 // Îáùèå êîìàíäû:
5816 cmd := LowerCase(P[0]);
5817 chstr := '';
5818 if (cmd = 'quit') or
5819 (cmd = 'exit') then
5820 begin
5821 g_Game_Free();
5822 g_Game_Quit();
5823 Exit;
5824 end
5825 else if cmd = 'pause' then
5826 begin
5827 if (g_ActiveWindow = nil) then
5828 g_Game_Pause(not gPauseMain);
5829 end
5830 else if cmd = 'endgame' then
5831 gExit := EXIT_SIMPLE
5832 else if cmd = 'restart' then
5833 begin
5834 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5835 begin
5836 if g_Game_IsClient then
5837 begin
5838 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5839 Exit;
5840 end;
5841 g_Game_Restart();
5842 end else
5843 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5844 end
5845 else if cmd = 'kick' then
5846 begin
5847 if g_Game_IsServer then
5848 begin
5849 if Length(P) < 2 then
5850 begin
5851 g_Console_Add('kick <name>');
5852 Exit;
5853 end;
5854 if P[1] = '' then
5855 begin
5856 g_Console_Add('kick <name>');
5857 Exit;
5858 end;
5860 if g_Game_IsNet then
5861 pl := g_Net_Client_ByName(P[1]);
5862 if (pl <> nil) then
5863 begin
5864 s := g_Net_ClientName_ByID(pl^.ID);
5865 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5866 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5867 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5868 if NetUseMaster then
5869 g_Net_Slist_Update;
5870 end else if gPlayers <> nil then
5871 for a := Low(gPlayers) to High(gPlayers) do
5872 if gPlayers[a] <> nil then
5873 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5874 begin
5875 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5876 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5877 continue;
5878 gPlayers[a].Lives := 0;
5879 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5880 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5881 g_Player_Remove(gPlayers[a].UID);
5882 if NetUseMaster then
5883 g_Net_Slist_Update;
5884 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5885 g_Bot_MixNames();
5886 end;
5887 end else
5888 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5889 end
5890 else if cmd = 'kick_id' then
5891 begin
5892 if g_Game_IsServer and g_Game_IsNet then
5893 begin
5894 if Length(P) < 2 then
5895 begin
5896 g_Console_Add('kick_id <client ID>');
5897 Exit;
5898 end;
5899 if P[1] = '' then
5900 begin
5901 g_Console_Add('kick_id <client ID>');
5902 Exit;
5903 end;
5905 a := StrToIntDef(P[1], 0);
5906 if (NetClients <> nil) and (a <= High(NetClients)) then
5907 begin
5908 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5909 begin
5910 s := g_Net_ClientName_ByID(NetClients[a].ID);
5911 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5912 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5913 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5914 if NetUseMaster then
5915 g_Net_Slist_Update;
5916 end;
5917 end;
5918 end else
5919 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5920 end
5921 else if cmd = 'ban' then
5922 begin
5923 if g_Game_IsServer and g_Game_IsNet then
5924 begin
5925 if Length(P) < 2 then
5926 begin
5927 g_Console_Add('ban <name>');
5928 Exit;
5929 end;
5930 if P[1] = '' then
5931 begin
5932 g_Console_Add('ban <name>');
5933 Exit;
5934 end;
5936 pl := g_Net_Client_ByName(P[1]);
5937 if (pl <> nil) then
5938 begin
5939 s := g_Net_ClientName_ByID(pl^.ID);
5940 g_Net_BanHost(pl^.Peer^.address.host, False);
5941 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5942 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5943 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5944 if NetUseMaster then
5945 g_Net_Slist_Update;
5946 end else
5947 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5948 end else
5949 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5950 end
5951 else if cmd = 'ban_id' then
5952 begin
5953 if g_Game_IsServer and g_Game_IsNet then
5954 begin
5955 if Length(P) < 2 then
5956 begin
5957 g_Console_Add('ban_id <client ID>');
5958 Exit;
5959 end;
5960 if P[1] = '' then
5961 begin
5962 g_Console_Add('ban_id <client ID>');
5963 Exit;
5964 end;
5966 a := StrToIntDef(P[1], 0);
5967 if (NetClients <> nil) and (a <= High(NetClients)) then
5968 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5969 begin
5970 s := g_Net_ClientName_ByID(NetClients[a].ID);
5971 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5972 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5973 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5974 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5975 if NetUseMaster then
5976 g_Net_Slist_Update;
5977 end;
5978 end else
5979 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5980 end
5981 else if cmd = 'permban' then
5982 begin
5983 if g_Game_IsServer and g_Game_IsNet then
5984 begin
5985 if Length(P) < 2 then
5986 begin
5987 g_Console_Add('permban <name>');
5988 Exit;
5989 end;
5990 if P[1] = '' then
5991 begin
5992 g_Console_Add('permban <name>');
5993 Exit;
5994 end;
5996 pl := g_Net_Client_ByName(P[1]);
5997 if (pl <> nil) then
5998 begin
5999 s := g_Net_ClientName_ByID(pl^.ID);
6000 g_Net_BanHost(pl^.Peer^.address.host);
6001 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
6002 g_Net_SaveBanList();
6003 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6004 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6005 if NetUseMaster then
6006 g_Net_Slist_Update;
6007 end else
6008 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6009 end else
6010 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6011 end
6012 else if cmd = 'permban_id' then
6013 begin
6014 if g_Game_IsServer and g_Game_IsNet then
6015 begin
6016 if Length(P) < 2 then
6017 begin
6018 g_Console_Add('permban_id <client ID>');
6019 Exit;
6020 end;
6021 if P[1] = '' then
6022 begin
6023 g_Console_Add('permban_id <client ID>');
6024 Exit;
6025 end;
6027 a := StrToIntDef(P[1], 0);
6028 if (NetClients <> nil) and (a <= High(NetClients)) then
6029 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6030 begin
6031 s := g_Net_ClientName_ByID(NetClients[a].ID);
6032 g_Net_BanHost(NetClients[a].Peer^.address.host);
6033 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
6034 g_Net_SaveBanList();
6035 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6036 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6037 if NetUseMaster then
6038 g_Net_Slist_Update;
6039 end;
6040 end else
6041 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6042 end
6043 else if cmd = 'unban' then
6044 begin
6045 if g_Game_IsServer and g_Game_IsNet then
6046 begin
6047 if Length(P) < 2 then
6048 begin
6049 g_Console_Add('unban <IP Address>');
6050 Exit;
6051 end;
6052 if P[1] = '' then
6053 begin
6054 g_Console_Add('unban <IP Address>');
6055 Exit;
6056 end;
6058 if g_Net_UnbanHost(P[1]) then
6059 begin
6060 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
6061 g_Net_SaveBanList();
6062 end else
6063 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
6064 end else
6065 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6066 end
6067 else if cmd = 'clientlist' then
6068 begin
6069 if g_Game_IsServer and g_Game_IsNet then
6070 begin
6071 b := 0;
6072 if NetClients <> nil then
6073 for a := Low(NetClients) to High(NetClients) do
6074 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6075 begin
6076 plr := g_Player_Get(NetClients[a].Player);
6077 if plr = nil then continue;
6078 Inc(b);
6079 g_Console_Add(Format('#%2d: %-15s | %s', [a,
6080 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
6081 end;
6082 if b = 0 then
6083 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
6084 end else
6085 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6086 end
6087 else if cmd = 'connect' then
6088 begin
6089 if (NetMode = NET_NONE) then
6090 begin
6091 if Length(P) < 2 then
6092 begin
6093 g_Console_Add('connect <IP> [port] [password]');
6094 Exit;
6095 end;
6096 if P[1] = '' then
6097 begin
6098 g_Console_Add('connect <IP> [port] [password]');
6099 Exit;
6100 end;
6102 if Length(P) > 2 then
6103 prt := StrToIntDef(P[2], 25666)
6104 else
6105 prt := 25666;
6107 if Length(P) > 3 then
6108 pw := P[3]
6109 else
6110 pw := '';
6112 g_Game_StartClient(P[1], prt, pw);
6113 end;
6114 end
6115 else if cmd = 'disconnect' then
6116 begin
6117 if (NetMode = NET_CLIENT) then
6118 g_Net_Disconnect();
6119 end
6120 else if cmd = 'reconnect' then
6121 begin
6122 if (NetMode = NET_SERVER) then
6123 Exit;
6125 if (NetMode = NET_CLIENT) then
6126 begin
6127 g_Net_Disconnect();
6128 gExit := EXIT_SIMPLE;
6129 EndGame;
6130 end;
6132 //TODO: Use last successful password to reconnect, instead of ''
6133 g_Game_StartClient(NetClientIP, NetClientPort, '');
6134 end
6135 else if (cmd = 'addbot') or
6136 (cmd = 'bot_add') then
6137 begin
6138 if Length(P) > 1 then
6139 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
6140 else
6141 g_Bot_Add(TEAM_NONE, 2);
6142 end
6143 else if cmd = 'bot_addlist' then
6144 begin
6145 if Length(P) > 1 then
6146 if Length(P) = 2 then
6147 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
6148 else
6149 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
6150 end
6151 else if cmd = 'bot_removeall' then
6152 g_Bot_RemoveAll()
6153 else if cmd = 'chat' then
6154 begin
6155 if g_Game_IsNet then
6156 begin
6157 if Length(P) > 1 then
6158 begin
6159 for a := 1 to High(P) do
6160 chstr := chstr + P[a] + ' ';
6162 if Length(chstr) > 200 then SetLength(chstr, 200);
6164 if Length(chstr) < 1 then
6165 begin
6166 g_Console_Add('chat <text>');
6167 Exit;
6168 end;
6170 chstr := b_Text_Format(chstr);
6171 if g_Game_IsClient then
6172 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
6173 else
6174 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
6175 end
6176 else
6177 g_Console_Add('chat <text>');
6178 end else
6179 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6180 end
6181 else if cmd = 'teamchat' then
6182 begin
6183 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
6184 begin
6185 if Length(P) > 1 then
6186 begin
6187 for a := 1 to High(P) do
6188 chstr := chstr + P[a] + ' ';
6190 if Length(chstr) > 200 then SetLength(chstr, 200);
6192 if Length(chstr) < 1 then
6193 begin
6194 g_Console_Add('teamchat <text>');
6195 Exit;
6196 end;
6198 chstr := b_Text_Format(chstr);
6199 if g_Game_IsClient then
6200 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
6201 else
6202 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
6203 gPlayer1Settings.Team);
6204 end
6205 else
6206 g_Console_Add('teamchat <text>');
6207 end else
6208 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6209 end
6210 else if cmd = 'game' then
6211 begin
6212 if gGameSettings.GameType <> GT_NONE then
6213 begin
6214 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6215 Exit;
6216 end;
6217 if Length(P) = 1 then
6218 begin
6219 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
6220 Exit;
6221 end;
6222 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
6223 P[1] := addWadExtension(P[1]);
6224 if FileExists(MapsDir + P[1]) then
6225 begin
6226 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6227 if Length(P) < 3 then
6228 begin
6229 SetLength(P, 3);
6230 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6231 end;
6233 s := P[1] + ':\' + UpperCase(P[2]);
6235 if g_Map_Exist(MapsDir + s) then
6236 begin
6237 // Çàïóñêàåì ñâîþ èãðó
6238 g_Game_Free();
6239 with gGameSettings do
6240 begin
6241 GameMode := g_Game_TextToMode(gcGameMode);
6242 if gSwitchGameMode <> GM_NONE then
6243 GameMode := gSwitchGameMode;
6244 if GameMode = GM_NONE then GameMode := GM_DM;
6245 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6246 b := 1;
6247 if Length(P) >= 4 then
6248 b := StrToIntDef(P[3], 1);
6249 g_Game_StartCustom(s, GameMode, TimeLimit,
6250 GoalLimit, MaxLives, Options, b);
6251 end;
6252 end
6253 else
6254 if P[2] = '' then
6255 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6256 else
6257 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[2]), P[1]]));
6258 end else
6259 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6260 end
6261 else if cmd = 'host' then
6262 begin
6263 if gGameSettings.GameType <> GT_NONE then
6264 begin
6265 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6266 Exit;
6267 end;
6268 if Length(P) < 4 then
6269 begin
6270 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
6271 Exit;
6272 end;
6273 if not StrToIp(P[1], listen) then
6274 Exit;
6275 prt := StrToIntDef(P[2], 25666);
6277 P[3] := addWadExtension(P[3]);
6278 if FileExists(MapsDir + P[3]) then
6279 begin
6280 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6281 if Length(P) < 5 then
6282 begin
6283 SetLength(P, 5);
6284 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
6285 end;
6287 s := P[3] + ':\' + UpperCase(P[4]);
6289 if g_Map_Exist(MapsDir + s) then
6290 begin
6291 // Çàïóñêàåì ñâîþ èãðó
6292 g_Game_Free();
6293 with gGameSettings do
6294 begin
6295 GameMode := g_Game_TextToMode(gcGameMode);
6296 if gSwitchGameMode <> GM_NONE then
6297 GameMode := gSwitchGameMode;
6298 if GameMode = GM_NONE then GameMode := GM_DM;
6299 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6300 b := 0;
6301 if Length(P) >= 6 then
6302 b := StrToIntDef(P[5], 0);
6303 g_Game_StartServer(s, GameMode, TimeLimit,
6304 GoalLimit, MaxLives, Options, b, listen, prt);
6305 end;
6306 end
6307 else
6308 if P[4] = '' then
6309 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6310 else
6311 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]));
6312 end else
6313 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6314 end
6315 else if cmd = 'map' then
6316 begin
6317 if Length(P) = 1 then
6318 begin
6319 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6320 begin
6321 g_Console_Add(cmd + ' <MAP>');
6322 g_Console_Add(cmd + ' <WAD> [MAP]');
6323 end else
6324 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6325 end else
6326 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6327 begin
6328 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6329 if Length(P) < 3 then
6330 begin
6331 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6332 s := UpperCase(P[1]);
6333 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6334 begin // Êàðòà íàøëàñü
6335 gExitByTrigger := False;
6336 if gGameOn then
6337 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6338 gNextMap := s;
6339 gExit := EXIT_ENDLEVELCUSTOM;
6340 end
6341 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6342 g_Game_ChangeMap(s);
6343 end else
6344 begin
6345 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6346 P[1] := addWadExtension(P[1]);
6347 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6348 if FileExists(MapsDir + P[1]) then
6349 begin
6350 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6351 SetLength(P, 3);
6352 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6354 s := P[1] + ':\' + P[2];
6356 if g_Map_Exist(MapsDir + s) then
6357 begin
6358 gExitByTrigger := False;
6359 if gGameOn then
6360 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6361 gNextMap := s;
6362 gExit := EXIT_ENDLEVELCUSTOM;
6363 end
6364 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6365 g_Game_ChangeMap(s);
6366 end else
6367 if P[2] = '' then
6368 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6369 else
6370 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6371 end else
6372 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6373 end;
6374 end else
6375 begin
6376 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6377 P[1] := addWadExtension(P[1]);
6378 if FileExists(MapsDir + P[1]) then
6379 begin
6380 // Íàøëè WAD ôàéë
6381 P[2] := UpperCase(P[2]);
6382 s := P[1] + ':\' + P[2];
6384 if g_Map_Exist(MapsDir + s) then
6385 begin // Íàøëè êàðòó
6386 gExitByTrigger := False;
6387 if gGameOn then
6388 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6389 gNextMap := s;
6390 gExit := EXIT_ENDLEVELCUSTOM;
6391 end
6392 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6393 g_Game_ChangeMap(s);
6394 end else
6395 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6396 end else
6397 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6398 end;
6399 end else
6400 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6401 end
6402 else if cmd = 'nextmap' then
6403 begin
6404 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6405 g_Console_Add(_lc[I_MSG_NOT_GAME])
6406 else begin
6407 nm := True;
6408 if Length(P) = 1 then
6409 begin
6410 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6411 begin
6412 g_Console_Add(cmd + ' <MAP>');
6413 g_Console_Add(cmd + ' <WAD> [MAP]');
6414 end else begin
6415 nm := False;
6416 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6417 end;
6418 end else
6419 begin
6420 nm := False;
6421 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6422 begin
6423 if Length(P) < 3 then
6424 begin
6425 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6426 s := UpperCase(P[1]);
6427 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6428 begin // Êàðòà íàøëàñü
6429 gExitByTrigger := False;
6430 gNextMap := s;
6431 nm := True;
6432 end else
6433 begin
6434 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6435 P[1] := addWadExtension(P[1]);
6436 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6437 if FileExists(MapsDir + P[1]) then
6438 begin
6439 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6440 SetLength(P, 3);
6441 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6443 s := P[1] + ':\' + P[2];
6445 if g_Map_Exist(MapsDir + s) then
6446 begin // Óñòàíàâëèâàåì êàðòó
6447 gExitByTrigger := False;
6448 gNextMap := s;
6449 nm := True;
6450 end else
6451 if P[2] = '' then
6452 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6453 else
6454 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6455 end else
6456 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6457 end;
6458 end else
6459 begin
6460 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6461 P[1] := addWadExtension(P[1]);
6462 if FileExists(MapsDir + P[1]) then
6463 begin
6464 // Íàøëè WAD ôàéë
6465 P[2] := UpperCase(P[2]);
6466 s := P[1] + ':\' + P[2];
6468 if g_Map_Exist(MapsDir + s) then
6469 begin // Íàøëè êàðòó
6470 gExitByTrigger := False;
6471 gNextMap := s;
6472 nm := True;
6473 end else
6474 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6475 end else
6476 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6477 end;
6478 end else
6479 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6480 end;
6481 if nm then
6482 if gNextMap = '' then
6483 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6484 else
6485 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6486 end;
6487 end
6488 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6489 begin
6490 if not gGameOn then
6491 g_Console_Add(_lc[I_MSG_NOT_GAME])
6492 else
6493 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6494 begin
6495 gExitByTrigger := False;
6496 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6497 if (gNextMap = '') and (gTriggers <> nil) then
6498 for a := 0 to High(gTriggers) do
6499 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6500 begin
6501 gExitByTrigger := True;
6502 //gNextMap := gTriggers[a].Data.MapName;
6503 gNextMap := gTriggers[a].tgcMap;
6504 Break;
6505 end;
6506 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6507 if gNextMap = '' then
6508 gNextMap := g_Game_GetNextMap();
6509 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6510 if not isWadPath(gNextMap) then
6511 s := gGameSettings.WAD + ':\' + gNextMap
6512 else
6513 s := gNextMap;
6514 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6515 if g_Map_Exist(MapsDir + s) then
6516 gExit := EXIT_ENDLEVELCUSTOM
6517 else
6518 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6519 end else
6520 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6521 end
6522 else if (cmd = 'event') then
6523 begin
6524 if (Length(P) <= 1) then
6525 begin
6526 for a := 0 to High(gEvents) do
6527 if gEvents[a].Command = '' then
6528 g_Console_Add(gEvents[a].Name + ' <none>')
6529 else
6530 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6531 Exit;
6532 end;
6533 if (Length(P) = 2) then
6534 begin
6535 for a := 0 to High(gEvents) do
6536 if gEvents[a].Name = P[1] then
6537 if gEvents[a].Command = '' then
6538 g_Console_Add(gEvents[a].Name + ' <none>')
6539 else
6540 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6541 Exit;
6542 end;
6543 for a := 0 to High(gEvents) do
6544 if gEvents[a].Name = P[1] then
6545 begin
6546 gEvents[a].Command := '';
6547 for b := 2 to High(P) do
6548 if Pos(' ', P[b]) = 0 then
6549 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6550 else
6551 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6552 gEvents[a].Command := Trim(gEvents[a].Command);
6553 Exit;
6554 end;
6555 end
6556 else if cmd = 'suicide' then
6557 begin
6558 if gGameOn then
6559 begin
6560 if g_Game_IsClient then
6561 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6562 else
6563 begin
6564 if gPlayer1 <> nil then
6565 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6566 if gPlayer2 <> nil then
6567 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6568 end;
6569 end;
6570 end
6571 // Êîìàíäû Ñâîåé èãðû:
6572 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6573 begin
6574 if cmd = 'bot_addred' then
6575 begin
6576 if Length(P) > 1 then
6577 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6578 else
6579 g_Bot_Add(TEAM_RED, 2);
6580 end
6581 else if cmd = 'bot_addblue' then
6582 begin
6583 if Length(P) > 1 then
6584 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6585 else
6586 g_Bot_Add(TEAM_BLUE, 2);
6587 end
6588 else if cmd = 'spectate' then
6589 begin
6590 if not gGameOn then
6591 Exit;
6592 g_Game_Spectate();
6593 end
6594 else if cmd = 'say' then
6595 begin
6596 if g_Game_IsServer and g_Game_IsNet then
6597 begin
6598 if Length(P) > 1 then
6599 begin
6600 chstr := '';
6601 for a := 1 to High(P) do
6602 chstr := chstr + P[a] + ' ';
6604 if Length(chstr) > 200 then SetLength(chstr, 200);
6606 if Length(chstr) < 1 then
6607 begin
6608 g_Console_Add('say <text>');
6609 Exit;
6610 end;
6612 chstr := b_Text_Format(chstr);
6613 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6614 end
6615 else g_Console_Add('say <text>');
6616 end else
6617 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6618 end
6619 else if cmd = 'tell' then
6620 begin
6621 if g_Game_IsServer and g_Game_IsNet then
6622 begin
6623 if (Length(P) > 2) and (P[1] <> '') then
6624 begin
6625 chstr := '';
6626 for a := 2 to High(P) do
6627 chstr := chstr + P[a] + ' ';
6629 if Length(chstr) > 200 then SetLength(chstr, 200);
6631 if Length(chstr) < 1 then
6632 begin
6633 g_Console_Add('tell <playername> <text>');
6634 Exit;
6635 end;
6637 pl := g_Net_Client_ByName(P[1]);
6638 if pl <> nil then
6639 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6640 else
6641 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6642 end
6643 else g_Console_Add('tell <playername> <text>');
6644 end else
6645 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6646 end
6647 else if (cmd = 'overtime') and not g_Game_IsClient then
6648 begin
6649 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6650 Exit;
6651 // Äîïîëíèòåëüíîå âðåìÿ:
6652 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6654 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6655 [gGameSettings.TimeLimit div 3600,
6656 (gGameSettings.TimeLimit div 60) mod 60,
6657 gGameSettings.TimeLimit mod 60]));
6658 if g_Game_IsNet then MH_SEND_GameSettings;
6659 end
6660 else if (cmd = 'rcon_password') and g_Game_IsClient then
6661 begin
6662 if (Length(P) <= 1) then
6663 g_Console_Add('rcon_password <password>')
6664 else
6665 MC_SEND_RCONPassword(P[1]);
6666 end
6667 else if cmd = 'rcon' then
6668 begin
6669 if g_Game_IsClient then
6670 begin
6671 if Length(P) > 1 then
6672 begin
6673 chstr := '';
6674 for a := 1 to High(P) do
6675 chstr := chstr + P[a] + ' ';
6677 if Length(chstr) > 200 then SetLength(chstr, 200);
6679 if Length(chstr) < 1 then
6680 begin
6681 g_Console_Add('rcon <command>');
6682 Exit;
6683 end;
6685 MC_SEND_RCONCommand(chstr);
6686 end
6687 else g_Console_Add('rcon <command>');
6688 end;
6689 end
6690 else if cmd = 'ready' then
6691 begin
6692 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6693 gLMSRespawnTime := gTime + 100;
6694 end
6695 else if (cmd = 'callvote') and g_Game_IsNet then
6696 begin
6697 if Length(P) > 1 then
6698 begin
6699 chstr := '';
6700 for a := 1 to High(P) do begin
6701 if a > 1 then chstr := chstr + ' ';
6702 chstr := chstr + P[a];
6703 end;
6705 if Length(chstr) > 200 then SetLength(chstr, 200);
6707 if Length(chstr) < 1 then
6708 begin
6709 g_Console_Add('callvote <command>');
6710 Exit;
6711 end;
6713 if g_Game_IsClient then
6714 MC_SEND_Vote(True, chstr)
6715 else
6716 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6717 g_Console_Process('vote', True);
6718 end
6719 else
6720 g_Console_Add('callvote <command>');
6721 end
6722 else if (cmd = 'vote') and g_Game_IsNet then
6723 begin
6724 if g_Game_IsClient then
6725 MC_SEND_Vote(False)
6726 else if gVoteInProgress then
6727 begin
6728 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6729 a := Floor((NetClientCount+1)/2.0) + 1
6730 else
6731 a := Floor(NetClientCount/2.0) + 1;
6732 if gVoted then
6733 begin
6734 Dec(gVoteCount);
6735 gVoted := False;
6736 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6737 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6738 end
6739 else
6740 begin
6741 Inc(gVoteCount);
6742 gVoted := True;
6743 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6744 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6745 g_Game_CheckVote;
6746 end;
6747 end;
6748 end
6749 end;
6750 end;
6752 procedure g_TakeScreenShot();
6753 var
6754 a: Word;
6755 FileName: string;
6756 ssdir, t: string;
6757 st: TStream;
6758 ok: Boolean;
6759 begin
6760 if e_NoGraphics then Exit;
6761 ssdir := GameDir+'/screenshots';
6762 if not findFileCI(ssdir, true) then
6763 begin
6764 // try to create dir
6765 try
6766 CreateDir(ssdir);
6767 except
6768 end;
6769 if not findFileCI(ssdir, true) then exit; // alas
6770 end;
6771 try
6772 for a := 1 to High(Word) do
6773 begin
6774 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6775 t := FileName;
6776 if findFileCI(t, true) then continue;
6777 if not findFileCI(FileName) then
6778 begin
6779 ok := false;
6780 st := createDiskFile(FileName);
6781 try
6782 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6783 ok := true;
6784 finally
6785 st.Free();
6786 end;
6787 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6788 break;
6789 end;
6790 end;
6791 except
6792 end;
6793 end;
6795 procedure g_Game_InGameMenu(Show: Boolean);
6796 begin
6797 if (g_ActiveWindow = nil) and Show then
6798 begin
6799 if gGameSettings.GameType = GT_SINGLE then
6800 g_GUI_ShowWindow('GameSingleMenu')
6801 else
6802 begin
6803 if g_Game_IsClient then
6804 g_GUI_ShowWindow('GameClientMenu')
6805 else
6806 if g_Game_IsNet then
6807 g_GUI_ShowWindow('GameServerMenu')
6808 else
6809 g_GUI_ShowWindow('GameCustomMenu');
6810 end;
6811 g_Sound_PlayEx('MENU_OPEN');
6813 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6814 if (not g_Game_IsNet) then
6815 g_Game_Pause(True);
6816 end
6817 else
6818 if (g_ActiveWindow <> nil) and (not Show) then
6819 begin
6820 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6821 if (not g_Game_IsNet) then
6822 g_Game_Pause(False);
6823 end;
6824 end;
6826 procedure g_Game_Pause (Enable: Boolean);
6827 var
6828 oldPause: Boolean;
6829 begin
6830 if not gGameOn then exit;
6832 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6834 oldPause := gPause;
6835 gPauseMain := Enable;
6837 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6838 end;
6840 procedure g_Game_HolmesPause (Enable: Boolean);
6841 var
6842 oldPause: Boolean;
6843 begin
6844 if not gGameOn then exit;
6845 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6847 oldPause := gPause;
6848 gPauseHolmes := Enable;
6850 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6851 end;
6853 procedure g_Game_PauseAllSounds(Enable: Boolean);
6854 var
6855 i: Integer;
6856 begin
6857 // Òðèããåðû:
6858 if gTriggers <> nil then
6859 for i := 0 to High(gTriggers) do
6860 with gTriggers[i] do
6861 if (TriggerType = TRIGGER_SOUND) and
6862 (Sound <> nil) and
6863 Sound.IsPlaying() then
6864 begin
6865 Sound.Pause(Enable);
6866 end;
6868 // Çâóêè èãðîêîâ:
6869 if gPlayers <> nil then
6870 for i := 0 to High(gPlayers) do
6871 if gPlayers[i] <> nil then
6872 gPlayers[i].PauseSounds(Enable);
6874 // Ìóçûêà:
6875 if gMusic <> nil then
6876 gMusic.Pause(Enable);
6877 end;
6879 procedure g_Game_StopAllSounds(all: Boolean);
6880 var
6881 i: Integer;
6882 begin
6883 if gTriggers <> nil then
6884 for i := 0 to High(gTriggers) do
6885 with gTriggers[i] do
6886 if (TriggerType = TRIGGER_SOUND) and
6887 (Sound <> nil) then
6888 Sound.Stop();
6890 if gMusic <> nil then
6891 gMusic.Stop();
6893 if all then
6894 e_StopChannels();
6895 end;
6897 procedure g_Game_UpdateTriggerSounds();
6898 var
6899 i: Integer;
6900 begin
6901 if gTriggers <> nil then
6902 for i := 0 to High(gTriggers) do
6903 with gTriggers[i] do
6904 if (TriggerType = TRIGGER_SOUND) and
6905 (Sound <> nil) and
6906 (tgcLocal) and
6907 Sound.IsPlaying() then
6908 begin
6909 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6910 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6911 begin
6912 Sound.SetPan(0.5 - tgcPan/255.0);
6913 Sound.SetVolume(tgcVolume/255.0);
6914 end
6915 else
6916 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
6917 end;
6918 end;
6920 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6921 begin
6922 Result := False;
6923 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6924 begin
6925 Result := True;
6926 Exit;
6927 end;
6928 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6929 begin
6930 Result := True;
6931 Exit;
6932 end;
6933 if gSpectMode <> SPECT_PLAYERS then
6934 Exit;
6935 if gSpectPID1 = UID then
6936 begin
6937 Result := True;
6938 Exit;
6939 end;
6940 if gSpectViewTwo and (gSpectPID2 = UID) then
6941 begin
6942 Result := True;
6943 Exit;
6944 end;
6945 end;
6947 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6948 var
6949 Pl: TPlayer;
6950 begin
6951 Result := False;
6952 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6953 begin
6954 Result := True;
6955 Exit;
6956 end;
6957 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6958 begin
6959 Result := True;
6960 Exit;
6961 end;
6962 if gSpectMode <> SPECT_PLAYERS then
6963 Exit;
6964 Pl := g_Player_Get(gSpectPID1);
6965 if (Pl <> nil) and (Pl.Team = Team) then
6966 begin
6967 Result := True;
6968 Exit;
6969 end;
6970 if gSpectViewTwo then
6971 begin
6972 Pl := g_Player_Get(gSpectPID2);
6973 if (Pl <> nil) and (Pl.Team = Team) then
6974 begin
6975 Result := True;
6976 Exit;
6977 end;
6978 end;
6979 end;
6981 procedure g_Game_Message(Msg: string; Time: Word);
6982 begin
6983 MessageLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont);
6984 MessageText := b_Text_Wrap(b_Text_Format(Msg), MessageLineLength);
6985 MessageTime := Time;
6986 end;
6988 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
6989 const
6990 punct: Array[0..13] of String =
6991 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
6992 var
6993 i, j: Integer;
6994 ok: Boolean;
6995 fpText: String;
6997 function IsPunctuation(S: String): Boolean;
6998 var
6999 i: Integer;
7000 begin
7001 Result := False;
7002 if Length(S) <> 1 then
7003 Exit;
7004 for i := Low(punct) to High(punct) do
7005 if S = punct[i] then
7006 begin
7007 Result := True;
7008 break;
7009 end;
7010 end;
7011 function FilterPunctuation(S: String): String;
7012 var
7013 i: Integer;
7014 begin
7015 for i := Low(punct) to High(punct) do
7016 S := StringReplace(S, punct[i], ' ', [rfReplaceAll]);
7017 Result := S;
7018 end;
7019 begin
7020 ok := False;
7022 if gUseChatSounds and Taunt and (gChatSounds <> nil) and (Pos(': ', Text) > 0) then
7023 begin
7024 // remove player name
7025 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
7026 // for FullWord check
7027 Text := toLowerCase1251(' ' + Text + ' ');
7028 fpText := FilterPunctuation(Text);
7030 for i := 0 to Length(gChatSounds) - 1 do
7031 begin
7032 ok := True;
7033 for j := 0 to Length(gChatSounds[i].Tags) - 1 do
7034 begin
7035 if gChatSounds[i].FullWord and (not IsPunctuation(gChatSounds[i].Tags[j])) then
7036 ok := Pos(' ' + gChatSounds[i].Tags[j] + ' ', fpText) > 0
7037 else
7038 ok := Pos(gChatSounds[i].Tags[j], Text) > 0;
7039 if not ok then
7040 break;
7041 end;
7042 if ok then
7043 begin
7044 gChatSounds[i].Sound.Play();
7045 break;
7046 end;
7047 end;
7048 end;
7049 if not ok then
7050 g_Sound_PlayEx('SOUND_GAME_RADIO');
7051 end;
7053 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
7054 var
7055 a: Integer;
7056 begin
7057 case gAnnouncer of
7058 ANNOUNCE_NONE:
7059 Exit;
7060 ANNOUNCE_ME,
7061 ANNOUNCE_MEPLUS:
7062 if not g_Game_IsWatchedPlayer(SpawnerUID) then
7063 Exit;
7064 end;
7065 for a := 0 to 3 do
7066 if goodsnd[a].IsPlaying() then
7067 Exit;
7069 goodsnd[Random(4)].Play();
7070 end;
7072 procedure g_Game_Announce_KillCombo(Param: Integer);
7073 var
7074 UID: Word;
7075 c, n: Byte;
7076 Pl: TPlayer;
7077 Name: String;
7078 begin
7079 UID := Param and $FFFF;
7080 c := Param shr 16;
7081 if c < 2 then
7082 Exit;
7084 Pl := g_Player_Get(UID);
7085 if Pl = nil then
7086 Name := '?'
7087 else
7088 Name := Pl.Name;
7090 case c of
7091 2: begin
7092 n := 0;
7093 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
7094 end;
7095 3: begin
7096 n := 1;
7097 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
7098 end;
7099 4: begin
7100 n := 2;
7101 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
7102 end;
7103 else begin
7104 n := 3;
7105 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
7106 end;
7107 end;
7109 case gAnnouncer of
7110 ANNOUNCE_NONE:
7111 Exit;
7112 ANNOUNCE_ME:
7113 if not g_Game_IsWatchedPlayer(UID) then
7114 Exit;
7115 ANNOUNCE_MEPLUS:
7116 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
7117 Exit;
7118 end;
7120 if killsnd[n].IsPlaying() then
7121 killsnd[n].Stop();
7122 killsnd[n].Play();
7123 end;
7125 procedure g_Game_StartVote(Command, Initiator: string);
7126 var
7127 Need: Integer;
7128 begin
7129 if not gVotesEnabled then Exit;
7130 if gGameSettings.GameType <> GT_SERVER then Exit;
7131 if gVoteInProgress or gVotePassed then
7132 begin
7133 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
7134 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
7135 Exit;
7136 end;
7137 gVoteInProgress := True;
7138 gVotePassed := False;
7139 gVoteTimer := gTime + gVoteTimeout * 1000;
7140 gVoteCount := 0;
7141 gVoted := False;
7142 gVoteCommand := Command;
7144 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7145 Need := Floor((NetClientCount+1)/2.0)+1
7146 else
7147 Need := Floor(NetClientCount/2.0)+1;
7148 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
7149 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
7150 end;
7152 procedure g_Game_CheckVote;
7153 var
7154 I, Need: Integer;
7155 begin
7156 if gGameSettings.GameType <> GT_SERVER then Exit;
7157 if not gVoteInProgress then Exit;
7159 if (gTime >= gVoteTimer) then
7160 begin
7161 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7162 Need := Floor((NetClientCount+1)/2.0) + 1
7163 else
7164 Need := Floor(NetClientCount/2.0) + 1;
7165 if gVoteCount >= Need then
7166 begin
7167 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7168 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7169 gVotePassed := True;
7170 gVoteCmdTimer := gTime + 5000;
7171 end
7172 else
7173 begin
7174 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
7175 MH_SEND_VoteEvent(NET_VE_FAILED);
7176 end;
7177 if NetClients <> nil then
7178 for I := Low(NetClients) to High(NetClients) do
7179 if NetClients[i].Used then
7180 NetClients[i].Voted := False;
7181 gVoteInProgress := False;
7182 gVoted := False;
7183 gVoteCount := 0;
7184 end
7185 else
7186 begin
7187 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7188 Need := Floor((NetClientCount+1)/2.0) + 1
7189 else
7190 Need := Floor(NetClientCount/2.0) + 1;
7191 if gVoteCount >= Need then
7192 begin
7193 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7194 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7195 gVoteInProgress := False;
7196 gVotePassed := True;
7197 gVoteCmdTimer := gTime + 5000;
7198 gVoted := False;
7199 gVoteCount := 0;
7200 if NetClients <> nil then
7201 for I := Low(NetClients) to High(NetClients) do
7202 if NetClients[i].Used then
7203 NetClients[i].Voted := False;
7204 end;
7205 end;
7206 end;
7208 procedure g_Game_LoadMapList(FileName: string);
7209 var
7210 ListFile: TextFile;
7211 s: string;
7212 begin
7213 MapList := nil;
7214 MapIndex := -1;
7216 if not FileExists(FileName) then Exit;
7218 AssignFile(ListFile, FileName);
7219 Reset(ListFile);
7220 while not EOF(ListFile) do
7221 begin
7222 ReadLn(ListFile, s);
7224 s := Trim(s);
7225 if s = '' then Continue;
7227 SetLength(MapList, Length(MapList)+1);
7228 MapList[High(MapList)] := s;
7229 end;
7230 CloseFile(ListFile);
7231 end;
7233 procedure g_Game_SetDebugMode();
7234 begin
7235 gDebugMode := True;
7236 // ×èòû (äàæå â ñâîåé èãðå):
7237 gCheats := True;
7238 end;
7240 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
7241 var
7242 i: Word;
7243 begin
7244 if Length(LoadingStat.Msgs) = 0 then
7245 Exit;
7247 with LoadingStat do
7248 begin
7249 if not reWrite then
7250 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
7251 if NextMsg = Length(Msgs) then
7252 begin // scroll
7253 for i := 0 to High(Msgs)-1 do
7254 Msgs[i] := Msgs[i+1];
7255 end
7256 else
7257 Inc(NextMsg);
7258 end else
7259 if NextMsg = 0 then
7260 Inc(NextMsg);
7262 Msgs[NextMsg-1] := Text;
7263 CurValue := 0;
7264 MaxValue := Max;
7265 ShowCount := 0;
7266 PBarWasHere := false;
7267 end;
7269 g_ActiveWindow := nil;
7271 ProcessLoading(true);
7272 end;
7274 procedure g_Game_StepLoading();
7275 begin
7276 with LoadingStat do
7277 begin
7278 Inc(CurValue);
7279 Inc(ShowCount);
7280 if (ShowCount > LOADING_SHOW_STEP) then
7281 begin
7282 ShowCount := 0;
7283 ProcessLoading();
7284 end;
7285 end;
7286 end;
7288 procedure g_Game_ClearLoading();
7289 var
7290 len: Word;
7291 begin
7292 with LoadingStat do
7293 begin
7294 CurValue := 0;
7295 MaxValue := 0;
7296 ShowCount := 0;
7297 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
7298 if len < 1 then len := 1;
7299 SetLength(Msgs, len);
7300 for len := Low(Msgs) to High(Msgs) do
7301 Msgs[len] := '';
7302 NextMsg := 0;
7303 PBarWasHere := false;
7304 end;
7305 end;
7307 procedure Parse_Params(var pars: TParamStrValues);
7308 var
7309 i: Integer;
7310 s: String;
7311 begin
7312 SetLength(pars, 0);
7313 i := 1;
7314 while i <= ParamCount do
7315 begin
7316 s := ParamStr(i);
7317 if (s[1] = '-') and (Length(s) > 1) then
7318 begin
7319 if (s[2] = '-') and (Length(s) > 2) then
7320 begin // Îäèíî÷íûé ïàðàìåòð
7321 SetLength(pars, Length(pars) + 1);
7322 with pars[High(pars)] do
7323 begin
7324 Name := LowerCase(s);
7325 Value := '+';
7326 end;
7327 end
7328 else
7329 if (i < ParamCount) then
7330 begin // Ïàðàìåòð ñî çíà÷åíèåì
7331 Inc(i);
7332 SetLength(pars, Length(pars) + 1);
7333 with pars[High(pars)] do
7334 begin
7335 Name := LowerCase(s);
7336 Value := LowerCase(ParamStr(i));
7337 end;
7338 end;
7339 end;
7341 Inc(i);
7342 end;
7343 end;
7345 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
7346 var
7347 i: Integer;
7348 begin
7349 Result := '';
7350 for i := 0 to High(pars) do
7351 if pars[i].Name = aName then
7352 begin
7353 Result := pars[i].Value;
7354 Break;
7355 end;
7356 end;
7358 procedure g_Game_Process_Params();
7359 var
7360 pars: TParamStrValues;
7361 map: String;
7362 GMode, n: Byte;
7363 LimT, LimS: Integer;
7364 Opt: LongWord;
7365 Lives: Integer;
7366 s: String;
7367 Port: Integer;
7368 ip: String;
7369 F: TextFile;
7370 begin
7371 Parse_Params(pars);
7373 // Debug mode:
7374 s := Find_Param_Value(pars, '--debug');
7375 if (s <> '') then
7376 begin
7377 g_Game_SetDebugMode();
7378 s := Find_Param_Value(pars, '--netdump');
7379 if (s <> '') then
7380 NetDump := True;
7381 end;
7383 // Connect when game loads
7384 ip := Find_Param_Value(pars, '-connect');
7386 if ip <> '' then
7387 begin
7388 s := Find_Param_Value(pars, '-port');
7389 if (s = '') or not TryStrToInt(s, Port) then
7390 Port := 25666;
7392 s := Find_Param_Value(pars, '-pw');
7394 g_Game_StartClient(ip, Port, s);
7395 Exit;
7396 end;
7398 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7399 if (s <> '') then
7400 begin
7401 gDefaultMegawadStart := s;
7402 end;
7404 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7405 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7406 begin
7407 gDefaultMegawadStart := DF_Default_Megawad_Start;
7408 end;
7410 // Start map when game loads:
7411 map := LowerCase(Find_Param_Value(pars, '-map'));
7412 if isWadPath(map) then
7413 begin
7414 // Game mode:
7415 s := Find_Param_Value(pars, '-gm');
7416 GMode := g_Game_TextToMode(s);
7417 if GMode = GM_NONE then GMode := GM_DM;
7418 if GMode = GM_SINGLE then GMode := GM_COOP;
7420 // Time limit:
7421 s := Find_Param_Value(pars, '-limt');
7422 if (s = '') or (not TryStrToInt(s, LimT)) then
7423 LimT := 0;
7424 if LimT < 0 then
7425 LimT := 0;
7427 // Goal limit:
7428 s := Find_Param_Value(pars, '-lims');
7429 if (s = '') or (not TryStrToInt(s, LimS)) then
7430 LimS := 0;
7431 if LimS < 0 then
7432 LimS := 0;
7434 // Lives limit:
7435 s := Find_Param_Value(pars, '-lives');
7436 if (s = '') or (not TryStrToInt(s, Lives)) then
7437 Lives := 0;
7438 if Lives < 0 then
7439 Lives := 0;
7441 // Options:
7442 s := Find_Param_Value(pars, '-opt');
7443 if (s = '') then
7444 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7445 else
7446 Opt := StrToIntDef(s, 0);
7447 if Opt = 0 then
7448 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7450 // Close after map:
7451 s := Find_Param_Value(pars, '--close');
7452 if (s <> '') then
7453 gMapOnce := True;
7455 // Override map to test:
7456 s := LowerCase(Find_Param_Value(pars, '-testmap'));
7457 if s <> '' then
7458 gTestMap := MapsDir + s;
7460 // Delete test map after play:
7461 s := Find_Param_Value(pars, '--testdelete');
7462 if (s <> '') then
7463 begin
7464 gMapToDelete := MapsDir + map;
7465 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
7466 Halt(1);
7467 end;
7469 // Delete temporary WAD after play:
7470 s := Find_Param_Value(pars, '--tempdelete');
7471 if (s <> '') and (gTestMap <> '') then
7472 begin
7473 gMapToDelete := gTestMap;
7474 gTempDelete := True;
7475 end;
7477 // Number of players:
7478 s := Find_Param_Value(pars, '-pl');
7479 if (s = '') then
7480 n := 1
7481 else
7482 n := StrToIntDef(s, 1);
7484 // Start:
7485 s := Find_Param_Value(pars, '-port');
7486 if (s = '') or not TryStrToInt(s, Port) then
7487 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7488 else
7489 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7490 end;
7492 // Execute script when game loads:
7493 s := Find_Param_Value(pars, '-exec');
7494 if s <> '' then
7495 begin
7496 if not isWadPath(s) then
7497 s := GameDir + '/' + s;
7499 {$I-}
7500 AssignFile(F, s);
7501 Reset(F);
7502 if IOResult <> 0 then
7503 begin
7504 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7505 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7506 CloseFile(F);
7507 Exit;
7508 end;
7509 e_WriteLog('Executing script: ' + s, TMsgType.Notify);
7510 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7512 while not EOF(F) do
7513 begin
7514 ReadLn(F, s);
7515 if IOResult <> 0 then
7516 begin
7517 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7518 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7519 CloseFile(F);
7520 Exit;
7521 end;
7522 if Pos('#', s) <> 1 then // script comment
7523 g_Console_Process(s, True);
7524 end;
7526 CloseFile(F);
7527 {$I+}
7528 end;
7530 SetLength(pars, 0);
7531 end;
7533 begin
7534 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7535 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7536 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7537 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7539 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7540 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7541 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7542 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7544 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7545 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7547 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7548 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7550 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7552 conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds, 'ignore level bounds', '', false);
7554 conRegVar('r_scale', @g_dbg_scale, 0.01, 100.0, 'render scale', '', false);
7556 conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
7557 conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
7558 end.