DEADSOFTWARE

fixed two compiler warnings (one is definitely false positive, but meh...)
[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 Result := Current;
1482 retry:
1483 case Random(7) of
1484 0: Result := SPECT_STATS;
1485 1: Result := SPECT_MAPVIEW;
1486 2: Result := SPECT_MAPVIEW;
1487 3: Result := SPECT_PLAYERS;
1488 4: Result := SPECT_PLAYERS;
1489 5: Result := SPECT_PLAYERS;
1490 6: Result := SPECT_PLAYERS;
1491 end;
1492 if (Current in [SPECT_STATS, SPECT_MAPVIEW]) and (Current = Result) then
1493 goto retry;
1494 end;
1496 function isKeyPressed (key1: Word; key2: Word): Boolean;
1497 begin
1498 if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
1499 if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
1500 result := false;
1501 end;
1503 procedure processPlayerControls (plr: TPlayer; var ctrl: TPlayerControl; var MoveButton: Byte; p2hack: Boolean=false);
1504 var
1505 time: Word;
1506 strafeDir: Byte;
1507 i: Integer;
1508 begin
1509 if (plr = nil) then exit;
1510 if (p2hack) then time := 1000 else time := 1;
1511 strafeDir := MoveButton shr 4;
1512 MoveButton := MoveButton and $0F;
1513 with ctrl do
1514 begin
1515 if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1516 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1517 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1519 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1520 if MoveButton = 1 then plr.PressKey(KEY_LEFT, time)
1521 else if MoveButton = 2 then plr.PressKey(KEY_RIGHT, time);
1523 // if we have "strafe" key, turn off old strafe mechanics
1524 if isKeyPressed(KeyStrafe, KeyStrafe2) then
1525 begin
1526 // new strafe mechanics
1527 if (strafeDir = 0) then strafeDir := MoveButton; // start strafing
1528 // now set direction according to strafe (reversed)
1529 if (strafeDir = 2) then plr.SetDirection(TDirection.D_LEFT)
1530 else if (strafeDir = 1) then plr.SetDirection(TDirection.D_RIGHT);
1531 end
1532 else
1533 begin
1534 strafeDir := 0; // not strafing anymore
1535 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1536 if (MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then plr.SetDirection(TDirection.D_LEFT)
1537 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1538 else if (MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then plr.SetDirection(TDirection.D_RIGHT)
1539 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1540 else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1));
1541 end;
1543 // fix movebutton state
1544 MoveButton := MoveButton or (strafeDir shl 4);
1546 // Îñòàëüíûå êëàâèøè:
1547 if isKeyPressed(KeyJump, KeyJump2) then plr.PressKey(KEY_JUMP, time);
1548 if isKeyPressed(KeyUp, KeyUp2) then plr.PressKey(KEY_UP, time);
1549 if isKeyPressed(KeyDown, KeyDown2) then plr.PressKey(KEY_DOWN, time);
1550 if isKeyPressed(KeyFire, KeyFire2) then plr.PressKey(KEY_FIRE);
1551 if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then plr.PressKey(KEY_NEXTWEAPON);
1552 if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then plr.PressKey(KEY_PREVWEAPON);
1553 if isKeyPressed(KeyOpen, KeyOpen2) then plr.PressKey(KEY_OPEN);
1555 for i := 0 to High(KeyWeapon) do
1556 if isKeyPressed(KeyWeapon[i], KeyWeapon2[i]) then
1557 plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
1558 end;
1560 // HACK: add dynlight here
1561 if gwin_k8_enable_light_experiments then
1562 begin
1563 if e_KeyPressed(IK_F8) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1564 begin
1565 g_playerLight := true;
1566 end;
1567 if e_KeyPressed(IK_F9) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1568 begin
1569 g_playerLight := false;
1570 end;
1571 end;
1573 if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
1574 end;
1576 procedure g_Game_Update();
1577 var
1578 Msg: g_gui.TMessage;
1579 Time: Int64;
1580 a: Byte;
1581 w: Word;
1582 i, b: Integer;
1584 function sendMonsPos (mon: TMonster): Boolean;
1585 begin
1586 result := false; // don't stop
1587 // this will also reset "need-send" flag
1588 if mon.gncNeedSend then
1589 begin
1590 MH_SEND_MonsterPos(mon.UID);
1591 end
1592 else if (mon.MonsterType = MONSTER_BARREL) then
1593 begin
1594 if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1595 end
1596 else if (mon.MonsterState <> MONSTATE_SLEEP) then
1597 begin
1598 if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1599 end;
1600 end;
1602 function sendMonsPosUnexpected (mon: TMonster): Boolean;
1603 begin
1604 result := false; // don't stop
1605 // this will also reset "need-send" flag
1606 if mon.gncNeedSend then MH_SEND_MonsterPos(mon.UID);
1607 end;
1609 var
1610 reliableUpdate: Boolean;
1611 begin
1612 g_ResetDynlights();
1613 framePool.reset();
1615 // Ïîðà âûêëþ÷àòü èãðó:
1616 if gExit = EXIT_QUIT then
1617 Exit;
1618 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1619 if gExit <> 0 then
1620 begin
1621 EndGame();
1622 if gExit = EXIT_QUIT then
1623 Exit;
1624 end;
1626 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî
1627 // no need to, as we'll do it in event handler
1629 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1630 g_Console_Update();
1632 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1633 begin
1634 gExit := EXIT_SIMPLE;
1635 EndGame();
1636 Exit;
1637 end;
1639 case gState of
1640 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1641 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1642 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1643 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1644 begin
1645 if g_Game_IsNet and g_Game_IsServer then
1646 begin
1647 gInterTime := gInterTime + GAME_TICK;
1648 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1649 if a <> gServInterTime then
1650 begin
1651 gServInterTime := a;
1652 MH_SEND_TimeSync(gServInterTime);
1653 end;
1654 end;
1656 if (not g_Game_IsClient) and
1660 e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or
1661 e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN)
1663 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1664 and (g_ActiveWindow = nil)
1666 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1668 then
1669 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1670 g_Game_StopAllSounds(True);
1672 if gMapOnce then // Ýòî áûë òåñò
1673 gExit := EXIT_SIMPLE
1674 else
1675 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1676 g_Game_ChangeMap(gNextMap)
1677 else // Ñëåäóþùåé êàðòû íåò
1678 begin
1679 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1680 begin
1681 // Âûõîä â ãëàâíîå ìåíþ:
1682 g_Game_Free;
1683 g_GUI_ShowWindow('MainMenu');
1684 gMusic.SetByName('MUSIC_MENU');
1685 gMusic.Play();
1686 gState := STATE_MENU;
1687 end else
1688 begin
1689 // Ôèíàëüíàÿ êàðòèíêà:
1690 g_Game_ExecuteEvent('onwadend');
1691 g_Game_Free();
1692 if not gMusic.SetByName('MUSIC_endmus') then
1693 gMusic.SetByName('MUSIC_STDENDMUS');
1694 gMusic.Play();
1695 gState := STATE_ENDPIC;
1696 end;
1697 g_Game_ExecuteEvent('ongameend');
1698 end;
1700 Exit;
1701 end;
1703 if gState = STATE_INTERTEXT then
1704 if InterText.counter > 0 then
1705 InterText.counter := InterText.counter - 1;
1706 end;
1708 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1709 begin
1710 if EndingGameCounter = 0 then
1711 begin
1712 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1713 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1714 begin
1715 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1716 begin
1717 g_Game_ExecuteEvent('onwadend');
1718 if not gMusic.SetByName('MUSIC_endmus') then
1719 gMusic.SetByName('MUSIC_STDENDMUS');
1720 end
1721 else
1722 gMusic.SetByName('MUSIC_ROUNDMUS');
1724 gMusic.Play();
1725 gState := STATE_INTERCUSTOM;
1726 e_UnpressAllKeys();
1727 end
1728 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1729 begin
1730 gMusic.SetByName('MUSIC_INTERMUS');
1731 gMusic.Play();
1732 gState := STATE_INTERSINGLE;
1733 e_UnpressAllKeys();
1734 end;
1735 g_Game_ExecuteEvent('oninter');
1736 end
1737 else
1738 DecMin(EndingGameCounter, 6, 0);
1739 end;
1741 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1742 begin
1743 if gMapOnce then // Ýòî áûë òåñò
1744 begin
1745 gExit := EXIT_SIMPLE;
1746 Exit;
1747 end;
1748 end;
1750 STATE_SLIST:
1751 g_Serverlist_Control(slCurrent, slTable);
1752 end;
1754 if g_Game_IsNet then
1755 if not gConsoleShow then
1756 if not gChatShow then
1757 begin
1758 if g_ActiveWindow = nil then
1759 begin
1760 if e_KeyPressed(gGameControls.GameControls.Chat) or e_KeyPressed(VK_CHAT) then
1761 g_Console_Chat_Switch(False)
1762 else if (e_KeyPressed(gGameControls.GameControls.TeamChat) or e_KeyPressed(VK_TEAM)) and
1763 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1764 g_Console_Chat_Switch(True);
1765 end;
1766 end else
1767 if not gChatEnter then
1768 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1769 and (not e_KeyPressed(gGameControls.GameControls.TeamChat))
1770 and (not e_KeyPressed(VK_CHAT))
1771 and (not e_KeyPressed(VK_TEAM)) then
1772 gChatEnter := True;
1774 // Ñòàòèñòèêà ïî Tab:
1775 if gGameOn then
1776 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1777 (gGameSettings.GameType <> GT_SINGLE) and
1778 (e_KeyPressed(gGameControls.GameControls.Stat) or e_KeyPressed(VK_STATUS));
1780 // Èãðà èäåò:
1781 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1782 begin
1783 // Âðåìÿ += 28 ìèëëèñåêóíä:
1784 gTime := gTime + GAME_TICK;
1786 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1787 if MessageTime = 0 then
1788 MessageText := '';
1789 if MessageTime > 0 then
1790 MessageTime := MessageTime - 1;
1792 if (g_Game_IsServer) then
1793 begin
1794 // Áûë çàäàí ëèìèò âðåìåíè:
1795 if (gGameSettings.TimeLimit > 0) then
1796 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1797 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1798 g_Game_NextLevel();
1799 Exit;
1800 end;
1802 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1803 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1804 g_Game_RestartRound(gLMSSoftSpawn);
1806 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1807 if gVoteInProgress and (gVoteTimer < gTime) then
1808 g_Game_CheckVote
1809 else if gVotePassed and (gVoteCmdTimer < gTime) then
1810 begin
1811 g_Console_Process(gVoteCommand);
1812 gVoteCommand := '';
1813 gVotePassed := False;
1814 end;
1816 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1817 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1818 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1819 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1820 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1822 // Áûë çàäàí ëèìèò ïîáåä:
1823 if (gGameSettings.GoalLimit > 0) then
1824 begin
1825 b := 0;
1827 if gGameSettings.GameMode = GM_DM then
1828 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1829 for i := 0 to High(gPlayers) do
1830 if gPlayers[i] <> nil then
1831 if gPlayers[i].Frags > b then
1832 b := gPlayers[i].Frags;
1833 end
1834 else
1835 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1836 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1837 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1838 end;
1840 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1841 if b >= gGameSettings.GoalLimit then
1842 begin
1843 g_Game_NextLevel();
1844 Exit;
1845 end;
1846 end;
1848 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1849 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1850 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1851 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1852 begin
1853 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1854 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1855 end // if not console
1856 else
1857 begin
1858 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1859 end;
1860 // process weapon switch queue
1861 end; // if server
1863 // Íàáëþäàòåëü
1864 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1865 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1866 begin
1867 if not gSpectKeyPress then
1868 begin
1869 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)
1870 and (not gSpectAuto) then
1871 begin
1872 // switch spect mode
1873 case gSpectMode of
1874 SPECT_NONE: ; // not spectator
1875 SPECT_STATS,
1876 SPECT_MAPVIEW: Inc(gSpectMode);
1877 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1878 end;
1879 gSpectKeyPress := True;
1880 end;
1881 if (gSpectMode = SPECT_MAPVIEW)
1882 and (not gSpectAuto) then
1883 begin
1884 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1885 gSpectX := Max(gSpectX - gSpectStep, 0);
1886 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1887 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1888 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1889 gSpectY := Max(gSpectY - gSpectStep, 0);
1890 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1891 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1892 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1893 begin
1894 // decrease step
1895 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1896 gSpectKeyPress := True;
1897 end;
1898 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1899 begin
1900 // increase step
1901 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1902 gSpectKeyPress := True;
1903 end;
1904 end;
1905 if (gSpectMode = SPECT_PLAYERS)
1906 and (not gSpectAuto) then
1907 begin
1908 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1909 begin
1910 // add second view
1911 gSpectViewTwo := True;
1912 gSpectKeyPress := True;
1913 end;
1914 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1915 begin
1916 // remove second view
1917 gSpectViewTwo := False;
1918 gSpectKeyPress := True;
1919 end;
1920 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1921 begin
1922 // prev player (view 1)
1923 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1924 gSpectKeyPress := True;
1925 end;
1926 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1927 begin
1928 // next player (view 1)
1929 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1930 gSpectKeyPress := True;
1931 end;
1932 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1933 begin
1934 // prev player (view 2)
1935 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1936 gSpectKeyPress := True;
1937 end;
1938 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1939 begin
1940 // next player (view 2)
1941 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1942 gSpectKeyPress := True;
1943 end;
1944 end;
1945 if isKeyPressed(gGameControls.P1Control.KeyFire, gGameControls.P1Control.KeyFire2) then
1946 begin
1947 if (gSpectMode = SPECT_STATS) and (not gSpectAuto) then
1948 begin
1949 gSpectAuto := True;
1950 gSpectAutoNext := 0;
1951 gSpectViewTwo := False;
1952 gSpectKeyPress := True;
1953 end
1954 else
1955 if gSpectAuto then
1956 begin
1957 gSpectMode := SPECT_STATS;
1958 gSpectAuto := False;
1959 gSpectKeyPress := True;
1960 end;
1961 end;
1962 end
1963 else
1964 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1965 (not isKeyPressed(gGameControls.P1Control.KeyFire, gGameControls.P1Control.KeyFire2)) and
1966 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1967 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1968 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1969 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1970 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1971 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1972 gSpectKeyPress := False;
1974 if gSpectAuto then
1975 begin
1976 if gSpectMode = SPECT_MAPVIEW then
1977 begin
1978 i := Min(Max(gSpectX + gSpectAutoStepX, 0), gMapInfo.Width - gScreenWidth);
1979 if i = gSpectX then
1980 gSpectAutoNext := gTime
1981 else
1982 gSpectX := i;
1983 i := Min(Max(gSpectY + gSpectAutoStepY, 0), gMapInfo.Height - gScreenHeight);
1984 if i = gSpectY then
1985 gSpectAutoNext := gTime
1986 else
1987 gSpectY := i;
1988 end;
1989 if gSpectAutoNext <= gTime then
1990 begin
1991 if gSpectAutoNext > 0 then
1992 begin
1993 gSpectMode := GetRandomSpectMode(gSpectMode);
1994 case gSpectMode of
1995 SPECT_MAPVIEW:
1996 begin
1997 gSpectX := Random(gMapInfo.Width - gScreenWidth);
1998 gSpectY := Random(gMapInfo.Height - gScreenHeight);
1999 gSpectAutoStepX := Random(9) - 4;
2000 gSpectAutoStepY := Random(9) - 4;
2001 if ((gSpectX < 800) and (gSpectAutoStepX < 0)) or
2002 ((gSpectX > gMapInfo.Width - gScreenWidth - 800) and (gSpectAutoStepX > 0)) then
2003 gSpectAutoStepX := gSpectAutoStepX * -1;
2004 if ((gSpectY < 800) and (gSpectAutoStepY < 0)) or
2005 ((gSpectY > gMapInfo.Height - gScreenHeight - 800) and (gSpectAutoStepY > 0)) then
2006 gSpectAutoStepY := gSpectAutoStepY * -1;
2007 end;
2008 SPECT_PLAYERS:
2009 begin
2010 gSpectPID1 := GetActivePlayerID_Random(gSpectPID1);
2011 end;
2012 end;
2013 end;
2014 case gSpectMode of
2015 SPECT_STATS: gSpectAutoNext := gTime + (Random(3) + 5) * 1000;
2016 SPECT_MAPVIEW: gSpectAutoNext := gTime + (Random(4) + 7) * 1000;
2017 SPECT_PLAYERS: gSpectAutoNext := gTime + (Random(7) + 8) * 1000;
2018 end;
2019 end;
2020 end;
2021 end;
2023 // Îáíîâëÿåì âñå îñòàëüíîå:
2024 g_Map_Update();
2025 g_Items_Update();
2026 g_Triggers_Update();
2027 g_Weapon_Update();
2028 g_Monsters_Update();
2029 g_GFX_Update();
2030 g_Player_UpdateAll();
2031 g_Player_UpdatePhysicalObjects();
2033 // server: send newly spawned monsters unconditionally
2034 if (gGameSettings.GameType = GT_SERVER) then
2035 begin
2036 if (Length(gMonstersSpawned) > 0) then
2037 begin
2038 for I := 0 to High(gMonstersSpawned) do MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
2039 SetLength(gMonstersSpawned, 0);
2040 end;
2041 end;
2043 if (gSoundTriggerTime > 8) then
2044 begin
2045 g_Game_UpdateTriggerSounds();
2046 gSoundTriggerTime := 0;
2047 end
2048 else
2049 begin
2050 Inc(gSoundTriggerTime);
2051 end;
2053 if (NetMode = NET_SERVER) then
2054 begin
2055 Inc(NetTimeToUpdate);
2056 Inc(NetTimeToReliable);
2058 // send monster updates
2059 if (NetTimeToReliable >= NetRelupdRate) or (NetTimeToUpdate >= NetUpdateRate) then
2060 begin
2061 // send all monsters (periodic sync)
2062 reliableUpdate := (NetTimeToReliable >= NetRelupdRate);
2064 for I := 0 to High(gPlayers) do
2065 begin
2066 if (gPlayers[I] <> nil) then MH_SEND_PlayerPos(reliableUpdate, gPlayers[I].UID);
2067 end;
2069 g_Mons_ForEach(sendMonsPos);
2071 if reliableUpdate then
2072 begin
2073 NetTimeToReliable := 0;
2074 NetTimeToUpdate := NetUpdateRate;
2075 end
2076 else
2077 begin
2078 NetTimeToUpdate := 0;
2079 end;
2080 end
2081 else
2082 begin
2083 // send only mosters with some unexpected changes
2084 g_Mons_ForEach(sendMonsPosUnexpected);
2085 end;
2087 // send unexpected platform changes
2088 g_Map_NetSendInterestingPanels();
2090 if NetUseMaster then
2091 begin
2092 if gTime >= NetTimeToMaster then
2093 begin
2094 if (NetMHost = nil) or (NetMPeer = nil) then
2095 begin
2096 if not g_Net_Slist_Connect then g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
2097 end;
2099 g_Net_Slist_Update;
2100 NetTimeToMaster := gTime + NetMasterRate;
2101 end;
2102 end;
2103 end
2104 else if (NetMode = NET_CLIENT) then
2105 begin
2106 MC_SEND_PlayerPos();
2107 end;
2108 end; // if gameOn ...
2110 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
2111 if g_ActiveWindow <> nil then
2112 begin
2113 w := e_GetFirstKeyPressed();
2115 if (w <> IK_INVALID) then
2116 begin
2117 Msg.Msg := MESSAGE_DIKEY;
2118 Msg.wParam := w;
2119 g_ActiveWindow.OnMessage(Msg);
2120 end;
2122 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
2123 if g_ActiveWindow <> nil then
2124 g_ActiveWindow.Update();
2126 // Íóæíî ñìåíèòü ðàçðåøåíèå:
2127 if gResolutionChange then
2128 begin
2129 e_WriteLog('Changing resolution', TMsgType.Notify);
2130 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
2131 gResolutionChange := False;
2132 g_ActiveWindow := nil;
2133 end;
2135 // Íóæíî ñìåíèòü ÿçûê:
2136 if gLanguageChange then
2137 begin
2138 //e_WriteLog('Read language file', MSG_NOTIFY);
2139 //g_Language_Load(DataDir + gLanguage + '.txt');
2140 g_Language_Set(gLanguage);
2141 g_Menu_Reset();
2142 gLanguageChange := False;
2143 end;
2144 end;
2146 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
2147 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) or e_KeyPressed(VK_PRINTSCR) then
2148 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
2149 begin
2150 g_TakeScreenShot();
2151 LastScreenShot := GetTimer();
2152 end;
2154 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
2155 if e_KeyPressed(IK_F10) and
2156 gGameOn and
2157 (not gConsoleShow) and
2158 (g_ActiveWindow = nil) then
2159 begin
2160 KeyPress(IK_F10);
2161 end;
2163 Time := GetTimer() {div 1000};
2165 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
2166 if gDelayedEvents <> nil then
2167 for a := 0 to High(gDelayedEvents) do
2168 if gDelayedEvents[a].Pending and
2170 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
2171 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
2172 ) then
2173 begin
2174 case gDelayedEvents[a].DEType of
2175 DE_GLOBEVENT:
2176 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
2177 DE_BFGHIT:
2178 if gGameOn then
2179 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
2180 DE_KILLCOMBO:
2181 if gGameOn then
2182 begin
2183 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
2184 if g_Game_IsNet and g_Game_IsServer then
2185 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
2186 end;
2187 end;
2188 gDelayedEvents[a].Pending := False;
2189 end;
2191 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2192 UPSCounter := UPSCounter + 1;
2193 if Time - UPSTime >= 1000 then
2194 begin
2195 UPS := UPSCounter;
2196 UPSCounter := 0;
2197 UPSTime := Time;
2198 end;
2200 if gGameOn then
2201 begin
2202 g_Weapon_AddDynLights();
2203 g_Items_AddDynLights();
2204 end;
2205 end;
2207 procedure g_Game_LoadChatSounds(Resource: string);
2208 var
2209 WAD: TWADFile;
2210 FileName, Snd: string;
2211 p: Pointer;
2212 len, cnt, tags, i, j: Integer;
2213 cfg: TConfig;
2214 begin
2215 FileName := g_ExtractWadName(Resource);
2217 WAD := TWADFile.Create();
2218 WAD.ReadFile(FileName);
2220 if not WAD.GetResource(g_ExtractFilePathName(Resource), p, len) then
2221 begin
2222 gChatSounds := nil;
2223 WAD.Free();
2224 Exit;
2225 end;
2227 cfg := TConfig.CreateMem(p, len);
2228 cnt := cfg.ReadInt('ChatSounds', 'Count', 0);
2230 SetLength(gChatSounds, cnt);
2231 for i := 0 to Length(gChatSounds) - 1 do
2232 begin
2233 gChatSounds[i].Sound := nil;
2234 Snd := Trim(cfg.ReadStr(IntToStr(i), 'Sound', ''));
2235 tags := cfg.ReadInt(IntToStr(i), 'Tags', 0);
2236 if (Snd = '') or (Tags <= 0) then
2237 continue;
2238 g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i), GameWAD+':'+Snd);
2239 gChatSounds[i].Sound := TPlayableSound.Create();
2240 gChatSounds[i].Sound.SetByName('SOUND_CHAT_MACRO' + IntToStr(i));
2241 SetLength(gChatSounds[i].Tags, tags);
2242 for j := 0 to tags - 1 do
2243 gChatSounds[i].Tags[j] := toLowerCase1251(cfg.ReadStr(IntToStr(i), 'Tag' + IntToStr(j), ''));
2244 gChatSounds[i].FullWord := cfg.ReadBool(IntToStr(i), 'FullWord', False);
2245 end;
2247 cfg.Free();
2248 WAD.Free();
2249 end;
2251 procedure g_Game_FreeChatSounds();
2252 var
2253 i: Integer;
2254 begin
2255 for i := 0 to Length(gChatSounds) - 1 do
2256 begin
2257 gChatSounds[i].Sound.Free();
2258 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i));
2259 end;
2260 SetLength(gChatSounds, 0);
2261 gChatSounds := nil;
2262 end;
2264 procedure g_Game_LoadData();
2265 var
2266 wl, hl: Integer;
2267 wr, hr: Integer;
2268 wb, hb: Integer;
2269 wm, hm: Integer;
2270 begin
2271 if DataLoaded then Exit;
2273 e_WriteLog('Loading game data...', TMsgType.Notify);
2275 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2276 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2277 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2278 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2279 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2280 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2281 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
2282 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
2283 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
2284 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
2285 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
2286 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
2287 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2288 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2290 hasPBarGfx := true;
2291 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false;
2292 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false;
2293 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false;
2294 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false;
2296 if hasPBarGfx then
2297 begin
2298 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2299 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2300 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2301 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2302 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
2303 begin
2304 // yay!
2305 end
2306 else
2307 begin
2308 hasPBarGfx := false;
2309 end;
2310 end;
2312 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2313 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD+':TEXTURES\PUNCH', 64, 64, 4, False);
2314 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2315 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2316 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2317 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2318 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2319 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2320 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2321 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2322 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2323 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2324 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2325 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2326 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2327 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2328 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2329 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2330 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2331 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2332 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2334 goodsnd[0] := TPlayableSound.Create();
2335 goodsnd[1] := TPlayableSound.Create();
2336 goodsnd[2] := TPlayableSound.Create();
2337 goodsnd[3] := TPlayableSound.Create();
2339 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2340 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2341 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2342 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2344 killsnd[0] := TPlayableSound.Create();
2345 killsnd[1] := TPlayableSound.Create();
2346 killsnd[2] := TPlayableSound.Create();
2347 killsnd[3] := TPlayableSound.Create();
2349 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2350 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2351 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2352 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2354 g_Game_LoadChatSounds(GameWAD+':CHATSND\SNDCFG');
2356 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2357 g_Items_LoadData();
2359 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2360 g_Weapon_LoadData();
2362 g_Monsters_LoadData();
2364 DataLoaded := True;
2365 end;
2367 procedure g_Game_FreeData();
2368 begin
2369 if not DataLoaded then Exit;
2371 g_Items_FreeData();
2372 g_Weapon_FreeData();
2373 g_Monsters_FreeData();
2375 e_WriteLog('Releasing game data...', TMsgType.Notify);
2377 g_Texture_Delete('NOTEXTURE');
2378 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2379 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2380 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2381 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2382 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2383 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2384 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2385 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2386 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2387 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2388 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2389 g_Frames_DeleteByName('FRAMES_TELEPORT');
2390 g_Frames_DeleteByName('FRAMES_PUNCH');
2391 g_Sound_Delete('SOUND_GAME_TELEPORT');
2392 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2393 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2394 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2395 g_Sound_Delete('SOUND_GAME_BULK1');
2396 g_Sound_Delete('SOUND_GAME_BULK2');
2397 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2398 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2399 g_Sound_Delete('SOUND_GAME_SWITCH1');
2400 g_Sound_Delete('SOUND_GAME_SWITCH0');
2402 goodsnd[0].Free();
2403 goodsnd[1].Free();
2404 goodsnd[2].Free();
2405 goodsnd[3].Free();
2407 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2408 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2409 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2410 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2412 killsnd[0].Free();
2413 killsnd[1].Free();
2414 killsnd[2].Free();
2415 killsnd[3].Free();
2417 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2418 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2419 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2420 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2422 g_Game_FreeChatSounds();
2424 DataLoaded := False;
2425 end;
2427 procedure DrawCustomStat();
2428 var
2429 pc, x, y, w, _y,
2430 w1, w2, w3,
2431 t, p, m: Integer;
2432 ww1, hh1: Word;
2433 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2434 s1, s2, topstr: String;
2435 begin
2436 e_TextureFontGetSize(gStdFont, ww2, hh2);
2438 g_ProcessMessages();
2440 if e_KeyPressed(IK_TAB) or e_KeyPressed(VK_STATUS) then
2441 begin
2442 if not gStatsPressed then
2443 begin
2444 gStatsOff := not gStatsOff;
2445 gStatsPressed := True;
2446 end;
2447 end
2448 else
2449 gStatsPressed := False;
2451 if gStatsOff then
2452 begin
2453 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2454 w := (Length(s1) * ww2) div 2;
2455 x := gScreenWidth div 2 - w;
2456 y := 8;
2457 e_TextureFontPrint(x, y, s1, gStdFont);
2458 Exit;
2459 end;
2461 if (gGameSettings.GameMode = GM_COOP) then
2462 begin
2463 if gMissionFailed then
2464 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2465 else
2466 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2467 end
2468 else
2469 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2471 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2472 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2474 if g_Game_IsNet then
2475 begin
2476 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2477 if not gChatShow then
2478 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2479 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2480 end;
2482 if g_Game_IsClient then
2483 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2484 else
2485 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2486 if not gChatShow then
2487 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2488 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2490 x := 32;
2491 y := 16+hh1+16;
2493 w := gScreenWidth-x*2;
2495 w2 := (w-16) div 6;
2496 w3 := w2;
2497 w1 := w-16-w2-w3;
2499 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2500 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2502 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2504 case CustomStat.GameMode of
2505 GM_DM:
2506 begin
2507 if gGameSettings.MaxLives = 0 then
2508 s1 := _lc[I_GAME_DM]
2509 else
2510 s1 := _lc[I_GAME_LMS];
2511 end;
2512 GM_TDM:
2513 begin
2514 if gGameSettings.MaxLives = 0 then
2515 s1 := _lc[I_GAME_TDM]
2516 else
2517 s1 := _lc[I_GAME_TLMS];
2518 end;
2519 GM_CTF: s1 := _lc[I_GAME_CTF];
2520 GM_COOP:
2521 begin
2522 if gGameSettings.MaxLives = 0 then
2523 s1 := _lc[I_GAME_COOP]
2524 else
2525 s1 := _lc[I_GAME_SURV];
2526 end;
2527 else s1 := '';
2528 end;
2530 _y := y+16;
2531 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2532 _y := _y+8;
2534 _y := _y+16;
2535 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2536 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2538 _y := _y+16;
2539 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2540 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2541 (CustomStat.GameTime div 1000 div 60) mod 60,
2542 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2544 pc := Length(CustomStat.PlayerStat);
2545 if pc = 0 then Exit;
2547 if CustomStat.GameMode = GM_COOP then
2548 begin
2549 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2550 _y := _y+32;
2551 s2 := _lc[I_GAME_MONSTERS];
2552 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2553 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2554 _y := _y+16;
2555 s2 := _lc[I_GAME_SECRETS];
2556 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2557 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2558 if gLastMap then
2559 begin
2560 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2561 _y := _y-16;
2562 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2563 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2564 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2565 _y := _y+16;
2566 s2 := _lc[I_GAME_SECRETS_TOTAL];
2567 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2568 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2569 end;
2570 end;
2572 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2573 begin
2574 _y := _y+16+16;
2576 with CustomStat do
2577 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2578 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2579 else s1 := _lc[I_GAME_WIN_DRAW];
2581 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2582 _y := _y+40;
2584 for t := TEAM_RED to TEAM_BLUE do
2585 begin
2586 if t = TEAM_RED then
2587 begin
2588 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2589 gStdFont, 255, 0, 0, 1);
2590 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2591 gStdFont, 255, 0, 0, 1);
2592 r := 255;
2593 g := 0;
2594 b := 0;
2595 end
2596 else
2597 begin
2598 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2599 gStdFont, 0, 0, 255, 1);
2600 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2601 gStdFont, 0, 0, 255, 1);
2602 r := 0;
2603 g := 0;
2604 b := 255;
2605 end;
2607 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2608 _y := _y+24;
2610 for p := 0 to High(CustomStat.PlayerStat) do
2611 if CustomStat.PlayerStat[p].Team = t then
2612 with CustomStat.PlayerStat[p] do
2613 begin
2614 if Spectator then
2615 begin
2616 rr := r div 2;
2617 gg := g div 2;
2618 bb := b div 2;
2619 end
2620 else
2621 begin
2622 rr := r;
2623 gg := g;
2624 bb := b;
2625 end;
2626 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2627 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2628 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2629 _y := _y+24;
2630 end;
2632 _y := _y+16+16;
2633 end;
2634 end
2635 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2636 begin
2637 _y := _y+40;
2638 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2639 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2640 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2642 _y := _y+24;
2643 for p := 0 to High(CustomStat.PlayerStat) do
2644 with CustomStat.PlayerStat[p] do
2645 begin
2646 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2648 if Spectator then
2649 r := 127
2650 else
2651 r := 255;
2653 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2654 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2655 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2656 _y := _y+24;
2657 end;
2658 end;
2659 end;
2661 procedure DrawSingleStat();
2662 var
2663 tm, key_x, val_x, y: Integer;
2664 w1, w2, h: Word;
2665 s1, s2: String;
2667 procedure player_stat(n: Integer);
2668 var
2669 kpm: Real;
2671 begin
2672 // "Kills: # / #":
2673 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2674 s2 := Format(' %d', [gTotalMonsters]);
2676 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2677 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2678 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2679 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2680 s1 := s1 + '/';
2681 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2682 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2684 // "Kills-per-minute: ##.#":
2685 s1 := _lc[I_MENU_INTER_KPM];
2686 if tm > 0 then
2687 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2688 else
2689 kpm := SingleStat.PlayerStat[n].Kills;
2690 s2 := Format(' %.1f', [kpm]);
2692 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2693 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2695 // "Secrets found: # / #":
2696 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2697 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2699 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2700 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2701 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2702 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2703 s1 := s1 + '/';
2704 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2705 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2706 end;
2708 begin
2709 // "Level Complete":
2710 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2711 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2713 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2714 s1 := _lc[I_MENU_INTER_KPM];
2715 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2716 Inc(w1, 16);
2717 s1 := ' 9999.9';
2718 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2720 key_x := (gScreenWidth-w1-w2) div 2;
2721 val_x := key_x + w1;
2723 // "Time: #:##:##":
2724 tm := SingleStat.GameTime div 1000;
2725 s1 := _lc[I_MENU_INTER_TIME];
2726 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2728 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2729 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2731 if SingleStat.TwoPlayers then
2732 begin
2733 // "Player 1":
2734 s1 := _lc[I_MENU_PLAYER_1];
2735 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2736 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2738 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2739 y := 176;
2740 player_stat(0);
2742 // "Player 2":
2743 s1 := _lc[I_MENU_PLAYER_2];
2744 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2745 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2747 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2748 y := 336;
2749 player_stat(1);
2750 end
2751 else
2752 begin
2753 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2754 y := 128;
2755 player_stat(0);
2756 end;
2757 end;
2759 procedure DrawLoadingStat();
2760 procedure drawRect (x, y, w, h: Integer);
2761 begin
2762 if (w < 1) or (h < 1) then exit;
2763 glBegin(GL_QUADS);
2764 glVertex2f(x+0.375, y+0.375);
2765 glVertex2f(x+w+0.375, y+0.375);
2766 glVertex2f(x+w+0.375, y+h+0.375);
2767 glVertex2f(x+0.375, y+h+0.375);
2768 glEnd();
2769 end;
2771 function drawPBar (cur, total: Integer; washere: Boolean): Boolean;
2772 var
2773 rectW, rectH: Integer;
2774 x0, y0: Integer;
2775 wdt: Integer;
2776 wl, hl: Integer;
2777 wr, hr: Integer;
2778 wb, hb: Integer;
2779 wm, hm: Integer;
2780 idl, idr, idb, idm: LongWord;
2781 f, my: Integer;
2782 begin
2783 result := false;
2784 if (total < 1) then exit;
2785 if (cur < 1) then exit; // don't blink
2786 if (not washere) and (cur >= total) then exit; // don't blink
2787 //if (cur < 0) then cur := 0;
2788 //if (cur > total) then cur := total;
2789 result := true;
2791 if (hasPBarGfx) then
2792 begin
2793 g_Texture_Get('UI_GFX_PBAR_LEFT', idl);
2794 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2795 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr);
2796 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2797 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb);
2798 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2799 g_Texture_Get('UI_GFX_PBAR_MARKER', idm);
2800 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2802 //rectW := gScreenWidth-360;
2803 rectW := trunc(624.0*gScreenWidth/1024.0);
2804 rectH := hl;
2806 x0 := (gScreenWidth-rectW) div 2;
2807 y0 := gScreenHeight-rectH-64;
2808 if (y0 < 2) then y0 := 2;
2810 glEnable(GL_SCISSOR_TEST);
2812 // left and right
2813 glScissor(x0, gScreenHeight-y0-rectH, rectW, rectH);
2814 e_DrawSize(idl, x0, y0, 0, true, false, wl, hl);
2815 e_DrawSize(idr, x0+rectW-wr, y0, 0, true, false, wr, hr);
2817 // body
2818 glScissor(x0+wl, gScreenHeight-y0-rectH, rectW-wl-wr, rectH);
2819 f := x0+wl;
2820 while (f < x0+rectW) do
2821 begin
2822 e_DrawSize(idb, f, y0, 0, true, false, wb, hb);
2823 f += wb;
2824 end;
2826 // filled part
2827 wdt := (rectW-wl-wr)*cur div total;
2828 if (wdt > rectW-wl-wr) then wdt := rectW-wr-wr;
2829 if (wdt > 0) then
2830 begin
2831 my := y0; // don't be so smart, ketmar: +(rectH-wm) div 2;
2832 glScissor(x0+wl, gScreenHeight-my-rectH, wdt, hm);
2833 f := x0+wl;
2834 while (wdt > 0) do
2835 begin
2836 e_DrawSize(idm, f, y0, 0, true, false, wm, hm);
2837 f += wm;
2838 wdt -= wm;
2839 end;
2840 end;
2842 glScissor(0, 0, gScreenWidth, gScreenHeight);
2843 end
2844 else
2845 begin
2846 rectW := gScreenWidth-64;
2847 rectH := 16;
2849 x0 := (gScreenWidth-rectW) div 2;
2850 y0 := gScreenHeight-rectH-64;
2851 if (y0 < 2) then y0 := 2;
2853 glDisable(GL_BLEND);
2854 glDisable(GL_TEXTURE_2D);
2856 //glClearColor(0, 0, 0, 0);
2857 //glClear(GL_COLOR_BUFFER_BIT);
2859 glColor4ub(127, 127, 127, 255);
2860 drawRect(x0-2, y0-2, rectW+4, rectH+4);
2862 glColor4ub(0, 0, 0, 255);
2863 drawRect(x0-1, y0-1, rectW+2, rectH+2);
2865 glColor4ub(127, 127, 127, 255);
2866 wdt := rectW*cur div total;
2867 if (wdt > rectW) then wdt := rectW;
2868 drawRect(x0, y0, wdt, rectH);
2869 end;
2870 end;
2872 var
2873 ww, hh: Word;
2874 xx, yy, i: Integer;
2875 s: String;
2876 begin
2877 if (Length(LoadingStat.Msgs) = 0) then exit;
2879 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2880 yy := (gScreenHeight div 3);
2881 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2882 xx := (gScreenWidth div 3);
2884 with LoadingStat do
2885 begin
2886 for i := 0 to NextMsg-1 do
2887 begin
2888 if (i = (NextMsg-1)) and (MaxValue > 0) then
2889 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2890 else
2891 s := Msgs[i];
2893 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2894 yy := yy + LOADING_INTERLINE;
2895 PBarWasHere := drawPBar(CurValue, MaxValue, PBarWasHere);
2896 end;
2897 end;
2898 end;
2900 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2901 var
2902 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2904 function monDraw (mon: TMonster): Boolean;
2905 begin
2906 result := false; // don't stop
2907 with mon do
2908 begin
2909 if alive then
2910 begin
2911 // Ëåâûé âåðõíèé óãîë
2912 aX := Obj.X div ScaleSz + 1;
2913 aY := Obj.Y div ScaleSz + 1;
2914 // Ðàçìåðû
2915 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2916 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2917 // Ïðàâûé íèæíèé óãîë
2918 aX2 := aX + aX2 - 1;
2919 aY2 := aY + aY2 - 1;
2920 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2921 end;
2922 end;
2923 end;
2925 begin
2926 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2927 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2928 begin
2929 Scale := 1;
2930 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2931 ScaleSz := 16 div Scale;
2932 // Ðàçìåðû ìèíè-êàðòû:
2933 aX := max(gMapInfo.Width div ScaleSz, 1);
2934 aY := max(gMapInfo.Height div ScaleSz, 1);
2935 // Ðàìêà êàðòû:
2936 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2938 if gWalls <> nil then
2939 begin
2940 // Ðèñóåì ñòåíû:
2941 for a := 0 to High(gWalls) do
2942 with gWalls[a] do
2943 if PanelType <> 0 then
2944 begin
2945 // Ëåâûé âåðõíèé óãîë:
2946 aX := X div ScaleSz;
2947 aY := Y div ScaleSz;
2948 // Ðàçìåðû:
2949 aX2 := max(Width div ScaleSz, 1);
2950 aY2 := max(Height div ScaleSz, 1);
2951 // Ïðàâûé íèæíèé óãîë:
2952 aX2 := aX + aX2 - 1;
2953 aY2 := aY + aY2 - 1;
2955 case PanelType of
2956 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2957 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2958 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2959 end;
2960 end;
2961 end;
2962 if gSteps <> nil then
2963 begin
2964 // Ðèñóåì ñòóïåíè:
2965 for a := 0 to High(gSteps) do
2966 with gSteps[a] do
2967 if PanelType <> 0 then
2968 begin
2969 // Ëåâûé âåðõíèé óãîë:
2970 aX := X div ScaleSz;
2971 aY := Y div ScaleSz;
2972 // Ðàçìåðû:
2973 aX2 := max(Width div ScaleSz, 1);
2974 aY2 := max(Height div ScaleSz, 1);
2975 // Ïðàâûé íèæíèé óãîë:
2976 aX2 := aX + aX2 - 1;
2977 aY2 := aY + aY2 - 1;
2979 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2980 end;
2981 end;
2982 if gLifts <> nil then
2983 begin
2984 // Ðèñóåì ëèôòû:
2985 for a := 0 to High(gLifts) do
2986 with gLifts[a] do
2987 if PanelType <> 0 then
2988 begin
2989 // Ëåâûé âåðõíèé óãîë:
2990 aX := X div ScaleSz;
2991 aY := Y div ScaleSz;
2992 // Ðàçìåðû:
2993 aX2 := max(Width div ScaleSz, 1);
2994 aY2 := max(Height div ScaleSz, 1);
2995 // Ïðàâûé íèæíèé óãîë:
2996 aX2 := aX + aX2 - 1;
2997 aY2 := aY + aY2 - 1;
2999 case LiftType of
3000 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
3001 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
3002 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
3003 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
3004 end;
3005 end;
3006 end;
3007 if gWater <> nil then
3008 begin
3009 // Ðèñóåì âîäó:
3010 for a := 0 to High(gWater) do
3011 with gWater[a] do
3012 if PanelType <> 0 then
3013 begin
3014 // Ëåâûé âåðõíèé óãîë:
3015 aX := X div ScaleSz;
3016 aY := Y div ScaleSz;
3017 // Ðàçìåðû:
3018 aX2 := max(Width div ScaleSz, 1);
3019 aY2 := max(Height div ScaleSz, 1);
3020 // Ïðàâûé íèæíèé óãîë:
3021 aX2 := aX + aX2 - 1;
3022 aY2 := aY + aY2 - 1;
3024 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
3025 end;
3026 end;
3027 if gAcid1 <> nil then
3028 begin
3029 // Ðèñóåì êèñëîòó 1:
3030 for a := 0 to High(gAcid1) do
3031 with gAcid1[a] do
3032 if PanelType <> 0 then
3033 begin
3034 // Ëåâûé âåðõíèé óãîë:
3035 aX := X div ScaleSz;
3036 aY := Y div ScaleSz;
3037 // Ðàçìåðû:
3038 aX2 := max(Width div ScaleSz, 1);
3039 aY2 := max(Height div ScaleSz, 1);
3040 // Ïðàâûé íèæíèé óãîë:
3041 aX2 := aX + aX2 - 1;
3042 aY2 := aY + aY2 - 1;
3044 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
3045 end;
3046 end;
3047 if gAcid2 <> nil then
3048 begin
3049 // Ðèñóåì êèñëîòó 2:
3050 for a := 0 to High(gAcid2) do
3051 with gAcid2[a] do
3052 if PanelType <> 0 then
3053 begin
3054 // Ëåâûé âåðõíèé óãîë:
3055 aX := X div ScaleSz;
3056 aY := Y div ScaleSz;
3057 // Ðàçìåðû:
3058 aX2 := max(Width div ScaleSz, 1);
3059 aY2 := max(Height div ScaleSz, 1);
3060 // Ïðàâûé íèæíèé óãîë:
3061 aX2 := aX + aX2 - 1;
3062 aY2 := aY + aY2 - 1;
3064 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
3065 end;
3066 end;
3067 if gPlayers <> nil then
3068 begin
3069 // Ðèñóåì èãðîêîâ:
3070 for a := 0 to High(gPlayers) do
3071 if gPlayers[a] <> nil then with gPlayers[a] do
3072 if alive then begin
3073 // Ëåâûé âåðõíèé óãîë:
3074 aX := Obj.X div ScaleSz + 1;
3075 aY := Obj.Y div ScaleSz + 1;
3076 // Ðàçìåðû:
3077 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
3078 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
3079 // Ïðàâûé íèæíèé óãîë:
3080 aX2 := aX + aX2 - 1;
3081 aY2 := aY + aY2 - 1;
3083 if gPlayers[a] = p then
3084 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
3085 else
3086 case Team of
3087 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
3088 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
3089 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
3090 end;
3091 end;
3092 end;
3093 // Ðèñóåì ìîíñòðîâ
3094 g_Mons_ForEach(monDraw);
3095 end;
3096 end;
3099 procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor);
3100 begin
3101 if not hasAmbient then exit;
3102 e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3103 end;
3106 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3107 //FIXME: broken for splitscreen mode
3108 procedure renderDynLightsInternal ();
3109 var
3110 //hasAmbient: Boolean;
3111 //ambColor: TDFColor;
3112 lln: Integer;
3113 lx, ly, lrad: Integer;
3114 scxywh: array[0..3] of GLint;
3115 wassc: Boolean;
3116 begin
3117 if e_NoGraphics then exit;
3119 //TODO: lights should be in separate grid, i think
3120 // but on the other side: grid may be slower for dynlights, as their lifetime is short
3121 if (not gwin_k8_enable_light_experiments) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit;
3123 // rendering mode
3124 //ambColor := gCurrentMap['light_ambient'].rgba;
3125 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3127 { // this will multiply incoming color to alpha from framebuffer
3128 glEnable(GL_BLEND);
3129 glBlendFunc(GL_DST_ALPHA, GL_ONE);
3132 (*
3133 * light rendering: (INVALID!)
3134 * glStencilFunc(GL_EQUAL, 0, $ff);
3135 * for each light:
3136 * glClear(GL_STENCIL_BUFFER_BIT);
3137 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3138 * draw shadow volume into stencil buffer
3139 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3140 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
3141 * turn off blending
3142 * draw color-less quad with light alpha (WARNING! don't touch color!)
3143 * glEnable(GL_BLEND);
3144 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
3145 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
3146 *)
3147 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
3148 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
3150 // setup OpenGL parameters
3151 glStencilMask($FFFFFFFF);
3152 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
3153 glEnable(GL_STENCIL_TEST);
3154 glEnable(GL_SCISSOR_TEST);
3155 glClear(GL_STENCIL_BUFFER_BIT);
3156 glStencilFunc(GL_EQUAL, 0, $ff);
3158 for lln := 0 to g_dynLightCount-1 do
3159 begin
3160 lx := g_dynLights[lln].x;
3161 ly := g_dynLights[lln].y;
3162 lrad := g_dynLights[lln].radius;
3163 if (lrad < 3) then continue;
3165 if (lx-sX+lrad < 0) then continue;
3166 if (ly-sY+lrad < 0) then continue;
3167 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
3168 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
3170 // set scissor to optimize drawing
3171 if (g_dbg_scale = 1.0) then
3172 begin
3173 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
3174 end
3175 else
3176 begin
3177 glScissor(0, 0, gWinSizeX, gWinSizeY);
3178 end;
3179 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3180 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
3181 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3182 // draw extruded panels
3183 glDisable(GL_TEXTURE_2D);
3184 glDisable(GL_BLEND);
3185 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
3186 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
3187 // render light texture
3188 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3189 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
3190 // blend it
3191 glEnable(GL_BLEND);
3192 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3193 glEnable(GL_TEXTURE_2D);
3194 // color and opacity
3195 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
3196 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
3197 glBegin(GL_QUADS);
3198 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
3199 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
3200 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
3201 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
3202 glEnd();
3203 end;
3205 // done
3206 glDisable(GL_STENCIL_TEST);
3207 glDisable(GL_BLEND);
3208 glDisable(GL_SCISSOR_TEST);
3209 //glScissor(0, 0, sWidth, sHeight);
3211 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
3212 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
3213 end;
3216 function fixViewportForScale (): Boolean;
3217 var
3218 nx0, ny0, nw, nh: Integer;
3219 begin
3220 result := false;
3221 if (g_dbg_scale <> 1.0) then
3222 begin
3223 result := true;
3224 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
3225 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
3226 nw := round(sWidth/g_dbg_scale);
3227 nh := round(sHeight/g_dbg_scale);
3228 sX := nx0;
3229 sY := ny0;
3230 sWidth := nw;
3231 sHeight := nh;
3232 end;
3233 end;
3236 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3237 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3238 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
3239 type
3240 TDrawCB = procedure ();
3242 var
3243 hasAmbient: Boolean;
3244 ambColor: TDFColor;
3245 doAmbient: Boolean = false;
3247 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
3248 var
3249 tagmask: Integer;
3250 pan: TPanel;
3251 begin
3252 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
3253 if gdbg_map_use_accel_render then
3254 begin
3255 tagmask := panelTypeToTag(panType);
3256 while (gDrawPanelList.count > 0) do
3257 begin
3258 pan := TPanel(gDrawPanelList.front());
3259 if ((pan.tag and tagmask) = 0) then break;
3260 if doDraw then pan.Draw(doAmbient, ambColor);
3261 gDrawPanelList.popFront();
3262 end;
3263 end
3264 else
3265 begin
3266 if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor);
3267 end;
3268 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3269 end;
3271 procedure drawOther (profname: AnsiString; cb: TDrawCB);
3272 begin
3273 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
3274 if assigned(cb) then cb();
3275 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3276 end;
3278 begin
3279 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('total');
3281 // our accelerated renderer will collect all panels to gDrawPanelList
3282 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3283 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('collect');
3284 if gdbg_map_use_accel_render then
3285 begin
3286 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
3287 end;
3288 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3290 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('skyback');
3291 g_Map_DrawBack(backXOfs, backYOfs);
3292 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3294 if setTransMatrix then
3295 begin
3296 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3297 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
3298 glTranslatef(-sX, -sY, 0);
3299 end;
3301 // rendering mode
3302 ambColor := gCurrentMap['light_ambient'].rgba;
3303 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3306 if hasAmbient then
3307 begin
3308 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3309 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3310 glClear(GL_COLOR_BUFFER_BIT);
3311 end;
3313 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3316 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
3317 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
3318 drawOther('items', @g_Items_Draw);
3319 drawOther('weapons', @g_Weapon_Draw);
3320 drawOther('shells', @g_Player_DrawShells);
3321 drawOther('drawall', @g_Player_DrawAll);
3322 drawOther('corpses', @g_Player_DrawCorpses);
3323 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
3324 drawOther('monsters', @g_Monsters_Draw);
3325 drawOther('itemdrop', @g_Items_DrawDrop);
3326 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
3327 drawOther('gfx', @g_GFX_Draw);
3328 drawOther('flags', @g_Map_DrawFlags);
3329 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
3330 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
3331 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
3332 drawOther('dynlights', @renderDynLightsInternal);
3334 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3335 begin
3336 renderAmbientQuad(hasAmbient, ambColor);
3337 end;
3339 doAmbient := true;
3340 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
3343 if g_debug_HealthBar then
3344 begin
3345 g_Monsters_DrawHealth();
3346 g_Player_DrawHealth();
3347 end;
3349 if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering
3350 end;
3353 procedure DrawMapView(x, y, w, h: Integer);
3355 var
3356 bx, by: Integer;
3357 begin
3358 glPushMatrix();
3360 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
3361 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
3363 sX := x;
3364 sY := y;
3365 sWidth := w;
3366 sHeight := h;
3368 fixViewportForScale();
3369 renderMapInternal(-bx, -by, true);
3371 glPopMatrix();
3372 end;
3375 procedure DrawPlayer(p: TPlayer);
3376 var
3377 px, py, a, b, c, d: Integer;
3378 //R: TRect;
3379 begin
3380 if (p = nil) or (p.FDummy) then
3381 begin
3382 glPushMatrix();
3383 g_Map_DrawBack(0, 0);
3384 glPopMatrix();
3385 Exit;
3386 end;
3388 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3389 if (profileFrameDraw <> nil) then profileFrameDraw.mainBegin(g_profile_frame_draw);
3391 gPlayerDrawn := p;
3393 glPushMatrix();
3395 px := p.GameX + PLAYER_RECT_CX;
3396 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
3398 if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then
3399 begin
3400 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3401 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3403 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3404 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3406 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3407 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3408 begin
3409 // hcenter
3410 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3411 end;
3413 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3414 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3415 begin
3416 // vcenter
3417 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3418 end;
3419 end
3420 else
3421 begin
3422 // scaled, ignore level bounds
3423 a := -px+(gPlayerScreenSize.X div 2);
3424 b := -py+(gPlayerScreenSize.Y div 2);
3425 end;
3427 if p.IncCam <> 0 then
3428 begin
3429 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
3430 begin
3431 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
3432 begin
3433 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
3434 end;
3435 end;
3437 if py < gPlayerScreenSize.Y div 2 then
3438 begin
3439 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
3440 begin
3441 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
3442 end;
3443 end;
3445 if p.IncCam < 0 then
3446 begin
3447 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
3448 end;
3450 if p.IncCam > 0 then
3451 begin
3452 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
3453 end;
3454 end;
3456 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
3457 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
3458 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
3460 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
3461 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
3462 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
3464 sX := -a;
3465 sY := -(b+p.IncCam);
3466 sWidth := gPlayerScreenSize.X;
3467 sHeight := gPlayerScreenSize.Y;
3469 //glTranslatef(a, b+p.IncCam, 0);
3471 //if (p = gPlayer1) and (g_dbg_scale >= 1.0) then g_Holmes_plrViewSize(sWidth, sHeight);
3473 //conwritefln('OLD: (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3474 fixViewportForScale();
3475 //conwritefln(' (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3477 if (g_dbg_scale <> 1.0) and (not g_dbg_ignore_bounds) then
3478 begin
3479 if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth;
3480 if (sY+sHeight > gMapInfo.Height) then sY := gMapInfo.Height-sHeight;
3481 if (sX < 0) then sX := 0;
3482 if (sY < 0) then sY := 0;
3484 if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth));
3485 if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight));
3486 end;
3488 //r_smallmap_h: 0: left; 1: center; 2: right
3489 //r_smallmap_v: 0: top; 1: center; 2: bottom
3490 // horiz small map?
3491 if (gMapInfo.Width = sWidth) then
3492 begin
3493 sX := 0;
3494 end
3495 else if (gMapInfo.Width < sWidth) then
3496 begin
3497 case r_smallmap_h of
3498 1: sX := -((sWidth-gMapInfo.Width) div 2); // center
3499 2: sX := -(sWidth-gMapInfo.Width); // right
3500 else sX := 0; // left
3501 end;
3502 end;
3503 // vert small map?
3504 if (gMapInfo.Height = sHeight) then
3505 begin
3506 sY := 0;
3507 end
3508 else if (gMapInfo.Height < sHeight) then
3509 begin
3510 case r_smallmap_v of
3511 1: sY := -((sHeight-gMapInfo.Height) div 2); // center
3512 2: sY := -(sHeight-gMapInfo.Height); // bottom
3513 else sY := 0; // top
3514 end;
3515 end;
3517 p.viewPortX := sX;
3518 p.viewPortY := sY;
3519 p.viewPortW := sWidth;
3520 p.viewPortH := sHeight;
3522 {$IFDEF ENABLE_HOLMES}
3523 if (p = gPlayer1) then
3524 begin
3525 g_Holmes_plrViewPos(sX, sY);
3526 g_Holmes_plrViewSize(sWidth, sHeight);
3527 end;
3528 {$ENDIF}
3530 renderMapInternal(-c, -d, true);
3532 if p.FSpectator then
3533 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3534 p.GameY + PLAYER_RECT_CY - 4,
3535 'X', gStdFont, 255, 255, 255, 1, True);
3537 for a := 0 to High(gCollideMap) do
3538 for b := 0 to High(gCollideMap[a]) do
3539 begin
3540 d := 0;
3541 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3542 d := d + 1;
3543 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3544 d := d + 2;
3546 case d of
3547 1: e_DrawPoint(1, b, a, 200, 200, 200);
3548 2: e_DrawPoint(1, b, a, 64, 64, 255);
3549 3: e_DrawPoint(1, b, a, 255, 0, 255);
3550 end;
3551 end;
3554 glPopMatrix();
3556 p.DrawPain();
3557 p.DrawPickup();
3558 p.DrawRulez();
3559 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3560 if g_Debug_Player then
3561 g_Player_DrawDebug(p);
3562 p.DrawGUI();
3563 end;
3565 procedure drawProfilers ();
3566 var
3567 px: Integer = -1;
3568 py: Integer = -1;
3569 begin
3570 if g_profile_frame_draw and (profileFrameDraw <> nil) then px := px-drawProfiles(px, py, profileFrameDraw);
3571 if g_profile_collision and (profMapCollision <> nil) then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3572 if g_profile_los and (profMonsLOS <> nil) then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3573 end;
3575 procedure g_Game_Draw();
3576 var
3577 ID: DWORD;
3578 w, h: Word;
3579 ww, hh: Byte;
3580 Time: Int64;
3581 back: string;
3582 plView1, plView2: TPlayer;
3583 Split: Boolean;
3584 begin
3585 if gExit = EXIT_QUIT then Exit;
3587 Time := GetTimer() {div 1000};
3588 FPSCounter := FPSCounter+1;
3589 if Time - FPSTime >= 1000 then
3590 begin
3591 FPS := FPSCounter;
3592 FPSCounter := 0;
3593 FPSTime := Time;
3594 end;
3596 if gGameOn or (gState = STATE_FOLD) then
3597 begin
3598 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3599 begin
3600 gSpectMode := SPECT_NONE;
3601 if not gRevertPlayers then
3602 begin
3603 plView1 := gPlayer1;
3604 plView2 := gPlayer2;
3605 end
3606 else
3607 begin
3608 plView1 := gPlayer2;
3609 plView2 := gPlayer1;
3610 end;
3611 end
3612 else
3613 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3614 begin
3615 gSpectMode := SPECT_NONE;
3616 if gPlayer2 = nil then
3617 plView1 := gPlayer1
3618 else
3619 plView1 := gPlayer2;
3620 plView2 := nil;
3621 end
3622 else
3623 begin
3624 plView1 := nil;
3625 plView2 := nil;
3626 end;
3628 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3629 gSpectMode := SPECT_STATS;
3631 if gSpectMode = SPECT_PLAYERS then
3632 if gPlayers <> nil then
3633 begin
3634 plView1 := GetActivePlayer_ByID(gSpectPID1);
3635 if plView1 = nil then
3636 begin
3637 gSpectPID1 := GetActivePlayerID_Next();
3638 plView1 := GetActivePlayer_ByID(gSpectPID1);
3639 end;
3640 if gSpectViewTwo then
3641 begin
3642 plView2 := GetActivePlayer_ByID(gSpectPID2);
3643 if plView2 = nil then
3644 begin
3645 gSpectPID2 := GetActivePlayerID_Next();
3646 plView2 := GetActivePlayer_ByID(gSpectPID2);
3647 end;
3648 end;
3649 end;
3651 if gSpectMode = SPECT_MAPVIEW then
3652 begin
3653 // Ðåæèì ïðîñìîòðà êàðòû
3654 Split := False;
3655 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3656 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3657 gHearPoint1.Active := True;
3658 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3659 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3660 gHearPoint2.Active := False;
3661 end
3662 else
3663 begin
3664 Split := (plView1 <> nil) and (plView2 <> nil);
3666 // Òî÷êè ñëóõà èãðîêîâ
3667 if plView1 <> nil then
3668 begin
3669 gHearPoint1.Active := True;
3670 gHearPoint1.Coords.X := plView1.GameX;
3671 gHearPoint1.Coords.Y := plView1.GameY;
3672 end else
3673 gHearPoint1.Active := False;
3674 if plView2 <> nil then
3675 begin
3676 gHearPoint2.Active := True;
3677 gHearPoint2.Coords.X := plView2.GameX;
3678 gHearPoint2.Coords.Y := plView2.GameY;
3679 end else
3680 gHearPoint2.Active := False;
3682 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3683 gPlayerScreenSize.X := gScreenWidth-196;
3684 if Split then
3685 begin
3686 gPlayerScreenSize.Y := gScreenHeight div 2;
3687 if gScreenHeight mod 2 = 0 then
3688 Dec(gPlayerScreenSize.Y);
3689 end
3690 else
3691 gPlayerScreenSize.Y := gScreenHeight;
3693 if Split then
3694 if gScreenHeight mod 2 = 0 then
3695 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3696 else
3697 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3699 DrawPlayer(plView1);
3700 gPlayer1ScreenCoord.X := sX;
3701 gPlayer1ScreenCoord.Y := sY;
3703 if Split then
3704 begin
3705 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3707 DrawPlayer(plView2);
3708 gPlayer2ScreenCoord.X := sX;
3709 gPlayer2ScreenCoord.Y := sY;
3710 end;
3712 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3714 if Split then
3715 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3716 end;
3718 {$IFDEF ENABLE_HOLMES}
3719 // draw inspector
3720 if (g_holmes_enabled) then g_Holmes_Draw();
3721 {$ENDIF}
3723 if MessageText <> '' then
3724 begin
3725 w := 0;
3726 h := 0;
3727 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3728 if Split then
3729 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3730 (gScreenHeight div 2)-(h div 2), MessageText)
3731 else
3732 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3733 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3734 end;
3736 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3738 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) and (not gSpectAuto) then
3739 begin
3740 // Draw spectator GUI
3741 ww := 0;
3742 hh := 0;
3743 e_TextureFontGetSize(gStdFont, ww, hh);
3744 case gSpectMode of
3745 SPECT_STATS:
3746 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3747 SPECT_MAPVIEW:
3748 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3749 SPECT_PLAYERS:
3750 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3751 end;
3752 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3753 if gSpectMode = SPECT_STATS then
3754 begin
3755 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2)*2, 'Autoview', gStdFont, 255, 255, 255, 1);
3756 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2), '< fire >', gStdFont, 255, 255, 255, 1);
3757 end;
3758 if gSpectMode = SPECT_MAPVIEW then
3759 begin
3760 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3761 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3762 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3763 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3764 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3765 end;
3766 if gSpectMode = SPECT_PLAYERS then
3767 begin
3768 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3769 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3770 if gSpectViewTwo then
3771 begin
3772 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3773 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3774 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3775 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3776 end
3777 else
3778 begin
3779 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3780 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3781 end;
3782 end;
3783 end;
3784 end;
3786 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
3787 begin
3788 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3789 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3791 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3792 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3793 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3794 end;
3796 if not gGameOn then
3797 begin
3798 if (gState = STATE_MENU) then
3799 begin
3800 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3801 begin
3802 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3803 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3804 end;
3805 // F3 at menu will show game loading dialog
3806 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3807 if (g_ActiveWindow <> nil) then
3808 begin
3809 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3810 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3811 end
3812 else
3813 begin
3814 // F3 at titlepic will show game loading dialog
3815 if e_KeyPressed(IK_F3) then
3816 begin
3817 g_Menu_Show_LoadMenu(true);
3818 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3819 end;
3820 end;
3821 end;
3823 if gState = STATE_FOLD then
3824 begin
3825 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3826 end;
3828 if gState = STATE_INTERCUSTOM then
3829 begin
3830 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3831 begin
3832 back := 'TEXTURE_endpic';
3833 if not g_Texture_Get(back, ID) then
3834 back := _lc[I_TEXTURE_ENDPIC];
3835 end
3836 else
3837 back := 'INTER';
3839 if g_Texture_Get(back, ID) then
3840 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3841 else
3842 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3844 DrawCustomStat();
3846 if g_ActiveWindow <> nil then
3847 begin
3848 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3849 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3850 end;
3851 end;
3853 if gState = STATE_INTERSINGLE then
3854 begin
3855 if EndingGameCounter > 0 then
3856 begin
3857 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3858 end
3859 else
3860 begin
3861 back := 'INTER';
3863 if g_Texture_Get(back, ID) then
3864 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3865 else
3866 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3868 DrawSingleStat();
3870 if g_ActiveWindow <> nil then
3871 begin
3872 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3873 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3874 end;
3875 end;
3876 end;
3878 if gState = STATE_ENDPIC then
3879 begin
3880 ID := DWORD(-1);
3881 if not g_Texture_Get('TEXTURE_endpic', ID) then
3882 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3884 if ID <> DWORD(-1) then
3885 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3886 else
3887 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3889 if g_ActiveWindow <> nil then
3890 begin
3891 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3892 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3893 end;
3894 end;
3896 if gState = STATE_SLIST then
3897 begin
3898 if g_Texture_Get('MENU_BACKGROUND', ID) then
3899 begin
3900 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3901 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3902 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3903 end;
3904 g_Serverlist_Draw(slCurrent, slTable);
3905 end;
3906 end;
3908 if g_ActiveWindow <> nil then
3909 begin
3910 if gGameOn then
3911 begin
3912 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3913 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3914 end;
3915 g_ActiveWindow.Draw();
3916 end;
3918 g_Console_Draw();
3920 if g_debug_Sounds and gGameOn then
3921 begin
3922 for w := 0 to High(e_SoundsArray) do
3923 for h := 0 to e_SoundsArray[w].nRefs do
3924 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3925 end;
3927 if gShowFPS then
3928 begin
3929 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3930 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3931 end;
3933 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3934 drawTime(gScreenWidth-72, gScreenHeight-16);
3936 if gGameOn then drawProfilers();
3938 {$IFDEF ENABLE_HOLMES}
3939 g_Holmes_DrawUI();
3940 {$ENDIF}
3942 g_Touch_Draw;
3943 end;
3945 procedure g_Game_Quit();
3946 begin
3947 g_Game_StopAllSounds(True);
3948 gMusic.Free();
3949 g_Game_SaveOptions();
3950 g_Game_FreeData();
3951 g_PlayerModel_FreeData();
3952 g_Texture_DeleteAll();
3953 g_Frames_DeleteAll();
3954 //g_Menu_Free(); //k8: this segfaults after resolution change; who cares?
3956 if NetInitDone then g_Net_Free;
3958 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3959 if gMapToDelete <> '' then
3960 g_Game_DeleteTestMap();
3962 gExit := EXIT_QUIT;
3963 PushExitEvent();
3964 end;
3966 procedure g_FatalError(Text: String);
3967 begin
3968 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3969 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning);
3971 gExit := EXIT_SIMPLE;
3972 end;
3974 procedure g_SimpleError(Text: String);
3975 begin
3976 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3977 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
3978 end;
3980 procedure g_Game_SetupScreenSize();
3981 const
3982 RES_FACTOR = 4.0 / 3.0;
3983 var
3984 s: Single;
3985 rf: Single;
3986 bw, bh: Word;
3987 begin
3988 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3989 gPlayerScreenSize.X := gScreenWidth-196;
3990 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3991 gPlayerScreenSize.Y := gScreenHeight div 2
3992 else
3993 gPlayerScreenSize.Y := gScreenHeight;
3995 // Ðàçìåð çàäíåãî ïëàíà:
3996 if BackID <> DWORD(-1) then
3997 begin
3998 s := SKY_STRETCH;
3999 if (gScreenWidth*s > gMapInfo.Width) or
4000 (gScreenHeight*s > gMapInfo.Height) then
4001 begin
4002 gBackSize.X := gScreenWidth;
4003 gBackSize.Y := gScreenHeight;
4004 end
4005 else
4006 begin
4007 e_GetTextureSize(BackID, @bw, @bh);
4008 rf := Single(bw) / Single(bh);
4009 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
4010 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
4011 s := Max(gScreenWidth / bw, gScreenHeight / bh);
4012 if (s < 1.0) then s := 1.0;
4013 gBackSize.X := Round(bw*s);
4014 gBackSize.Y := Round(bh*s);
4015 end;
4016 end;
4017 end;
4019 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
4020 begin
4021 g_Window_SetSize(newWidth, newHeight, nowFull);
4022 end;
4024 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
4025 begin
4026 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
4027 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
4028 Exit;
4029 if gPlayer1 = nil then
4030 begin
4031 if g_Game_IsClient then
4032 begin
4033 if NetPlrUID1 > -1 then
4034 begin
4035 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
4036 gPlayer1 := g_Player_Get(NetPlrUID1);
4037 end;
4038 Exit;
4039 end;
4041 if not (Team in [TEAM_RED, TEAM_BLUE]) then
4042 Team := gPlayer1Settings.Team;
4044 // Ñîçäàíèå ïåðâîãî èãðîêà:
4045 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4046 gPlayer1Settings.Color,
4047 Team, False));
4048 if gPlayer1 = nil then
4049 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
4050 else
4051 begin
4052 gPlayer1.Name := gPlayer1Settings.Name;
4053 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
4054 if g_Game_IsServer and g_Game_IsNet then
4055 MH_SEND_PlayerCreate(gPlayer1.UID);
4056 gPlayer1.Respawn(False, True);
4058 if g_Game_IsNet and NetUseMaster then
4059 g_Net_Slist_Update;
4060 end;
4062 Exit;
4063 end;
4064 if gPlayer2 = nil then
4065 begin
4066 if g_Game_IsClient then
4067 begin
4068 if NetPlrUID2 > -1 then
4069 gPlayer2 := g_Player_Get(NetPlrUID2);
4070 Exit;
4071 end;
4073 if not (Team in [TEAM_RED, TEAM_BLUE]) then
4074 Team := gPlayer2Settings.Team;
4076 // Ñîçäàíèå âòîðîãî èãðîêà:
4077 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4078 gPlayer2Settings.Color,
4079 Team, False));
4080 if gPlayer2 = nil then
4081 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
4082 else
4083 begin
4084 gPlayer2.Name := gPlayer2Settings.Name;
4085 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
4086 if g_Game_IsServer and g_Game_IsNet then
4087 MH_SEND_PlayerCreate(gPlayer2.UID);
4088 gPlayer2.Respawn(False, True);
4090 if g_Game_IsNet and NetUseMaster then
4091 g_Net_Slist_Update;
4092 end;
4094 Exit;
4095 end;
4096 end;
4098 procedure g_Game_RemovePlayer();
4099 var
4100 Pl: TPlayer;
4101 begin
4102 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
4103 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
4104 Exit;
4105 Pl := gPlayer2;
4106 if Pl <> nil then
4107 begin
4108 if g_Game_IsServer then
4109 begin
4110 Pl.Lives := 0;
4111 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4112 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
4113 g_Player_Remove(Pl.UID);
4115 if g_Game_IsNet and NetUseMaster then
4116 g_Net_Slist_Update;
4117 end else
4118 gPlayer2 := nil;
4119 Exit;
4120 end;
4121 Pl := gPlayer1;
4122 if Pl <> nil then
4123 begin
4124 if g_Game_IsServer then
4125 begin
4126 Pl.Lives := 0;
4127 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4128 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
4129 g_Player_Remove(Pl.UID);
4131 if g_Game_IsNet and NetUseMaster then
4132 g_Net_Slist_Update;
4133 end else
4134 begin
4135 gPlayer1 := nil;
4136 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
4137 end;
4138 Exit;
4139 end;
4140 end;
4142 procedure g_Game_Spectate();
4143 begin
4144 g_Game_RemovePlayer();
4145 if gPlayer1 <> nil then
4146 g_Game_RemovePlayer();
4147 end;
4149 procedure g_Game_SpectateCenterView();
4150 begin
4151 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
4152 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
4153 end;
4155 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
4156 var
4157 i, nPl: Integer;
4158 tmps: AnsiString;
4159 begin
4160 g_Game_Free();
4162 e_WriteLog('Starting singleplayer game...', TMsgType.Notify);
4164 g_Game_ClearLoading();
4166 // Íàñòðîéêè èãðû:
4167 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
4168 gAimLine := False;
4169 gShowMap := False;
4170 gGameSettings.GameType := GT_SINGLE;
4171 gGameSettings.MaxLives := 0;
4172 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
4173 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
4174 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
4175 gSwitchGameMode := GM_SINGLE;
4177 g_Game_ExecuteEvent('ongamestart');
4179 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4180 g_Game_SetupScreenSize();
4182 // Ñîçäàíèå ïåðâîãî èãðîêà:
4183 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4184 gPlayer1Settings.Color,
4185 gPlayer1Settings.Team, False));
4186 if gPlayer1 = nil then
4187 begin
4188 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4189 Exit;
4190 end;
4192 gPlayer1.Name := gPlayer1Settings.Name;
4193 nPl := 1;
4195 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
4196 if TwoPlayers then
4197 begin
4198 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4199 gPlayer2Settings.Color,
4200 gPlayer2Settings.Team, False));
4201 if gPlayer2 = nil then
4202 begin
4203 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4204 Exit;
4205 end;
4207 gPlayer2.Name := gPlayer2Settings.Name;
4208 Inc(nPl);
4209 end;
4211 // Çàãðóçêà è çàïóñê êàðòû:
4212 if not g_Game_StartMap(MAP, True) then
4213 begin
4214 if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP;
4215 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps]));
4216 Exit;
4217 end;
4219 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4220 g_Player_Init();
4222 // Ñîçäàåì áîòîâ:
4223 for i := nPl+1 to nPlayers do
4224 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4225 end;
4227 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
4228 TimeLimit, GoalLimit: Word;
4229 MaxLives: Byte;
4230 Options: LongWord; nPlayers: Byte);
4231 var
4232 i, nPl: Integer;
4233 begin
4234 g_Game_Free();
4236 e_WriteLog('Starting custom game...', TMsgType.Notify);
4238 g_Game_ClearLoading();
4240 // Íàñòðîéêè èãðû:
4241 gGameSettings.GameType := GT_CUSTOM;
4242 gGameSettings.GameMode := GameMode;
4243 gSwitchGameMode := GameMode;
4244 gGameSettings.TimeLimit := TimeLimit;
4245 gGameSettings.GoalLimit := GoalLimit;
4246 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4247 gGameSettings.Options := Options;
4249 gCoopTotalMonstersKilled := 0;
4250 gCoopTotalSecretsFound := 0;
4251 gCoopTotalMonsters := 0;
4252 gCoopTotalSecrets := 0;
4253 gAimLine := False;
4254 gShowMap := False;
4256 g_Game_ExecuteEvent('ongamestart');
4258 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4259 g_Game_SetupScreenSize();
4261 // Ðåæèì íàáëþäàòåëÿ:
4262 if nPlayers = 0 then
4263 begin
4264 gPlayer1 := nil;
4265 gPlayer2 := nil;
4266 end;
4268 nPl := 0;
4269 if nPlayers >= 1 then
4270 begin
4271 // Ñîçäàíèå ïåðâîãî èãðîêà:
4272 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4273 gPlayer1Settings.Color,
4274 gPlayer1Settings.Team, False));
4275 if gPlayer1 = nil then
4276 begin
4277 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4278 Exit;
4279 end;
4281 gPlayer1.Name := gPlayer1Settings.Name;
4282 Inc(nPl);
4283 end;
4285 if nPlayers >= 2 then
4286 begin
4287 // Ñîçäàíèå âòîðîãî èãðîêà:
4288 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4289 gPlayer2Settings.Color,
4290 gPlayer2Settings.Team, False));
4291 if gPlayer2 = nil then
4292 begin
4293 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4294 Exit;
4295 end;
4297 gPlayer2.Name := gPlayer2Settings.Name;
4298 Inc(nPl);
4299 end;
4301 // Çàãðóçêà è çàïóñê êàðòû:
4302 if not g_Game_StartMap(Map, True) then
4303 begin
4304 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4305 Exit;
4306 end;
4308 // Íåò òî÷åê ïîÿâëåíèÿ:
4309 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4310 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4311 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4312 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4313 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4314 begin
4315 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4316 Exit;
4317 end;
4319 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4320 g_Player_Init();
4322 // Ñîçäàåì áîòîâ:
4323 for i := nPl+1 to nPlayers do
4324 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4325 end;
4327 procedure g_Game_StartServer(Map: String; GameMode: Byte;
4328 TimeLimit, GoalLimit: Word; MaxLives: Byte;
4329 Options: LongWord; nPlayers: Byte;
4330 IPAddr: LongWord; Port: Word);
4331 begin
4332 g_Game_Free();
4334 e_WriteLog('Starting net game (server)...', TMsgType.Notify);
4336 g_Game_ClearLoading();
4338 // Íàñòðîéêè èãðû:
4339 gGameSettings.GameType := GT_SERVER;
4340 gGameSettings.GameMode := GameMode;
4341 gSwitchGameMode := GameMode;
4342 gGameSettings.TimeLimit := TimeLimit;
4343 gGameSettings.GoalLimit := GoalLimit;
4344 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4345 gGameSettings.Options := Options;
4347 gCoopTotalMonstersKilled := 0;
4348 gCoopTotalSecretsFound := 0;
4349 gCoopTotalMonsters := 0;
4350 gCoopTotalSecrets := 0;
4351 gAimLine := False;
4352 gShowMap := False;
4354 g_Game_ExecuteEvent('ongamestart');
4356 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
4357 g_Game_SetupScreenSize();
4359 // Ðåæèì íàáëþäàòåëÿ:
4360 if nPlayers = 0 then
4361 begin
4362 gPlayer1 := nil;
4363 gPlayer2 := nil;
4364 end;
4366 if nPlayers >= 1 then
4367 begin
4368 // Ñîçäàíèå ïåðâîãî èãðîêà:
4369 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4370 gPlayer1Settings.Color,
4371 gPlayer1Settings.Team, False));
4372 if gPlayer1 = nil then
4373 begin
4374 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4375 Exit;
4376 end;
4378 gPlayer1.Name := gPlayer1Settings.Name;
4379 end;
4381 if nPlayers >= 2 then
4382 begin
4383 // Ñîçäàíèå âòîðîãî èãðîêà:
4384 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4385 gPlayer2Settings.Color,
4386 gPlayer2Settings.Team, False));
4387 if gPlayer2 = nil then
4388 begin
4389 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4390 Exit;
4391 end;
4393 gPlayer2.Name := gPlayer2Settings.Name;
4394 end;
4396 g_Game_SetLoadingText(_lc[I_LOAD_HOST], 0, False);
4397 if NetForwardPorts then
4398 g_Game_SetLoadingText(_lc[I_LOAD_PORTS], 0, False);
4400 // Ñòàðòóåì ñåðâåð
4401 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
4402 begin
4403 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
4404 Exit;
4405 end;
4407 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
4409 // Çàãðóçêà è çàïóñê êàðòû:
4410 if not g_Game_StartMap(Map, True) then
4411 begin
4412 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4413 Exit;
4414 end;
4416 // Íåò òî÷åê ïîÿâëåíèÿ:
4417 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4418 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4419 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4420 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4421 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4422 begin
4423 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4424 Exit;
4425 end;
4427 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4428 g_Player_Init();
4430 NetState := NET_STATE_GAME;
4431 end;
4433 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
4434 var
4435 Map: String;
4436 WadName: string;
4437 Ptr: Pointer;
4438 T: Cardinal;
4439 MID: Byte;
4440 State: Byte;
4441 OuterLoop: Boolean;
4442 newResPath: string;
4443 InMsg: TMsg;
4444 begin
4445 g_Game_Free();
4447 State := 0;
4448 e_WriteLog('Starting net game (client)...', TMsgType.Notify);
4449 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', TMsgType.Notify);
4451 g_Game_ClearLoading();
4453 // Íàñòðîéêè èãðû:
4454 gGameSettings.GameType := GT_CLIENT;
4456 gCoopTotalMonstersKilled := 0;
4457 gCoopTotalSecretsFound := 0;
4458 gCoopTotalMonsters := 0;
4459 gCoopTotalSecrets := 0;
4460 gAimLine := False;
4461 gShowMap := False;
4463 g_Game_ExecuteEvent('ongamestart');
4465 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4466 g_Game_SetupScreenSize();
4468 NetState := NET_STATE_AUTH;
4470 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4471 // Ñòàðòóåì êëèåíò
4472 if not g_Net_Connect(Addr, Port) then
4473 begin
4474 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4475 NetState := NET_STATE_NONE;
4476 Exit;
4477 end;
4479 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4480 MC_SEND_Info(PW);
4481 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4483 OuterLoop := True;
4484 while OuterLoop do
4485 begin
4486 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4487 begin
4488 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4489 begin
4490 Ptr := NetEvent.packet^.data;
4491 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4492 continue;
4494 MID := InMsg.ReadByte();
4496 if (MID = NET_MSG_INFO) and (State = 0) then
4497 begin
4498 NetMyID := InMsg.ReadByte();
4499 NetPlrUID1 := InMsg.ReadWord();
4501 WadName := InMsg.ReadString();
4502 Map := InMsg.ReadString();
4504 gWADHash := InMsg.ReadMD5();
4506 gGameSettings.GameMode := InMsg.ReadByte();
4507 gSwitchGameMode := gGameSettings.GameMode;
4508 gGameSettings.GoalLimit := InMsg.ReadWord();
4509 gGameSettings.TimeLimit := InMsg.ReadWord();
4510 gGameSettings.MaxLives := InMsg.ReadByte();
4511 gGameSettings.Options := InMsg.ReadLongWord();
4512 T := InMsg.ReadLongWord();
4514 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4515 if newResPath = '' then
4516 begin
4517 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4518 newResPath := g_Res_DownloadWAD(WadName);
4519 if newResPath = '' then
4520 begin
4521 g_FatalError(_lc[I_NET_ERR_HASH]);
4522 enet_packet_destroy(NetEvent.packet);
4523 NetState := NET_STATE_NONE;
4524 Exit;
4525 end;
4526 end;
4527 newResPath := ExtractRelativePath(MapsDir, newResPath);
4529 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4530 gPlayer1Settings.Color,
4531 gPlayer1Settings.Team, False));
4533 if gPlayer1 = nil then
4534 begin
4535 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4537 enet_packet_destroy(NetEvent.packet);
4538 NetState := NET_STATE_NONE;
4539 Exit;
4540 end;
4542 gPlayer1.Name := gPlayer1Settings.Name;
4543 gPlayer1.UID := NetPlrUID1;
4544 gPlayer1.Reset(True);
4546 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4547 begin
4548 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4550 enet_packet_destroy(NetEvent.packet);
4551 NetState := NET_STATE_NONE;
4552 Exit;
4553 end;
4555 gTime := T;
4557 State := 1;
4558 OuterLoop := False;
4559 enet_packet_destroy(NetEvent.packet);
4560 break;
4561 end
4562 else
4563 enet_packet_destroy(NetEvent.packet);
4564 end
4565 else
4566 begin
4567 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4568 begin
4569 State := 0;
4570 if (NetEvent.data <= NET_DISC_MAX) then
4571 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4572 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4573 OuterLoop := False;
4574 Break;
4575 end;
4576 end;
4577 end;
4579 ProcessLoading(true);
4581 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) or e_KeyPressed(VK_ESCAPE) then
4582 begin
4583 State := 0;
4584 break;
4585 end;
4586 end;
4588 if State <> 1 then
4589 begin
4590 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4591 NetState := NET_STATE_NONE;
4592 Exit;
4593 end;
4595 gLMSRespawn := LMS_RESPAWN_NONE;
4596 gLMSRespawnTime := 0;
4598 g_Player_Init();
4599 NetState := NET_STATE_GAME;
4600 MC_SEND_FullStateRequest;
4601 e_WriteLog('NET: Connection successful.', TMsgType.Notify);
4602 end;
4604 procedure g_Game_SaveOptions();
4605 begin
4606 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4607 end;
4609 procedure g_Game_ChangeMap(const MapPath: String);
4610 var
4611 Force: Boolean;
4612 begin
4613 g_Game_ClearLoading();
4615 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4616 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4617 if gExitByTrigger then
4618 begin
4619 Force := False;
4620 gExitByTrigger := False;
4621 end;
4622 if not g_Game_StartMap(MapPath, Force) then
4623 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4624 end;
4626 procedure g_Game_Restart();
4627 var
4628 Map: string;
4629 begin
4630 if g_Game_IsClient then
4631 Exit;
4632 map := g_ExtractFileName(gMapInfo.Map);
4634 MessageTime := 0;
4635 gGameOn := False;
4636 g_Game_ClearLoading();
4637 g_Game_StartMap(Map, True, gCurrentMapFileName);
4638 end;
4640 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4641 var
4642 NewWAD, ResName: String;
4643 I: Integer;
4644 begin
4645 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4646 g_Player_RemoveAllCorpses();
4648 if (not g_Game_IsClient) and
4649 (gSwitchGameMode <> gGameSettings.GameMode) and
4650 (gGameSettings.GameMode <> GM_SINGLE) then
4651 begin
4652 if gSwitchGameMode = GM_CTF then
4653 gGameSettings.MaxLives := 0;
4654 gGameSettings.GameMode := gSwitchGameMode;
4655 Force := True;
4656 end else
4657 gSwitchGameMode := gGameSettings.GameMode;
4659 g_Player_ResetTeams();
4661 if isWadPath(Map) then
4662 begin
4663 NewWAD := g_ExtractWadName(Map);
4664 ResName := g_ExtractFileName(Map);
4665 if g_Game_IsServer then
4666 begin
4667 gWADHash := MD5File(MapsDir + NewWAD);
4668 g_Game_LoadWAD(NewWAD);
4669 end else
4670 // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART
4671 g_Game_ClientWAD(NewWAD, gWADHash);
4672 end else
4673 ResName := Map;
4675 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4676 if Result then
4677 begin
4678 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4680 gState := STATE_NONE;
4681 g_ActiveWindow := nil;
4682 gGameOn := True;
4684 DisableCheats();
4685 ResetTimer();
4687 if gGameSettings.GameMode = GM_CTF then
4688 begin
4689 g_Map_ResetFlag(FLAG_RED);
4690 g_Map_ResetFlag(FLAG_BLUE);
4691 // CTF, à ôëàãîâ íåò:
4692 if not g_Map_HaveFlagPoints() then
4693 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4694 end;
4695 end
4696 else
4697 begin
4698 gState := STATE_MENU;
4699 gGameOn := False;
4700 end;
4702 gExit := 0;
4703 gPauseMain := false;
4704 gPauseHolmes := false;
4705 gTime := 0;
4706 NetTimeToUpdate := 1;
4707 NetTimeToReliable := 0;
4708 NetTimeToMaster := NetMasterRate;
4709 gLMSRespawn := LMS_RESPAWN_NONE;
4710 gLMSRespawnTime := 0;
4711 gMissionFailed := False;
4712 gNextMap := '';
4714 gCoopMonstersKilled := 0;
4715 gCoopSecretsFound := 0;
4717 gVoteInProgress := False;
4718 gVotePassed := False;
4719 gVoteCount := 0;
4720 gVoted := False;
4722 gStatsOff := False;
4724 if not gGameOn then Exit;
4726 g_Game_SpectateCenterView();
4728 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4729 begin
4730 gLMSRespawn := LMS_RESPAWN_WARMUP;
4731 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4732 gLMSSoftSpawn := True;
4733 if NetMode = NET_SERVER then
4734 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4735 else
4736 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4737 end;
4739 if NetMode = NET_SERVER then
4740 begin
4741 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4743 // Ìàñòåðñåðâåð
4744 if NetUseMaster then
4745 begin
4746 if (NetMHost = nil) or (NetMPeer = nil) then
4747 if not g_Net_Slist_Connect then
4748 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4750 g_Net_Slist_Update;
4751 end;
4753 if NetClients <> nil then
4754 for I := 0 to High(NetClients) do
4755 if NetClients[I].Used then
4756 begin
4757 NetClients[I].Voted := False;
4758 if NetClients[I].RequestedFullUpdate then
4759 begin
4760 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4761 NetClients[I].RequestedFullUpdate := False;
4762 end;
4763 end;
4765 g_Net_UnbanNonPermHosts();
4766 end;
4768 if gLastMap then
4769 begin
4770 gCoopTotalMonstersKilled := 0;
4771 gCoopTotalSecretsFound := 0;
4772 gCoopTotalMonsters := 0;
4773 gCoopTotalSecrets := 0;
4774 gLastMap := False;
4775 end;
4777 g_Game_ExecuteEvent('onmapstart');
4778 end;
4780 procedure SetFirstLevel();
4781 begin
4782 gNextMap := '';
4784 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4785 if MapList = nil then
4786 Exit;
4788 SortSArray(MapList);
4789 gNextMap := MapList[Low(MapList)];
4791 MapList := nil;
4792 end;
4794 procedure g_Game_ExitLevel(const Map: AnsiString);
4795 begin
4796 gNextMap := Map;
4798 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4799 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4800 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4801 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4803 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4804 if gGameSettings.GameType = GT_SINGLE then
4805 gExit := EXIT_ENDLEVELSINGLE
4806 else // Âûøëè â âûõîä â Ñâîåé èãðå
4807 begin
4808 gExit := EXIT_ENDLEVELCUSTOM;
4809 if gGameSettings.GameMode = GM_COOP then
4810 g_Player_RememberAll;
4812 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4813 begin
4814 gLastMap := True;
4815 if gGameSettings.GameMode = GM_COOP then
4816 gStatsOff := True;
4818 gStatsPressed := True;
4819 gNextMap := 'MAP01';
4821 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4822 g_Game_NextLevel;
4824 if g_Game_IsNet then
4825 begin
4826 MH_SEND_GameStats();
4827 MH_SEND_CoopStats();
4828 end;
4829 end;
4830 end;
4831 end;
4833 procedure g_Game_RestartLevel();
4834 var
4835 Map: string;
4836 begin
4837 if gGameSettings.GameMode = GM_SINGLE then
4838 begin
4839 g_Game_Restart();
4840 Exit;
4841 end;
4842 gExit := EXIT_ENDLEVELCUSTOM;
4843 Map := g_ExtractFileName(gMapInfo.Map);
4844 gNextMap := Map;
4845 end;
4847 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4848 var
4849 gWAD: String;
4850 begin
4851 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4852 Exit;
4853 if not g_Game_IsClient then
4854 Exit;
4855 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4856 if gWAD = '' then
4857 begin
4858 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4859 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4860 if gWAD = '' then
4861 begin
4862 g_Game_Free();
4863 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4864 Exit;
4865 end;
4866 end;
4867 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4868 g_Game_LoadWAD(NewWAD);
4869 end;
4871 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4872 var
4873 i, n, nb, nr: Integer;
4875 function monRespawn (mon: TMonster): Boolean;
4876 begin
4877 result := false; // don't stop
4878 if not mon.FNoRespawn then mon.Respawn();
4879 end;
4881 begin
4882 if not g_Game_IsServer then Exit;
4883 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4884 gLMSRespawn := LMS_RESPAWN_NONE;
4885 gLMSRespawnTime := 0;
4886 MessageTime := 0;
4888 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4889 begin
4890 gMissionFailed := True;
4891 g_Game_RestartLevel;
4892 Exit;
4893 end;
4895 n := 0; nb := 0; nr := 0;
4896 for i := Low(gPlayers) to High(gPlayers) do
4897 if (gPlayers[i] <> nil) and
4898 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4899 (gPlayers[i] is TBot)) then
4900 begin
4901 Inc(n);
4902 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4903 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4904 end;
4906 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4907 begin
4908 // wait a second until the fuckers finally decide to join
4909 gLMSRespawn := LMS_RESPAWN_WARMUP;
4910 gLMSRespawnTime := gTime + 1000;
4911 gLMSSoftSpawn := NoMapRestart;
4912 Exit;
4913 end;
4915 g_Player_RemoveAllCorpses;
4916 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4917 if g_Game_IsNet then
4918 MH_SEND_GameEvent(NET_EV_LMS_START);
4920 for i := Low(gPlayers) to High(gPlayers) do
4921 begin
4922 if gPlayers[i] = nil then continue;
4923 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4924 // don't touch normal spectators
4925 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4926 begin
4927 gPlayers[i].FNoRespawn := True;
4928 gPlayers[i].Lives := 0;
4929 if g_Game_IsNet then
4930 MH_SEND_PlayerStats(gPlayers[I].UID);
4931 continue;
4932 end;
4933 gPlayers[i].FNoRespawn := False;
4934 gPlayers[i].Lives := gGameSettings.MaxLives;
4935 gPlayers[i].Respawn(False, True);
4936 if gGameSettings.GameMode = GM_COOP then
4937 begin
4938 gPlayers[i].Frags := 0;
4939 gPlayers[i].RecallState;
4940 end;
4941 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4942 gPlayer1 := g_Player_Get(gLMSPID1);
4943 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4944 gPlayer2 := g_Player_Get(gLMSPID2);
4945 end;
4947 g_Items_RestartRound();
4950 g_Mons_ForEach(monRespawn);
4952 gLMSSoftSpawn := False;
4953 end;
4955 function g_Game_GetFirstMap(WAD: String): String;
4956 begin
4957 Result := '';
4959 MapList := g_Map_GetMapsList(WAD);
4960 if MapList = nil then
4961 Exit;
4963 SortSArray(MapList);
4964 Result := MapList[Low(MapList)];
4966 if not g_Map_Exist(WAD + ':\' + Result) then
4967 Result := '';
4969 MapList := nil;
4970 end;
4972 function g_Game_GetNextMap(): String;
4973 var
4974 I: Integer;
4975 Map: string;
4976 begin
4977 Result := '';
4979 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4980 if MapList = nil then
4981 Exit;
4983 Map := g_ExtractFileName(gMapInfo.Map);
4985 SortSArray(MapList);
4986 MapIndex := -255;
4987 for I := Low(MapList) to High(MapList) do
4988 if Map = MapList[I] then
4989 begin
4990 MapIndex := I;
4991 Break;
4992 end;
4994 if MapIndex <> -255 then
4995 begin
4996 if MapIndex = High(MapList) then
4997 Result := MapList[Low(MapList)]
4998 else
4999 Result := MapList[MapIndex + 1];
5001 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
5002 end;
5004 MapList := nil;
5005 end;
5007 procedure g_Game_NextLevel();
5008 begin
5009 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
5010 gExit := EXIT_ENDLEVELCUSTOM
5011 else
5012 begin
5013 gExit := EXIT_ENDLEVELSINGLE;
5014 Exit;
5015 end;
5017 if gNextMap <> '' then Exit;
5018 gNextMap := g_Game_GetNextMap();
5019 end;
5021 function g_Game_IsTestMap(): Boolean;
5022 begin
5023 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
5024 end;
5026 procedure g_Game_DeleteTestMap();
5027 var
5028 a: Integer;
5029 //MapName: AnsiString;
5030 WadName: string;
5032 WAD: TWADFile;
5033 MapList: SSArray;
5034 time: Integer;
5036 begin
5037 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
5038 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
5039 if (a = 0) then exit;
5041 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
5042 WadName := Copy(gMapToDelete, 1, a+3);
5043 Delete(gMapToDelete, 1, a+5);
5044 gMapToDelete := UpperCase(gMapToDelete);
5045 //MapName := '';
5046 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
5049 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
5050 if MapName <> TEST_MAP_NAME then
5051 Exit;
5053 if not gTempDelete then
5054 begin
5055 time := g_GetFileTime(WadName);
5056 WAD := TWADFile.Create();
5058 // ×èòàåì Wad-ôàéë:
5059 if not WAD.ReadFile(WadName) then
5060 begin // Íåò òàêîãî WAD-ôàéëà
5061 WAD.Free();
5062 Exit;
5063 end;
5065 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
5066 WAD.CreateImage();
5067 MapList := WAD.GetResourcesList('');
5069 if MapList <> nil then
5070 for a := 0 to High(MapList) do
5071 if MapList[a] = MapName then
5072 begin
5073 // Óäàëÿåì è ñîõðàíÿåì:
5074 WAD.RemoveResource('', MapName);
5075 WAD.SaveTo(WadName);
5076 Break;
5077 end;
5079 WAD.Free();
5080 g_SetFileTime(WadName, time);
5081 end else
5083 if gTempDelete then DeleteFile(WadName);
5084 end;
5086 procedure GameCVars(P: SSArray);
5087 var
5088 a, b: Integer;
5089 stat: TPlayerStatArray;
5090 cmd, s: string;
5091 config: TConfig;
5092 begin
5093 stat := nil;
5094 cmd := LowerCase(P[0]);
5095 if cmd = 'r_showfps' then
5096 begin
5097 if (Length(P) > 1) and
5098 ((P[1] = '1') or (P[1] = '0')) then
5099 gShowFPS := (P[1][1] = '1');
5101 if gShowFPS then
5102 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
5103 else
5104 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
5105 end
5106 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
5107 begin
5108 with gGameSettings do
5109 begin
5110 if (Length(P) > 1) and
5111 ((P[1] = '1') or (P[1] = '0')) then
5112 begin
5113 if (P[1][1] = '1') then
5114 Options := Options or GAME_OPTION_TEAMDAMAGE
5115 else
5116 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
5117 end;
5119 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
5120 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
5121 else
5122 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
5124 if g_Game_IsNet then MH_SEND_GameSettings;
5125 end;
5126 end
5127 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
5128 begin
5129 with gGameSettings do
5130 begin
5131 if (Length(P) > 1) and
5132 ((P[1] = '1') or (P[1] = '0')) then
5133 begin
5134 if (P[1][1] = '1') then
5135 Options := Options or GAME_OPTION_WEAPONSTAY
5136 else
5137 Options := Options and (not GAME_OPTION_WEAPONSTAY);
5138 end;
5140 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
5141 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
5142 else
5143 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
5145 if g_Game_IsNet then MH_SEND_GameSettings;
5146 end;
5147 end
5148 else if cmd = 'g_gamemode' then
5149 begin
5150 a := g_Game_TextToMode(P[1]);
5151 if a = GM_SINGLE then a := GM_COOP;
5152 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
5153 begin
5154 gSwitchGameMode := a;
5155 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
5156 (gState = STATE_INTERSINGLE) then
5157 gSwitchGameMode := GM_SINGLE;
5158 if not gGameOn then
5159 gGameSettings.GameMode := gSwitchGameMode;
5160 end;
5161 if gSwitchGameMode = gGameSettings.GameMode then
5162 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
5163 [g_Game_ModeToText(gGameSettings.GameMode)]))
5164 else
5165 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
5166 [g_Game_ModeToText(gGameSettings.GameMode),
5167 g_Game_ModeToText(gSwitchGameMode)]));
5168 end
5169 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
5170 begin
5171 with gGameSettings do
5172 begin
5173 if (Length(P) > 1) and
5174 ((P[1] = '1') or (P[1] = '0')) then
5175 begin
5176 if (P[1][1] = '1') then
5177 Options := Options or GAME_OPTION_ALLOWEXIT
5178 else
5179 Options := Options and (not GAME_OPTION_ALLOWEXIT);
5180 end;
5182 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
5183 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
5184 else
5185 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
5186 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5188 if g_Game_IsNet then MH_SEND_GameSettings;
5189 end;
5190 end
5191 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
5192 begin
5193 with gGameSettings do
5194 begin
5195 if (Length(P) > 1) and
5196 ((P[1] = '1') or (P[1] = '0')) then
5197 begin
5198 if (P[1][1] = '1') then
5199 Options := Options or GAME_OPTION_MONSTERS
5200 else
5201 Options := Options and (not GAME_OPTION_MONSTERS);
5202 end;
5204 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
5205 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
5206 else
5207 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
5208 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5210 if g_Game_IsNet then MH_SEND_GameSettings;
5211 end;
5212 end
5213 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
5214 begin
5215 with gGameSettings do
5216 begin
5217 if (Length(P) > 1) and
5218 ((P[1] = '1') or (P[1] = '0')) then
5219 begin
5220 if (P[1][1] = '1') then
5221 Options := Options or GAME_OPTION_BOTVSPLAYER
5222 else
5223 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
5224 end;
5226 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
5227 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
5228 else
5229 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
5231 if g_Game_IsNet then MH_SEND_GameSettings;
5232 end;
5233 end
5234 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
5235 begin
5236 with gGameSettings do
5237 begin
5238 if (Length(P) > 1) and
5239 ((P[1] = '1') or (P[1] = '0')) then
5240 begin
5241 if (P[1][1] = '1') then
5242 Options := Options or GAME_OPTION_BOTVSMONSTER
5243 else
5244 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
5245 end;
5247 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
5248 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
5249 else
5250 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
5252 if g_Game_IsNet then MH_SEND_GameSettings;
5253 end;
5254 end
5255 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
5256 begin
5257 if Length(P) > 1 then
5258 begin
5259 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
5260 gGameSettings.WarmupTime := 30
5261 else
5262 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
5263 end;
5265 g_Console_Add(Format(_lc[I_MSG_WARMUP],
5266 [gGameSettings.WarmupTime]));
5267 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5268 end
5269 else if cmd = 'net_interp' then
5270 begin
5271 if (Length(P) > 1) then
5272 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
5274 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
5275 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5276 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
5277 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5278 config.Free();
5279 end
5280 else if cmd = 'net_forceplayerupdate' then
5281 begin
5282 if (Length(P) > 1) and
5283 ((P[1] = '1') or (P[1] = '0')) then
5284 NetForcePlayerUpdate := (P[1][1] = '1');
5286 if NetForcePlayerUpdate then
5287 g_Console_Add('net_forceplayerupdate = 1')
5288 else
5289 g_Console_Add('net_forceplayerupdate = 0');
5290 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5291 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
5292 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5293 config.Free();
5294 end
5295 else if cmd = 'net_predictself' then
5296 begin
5297 if (Length(P) > 1) and
5298 ((P[1] = '1') or (P[1] = '0')) then
5299 NetPredictSelf := (P[1][1] = '1');
5301 if NetPredictSelf then
5302 g_Console_Add('net_predictself = 1')
5303 else
5304 g_Console_Add('net_predictself = 0');
5305 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5306 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
5307 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5308 config.Free();
5309 end
5310 else if cmd = 'sv_name' then
5311 begin
5312 if (Length(P) > 1) and (Length(P[1]) > 0) then
5313 begin
5314 NetServerName := P[1];
5315 if Length(NetServerName) > 64 then
5316 SetLength(NetServerName, 64);
5317 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5318 g_Net_Slist_Update;
5319 end;
5321 g_Console_Add(cmd + ' = "' + NetServerName + '"');
5322 end
5323 else if cmd = 'sv_passwd' then
5324 begin
5325 if (Length(P) > 1) and (Length(P[1]) > 0) then
5326 begin
5327 NetPassword := P[1];
5328 if Length(NetPassword) > 24 then
5329 SetLength(NetPassword, 24);
5330 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5331 g_Net_Slist_Update;
5332 end;
5334 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
5335 end
5336 else if cmd = 'sv_maxplrs' then
5337 begin
5338 if (Length(P) > 1) then
5339 begin
5340 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
5341 if g_Game_IsServer and g_Game_IsNet then
5342 begin
5343 b := 0;
5344 for a := 0 to High(NetClients) do
5345 if NetClients[a].Used then
5346 begin
5347 Inc(b);
5348 if b > NetMaxClients then
5349 begin
5350 s := g_Player_Get(NetClients[a].Player).Name;
5351 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
5352 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5353 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5354 end;
5355 end;
5356 if NetUseMaster then
5357 g_Net_Slist_Update;
5358 end;
5359 end;
5361 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
5362 end
5363 else if cmd = 'sv_public' then
5364 begin
5365 if (Length(P) > 1) then
5366 begin
5367 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
5368 if g_Game_IsServer and g_Game_IsNet then
5369 if NetUseMaster then
5370 begin
5371 if NetMPeer = nil then
5372 if not g_Net_Slist_Connect() then
5373 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
5374 g_Net_Slist_Update();
5375 end
5376 else
5377 if NetMPeer <> nil then
5378 g_Net_Slist_Disconnect();
5379 end;
5381 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
5382 end
5383 else if cmd = 'sv_intertime' then
5384 begin
5385 if (Length(P) > 1) then
5386 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
5388 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
5389 end
5390 else if cmd = 'p1_name' then
5391 begin
5392 if (Length(P) > 1) and gGameOn then
5393 begin
5394 if g_Game_IsClient then
5395 begin
5396 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5397 MC_SEND_PlayerSettings;
5398 end
5399 else
5400 if gPlayer1 <> nil then
5401 begin
5402 gPlayer1.Name := b_Text_Unformat(P[1]);
5403 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5404 end
5405 else
5406 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5407 end;
5408 end
5409 else if cmd = 'p2_name' then
5410 begin
5411 if (Length(P) > 1) and gGameOn then
5412 begin
5413 if g_Game_IsClient then
5414 begin
5415 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5416 MC_SEND_PlayerSettings;
5417 end
5418 else
5419 if gPlayer2 <> nil then
5420 begin
5421 gPlayer2.Name := b_Text_Unformat(P[1]);
5422 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5423 end
5424 else
5425 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5426 end;
5427 end
5428 else if cmd = 'p1_color' then
5429 begin
5430 if Length(P) > 3 then
5431 if g_Game_IsClient then
5432 begin
5433 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5434 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5435 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5436 MC_SEND_PlayerSettings;
5437 end
5438 else
5439 if gPlayer1 <> nil then
5440 begin
5441 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5442 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5443 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5444 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5445 end
5446 else
5447 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5448 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5449 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5450 end
5451 else if (cmd = 'p2_color') and not g_Game_IsNet then
5452 begin
5453 if Length(P) > 3 then
5454 if g_Game_IsClient then
5455 begin
5456 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5457 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5458 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5459 MC_SEND_PlayerSettings;
5460 end
5461 else
5462 if gPlayer2 <> nil then
5463 begin
5464 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5465 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5466 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5467 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5468 end
5469 else
5470 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5471 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5472 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5473 end
5474 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5475 begin
5476 if cmd = 'r_showtime' then
5477 begin
5478 if (Length(P) > 1) and
5479 ((P[1] = '1') or (P[1] = '0')) then
5480 gShowTime := (P[1][1] = '1');
5482 if gShowTime then
5483 g_Console_Add(_lc[I_MSG_TIME_ON])
5484 else
5485 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5486 end
5487 else if cmd = 'r_showscore' then
5488 begin
5489 if (Length(P) > 1) and
5490 ((P[1] = '1') or (P[1] = '0')) then
5491 gShowGoals := (P[1][1] = '1');
5493 if gShowGoals then
5494 g_Console_Add(_lc[I_MSG_SCORE_ON])
5495 else
5496 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5497 end
5498 else if cmd = 'r_showstat' then
5499 begin
5500 if (Length(P) > 1) and
5501 ((P[1] = '1') or (P[1] = '0')) then
5502 gShowStat := (P[1][1] = '1');
5504 if gShowStat then
5505 g_Console_Add(_lc[I_MSG_STATS_ON])
5506 else
5507 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5508 end
5509 else if cmd = 'r_showkillmsg' then
5510 begin
5511 if (Length(P) > 1) and
5512 ((P[1] = '1') or (P[1] = '0')) then
5513 gShowKillMsg := (P[1][1] = '1');
5515 if gShowKillMsg then
5516 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5517 else
5518 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5519 end
5520 else if cmd = 'r_showlives' then
5521 begin
5522 if (Length(P) > 1) and
5523 ((P[1] = '1') or (P[1] = '0')) then
5524 gShowLives := (P[1][1] = '1');
5526 if gShowLives then
5527 g_Console_Add(_lc[I_MSG_LIVES_ON])
5528 else
5529 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5530 end
5531 else if cmd = 'r_showspect' then
5532 begin
5533 if (Length(P) > 1) and
5534 ((P[1] = '1') or (P[1] = '0')) then
5535 gSpectHUD := (P[1][1] = '1');
5537 if gSpectHUD then
5538 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5539 else
5540 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5541 end
5542 else if cmd = 'r_showping' then
5543 begin
5544 if (Length(P) > 1) and
5545 ((P[1] = '1') or (P[1] = '0')) then
5546 gShowPing := (P[1][1] = '1');
5548 if gShowPing then
5549 g_Console_Add(_lc[I_MSG_PING_ON])
5550 else
5551 g_Console_Add(_lc[I_MSG_PING_OFF]);
5552 end
5553 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5554 begin
5555 if Length(P) > 1 then
5556 begin
5557 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5558 gGameSettings.GoalLimit := 0
5559 else
5560 begin
5561 b := 0;
5563 if gGameSettings.GameMode = GM_DM then
5564 begin // DM
5565 stat := g_Player_GetStats();
5566 if stat <> nil then
5567 for a := 0 to High(stat) do
5568 if stat[a].Frags > b then
5569 b := stat[a].Frags;
5570 end
5571 else // TDM/CTF
5572 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5574 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5575 end;
5577 if g_Game_IsNet then MH_SEND_GameSettings;
5578 end;
5580 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5581 end
5582 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5583 begin
5584 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5585 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5587 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5588 [gGameSettings.TimeLimit div 3600,
5589 (gGameSettings.TimeLimit div 60) mod 60,
5590 gGameSettings.TimeLimit mod 60]));
5591 if g_Game_IsNet then MH_SEND_GameSettings;
5592 end
5593 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5594 begin
5595 if Length(P) > 1 then
5596 begin
5597 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5598 gGameSettings.MaxLives := 0
5599 else
5600 begin
5601 b := 0;
5602 stat := g_Player_GetStats();
5603 if stat <> nil then
5604 for a := 0 to High(stat) do
5605 if stat[a].Lives > b then
5606 b := stat[a].Lives;
5607 gGameSettings.MaxLives :=
5608 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5609 end;
5610 end;
5612 g_Console_Add(Format(_lc[I_MSG_LIVES],
5613 [gGameSettings.MaxLives]));
5614 if g_Game_IsNet then MH_SEND_GameSettings;
5615 end;
5616 end;
5617 end;
5619 procedure PrintHeapStats();
5620 var
5621 hs: TFPCHeapStatus;
5622 begin
5623 hs := GetFPCHeapStatus();
5624 e_LogWriteLn ('v===== heap status =====v');
5625 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
5626 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
5627 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
5628 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
5629 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
5630 e_LogWriteLn ('^=======================^');
5631 end;
5633 procedure DebugCommands(P: SSArray);
5634 var
5635 a, b: Integer;
5636 cmd: string;
5637 //pt: TDFPoint;
5638 mon: TMonster;
5639 begin
5640 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5641 if {gDebugMode}conIsCheatsEnabled then
5642 begin
5643 cmd := LowerCase(P[0]);
5644 if cmd = 'd_window' then
5645 begin
5646 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5647 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5648 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5649 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5650 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5651 end
5652 else if cmd = 'd_sounds' then
5653 begin
5654 if (Length(P) > 1) and
5655 ((P[1] = '1') or (P[1] = '0')) then
5656 g_Debug_Sounds := (P[1][1] = '1');
5658 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5659 end
5660 else if cmd = 'd_frames' then
5661 begin
5662 if (Length(P) > 1) and
5663 ((P[1] = '1') or (P[1] = '0')) then
5664 g_Debug_Frames := (P[1][1] = '1');
5666 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5667 end
5668 else if cmd = 'd_winmsg' then
5669 begin
5670 if (Length(P) > 1) and
5671 ((P[1] = '1') or (P[1] = '0')) then
5672 g_Debug_WinMsgs := (P[1][1] = '1');
5674 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5675 end
5676 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5677 begin
5678 if (Length(P) > 1) and
5679 ((P[1] = '1') or (P[1] = '0')) then
5680 g_Debug_MonsterOff := (P[1][1] = '1');
5682 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5683 end
5684 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5685 begin
5686 if Length(P) > 1 then
5687 case P[1][1] of
5688 '0': g_debug_BotAIOff := 0;
5689 '1': g_debug_BotAIOff := 1;
5690 '2': g_debug_BotAIOff := 2;
5691 '3': g_debug_BotAIOff := 3;
5692 end;
5694 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5695 end
5696 else if cmd = 'd_monster' then
5697 begin
5698 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5699 if Length(P) < 2 then
5700 begin
5701 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5702 g_Console_Add('ID | Name');
5703 for b := MONSTER_DEMON to MONSTER_MAN do
5704 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5705 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
5706 end else
5707 begin
5708 a := StrToIntDef(P[1], 0);
5709 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5710 a := g_Mons_TypeIdByName(P[1]);
5712 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5713 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5714 else
5715 begin
5716 with gPlayer1.Obj do
5717 begin
5718 mon := g_Monsters_Create(a,
5719 X + Rect.X + (Rect.Width div 2),
5720 Y + Rect.Y + Rect.Height,
5721 gPlayer1.Direction, True);
5722 end;
5723 if (Length(P) > 2) and (mon <> nil) then
5724 begin
5725 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
5726 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
5727 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
5728 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
5729 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
5730 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
5731 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
5732 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
5733 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5734 end;
5735 end;
5736 end;
5737 end
5738 else if (cmd = 'd_health') then
5739 begin
5740 if (Length(P) > 1) and
5741 ((P[1] = '1') or (P[1] = '0')) then
5742 g_debug_HealthBar := (P[1][1] = '1');
5744 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5745 end
5746 else if (cmd = 'd_player') then
5747 begin
5748 if (Length(P) > 1) and
5749 ((P[1] = '1') or (P[1] = '0')) then
5750 g_debug_Player := (P[1][1] = '1');
5752 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5753 end
5754 else if (cmd = 'd_joy') then
5755 begin
5756 for a := 1 to 8 do
5757 g_Console_Add(e_JoystickStateToString(a));
5758 end
5759 else if (cmd = 'd_mem') then
5760 begin
5761 PrintHeapStats();
5762 end;
5763 end
5764 else
5765 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5766 end;
5769 procedure GameCheats(P: SSArray);
5770 var
5771 cmd: string;
5772 f, a: Integer;
5773 plr: TPlayer;
5774 begin
5775 if (not gGameOn) or (not conIsCheatsEnabled) then
5776 begin
5777 g_Console_Add('not available');
5778 exit;
5779 end;
5780 plr := gPlayer1;
5781 if plr = nil then
5782 begin
5783 g_Console_Add('where is the player?!');
5784 exit;
5785 end;
5786 cmd := LowerCase(P[0]);
5787 // god
5788 if cmd = 'god' then
5789 begin
5790 plr.GodMode := not plr.GodMode;
5791 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5792 exit;
5793 end;
5794 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5795 if cmd = 'give' then
5796 begin
5797 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5798 for f := 1 to High(P) do
5799 begin
5800 cmd := LowerCase(P[f]);
5801 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5802 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5803 if cmd = 'exit' then
5804 begin
5805 if gTriggers <> nil then
5806 begin
5807 for a := 0 to High(gTriggers) do
5808 begin
5809 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5810 begin
5811 g_Console_Add('player left the map');
5812 gExitByTrigger := True;
5813 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5814 g_Game_ExitLevel(gTriggers[a].tgcMap);
5815 break;
5816 end;
5817 end;
5818 end;
5819 continue;
5820 end;
5822 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5823 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5824 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5825 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5826 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5828 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5829 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5831 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5832 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;
5834 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5835 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5837 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5838 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5840 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5841 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5843 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5844 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5845 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5847 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5848 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5849 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5850 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;
5851 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5852 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5854 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;
5855 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;
5856 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;
5857 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;
5858 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;
5859 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;
5861 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5862 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;
5864 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;
5865 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;
5867 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5869 if cmd = 'ammo' then
5870 begin
5871 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5872 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5873 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5874 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5875 plr.GiveItem(ITEM_AMMO_FUELCAN);
5876 g_Console_Add('player got some ammo');
5877 continue;
5878 end;
5880 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5881 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5883 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5884 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5886 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5887 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5889 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5890 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5892 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5894 if cmd = 'weapons' then
5895 begin
5896 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5897 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5898 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5899 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5900 plr.GiveItem(ITEM_WEAPON_PLASMA);
5901 plr.GiveItem(ITEM_WEAPON_BFG);
5902 g_Console_Add('player got weapons');
5903 continue;
5904 end;
5906 if cmd = 'keys' then
5907 begin
5908 plr.GiveItem(ITEM_KEY_RED);
5909 plr.GiveItem(ITEM_KEY_GREEN);
5910 plr.GiveItem(ITEM_KEY_BLUE);
5911 g_Console_Add('player got all keys');
5912 continue;
5913 end;
5915 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5916 end;
5917 exit;
5918 end;
5919 // open
5920 if cmd = 'open' then
5921 begin
5922 g_Console_Add('player activated sesame');
5923 g_Triggers_OpenAll();
5924 exit;
5925 end;
5926 // fly
5927 if cmd = 'fly' then
5928 begin
5929 gFly := not gFly;
5930 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5931 exit;
5932 end;
5933 // noclip
5934 if cmd = 'noclip' then
5935 begin
5936 plr.SwitchNoClip;
5937 g_Console_Add('wall hardeness adjusted');
5938 exit;
5939 end;
5940 // notarget
5941 if cmd = 'notarget' then
5942 begin
5943 plr.NoTarget := not plr.NoTarget;
5944 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5945 exit;
5946 end;
5947 // noreload
5948 if cmd = 'noreload' then
5949 begin
5950 plr.NoReload := not plr.NoReload;
5951 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5952 exit;
5953 end;
5954 // speedy
5955 if cmd = 'speedy' then
5956 begin
5957 MAX_RUNVEL := 32-MAX_RUNVEL;
5958 g_Console_Add('speed adjusted');
5959 exit;
5960 end;
5961 // jumpy
5962 if cmd = 'jumpy' then
5963 begin
5964 VEL_JUMP := 30-VEL_JUMP;
5965 g_Console_Add('jump height adjusted');
5966 exit;
5967 end;
5968 // automap
5969 if cmd = 'automap' then
5970 begin
5971 gShowMap := not gShowMap;
5972 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5973 exit;
5974 end;
5975 // aimline
5976 if cmd = 'aimline' then
5977 begin
5978 gAimLine := not gAimLine;
5979 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5980 exit;
5981 end;
5982 end;
5984 procedure GameCommands(P: SSArray);
5985 var
5986 a, b: Integer;
5987 s, pw: String;
5988 chstr: string;
5989 cmd: string;
5990 pl: pTNetClient = nil;
5991 plr: TPlayer;
5992 prt: Word;
5993 nm: Boolean;
5994 listen: LongWord;
5995 begin
5996 // Îáùèå êîìàíäû:
5997 cmd := LowerCase(P[0]);
5998 chstr := '';
5999 if (cmd = 'quit') or
6000 (cmd = 'exit') then
6001 begin
6002 g_Game_Free();
6003 g_Game_Quit();
6004 Exit;
6005 end
6006 else if cmd = 'pause' then
6007 begin
6008 if (g_ActiveWindow = nil) then
6009 g_Game_Pause(not gPauseMain);
6010 end
6011 else if cmd = 'endgame' then
6012 gExit := EXIT_SIMPLE
6013 else if cmd = 'restart' then
6014 begin
6015 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
6016 begin
6017 if g_Game_IsClient then
6018 begin
6019 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6020 Exit;
6021 end;
6022 g_Game_Restart();
6023 end else
6024 g_Console_Add(_lc[I_MSG_NOT_GAME]);
6025 end
6026 else if cmd = 'kick' then
6027 begin
6028 if g_Game_IsServer then
6029 begin
6030 if Length(P) < 2 then
6031 begin
6032 g_Console_Add('kick <name>');
6033 Exit;
6034 end;
6035 if P[1] = '' then
6036 begin
6037 g_Console_Add('kick <name>');
6038 Exit;
6039 end;
6041 if g_Game_IsNet then
6042 pl := g_Net_Client_ByName(P[1]);
6043 if (pl <> nil) then
6044 begin
6045 s := g_Net_ClientName_ByID(pl^.ID);
6046 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
6047 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
6048 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
6049 if NetUseMaster then
6050 g_Net_Slist_Update;
6051 end else if gPlayers <> nil then
6052 for a := Low(gPlayers) to High(gPlayers) do
6053 if gPlayers[a] <> nil then
6054 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
6055 begin
6056 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
6057 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
6058 continue;
6059 gPlayers[a].Lives := 0;
6060 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
6061 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
6062 g_Player_Remove(gPlayers[a].UID);
6063 if NetUseMaster then
6064 g_Net_Slist_Update;
6065 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
6066 g_Bot_MixNames();
6067 end;
6068 end else
6069 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6070 end
6071 else if cmd = 'kick_id' then
6072 begin
6073 if g_Game_IsServer and g_Game_IsNet then
6074 begin
6075 if Length(P) < 2 then
6076 begin
6077 g_Console_Add('kick_id <client ID>');
6078 Exit;
6079 end;
6080 if P[1] = '' then
6081 begin
6082 g_Console_Add('kick_id <client ID>');
6083 Exit;
6084 end;
6086 a := StrToIntDef(P[1], 0);
6087 if (NetClients <> nil) and (a <= High(NetClients)) then
6088 begin
6089 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6090 begin
6091 s := g_Net_ClientName_ByID(NetClients[a].ID);
6092 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
6093 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
6094 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
6095 if NetUseMaster then
6096 g_Net_Slist_Update;
6097 end;
6098 end;
6099 end else
6100 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6101 end
6102 else if cmd = 'ban' then
6103 begin
6104 if g_Game_IsServer and g_Game_IsNet then
6105 begin
6106 if Length(P) < 2 then
6107 begin
6108 g_Console_Add('ban <name>');
6109 Exit;
6110 end;
6111 if P[1] = '' then
6112 begin
6113 g_Console_Add('ban <name>');
6114 Exit;
6115 end;
6117 pl := g_Net_Client_ByName(P[1]);
6118 if (pl <> nil) then
6119 begin
6120 s := g_Net_ClientName_ByID(pl^.ID);
6121 g_Net_BanHost(pl^.Peer^.address.host, False);
6122 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
6123 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6124 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6125 if NetUseMaster then
6126 g_Net_Slist_Update;
6127 end else
6128 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6129 end else
6130 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6131 end
6132 else if cmd = 'ban_id' then
6133 begin
6134 if g_Game_IsServer and g_Game_IsNet then
6135 begin
6136 if Length(P) < 2 then
6137 begin
6138 g_Console_Add('ban_id <client ID>');
6139 Exit;
6140 end;
6141 if P[1] = '' then
6142 begin
6143 g_Console_Add('ban_id <client ID>');
6144 Exit;
6145 end;
6147 a := StrToIntDef(P[1], 0);
6148 if (NetClients <> nil) and (a <= High(NetClients)) then
6149 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6150 begin
6151 s := g_Net_ClientName_ByID(NetClients[a].ID);
6152 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
6153 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
6154 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6155 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6156 if NetUseMaster then
6157 g_Net_Slist_Update;
6158 end;
6159 end else
6160 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6161 end
6162 else if cmd = 'permban' then
6163 begin
6164 if g_Game_IsServer and g_Game_IsNet then
6165 begin
6166 if Length(P) < 2 then
6167 begin
6168 g_Console_Add('permban <name>');
6169 Exit;
6170 end;
6171 if P[1] = '' then
6172 begin
6173 g_Console_Add('permban <name>');
6174 Exit;
6175 end;
6177 pl := g_Net_Client_ByName(P[1]);
6178 if (pl <> nil) then
6179 begin
6180 s := g_Net_ClientName_ByID(pl^.ID);
6181 g_Net_BanHost(pl^.Peer^.address.host);
6182 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
6183 g_Net_SaveBanList();
6184 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6185 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6186 if NetUseMaster then
6187 g_Net_Slist_Update;
6188 end else
6189 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6190 end else
6191 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6192 end
6193 else if cmd = 'permban_id' then
6194 begin
6195 if g_Game_IsServer and g_Game_IsNet then
6196 begin
6197 if Length(P) < 2 then
6198 begin
6199 g_Console_Add('permban_id <client ID>');
6200 Exit;
6201 end;
6202 if P[1] = '' then
6203 begin
6204 g_Console_Add('permban_id <client ID>');
6205 Exit;
6206 end;
6208 a := StrToIntDef(P[1], 0);
6209 if (NetClients <> nil) and (a <= High(NetClients)) then
6210 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6211 begin
6212 s := g_Net_ClientName_ByID(NetClients[a].ID);
6213 g_Net_BanHost(NetClients[a].Peer^.address.host);
6214 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
6215 g_Net_SaveBanList();
6216 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6217 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6218 if NetUseMaster then
6219 g_Net_Slist_Update;
6220 end;
6221 end else
6222 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6223 end
6224 else if cmd = 'unban' then
6225 begin
6226 if g_Game_IsServer and g_Game_IsNet then
6227 begin
6228 if Length(P) < 2 then
6229 begin
6230 g_Console_Add('unban <IP Address>');
6231 Exit;
6232 end;
6233 if P[1] = '' then
6234 begin
6235 g_Console_Add('unban <IP Address>');
6236 Exit;
6237 end;
6239 if g_Net_UnbanHost(P[1]) then
6240 begin
6241 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
6242 g_Net_SaveBanList();
6243 end else
6244 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
6245 end else
6246 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6247 end
6248 else if cmd = 'clientlist' then
6249 begin
6250 if g_Game_IsServer and g_Game_IsNet then
6251 begin
6252 b := 0;
6253 if NetClients <> nil then
6254 for a := Low(NetClients) to High(NetClients) do
6255 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6256 begin
6257 plr := g_Player_Get(NetClients[a].Player);
6258 if plr = nil then continue;
6259 Inc(b);
6260 g_Console_Add(Format('#%2d: %-15s | %s', [a,
6261 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
6262 end;
6263 if b = 0 then
6264 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
6265 end else
6266 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6267 end
6268 else if cmd = 'connect' then
6269 begin
6270 if (NetMode = NET_NONE) then
6271 begin
6272 if Length(P) < 2 then
6273 begin
6274 g_Console_Add('connect <IP> [port] [password]');
6275 Exit;
6276 end;
6277 if P[1] = '' then
6278 begin
6279 g_Console_Add('connect <IP> [port] [password]');
6280 Exit;
6281 end;
6283 if Length(P) > 2 then
6284 prt := StrToIntDef(P[2], 25666)
6285 else
6286 prt := 25666;
6288 if Length(P) > 3 then
6289 pw := P[3]
6290 else
6291 pw := '';
6293 g_Game_StartClient(P[1], prt, pw);
6294 end;
6295 end
6296 else if cmd = 'disconnect' then
6297 begin
6298 if (NetMode = NET_CLIENT) then
6299 g_Net_Disconnect();
6300 end
6301 else if cmd = 'reconnect' then
6302 begin
6303 if (NetMode = NET_SERVER) then
6304 Exit;
6306 if (NetMode = NET_CLIENT) then
6307 begin
6308 g_Net_Disconnect();
6309 gExit := EXIT_SIMPLE;
6310 EndGame;
6311 end;
6313 //TODO: Use last successful password to reconnect, instead of ''
6314 g_Game_StartClient(NetClientIP, NetClientPort, '');
6315 end
6316 else if (cmd = 'addbot') or
6317 (cmd = 'bot_add') then
6318 begin
6319 if Length(P) > 1 then
6320 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
6321 else
6322 g_Bot_Add(TEAM_NONE, 2);
6323 end
6324 else if cmd = 'bot_addlist' then
6325 begin
6326 if Length(P) > 1 then
6327 if Length(P) = 2 then
6328 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
6329 else
6330 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
6331 end
6332 else if cmd = 'bot_removeall' then
6333 g_Bot_RemoveAll()
6334 else if cmd = 'chat' then
6335 begin
6336 if g_Game_IsNet then
6337 begin
6338 if Length(P) > 1 then
6339 begin
6340 for a := 1 to High(P) do
6341 chstr := chstr + P[a] + ' ';
6343 if Length(chstr) > 200 then SetLength(chstr, 200);
6345 if Length(chstr) < 1 then
6346 begin
6347 g_Console_Add('chat <text>');
6348 Exit;
6349 end;
6351 chstr := b_Text_Format(chstr);
6352 if g_Game_IsClient then
6353 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
6354 else
6355 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
6356 end
6357 else
6358 g_Console_Add('chat <text>');
6359 end else
6360 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6361 end
6362 else if cmd = 'teamchat' then
6363 begin
6364 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
6365 begin
6366 if Length(P) > 1 then
6367 begin
6368 for a := 1 to High(P) do
6369 chstr := chstr + P[a] + ' ';
6371 if Length(chstr) > 200 then SetLength(chstr, 200);
6373 if Length(chstr) < 1 then
6374 begin
6375 g_Console_Add('teamchat <text>');
6376 Exit;
6377 end;
6379 chstr := b_Text_Format(chstr);
6380 if g_Game_IsClient then
6381 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
6382 else
6383 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
6384 gPlayer1Settings.Team);
6385 end
6386 else
6387 g_Console_Add('teamchat <text>');
6388 end else
6389 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6390 end
6391 else if cmd = 'game' then
6392 begin
6393 if gGameSettings.GameType <> GT_NONE then
6394 begin
6395 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6396 Exit;
6397 end;
6398 if Length(P) = 1 then
6399 begin
6400 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
6401 Exit;
6402 end;
6403 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
6404 P[1] := addWadExtension(P[1]);
6405 if FileExists(MapsDir + P[1]) then
6406 begin
6407 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6408 if Length(P) < 3 then
6409 begin
6410 SetLength(P, 3);
6411 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6412 end;
6414 s := P[1] + ':\' + UpperCase(P[2]);
6416 if g_Map_Exist(MapsDir + s) then
6417 begin
6418 // Çàïóñêàåì ñâîþ èãðó
6419 g_Game_Free();
6420 with gGameSettings do
6421 begin
6422 GameMode := g_Game_TextToMode(gcGameMode);
6423 if gSwitchGameMode <> GM_NONE then
6424 GameMode := gSwitchGameMode;
6425 if GameMode = GM_NONE then GameMode := GM_DM;
6426 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6427 b := 1;
6428 if Length(P) >= 4 then
6429 b := StrToIntDef(P[3], 1);
6430 g_Game_StartCustom(s, GameMode, TimeLimit,
6431 GoalLimit, MaxLives, Options, b);
6432 end;
6433 end
6434 else
6435 if P[2] = '' then
6436 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6437 else
6438 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[2]), P[1]]));
6439 end else
6440 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6441 end
6442 else if cmd = 'host' then
6443 begin
6444 if gGameSettings.GameType <> GT_NONE then
6445 begin
6446 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6447 Exit;
6448 end;
6449 if Length(P) < 4 then
6450 begin
6451 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
6452 Exit;
6453 end;
6454 if not StrToIp(P[1], listen) then
6455 Exit;
6456 prt := StrToIntDef(P[2], 25666);
6458 P[3] := addWadExtension(P[3]);
6459 if FileExists(MapsDir + P[3]) then
6460 begin
6461 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6462 if Length(P) < 5 then
6463 begin
6464 SetLength(P, 5);
6465 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
6466 end;
6468 s := P[3] + ':\' + UpperCase(P[4]);
6470 if g_Map_Exist(MapsDir + s) then
6471 begin
6472 // Çàïóñêàåì ñâîþ èãðó
6473 g_Game_Free();
6474 with gGameSettings do
6475 begin
6476 GameMode := g_Game_TextToMode(gcGameMode);
6477 if gSwitchGameMode <> GM_NONE then
6478 GameMode := gSwitchGameMode;
6479 if GameMode = GM_NONE then GameMode := GM_DM;
6480 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6481 b := 0;
6482 if Length(P) >= 6 then
6483 b := StrToIntDef(P[5], 0);
6484 g_Game_StartServer(s, GameMode, TimeLimit,
6485 GoalLimit, MaxLives, Options, b, listen, prt);
6486 end;
6487 end
6488 else
6489 if P[4] = '' then
6490 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6491 else
6492 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]));
6493 end else
6494 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6495 end
6496 else if cmd = 'map' then
6497 begin
6498 if Length(P) = 1 then
6499 begin
6500 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6501 begin
6502 g_Console_Add(cmd + ' <MAP>');
6503 g_Console_Add(cmd + ' <WAD> [MAP]');
6504 end else
6505 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6506 end else
6507 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6508 begin
6509 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6510 if Length(P) < 3 then
6511 begin
6512 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6513 s := UpperCase(P[1]);
6514 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6515 begin // Êàðòà íàøëàñü
6516 gExitByTrigger := False;
6517 if gGameOn then
6518 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6519 gNextMap := s;
6520 gExit := EXIT_ENDLEVELCUSTOM;
6521 end
6522 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6523 g_Game_ChangeMap(s);
6524 end else
6525 begin
6526 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6527 P[1] := addWadExtension(P[1]);
6528 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6529 if FileExists(MapsDir + P[1]) then
6530 begin
6531 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6532 SetLength(P, 3);
6533 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6535 s := P[1] + ':\' + P[2];
6537 if g_Map_Exist(MapsDir + s) then
6538 begin
6539 gExitByTrigger := False;
6540 if gGameOn then
6541 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6542 gNextMap := s;
6543 gExit := EXIT_ENDLEVELCUSTOM;
6544 end
6545 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6546 g_Game_ChangeMap(s);
6547 end else
6548 if P[2] = '' then
6549 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6550 else
6551 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6552 end else
6553 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6554 end;
6555 end else
6556 begin
6557 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6558 P[1] := addWadExtension(P[1]);
6559 if FileExists(MapsDir + P[1]) then
6560 begin
6561 // Íàøëè WAD ôàéë
6562 P[2] := UpperCase(P[2]);
6563 s := P[1] + ':\' + P[2];
6565 if g_Map_Exist(MapsDir + s) then
6566 begin // Íàøëè êàðòó
6567 gExitByTrigger := False;
6568 if gGameOn then
6569 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6570 gNextMap := s;
6571 gExit := EXIT_ENDLEVELCUSTOM;
6572 end
6573 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6574 g_Game_ChangeMap(s);
6575 end else
6576 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6577 end else
6578 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6579 end;
6580 end else
6581 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6582 end
6583 else if cmd = 'nextmap' then
6584 begin
6585 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6586 g_Console_Add(_lc[I_MSG_NOT_GAME])
6587 else begin
6588 nm := True;
6589 if Length(P) = 1 then
6590 begin
6591 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6592 begin
6593 g_Console_Add(cmd + ' <MAP>');
6594 g_Console_Add(cmd + ' <WAD> [MAP]');
6595 end else begin
6596 nm := False;
6597 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6598 end;
6599 end else
6600 begin
6601 nm := False;
6602 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6603 begin
6604 if Length(P) < 3 then
6605 begin
6606 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6607 s := UpperCase(P[1]);
6608 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6609 begin // Êàðòà íàøëàñü
6610 gExitByTrigger := False;
6611 gNextMap := s;
6612 nm := True;
6613 end else
6614 begin
6615 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6616 P[1] := addWadExtension(P[1]);
6617 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6618 if FileExists(MapsDir + P[1]) then
6619 begin
6620 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6621 SetLength(P, 3);
6622 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6624 s := P[1] + ':\' + P[2];
6626 if g_Map_Exist(MapsDir + s) then
6627 begin // Óñòàíàâëèâàåì êàðòó
6628 gExitByTrigger := False;
6629 gNextMap := s;
6630 nm := True;
6631 end else
6632 if P[2] = '' then
6633 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6634 else
6635 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6636 end else
6637 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6638 end;
6639 end else
6640 begin
6641 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6642 P[1] := addWadExtension(P[1]);
6643 if FileExists(MapsDir + P[1]) then
6644 begin
6645 // Íàøëè WAD ôàéë
6646 P[2] := UpperCase(P[2]);
6647 s := P[1] + ':\' + P[2];
6649 if g_Map_Exist(MapsDir + s) then
6650 begin // Íàøëè êàðòó
6651 gExitByTrigger := False;
6652 gNextMap := s;
6653 nm := True;
6654 end else
6655 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6656 end else
6657 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6658 end;
6659 end else
6660 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6661 end;
6662 if nm then
6663 if gNextMap = '' then
6664 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6665 else
6666 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6667 end;
6668 end
6669 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6670 begin
6671 if not gGameOn then
6672 g_Console_Add(_lc[I_MSG_NOT_GAME])
6673 else
6674 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6675 begin
6676 gExitByTrigger := False;
6677 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6678 if (gNextMap = '') and (gTriggers <> nil) then
6679 for a := 0 to High(gTriggers) do
6680 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6681 begin
6682 gExitByTrigger := True;
6683 //gNextMap := gTriggers[a].Data.MapName;
6684 gNextMap := gTriggers[a].tgcMap;
6685 Break;
6686 end;
6687 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6688 if gNextMap = '' then
6689 gNextMap := g_Game_GetNextMap();
6690 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6691 if not isWadPath(gNextMap) then
6692 s := gGameSettings.WAD + ':\' + gNextMap
6693 else
6694 s := gNextMap;
6695 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6696 if g_Map_Exist(MapsDir + s) then
6697 gExit := EXIT_ENDLEVELCUSTOM
6698 else
6699 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6700 end else
6701 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6702 end
6703 else if (cmd = 'event') then
6704 begin
6705 if (Length(P) <= 1) then
6706 begin
6707 for a := 0 to High(gEvents) do
6708 if gEvents[a].Command = '' then
6709 g_Console_Add(gEvents[a].Name + ' <none>')
6710 else
6711 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6712 Exit;
6713 end;
6714 if (Length(P) = 2) then
6715 begin
6716 for a := 0 to High(gEvents) do
6717 if gEvents[a].Name = P[1] then
6718 if gEvents[a].Command = '' then
6719 g_Console_Add(gEvents[a].Name + ' <none>')
6720 else
6721 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6722 Exit;
6723 end;
6724 for a := 0 to High(gEvents) do
6725 if gEvents[a].Name = P[1] then
6726 begin
6727 gEvents[a].Command := '';
6728 for b := 2 to High(P) do
6729 if Pos(' ', P[b]) = 0 then
6730 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6731 else
6732 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6733 gEvents[a].Command := Trim(gEvents[a].Command);
6734 Exit;
6735 end;
6736 end
6737 else if cmd = 'suicide' then
6738 begin
6739 if gGameOn then
6740 begin
6741 if g_Game_IsClient then
6742 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6743 else
6744 begin
6745 if gPlayer1 <> nil then
6746 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6747 if gPlayer2 <> nil then
6748 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6749 end;
6750 end;
6751 end
6752 // Êîìàíäû Ñâîåé èãðû:
6753 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6754 begin
6755 if cmd = 'bot_addred' then
6756 begin
6757 if Length(P) > 1 then
6758 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6759 else
6760 g_Bot_Add(TEAM_RED, 2);
6761 end
6762 else if cmd = 'bot_addblue' then
6763 begin
6764 if Length(P) > 1 then
6765 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6766 else
6767 g_Bot_Add(TEAM_BLUE, 2);
6768 end
6769 else if cmd = 'spectate' then
6770 begin
6771 if not gGameOn then
6772 Exit;
6773 g_Game_Spectate();
6774 end
6775 else if cmd = 'say' then
6776 begin
6777 if g_Game_IsServer and g_Game_IsNet then
6778 begin
6779 if Length(P) > 1 then
6780 begin
6781 chstr := '';
6782 for a := 1 to High(P) do
6783 chstr := chstr + P[a] + ' ';
6785 if Length(chstr) > 200 then SetLength(chstr, 200);
6787 if Length(chstr) < 1 then
6788 begin
6789 g_Console_Add('say <text>');
6790 Exit;
6791 end;
6793 chstr := b_Text_Format(chstr);
6794 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6795 end
6796 else g_Console_Add('say <text>');
6797 end else
6798 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6799 end
6800 else if cmd = 'tell' then
6801 begin
6802 if g_Game_IsServer and g_Game_IsNet then
6803 begin
6804 if (Length(P) > 2) and (P[1] <> '') then
6805 begin
6806 chstr := '';
6807 for a := 2 to High(P) do
6808 chstr := chstr + P[a] + ' ';
6810 if Length(chstr) > 200 then SetLength(chstr, 200);
6812 if Length(chstr) < 1 then
6813 begin
6814 g_Console_Add('tell <playername> <text>');
6815 Exit;
6816 end;
6818 pl := g_Net_Client_ByName(P[1]);
6819 if pl <> nil then
6820 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6821 else
6822 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6823 end
6824 else g_Console_Add('tell <playername> <text>');
6825 end else
6826 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6827 end
6828 else if (cmd = 'overtime') and not g_Game_IsClient then
6829 begin
6830 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6831 Exit;
6832 // Äîïîëíèòåëüíîå âðåìÿ:
6833 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6835 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6836 [gGameSettings.TimeLimit div 3600,
6837 (gGameSettings.TimeLimit div 60) mod 60,
6838 gGameSettings.TimeLimit mod 60]));
6839 if g_Game_IsNet then MH_SEND_GameSettings;
6840 end
6841 else if (cmd = 'rcon_password') and g_Game_IsClient then
6842 begin
6843 if (Length(P) <= 1) then
6844 g_Console_Add('rcon_password <password>')
6845 else
6846 MC_SEND_RCONPassword(P[1]);
6847 end
6848 else if cmd = 'rcon' then
6849 begin
6850 if g_Game_IsClient then
6851 begin
6852 if Length(P) > 1 then
6853 begin
6854 chstr := '';
6855 for a := 1 to High(P) do
6856 chstr := chstr + P[a] + ' ';
6858 if Length(chstr) > 200 then SetLength(chstr, 200);
6860 if Length(chstr) < 1 then
6861 begin
6862 g_Console_Add('rcon <command>');
6863 Exit;
6864 end;
6866 MC_SEND_RCONCommand(chstr);
6867 end
6868 else g_Console_Add('rcon <command>');
6869 end;
6870 end
6871 else if cmd = 'ready' then
6872 begin
6873 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6874 gLMSRespawnTime := gTime + 100;
6875 end
6876 else if (cmd = 'callvote') and g_Game_IsNet then
6877 begin
6878 if Length(P) > 1 then
6879 begin
6880 chstr := '';
6881 for a := 1 to High(P) do begin
6882 if a > 1 then chstr := chstr + ' ';
6883 chstr := chstr + P[a];
6884 end;
6886 if Length(chstr) > 200 then SetLength(chstr, 200);
6888 if Length(chstr) < 1 then
6889 begin
6890 g_Console_Add('callvote <command>');
6891 Exit;
6892 end;
6894 if g_Game_IsClient then
6895 MC_SEND_Vote(True, chstr)
6896 else
6897 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6898 g_Console_Process('vote', True);
6899 end
6900 else
6901 g_Console_Add('callvote <command>');
6902 end
6903 else if (cmd = 'vote') and g_Game_IsNet then
6904 begin
6905 if g_Game_IsClient then
6906 MC_SEND_Vote(False)
6907 else if gVoteInProgress then
6908 begin
6909 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6910 a := Floor((NetClientCount+1)/2.0) + 1
6911 else
6912 a := Floor(NetClientCount/2.0) + 1;
6913 if gVoted then
6914 begin
6915 Dec(gVoteCount);
6916 gVoted := False;
6917 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6918 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6919 end
6920 else
6921 begin
6922 Inc(gVoteCount);
6923 gVoted := True;
6924 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6925 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6926 g_Game_CheckVote;
6927 end;
6928 end;
6929 end
6930 end;
6931 end;
6933 procedure g_TakeScreenShot();
6934 var
6935 a: Word;
6936 FileName: string;
6937 ssdir, t: string;
6938 st: TStream;
6939 ok: Boolean;
6940 begin
6941 if e_NoGraphics then Exit;
6942 ssdir := GameDir+'/screenshots';
6943 if not findFileCI(ssdir, true) then
6944 begin
6945 // try to create dir
6946 try
6947 CreateDir(ssdir);
6948 except
6949 end;
6950 if not findFileCI(ssdir, true) then exit; // alas
6951 end;
6952 try
6953 for a := 1 to High(Word) do
6954 begin
6955 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6956 t := FileName;
6957 if findFileCI(t, true) then continue;
6958 if not findFileCI(FileName) then
6959 begin
6960 ok := false;
6961 st := createDiskFile(FileName);
6962 try
6963 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6964 ok := true;
6965 finally
6966 st.Free();
6967 end;
6968 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6969 break;
6970 end;
6971 end;
6972 except
6973 end;
6974 end;
6976 procedure g_Game_InGameMenu(Show: Boolean);
6977 begin
6978 if (g_ActiveWindow = nil) and Show then
6979 begin
6980 if gGameSettings.GameType = GT_SINGLE then
6981 g_GUI_ShowWindow('GameSingleMenu')
6982 else
6983 begin
6984 if g_Game_IsClient then
6985 g_GUI_ShowWindow('GameClientMenu')
6986 else
6987 if g_Game_IsNet then
6988 g_GUI_ShowWindow('GameServerMenu')
6989 else
6990 g_GUI_ShowWindow('GameCustomMenu');
6991 end;
6992 g_Sound_PlayEx('MENU_OPEN');
6994 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6995 if (not g_Game_IsNet) then
6996 g_Game_Pause(True);
6997 end
6998 else
6999 if (g_ActiveWindow <> nil) and (not Show) then
7000 begin
7001 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7002 if (not g_Game_IsNet) then
7003 g_Game_Pause(False);
7004 end;
7005 end;
7007 procedure g_Game_Pause (Enable: Boolean);
7008 var
7009 oldPause: Boolean;
7010 begin
7011 if not gGameOn then exit;
7013 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
7015 oldPause := gPause;
7016 gPauseMain := Enable;
7018 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
7019 end;
7021 procedure g_Game_HolmesPause (Enable: Boolean);
7022 var
7023 oldPause: Boolean;
7024 begin
7025 if not gGameOn then exit;
7026 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
7028 oldPause := gPause;
7029 gPauseHolmes := Enable;
7031 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
7032 end;
7034 procedure g_Game_PauseAllSounds(Enable: Boolean);
7035 var
7036 i: Integer;
7037 begin
7038 // Òðèããåðû:
7039 if gTriggers <> nil then
7040 for i := 0 to High(gTriggers) do
7041 with gTriggers[i] do
7042 if (TriggerType = TRIGGER_SOUND) and
7043 (Sound <> nil) and
7044 Sound.IsPlaying() then
7045 begin
7046 Sound.Pause(Enable);
7047 end;
7049 // Çâóêè èãðîêîâ:
7050 if gPlayers <> nil then
7051 for i := 0 to High(gPlayers) do
7052 if gPlayers[i] <> nil then
7053 gPlayers[i].PauseSounds(Enable);
7055 // Ìóçûêà:
7056 if gMusic <> nil then
7057 gMusic.Pause(Enable);
7058 end;
7060 procedure g_Game_StopAllSounds(all: Boolean);
7061 var
7062 i: Integer;
7063 begin
7064 if gTriggers <> nil then
7065 for i := 0 to High(gTriggers) do
7066 with gTriggers[i] do
7067 if (TriggerType = TRIGGER_SOUND) and
7068 (Sound <> nil) then
7069 Sound.Stop();
7071 if gMusic <> nil then
7072 gMusic.Stop();
7074 if all then
7075 e_StopChannels();
7076 end;
7078 procedure g_Game_UpdateTriggerSounds();
7079 var
7080 i: Integer;
7081 begin
7082 if gTriggers <> nil then
7083 for i := 0 to High(gTriggers) do
7084 with gTriggers[i] do
7085 if (TriggerType = TRIGGER_SOUND) and
7086 (Sound <> nil) and
7087 (tgcLocal) and
7088 Sound.IsPlaying() then
7089 begin
7090 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
7091 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
7092 begin
7093 Sound.SetPan(0.5 - tgcPan/255.0);
7094 Sound.SetVolume(tgcVolume/255.0);
7095 end
7096 else
7097 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
7098 end;
7099 end;
7101 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
7102 begin
7103 Result := False;
7104 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
7105 begin
7106 Result := True;
7107 Exit;
7108 end;
7109 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
7110 begin
7111 Result := True;
7112 Exit;
7113 end;
7114 if gSpectMode <> SPECT_PLAYERS then
7115 Exit;
7116 if gSpectPID1 = UID then
7117 begin
7118 Result := True;
7119 Exit;
7120 end;
7121 if gSpectViewTwo and (gSpectPID2 = UID) then
7122 begin
7123 Result := True;
7124 Exit;
7125 end;
7126 end;
7128 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
7129 var
7130 Pl: TPlayer;
7131 begin
7132 Result := False;
7133 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
7134 begin
7135 Result := True;
7136 Exit;
7137 end;
7138 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
7139 begin
7140 Result := True;
7141 Exit;
7142 end;
7143 if gSpectMode <> SPECT_PLAYERS then
7144 Exit;
7145 Pl := g_Player_Get(gSpectPID1);
7146 if (Pl <> nil) and (Pl.Team = Team) then
7147 begin
7148 Result := True;
7149 Exit;
7150 end;
7151 if gSpectViewTwo then
7152 begin
7153 Pl := g_Player_Get(gSpectPID2);
7154 if (Pl <> nil) and (Pl.Team = Team) then
7155 begin
7156 Result := True;
7157 Exit;
7158 end;
7159 end;
7160 end;
7162 procedure g_Game_Message(Msg: string; Time: Word);
7163 begin
7164 MessageLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont);
7165 MessageText := b_Text_Wrap(b_Text_Format(Msg), MessageLineLength);
7166 MessageTime := Time;
7167 end;
7169 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
7170 const
7171 punct: Array[0..13] of String =
7172 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
7173 var
7174 i, j: Integer;
7175 ok: Boolean;
7176 fpText: String;
7178 function IsPunctuation(S: String): Boolean;
7179 var
7180 i: Integer;
7181 begin
7182 Result := False;
7183 if Length(S) <> 1 then
7184 Exit;
7185 for i := Low(punct) to High(punct) do
7186 if S = punct[i] then
7187 begin
7188 Result := True;
7189 break;
7190 end;
7191 end;
7192 function FilterPunctuation(S: String): String;
7193 var
7194 i: Integer;
7195 begin
7196 for i := Low(punct) to High(punct) do
7197 S := StringReplace(S, punct[i], ' ', [rfReplaceAll]);
7198 Result := S;
7199 end;
7200 begin
7201 ok := False;
7203 if gUseChatSounds and Taunt and (gChatSounds <> nil) and (Pos(': ', Text) > 0) then
7204 begin
7205 // remove player name
7206 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
7207 // for FullWord check
7208 Text := toLowerCase1251(' ' + Text + ' ');
7209 fpText := FilterPunctuation(Text);
7211 for i := 0 to Length(gChatSounds) - 1 do
7212 begin
7213 ok := True;
7214 for j := 0 to Length(gChatSounds[i].Tags) - 1 do
7215 begin
7216 if gChatSounds[i].FullWord and (not IsPunctuation(gChatSounds[i].Tags[j])) then
7217 ok := Pos(' ' + gChatSounds[i].Tags[j] + ' ', fpText) > 0
7218 else
7219 ok := Pos(gChatSounds[i].Tags[j], Text) > 0;
7220 if not ok then
7221 break;
7222 end;
7223 if ok then
7224 begin
7225 gChatSounds[i].Sound.Play();
7226 break;
7227 end;
7228 end;
7229 end;
7230 if not ok then
7231 g_Sound_PlayEx('SOUND_GAME_RADIO');
7232 end;
7234 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
7235 var
7236 a: Integer;
7237 begin
7238 case gAnnouncer of
7239 ANNOUNCE_NONE:
7240 Exit;
7241 ANNOUNCE_ME,
7242 ANNOUNCE_MEPLUS:
7243 if not g_Game_IsWatchedPlayer(SpawnerUID) then
7244 Exit;
7245 end;
7246 for a := 0 to 3 do
7247 if goodsnd[a].IsPlaying() then
7248 Exit;
7250 goodsnd[Random(4)].Play();
7251 end;
7253 procedure g_Game_Announce_KillCombo(Param: Integer);
7254 var
7255 UID: Word;
7256 c, n: Byte;
7257 Pl: TPlayer;
7258 Name: String;
7259 begin
7260 UID := Param and $FFFF;
7261 c := Param shr 16;
7262 if c < 2 then
7263 Exit;
7265 Pl := g_Player_Get(UID);
7266 if Pl = nil then
7267 Name := '?'
7268 else
7269 Name := Pl.Name;
7271 case c of
7272 2: begin
7273 n := 0;
7274 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
7275 end;
7276 3: begin
7277 n := 1;
7278 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
7279 end;
7280 4: begin
7281 n := 2;
7282 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
7283 end;
7284 else begin
7285 n := 3;
7286 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
7287 end;
7288 end;
7290 case gAnnouncer of
7291 ANNOUNCE_NONE:
7292 Exit;
7293 ANNOUNCE_ME:
7294 if not g_Game_IsWatchedPlayer(UID) then
7295 Exit;
7296 ANNOUNCE_MEPLUS:
7297 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
7298 Exit;
7299 end;
7301 if killsnd[n].IsPlaying() then
7302 killsnd[n].Stop();
7303 killsnd[n].Play();
7304 end;
7306 procedure g_Game_StartVote(Command, Initiator: string);
7307 var
7308 Need: Integer;
7309 begin
7310 if not gVotesEnabled then Exit;
7311 if gGameSettings.GameType <> GT_SERVER then Exit;
7312 if gVoteInProgress or gVotePassed then
7313 begin
7314 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
7315 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
7316 Exit;
7317 end;
7318 gVoteInProgress := True;
7319 gVotePassed := False;
7320 gVoteTimer := gTime + gVoteTimeout * 1000;
7321 gVoteCount := 0;
7322 gVoted := False;
7323 gVoteCommand := Command;
7325 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7326 Need := Floor((NetClientCount+1)/2.0)+1
7327 else
7328 Need := Floor(NetClientCount/2.0)+1;
7329 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
7330 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
7331 end;
7333 procedure g_Game_CheckVote;
7334 var
7335 I, Need: Integer;
7336 begin
7337 if gGameSettings.GameType <> GT_SERVER then Exit;
7338 if not gVoteInProgress then Exit;
7340 if (gTime >= gVoteTimer) then
7341 begin
7342 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7343 Need := Floor((NetClientCount+1)/2.0) + 1
7344 else
7345 Need := Floor(NetClientCount/2.0) + 1;
7346 if gVoteCount >= Need then
7347 begin
7348 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7349 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7350 gVotePassed := True;
7351 gVoteCmdTimer := gTime + 5000;
7352 end
7353 else
7354 begin
7355 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
7356 MH_SEND_VoteEvent(NET_VE_FAILED);
7357 end;
7358 if NetClients <> nil then
7359 for I := Low(NetClients) to High(NetClients) do
7360 if NetClients[i].Used then
7361 NetClients[i].Voted := False;
7362 gVoteInProgress := False;
7363 gVoted := False;
7364 gVoteCount := 0;
7365 end
7366 else
7367 begin
7368 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7369 Need := Floor((NetClientCount+1)/2.0) + 1
7370 else
7371 Need := Floor(NetClientCount/2.0) + 1;
7372 if gVoteCount >= Need then
7373 begin
7374 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7375 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7376 gVoteInProgress := False;
7377 gVotePassed := True;
7378 gVoteCmdTimer := gTime + 5000;
7379 gVoted := False;
7380 gVoteCount := 0;
7381 if NetClients <> nil then
7382 for I := Low(NetClients) to High(NetClients) do
7383 if NetClients[i].Used then
7384 NetClients[i].Voted := False;
7385 end;
7386 end;
7387 end;
7389 procedure g_Game_LoadMapList(FileName: string);
7390 var
7391 ListFile: TextFile;
7392 s: string;
7393 begin
7394 MapList := nil;
7395 MapIndex := -1;
7397 if not FileExists(FileName) then Exit;
7399 AssignFile(ListFile, FileName);
7400 Reset(ListFile);
7401 while not EOF(ListFile) do
7402 begin
7403 ReadLn(ListFile, s);
7405 s := Trim(s);
7406 if s = '' then Continue;
7408 SetLength(MapList, Length(MapList)+1);
7409 MapList[High(MapList)] := s;
7410 end;
7411 CloseFile(ListFile);
7412 end;
7414 procedure g_Game_SetDebugMode();
7415 begin
7416 gDebugMode := True;
7417 // ×èòû (äàæå â ñâîåé èãðå):
7418 gCheats := True;
7419 end;
7421 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
7422 var
7423 i: Word;
7424 begin
7425 if Length(LoadingStat.Msgs) = 0 then
7426 Exit;
7428 with LoadingStat do
7429 begin
7430 if not reWrite then
7431 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
7432 if NextMsg = Length(Msgs) then
7433 begin // scroll
7434 for i := 0 to High(Msgs)-1 do
7435 Msgs[i] := Msgs[i+1];
7436 end
7437 else
7438 Inc(NextMsg);
7439 end else
7440 if NextMsg = 0 then
7441 Inc(NextMsg);
7443 Msgs[NextMsg-1] := Text;
7444 CurValue := 0;
7445 MaxValue := Max;
7446 ShowCount := 0;
7447 PBarWasHere := false;
7448 end;
7450 g_ActiveWindow := nil;
7452 ProcessLoading(true);
7453 end;
7455 procedure g_Game_StepLoading(Value: Integer = -1);
7456 begin
7457 with LoadingStat do
7458 begin
7459 if Value = -1 then
7460 begin
7461 Inc(CurValue);
7462 Inc(ShowCount);
7463 end
7464 else
7465 CurValue := Value;
7466 if (ShowCount > LOADING_SHOW_STEP) or (Value > -1) then
7467 begin
7468 ShowCount := 0;
7469 ProcessLoading();
7470 end;
7471 end;
7472 end;
7474 procedure g_Game_ClearLoading();
7475 var
7476 len: Word;
7477 begin
7478 with LoadingStat do
7479 begin
7480 CurValue := 0;
7481 MaxValue := 0;
7482 ShowCount := 0;
7483 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
7484 if len < 1 then len := 1;
7485 SetLength(Msgs, len);
7486 for len := Low(Msgs) to High(Msgs) do
7487 Msgs[len] := '';
7488 NextMsg := 0;
7489 PBarWasHere := false;
7490 end;
7491 end;
7493 procedure Parse_Params(var pars: TParamStrValues);
7494 var
7495 i: Integer;
7496 s: String;
7497 begin
7498 SetLength(pars, 0);
7499 i := 1;
7500 while i <= ParamCount do
7501 begin
7502 s := ParamStr(i);
7503 if (s[1] = '-') and (Length(s) > 1) then
7504 begin
7505 if (s[2] = '-') and (Length(s) > 2) then
7506 begin // Îäèíî÷íûé ïàðàìåòð
7507 SetLength(pars, Length(pars) + 1);
7508 with pars[High(pars)] do
7509 begin
7510 Name := LowerCase(s);
7511 Value := '+';
7512 end;
7513 end
7514 else
7515 if (i < ParamCount) then
7516 begin // Ïàðàìåòð ñî çíà÷åíèåì
7517 Inc(i);
7518 SetLength(pars, Length(pars) + 1);
7519 with pars[High(pars)] do
7520 begin
7521 Name := LowerCase(s);
7522 Value := LowerCase(ParamStr(i));
7523 end;
7524 end;
7525 end;
7527 Inc(i);
7528 end;
7529 end;
7531 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
7532 var
7533 i: Integer;
7534 begin
7535 Result := '';
7536 for i := 0 to High(pars) do
7537 if pars[i].Name = aName then
7538 begin
7539 Result := pars[i].Value;
7540 Break;
7541 end;
7542 end;
7544 procedure g_Game_Process_Params();
7545 var
7546 pars: TParamStrValues;
7547 map: String;
7548 GMode, n: Byte;
7549 LimT, LimS: Integer;
7550 Opt: LongWord;
7551 Lives: Integer;
7552 s: String;
7553 Port: Integer;
7554 ip: String;
7555 F: TextFile;
7556 begin
7557 Parse_Params(pars);
7559 // Debug mode:
7560 s := Find_Param_Value(pars, '--debug');
7561 if (s <> '') then
7562 begin
7563 g_Game_SetDebugMode();
7564 s := Find_Param_Value(pars, '--netdump');
7565 if (s <> '') then
7566 NetDump := True;
7567 end;
7569 // Connect when game loads
7570 ip := Find_Param_Value(pars, '-connect');
7572 if ip <> '' then
7573 begin
7574 s := Find_Param_Value(pars, '-port');
7575 if (s = '') or not TryStrToInt(s, Port) then
7576 Port := 25666;
7578 s := Find_Param_Value(pars, '-pw');
7580 g_Game_StartClient(ip, Port, s);
7581 Exit;
7582 end;
7584 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7585 if (s <> '') then
7586 begin
7587 gDefaultMegawadStart := s;
7588 end;
7590 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7591 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7592 begin
7593 gDefaultMegawadStart := DF_Default_Megawad_Start;
7594 end;
7596 // Start map when game loads:
7597 map := LowerCase(Find_Param_Value(pars, '-map'));
7598 if isWadPath(map) then
7599 begin
7600 // Game mode:
7601 s := Find_Param_Value(pars, '-gm');
7602 GMode := g_Game_TextToMode(s);
7603 if GMode = GM_NONE then GMode := GM_DM;
7604 if GMode = GM_SINGLE then GMode := GM_COOP;
7606 // Time limit:
7607 s := Find_Param_Value(pars, '-limt');
7608 if (s = '') or (not TryStrToInt(s, LimT)) then
7609 LimT := 0;
7610 if LimT < 0 then
7611 LimT := 0;
7613 // Goal limit:
7614 s := Find_Param_Value(pars, '-lims');
7615 if (s = '') or (not TryStrToInt(s, LimS)) then
7616 LimS := 0;
7617 if LimS < 0 then
7618 LimS := 0;
7620 // Lives limit:
7621 s := Find_Param_Value(pars, '-lives');
7622 if (s = '') or (not TryStrToInt(s, Lives)) then
7623 Lives := 0;
7624 if Lives < 0 then
7625 Lives := 0;
7627 // Options:
7628 s := Find_Param_Value(pars, '-opt');
7629 if (s = '') then
7630 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7631 else
7632 Opt := StrToIntDef(s, 0);
7633 if Opt = 0 then
7634 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7636 // Close after map:
7637 s := Find_Param_Value(pars, '--close');
7638 if (s <> '') then
7639 gMapOnce := True;
7641 // Override map to test:
7642 s := LowerCase(Find_Param_Value(pars, '-testmap'));
7643 if s <> '' then
7644 gTestMap := MapsDir + s;
7646 // Delete test map after play:
7647 s := Find_Param_Value(pars, '--testdelete');
7648 if (s <> '') then
7649 begin
7650 gMapToDelete := MapsDir + map;
7651 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
7652 Halt(1);
7653 end;
7655 // Delete temporary WAD after play:
7656 s := Find_Param_Value(pars, '--tempdelete');
7657 if (s <> '') and (gTestMap <> '') then
7658 begin
7659 gMapToDelete := gTestMap;
7660 gTempDelete := True;
7661 end;
7663 // Number of players:
7664 s := Find_Param_Value(pars, '-pl');
7665 if (s = '') then
7666 n := 1
7667 else
7668 n := StrToIntDef(s, 1);
7670 // Start:
7671 s := Find_Param_Value(pars, '-port');
7672 if (s = '') or not TryStrToInt(s, Port) then
7673 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7674 else
7675 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7676 end;
7678 // Execute script when game loads:
7679 s := Find_Param_Value(pars, '-exec');
7680 if s <> '' then
7681 begin
7682 if not isWadPath(s) then
7683 s := GameDir + '/' + s;
7685 {$I-}
7686 AssignFile(F, s);
7687 Reset(F);
7688 if IOResult <> 0 then
7689 begin
7690 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7691 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7692 CloseFile(F);
7693 Exit;
7694 end;
7695 e_WriteLog('Executing script: ' + s, TMsgType.Notify);
7696 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7698 while not EOF(F) do
7699 begin
7700 ReadLn(F, s);
7701 if IOResult <> 0 then
7702 begin
7703 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7704 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7705 CloseFile(F);
7706 Exit;
7707 end;
7708 if Pos('#', s) <> 1 then // script comment
7709 g_Console_Process(s, True);
7710 end;
7712 CloseFile(F);
7713 {$I+}
7714 end;
7716 SetLength(pars, 0);
7717 end;
7719 begin
7720 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7721 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7722 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7723 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7725 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7726 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7727 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7728 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7730 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7731 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7733 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7734 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7736 {$IFDEF ENABLE_HOLMES}
7737 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7738 {$ENDIF}
7740 conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds, 'ignore level bounds', '', false);
7742 conRegVar('r_scale', @g_dbg_scale, 0.01, 100.0, 'render scale', '', false);
7744 conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
7745 conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
7747 conRegVar('r_smallmap_align_h', @r_smallmap_h, 'halign: 0: left; 1: center; 2: right', 'horizontal aligning of small maps');
7748 conRegVar('r_smallmap_align_v', @r_smallmap_v, 'valign: 0: top; 1: center; 2: bottom', 'vertial aligning of small maps');
7749 end.