DEADSOFTWARE

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