DEADSOFTWARE

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