DEADSOFTWARE

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