DEADSOFTWARE

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