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');
2333 hasPBarGfx := true;
2334 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false;
2335 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false;
2336 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false;
2337 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false;
2339 if hasPBarGfx then
2340 begin
2341 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2342 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2343 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2344 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2345 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
2346 begin
2347 // yay!
2348 end
2349 else
2350 begin
2351 hasPBarGfx := false;
2352 end;
2353 end;
2355 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2356 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD+':WEAPONS\PUNCH', 64, 64, 4, False);
2357 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD+':WEAPONS\PUNCH_UP', 64, 64, 4, False);
2358 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD+':WEAPONS\PUNCH_DN', 64, 64, 4, False);
2359 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD+':WEAPONS\PUNCHB', 64, 64, 4, False);
2360 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD+':WEAPONS\PUNCHB_UP', 64, 64, 4, False);
2361 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD+':WEAPONS\PUNCHB_DN', 64, 64, 4, False);
2362 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2363 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2364 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2365 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2366 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2367 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2368 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2369 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2370 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2371 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2372 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2373 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2374 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2375 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2376 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2377 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2378 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2379 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2380 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2381 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD+':SOUNDS\MUHAHA1');
2382 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD+':SOUNDS\MUHAHA2');
2383 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD+':SOUNDS\MUHAHA3');
2385 goodsnd[0] := TPlayableSound.Create();
2386 goodsnd[1] := TPlayableSound.Create();
2387 goodsnd[2] := TPlayableSound.Create();
2388 goodsnd[3] := TPlayableSound.Create();
2390 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2391 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2392 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2393 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2395 killsnd[0] := TPlayableSound.Create();
2396 killsnd[1] := TPlayableSound.Create();
2397 killsnd[2] := TPlayableSound.Create();
2398 killsnd[3] := TPlayableSound.Create();
2400 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2401 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2402 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2403 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2405 hahasnd[0] := TPlayableSound.Create();
2406 hahasnd[1] := TPlayableSound.Create();
2407 hahasnd[2] := TPlayableSound.Create();
2409 hahasnd[0].SetByName('SOUND_ANNOUNCER_MUHAHA1');
2410 hahasnd[1].SetByName('SOUND_ANNOUNCER_MUHAHA2');
2411 hahasnd[2].SetByName('SOUND_ANNOUNCER_MUHAHA3');
2413 g_Game_LoadChatSounds(GameWAD+':CHATSND\SNDCFG');
2415 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2416 g_Items_LoadData();
2418 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2419 g_Weapon_LoadData();
2421 g_Monsters_LoadData();
2423 DataLoaded := True;
2424 end;
2426 procedure g_Game_FreeData();
2427 begin
2428 if not DataLoaded then Exit;
2430 g_Items_FreeData();
2431 g_Weapon_FreeData();
2432 g_Monsters_FreeData();
2434 e_WriteLog('Releasing game data...', TMsgType.Notify);
2436 g_Texture_Delete('NOTEXTURE');
2437 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2438 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2439 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2440 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2441 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2442 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2443 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2444 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2445 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2446 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2447 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2448 g_Frames_DeleteByName('FRAMES_TELEPORT');
2449 g_Frames_DeleteByName('FRAMES_PUNCH');
2450 g_Frames_DeleteByName('FRAMES_PUNCH_UP');
2451 g_Frames_DeleteByName('FRAMES_PUNCH_DN');
2452 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK');
2453 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP');
2454 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
2455 g_Sound_Delete('SOUND_GAME_TELEPORT');
2456 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2457 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2458 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2459 g_Sound_Delete('SOUND_GAME_BULK1');
2460 g_Sound_Delete('SOUND_GAME_BULK2');
2461 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2462 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2463 g_Sound_Delete('SOUND_GAME_SWITCH1');
2464 g_Sound_Delete('SOUND_GAME_SWITCH0');
2466 goodsnd[0].Free();
2467 goodsnd[1].Free();
2468 goodsnd[2].Free();
2469 goodsnd[3].Free();
2471 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2472 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2473 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2474 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2476 killsnd[0].Free();
2477 killsnd[1].Free();
2478 killsnd[2].Free();
2479 killsnd[3].Free();
2481 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2482 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2483 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2484 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2486 hahasnd[0].Free();
2487 hahasnd[1].Free();
2488 hahasnd[2].Free();
2490 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1');
2491 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2');
2492 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3');
2494 g_Game_FreeChatSounds();
2496 DataLoaded := False;
2497 end;
2499 procedure DrawCustomStat();
2500 var
2501 pc, x, y, w, _y,
2502 w1, w2, w3,
2503 t, p, m: Integer;
2504 ww1, hh1: Word;
2505 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2506 s1, s2, topstr: String;
2507 begin
2508 e_TextureFontGetSize(gStdFont, ww2, hh2);
2510 g_ProcessMessages();
2512 if e_KeyPressed(IK_TAB) or e_KeyPressed(VK_STATUS) then
2513 begin
2514 if not gStatsPressed then
2515 begin
2516 gStatsOff := not gStatsOff;
2517 gStatsPressed := True;
2518 end;
2519 end
2520 else
2521 gStatsPressed := False;
2523 if gStatsOff then
2524 begin
2525 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2526 w := (Length(s1) * ww2) div 2;
2527 x := gScreenWidth div 2 - w;
2528 y := 8;
2529 e_TextureFontPrint(x, y, s1, gStdFont);
2530 Exit;
2531 end;
2533 if (gGameSettings.GameMode = GM_COOP) then
2534 begin
2535 if gMissionFailed then
2536 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2537 else
2538 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2539 end
2540 else
2541 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2543 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2544 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2546 if g_Game_IsNet then
2547 begin
2548 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2549 if not gChatShow then
2550 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2551 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2552 end;
2554 if g_Game_IsClient then
2555 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2556 else
2557 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2558 if not gChatShow then
2559 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2560 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2562 x := 32;
2563 y := 16+hh1+16;
2565 w := gScreenWidth-x*2;
2567 w2 := (w-16) div 6;
2568 w3 := w2;
2569 w1 := w-16-w2-w3;
2571 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2572 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2574 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2576 case CustomStat.GameMode of
2577 GM_DM:
2578 begin
2579 if gGameSettings.MaxLives = 0 then
2580 s1 := _lc[I_GAME_DM]
2581 else
2582 s1 := _lc[I_GAME_LMS];
2583 end;
2584 GM_TDM:
2585 begin
2586 if gGameSettings.MaxLives = 0 then
2587 s1 := _lc[I_GAME_TDM]
2588 else
2589 s1 := _lc[I_GAME_TLMS];
2590 end;
2591 GM_CTF: s1 := _lc[I_GAME_CTF];
2592 GM_COOP:
2593 begin
2594 if gGameSettings.MaxLives = 0 then
2595 s1 := _lc[I_GAME_COOP]
2596 else
2597 s1 := _lc[I_GAME_SURV];
2598 end;
2599 else s1 := '';
2600 end;
2602 _y := y+16;
2603 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2604 _y := _y+8;
2606 _y := _y+16;
2607 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2608 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2610 _y := _y+16;
2611 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2612 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2613 (CustomStat.GameTime div 1000 div 60) mod 60,
2614 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2616 pc := Length(CustomStat.PlayerStat);
2617 if pc = 0 then Exit;
2619 if CustomStat.GameMode = GM_COOP then
2620 begin
2621 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2622 _y := _y+32;
2623 s2 := _lc[I_GAME_MONSTERS];
2624 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2625 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2626 _y := _y+16;
2627 s2 := _lc[I_GAME_SECRETS];
2628 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2629 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2630 if gLastMap then
2631 begin
2632 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2633 _y := _y-16;
2634 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2635 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2636 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2637 _y := _y+16;
2638 s2 := _lc[I_GAME_SECRETS_TOTAL];
2639 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2640 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2641 end;
2642 end;
2644 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2645 begin
2646 _y := _y+16+16;
2648 with CustomStat do
2649 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2650 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2651 else s1 := _lc[I_GAME_WIN_DRAW];
2653 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2654 _y := _y+40;
2656 for t := TEAM_RED to TEAM_BLUE do
2657 begin
2658 if t = TEAM_RED then
2659 begin
2660 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2661 gStdFont, 255, 0, 0, 1);
2662 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2663 gStdFont, 255, 0, 0, 1);
2664 r := 255;
2665 g := 0;
2666 b := 0;
2667 end
2668 else
2669 begin
2670 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2671 gStdFont, 0, 0, 255, 1);
2672 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2673 gStdFont, 0, 0, 255, 1);
2674 r := 0;
2675 g := 0;
2676 b := 255;
2677 end;
2679 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2680 _y := _y+24;
2682 for p := 0 to High(CustomStat.PlayerStat) do
2683 if CustomStat.PlayerStat[p].Team = t then
2684 with CustomStat.PlayerStat[p] do
2685 begin
2686 if Spectator then
2687 begin
2688 rr := r div 2;
2689 gg := g div 2;
2690 bb := b div 2;
2691 end
2692 else
2693 begin
2694 rr := r;
2695 gg := g;
2696 bb := b;
2697 end;
2698 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2699 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2700 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2701 _y := _y+24;
2702 end;
2704 _y := _y+16+16;
2705 end;
2706 end
2707 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2708 begin
2709 _y := _y+40;
2710 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2711 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2712 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2714 _y := _y+24;
2715 for p := 0 to High(CustomStat.PlayerStat) do
2716 with CustomStat.PlayerStat[p] do
2717 begin
2718 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2720 if Spectator then
2721 r := 127
2722 else
2723 r := 255;
2725 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2726 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2727 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2728 _y := _y+24;
2729 end;
2730 end;
2731 end;
2733 procedure DrawSingleStat();
2734 var
2735 tm, key_x, val_x, y: Integer;
2736 w1, w2, h: Word;
2737 s1, s2: String;
2739 procedure player_stat(n: Integer);
2740 var
2741 kpm: Real;
2743 begin
2744 // "Kills: # / #":
2745 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2746 s2 := Format(' %d', [gTotalMonsters]);
2748 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2749 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2750 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2751 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2752 s1 := s1 + '/';
2753 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2754 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2756 // "Kills-per-minute: ##.#":
2757 s1 := _lc[I_MENU_INTER_KPM];
2758 if tm > 0 then
2759 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2760 else
2761 kpm := SingleStat.PlayerStat[n].Kills;
2762 s2 := Format(' %.1f', [kpm]);
2764 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2765 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2767 // "Secrets found: # / #":
2768 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2769 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2771 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2772 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2773 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2774 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2775 s1 := s1 + '/';
2776 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2777 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2778 end;
2780 begin
2781 // "Level Complete":
2782 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2783 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2785 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2786 s1 := _lc[I_MENU_INTER_KPM];
2787 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2788 Inc(w1, 16);
2789 s1 := ' 9999.9';
2790 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2792 key_x := (gScreenWidth-w1-w2) div 2;
2793 val_x := key_x + w1;
2795 // "Time: #:##:##":
2796 tm := SingleStat.GameTime div 1000;
2797 s1 := _lc[I_MENU_INTER_TIME];
2798 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2800 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2801 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2803 if SingleStat.TwoPlayers then
2804 begin
2805 // "Player 1":
2806 s1 := _lc[I_MENU_PLAYER_1];
2807 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2808 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2810 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2811 y := 176;
2812 player_stat(0);
2814 // "Player 2":
2815 s1 := _lc[I_MENU_PLAYER_2];
2816 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2817 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2819 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2820 y := 336;
2821 player_stat(1);
2822 end
2823 else
2824 begin
2825 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2826 y := 128;
2827 player_stat(0);
2828 end;
2829 end;
2831 procedure DrawLoadingStat();
2832 procedure drawRect (x, y, w, h: Integer);
2833 begin
2834 if (w < 1) or (h < 1) then exit;
2835 glBegin(GL_QUADS);
2836 glVertex2f(x+0.375, y+0.375);
2837 glVertex2f(x+w+0.375, y+0.375);
2838 glVertex2f(x+w+0.375, y+h+0.375);
2839 glVertex2f(x+0.375, y+h+0.375);
2840 glEnd();
2841 end;
2843 function drawPBar (cur, total: Integer; washere: Boolean): Boolean;
2844 var
2845 rectW, rectH: Integer;
2846 x0, y0: Integer;
2847 wdt: Integer;
2848 wl, hl: Integer;
2849 wr, hr: Integer;
2850 wb, hb: Integer;
2851 wm, hm: Integer;
2852 idl, idr, idb, idm: LongWord;
2853 f, my: Integer;
2854 begin
2855 result := false;
2856 if (total < 1) then exit;
2857 if (cur < 1) then exit; // don't blink
2858 if (not washere) and (cur >= total) then exit; // don't blink
2859 //if (cur < 0) then cur := 0;
2860 //if (cur > total) then cur := total;
2861 result := true;
2863 if (hasPBarGfx) then
2864 begin
2865 g_Texture_Get('UI_GFX_PBAR_LEFT', idl);
2866 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2867 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr);
2868 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2869 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb);
2870 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2871 g_Texture_Get('UI_GFX_PBAR_MARKER', idm);
2872 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2874 //rectW := gScreenWidth-360;
2875 rectW := trunc(624.0*gScreenWidth/1024.0);
2876 rectH := hl;
2878 x0 := (gScreenWidth-rectW) div 2;
2879 y0 := gScreenHeight-rectH-64;
2880 if (y0 < 2) then y0 := 2;
2882 glEnable(GL_SCISSOR_TEST);
2884 // left and right
2885 glScissor(x0, gScreenHeight-y0-rectH, rectW, rectH);
2886 e_DrawSize(idl, x0, y0, 0, true, false, wl, hl);
2887 e_DrawSize(idr, x0+rectW-wr, y0, 0, true, false, wr, hr);
2889 // body
2890 glScissor(x0+wl, gScreenHeight-y0-rectH, rectW-wl-wr, rectH);
2891 f := x0+wl;
2892 while (f < x0+rectW) do
2893 begin
2894 e_DrawSize(idb, f, y0, 0, true, false, wb, hb);
2895 f += wb;
2896 end;
2898 // filled part
2899 wdt := (rectW-wl-wr)*cur div total;
2900 if (wdt > rectW-wl-wr) then wdt := rectW-wr-wr;
2901 if (wdt > 0) then
2902 begin
2903 my := y0; // don't be so smart, ketmar: +(rectH-wm) div 2;
2904 glScissor(x0+wl, gScreenHeight-my-rectH, wdt, hm);
2905 f := x0+wl;
2906 while (wdt > 0) do
2907 begin
2908 e_DrawSize(idm, f, y0, 0, true, false, wm, hm);
2909 f += wm;
2910 wdt -= wm;
2911 end;
2912 end;
2914 glScissor(0, 0, gScreenWidth, gScreenHeight);
2915 end
2916 else
2917 begin
2918 rectW := gScreenWidth-64;
2919 rectH := 16;
2921 x0 := (gScreenWidth-rectW) div 2;
2922 y0 := gScreenHeight-rectH-64;
2923 if (y0 < 2) then y0 := 2;
2925 glDisable(GL_BLEND);
2926 glDisable(GL_TEXTURE_2D);
2928 //glClearColor(0, 0, 0, 0);
2929 //glClear(GL_COLOR_BUFFER_BIT);
2931 glColor4ub(127, 127, 127, 255);
2932 drawRect(x0-2, y0-2, rectW+4, rectH+4);
2934 glColor4ub(0, 0, 0, 255);
2935 drawRect(x0-1, y0-1, rectW+2, rectH+2);
2937 glColor4ub(127, 127, 127, 255);
2938 wdt := rectW*cur div total;
2939 if (wdt > rectW) then wdt := rectW;
2940 drawRect(x0, y0, wdt, rectH);
2941 end;
2942 end;
2944 var
2945 ww, hh: Word;
2946 xx, yy, i: Integer;
2947 s: String;
2948 begin
2949 if (Length(LoadingStat.Msgs) = 0) then exit;
2951 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2952 yy := (gScreenHeight div 3);
2953 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2954 xx := (gScreenWidth div 3);
2956 with LoadingStat do
2957 begin
2958 for i := 0 to NextMsg-1 do
2959 begin
2960 if (i = (NextMsg-1)) and (MaxValue > 0) then
2961 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2962 else
2963 s := Msgs[i];
2965 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2966 yy := yy + LOADING_INTERLINE;
2967 PBarWasHere := drawPBar(CurValue, MaxValue, PBarWasHere);
2968 end;
2969 end;
2970 end;
2972 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2973 var
2974 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2976 function monDraw (mon: TMonster): Boolean;
2977 begin
2978 result := false; // don't stop
2979 with mon do
2980 begin
2981 if alive then
2982 begin
2983 // Ëåâûé âåðõíèé óãîë
2984 aX := Obj.X div ScaleSz + 1;
2985 aY := Obj.Y div ScaleSz + 1;
2986 // Ðàçìåðû
2987 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2988 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2989 // Ïðàâûé íèæíèé óãîë
2990 aX2 := aX + aX2 - 1;
2991 aY2 := aY + aY2 - 1;
2992 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2993 end;
2994 end;
2995 end;
2997 begin
2998 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2999 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
3000 begin
3001 Scale := 1;
3002 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
3003 ScaleSz := 16 div Scale;
3004 // Ðàçìåðû ìèíè-êàðòû:
3005 aX := max(gMapInfo.Width div ScaleSz, 1);
3006 aY := max(gMapInfo.Height div ScaleSz, 1);
3007 // Ðàìêà êàðòû:
3008 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
3010 if gWalls <> nil then
3011 begin
3012 // Ðèñóåì ñòåíû:
3013 for a := 0 to High(gWalls) do
3014 with gWalls[a] do
3015 if PanelType <> 0 then
3016 begin
3017 // Ëåâûé âåðõíèé óãîë:
3018 aX := X div ScaleSz;
3019 aY := Y div ScaleSz;
3020 // Ðàçìåðû:
3021 aX2 := max(Width div ScaleSz, 1);
3022 aY2 := max(Height div ScaleSz, 1);
3023 // Ïðàâûé íèæíèé óãîë:
3024 aX2 := aX + aX2 - 1;
3025 aY2 := aY + aY2 - 1;
3027 case PanelType of
3028 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
3029 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
3030 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
3031 end;
3032 end;
3033 end;
3034 if gSteps <> nil then
3035 begin
3036 // Ðèñóåì ñòóïåíè:
3037 for a := 0 to High(gSteps) do
3038 with gSteps[a] do
3039 if PanelType <> 0 then
3040 begin
3041 // Ëåâûé âåðõíèé óãîë:
3042 aX := X div ScaleSz;
3043 aY := Y div ScaleSz;
3044 // Ðàçìåðû:
3045 aX2 := max(Width div ScaleSz, 1);
3046 aY2 := max(Height div ScaleSz, 1);
3047 // Ïðàâûé íèæíèé óãîë:
3048 aX2 := aX + aX2 - 1;
3049 aY2 := aY + aY2 - 1;
3051 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
3052 end;
3053 end;
3054 if gLifts <> nil then
3055 begin
3056 // Ðèñóåì ëèôòû:
3057 for a := 0 to High(gLifts) do
3058 with gLifts[a] do
3059 if PanelType <> 0 then
3060 begin
3061 // Ëåâûé âåðõíèé óãîë:
3062 aX := X div ScaleSz;
3063 aY := Y div ScaleSz;
3064 // Ðàçìåðû:
3065 aX2 := max(Width div ScaleSz, 1);
3066 aY2 := max(Height div ScaleSz, 1);
3067 // Ïðàâûé íèæíèé óãîë:
3068 aX2 := aX + aX2 - 1;
3069 aY2 := aY + aY2 - 1;
3071 case LiftType of
3072 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
3073 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
3074 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
3075 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
3076 end;
3077 end;
3078 end;
3079 if gWater <> nil then
3080 begin
3081 // Ðèñóåì âîäó:
3082 for a := 0 to High(gWater) do
3083 with gWater[a] do
3084 if PanelType <> 0 then
3085 begin
3086 // Ëåâûé âåðõíèé óãîë:
3087 aX := X div ScaleSz;
3088 aY := Y div ScaleSz;
3089 // Ðàçìåðû:
3090 aX2 := max(Width div ScaleSz, 1);
3091 aY2 := max(Height div ScaleSz, 1);
3092 // Ïðàâûé íèæíèé óãîë:
3093 aX2 := aX + aX2 - 1;
3094 aY2 := aY + aY2 - 1;
3096 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
3097 end;
3098 end;
3099 if gAcid1 <> nil then
3100 begin
3101 // Ðèñóåì êèñëîòó 1:
3102 for a := 0 to High(gAcid1) do
3103 with gAcid1[a] do
3104 if PanelType <> 0 then
3105 begin
3106 // Ëåâûé âåðõíèé óãîë:
3107 aX := X div ScaleSz;
3108 aY := Y div ScaleSz;
3109 // Ðàçìåðû:
3110 aX2 := max(Width div ScaleSz, 1);
3111 aY2 := max(Height div ScaleSz, 1);
3112 // Ïðàâûé íèæíèé óãîë:
3113 aX2 := aX + aX2 - 1;
3114 aY2 := aY + aY2 - 1;
3116 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
3117 end;
3118 end;
3119 if gAcid2 <> nil then
3120 begin
3121 // Ðèñóåì êèñëîòó 2:
3122 for a := 0 to High(gAcid2) do
3123 with gAcid2[a] do
3124 if PanelType <> 0 then
3125 begin
3126 // Ëåâûé âåðõíèé óãîë:
3127 aX := X div ScaleSz;
3128 aY := Y div ScaleSz;
3129 // Ðàçìåðû:
3130 aX2 := max(Width div ScaleSz, 1);
3131 aY2 := max(Height div ScaleSz, 1);
3132 // Ïðàâûé íèæíèé óãîë:
3133 aX2 := aX + aX2 - 1;
3134 aY2 := aY + aY2 - 1;
3136 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
3137 end;
3138 end;
3139 if gPlayers <> nil then
3140 begin
3141 // Ðèñóåì èãðîêîâ:
3142 for a := 0 to High(gPlayers) do
3143 if gPlayers[a] <> nil then with gPlayers[a] do
3144 if alive then begin
3145 // Ëåâûé âåðõíèé óãîë:
3146 aX := Obj.X div ScaleSz + 1;
3147 aY := Obj.Y div ScaleSz + 1;
3148 // Ðàçìåðû:
3149 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
3150 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
3151 // Ïðàâûé íèæíèé óãîë:
3152 aX2 := aX + aX2 - 1;
3153 aY2 := aY + aY2 - 1;
3155 if gPlayers[a] = p then
3156 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
3157 else
3158 case Team of
3159 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
3160 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
3161 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
3162 end;
3163 end;
3164 end;
3165 // Ðèñóåì ìîíñòðîâ
3166 g_Mons_ForEach(monDraw);
3167 end;
3168 end;
3171 procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor);
3172 begin
3173 if not hasAmbient then exit;
3174 e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3175 end;
3178 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3179 //FIXME: broken for splitscreen mode
3180 procedure renderDynLightsInternal ();
3181 var
3182 //hasAmbient: Boolean;
3183 //ambColor: TDFColor;
3184 lln: Integer;
3185 lx, ly, lrad: Integer;
3186 scxywh: array[0..3] of GLint;
3187 wassc: Boolean;
3188 begin
3189 if e_NoGraphics then exit;
3191 //TODO: lights should be in separate grid, i think
3192 // but on the other side: grid may be slower for dynlights, as their lifetime is short
3193 if (not gwin_k8_enable_light_experiments) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit;
3195 // rendering mode
3196 //ambColor := gCurrentMap['light_ambient'].rgba;
3197 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3199 { // this will multiply incoming color to alpha from framebuffer
3200 glEnable(GL_BLEND);
3201 glBlendFunc(GL_DST_ALPHA, GL_ONE);
3204 (*
3205 * light rendering: (INVALID!)
3206 * glStencilFunc(GL_EQUAL, 0, $ff);
3207 * for each light:
3208 * glClear(GL_STENCIL_BUFFER_BIT);
3209 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3210 * draw shadow volume into stencil buffer
3211 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3212 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
3213 * turn off blending
3214 * draw color-less quad with light alpha (WARNING! don't touch color!)
3215 * glEnable(GL_BLEND);
3216 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
3217 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
3218 *)
3219 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
3220 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
3222 // setup OpenGL parameters
3223 glStencilMask($FFFFFFFF);
3224 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
3225 glEnable(GL_STENCIL_TEST);
3226 glEnable(GL_SCISSOR_TEST);
3227 glClear(GL_STENCIL_BUFFER_BIT);
3228 glStencilFunc(GL_EQUAL, 0, $ff);
3230 for lln := 0 to g_dynLightCount-1 do
3231 begin
3232 lx := g_dynLights[lln].x;
3233 ly := g_dynLights[lln].y;
3234 lrad := g_dynLights[lln].radius;
3235 if (lrad < 3) then continue;
3237 if (lx-sX+lrad < 0) then continue;
3238 if (ly-sY+lrad < 0) then continue;
3239 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
3240 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
3242 // set scissor to optimize drawing
3243 if (g_dbg_scale = 1.0) then
3244 begin
3245 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
3246 end
3247 else
3248 begin
3249 glScissor(0, 0, gWinSizeX, gWinSizeY);
3250 end;
3251 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3252 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
3253 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3254 // draw extruded panels
3255 glDisable(GL_TEXTURE_2D);
3256 glDisable(GL_BLEND);
3257 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
3258 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
3259 // render light texture
3260 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3261 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
3262 // blend it
3263 glEnable(GL_BLEND);
3264 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3265 glEnable(GL_TEXTURE_2D);
3266 // color and opacity
3267 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
3268 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
3269 glBegin(GL_QUADS);
3270 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
3271 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
3272 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
3273 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
3274 glEnd();
3275 end;
3277 // done
3278 glDisable(GL_STENCIL_TEST);
3279 glDisable(GL_BLEND);
3280 glDisable(GL_SCISSOR_TEST);
3281 //glScissor(0, 0, sWidth, sHeight);
3283 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
3284 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
3285 end;
3288 function fixViewportForScale (): Boolean;
3289 var
3290 nx0, ny0, nw, nh: Integer;
3291 begin
3292 result := false;
3293 if (g_dbg_scale <> 1.0) then
3294 begin
3295 result := true;
3296 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
3297 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
3298 nw := round(sWidth/g_dbg_scale);
3299 nh := round(sHeight/g_dbg_scale);
3300 sX := nx0;
3301 sY := ny0;
3302 sWidth := nw;
3303 sHeight := nh;
3304 end;
3305 end;
3308 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3309 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3310 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
3311 type
3312 TDrawCB = procedure ();
3314 var
3315 hasAmbient: Boolean;
3316 ambColor: TDFColor;
3317 doAmbient: Boolean = false;
3319 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
3320 var
3321 tagmask: Integer;
3322 pan: TPanel;
3323 begin
3324 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
3325 if gdbg_map_use_accel_render then
3326 begin
3327 tagmask := panelTypeToTag(panType);
3328 while (gDrawPanelList.count > 0) do
3329 begin
3330 pan := TPanel(gDrawPanelList.front());
3331 if ((pan.tag and tagmask) = 0) then break;
3332 if doDraw then pan.Draw(doAmbient, ambColor);
3333 gDrawPanelList.popFront();
3334 end;
3335 end
3336 else
3337 begin
3338 if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor);
3339 end;
3340 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3341 end;
3343 procedure drawOther (profname: AnsiString; cb: TDrawCB);
3344 begin
3345 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
3346 if assigned(cb) then cb();
3347 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3348 end;
3350 begin
3351 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('total');
3353 // our accelerated renderer will collect all panels to gDrawPanelList
3354 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3355 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('collect');
3356 if gdbg_map_use_accel_render then
3357 begin
3358 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
3359 end;
3360 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3362 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('skyback');
3363 g_Map_DrawBack(backXOfs, backYOfs);
3364 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3366 if setTransMatrix then
3367 begin
3368 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3369 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
3370 glTranslatef(-sX, -sY, 0);
3371 end;
3373 // rendering mode
3374 ambColor := gCurrentMap['light_ambient'].rgba;
3375 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3378 if hasAmbient then
3379 begin
3380 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3381 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3382 glClear(GL_COLOR_BUFFER_BIT);
3383 end;
3385 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3388 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
3389 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
3390 drawOther('items', @g_Items_Draw);
3391 drawOther('weapons', @g_Weapon_Draw);
3392 drawOther('shells', @g_Player_DrawShells);
3393 drawOther('drawall', @g_Player_DrawAll);
3394 drawOther('corpses', @g_Player_DrawCorpses);
3395 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
3396 drawOther('monsters', @g_Monsters_Draw);
3397 drawOther('itemdrop', @g_Items_DrawDrop);
3398 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
3399 drawOther('gfx', @g_GFX_Draw);
3400 drawOther('flags', @g_Map_DrawFlags);
3401 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
3402 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
3403 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
3404 drawOther('dynlights', @renderDynLightsInternal);
3406 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3407 begin
3408 renderAmbientQuad(hasAmbient, ambColor);
3409 end;
3411 doAmbient := true;
3412 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
3415 if g_debug_HealthBar then
3416 begin
3417 g_Monsters_DrawHealth();
3418 g_Player_DrawHealth();
3419 end;
3421 if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering
3422 end;
3425 procedure DrawMapView(x, y, w, h: Integer);
3427 var
3428 bx, by: Integer;
3429 begin
3430 glPushMatrix();
3432 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
3433 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
3435 sX := x;
3436 sY := y;
3437 sWidth := w;
3438 sHeight := h;
3440 fixViewportForScale();
3441 renderMapInternal(-bx, -by, true);
3443 glPopMatrix();
3444 end;
3447 procedure DrawPlayer(p: TPlayer);
3448 var
3449 px, py, a, b, c, d: Integer;
3450 //R: TRect;
3451 begin
3452 if (p = nil) or (p.FDummy) then
3453 begin
3454 glPushMatrix();
3455 g_Map_DrawBack(0, 0);
3456 glPopMatrix();
3457 Exit;
3458 end;
3460 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3461 if (profileFrameDraw <> nil) then profileFrameDraw.mainBegin(g_profile_frame_draw);
3463 gPlayerDrawn := p;
3465 glPushMatrix();
3467 px := p.GameX + PLAYER_RECT_CX;
3468 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
3470 if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then
3471 begin
3472 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3473 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3475 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3476 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3478 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3479 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3480 begin
3481 // hcenter
3482 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3483 end;
3485 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3486 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3487 begin
3488 // vcenter
3489 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3490 end;
3491 end
3492 else
3493 begin
3494 // scaled, ignore level bounds
3495 a := -px+(gPlayerScreenSize.X div 2);
3496 b := -py+(gPlayerScreenSize.Y div 2);
3497 end;
3499 if p.IncCam <> 0 then
3500 begin
3501 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
3502 begin
3503 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
3504 begin
3505 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
3506 end;
3507 end;
3509 if py < gPlayerScreenSize.Y div 2 then
3510 begin
3511 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
3512 begin
3513 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
3514 end;
3515 end;
3517 if p.IncCam < 0 then
3518 begin
3519 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
3520 end;
3522 if p.IncCam > 0 then
3523 begin
3524 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
3525 end;
3526 end;
3528 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
3529 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
3530 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
3532 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
3533 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
3534 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
3536 sX := -a;
3537 sY := -(b+p.IncCam);
3538 sWidth := gPlayerScreenSize.X;
3539 sHeight := gPlayerScreenSize.Y;
3541 //glTranslatef(a, b+p.IncCam, 0);
3543 //if (p = gPlayer1) and (g_dbg_scale >= 1.0) then g_Holmes_plrViewSize(sWidth, sHeight);
3545 //conwritefln('OLD: (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3546 fixViewportForScale();
3547 //conwritefln(' (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3549 if (g_dbg_scale <> 1.0) and (not g_dbg_ignore_bounds) then
3550 begin
3551 if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth;
3552 if (sY+sHeight > gMapInfo.Height) then sY := gMapInfo.Height-sHeight;
3553 if (sX < 0) then sX := 0;
3554 if (sY < 0) then sY := 0;
3556 if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth));
3557 if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight));
3558 end;
3560 //r_smallmap_h: 0: left; 1: center; 2: right
3561 //r_smallmap_v: 0: top; 1: center; 2: bottom
3562 // horiz small map?
3563 if (gMapInfo.Width = sWidth) then
3564 begin
3565 sX := 0;
3566 end
3567 else if (gMapInfo.Width < sWidth) then
3568 begin
3569 case r_smallmap_h of
3570 1: sX := -((sWidth-gMapInfo.Width) div 2); // center
3571 2: sX := -(sWidth-gMapInfo.Width); // right
3572 else sX := 0; // left
3573 end;
3574 end;
3575 // vert small map?
3576 if (gMapInfo.Height = sHeight) then
3577 begin
3578 sY := 0;
3579 end
3580 else if (gMapInfo.Height < sHeight) then
3581 begin
3582 case r_smallmap_v of
3583 1: sY := -((sHeight-gMapInfo.Height) div 2); // center
3584 2: sY := -(sHeight-gMapInfo.Height); // bottom
3585 else sY := 0; // top
3586 end;
3587 end;
3589 p.viewPortX := sX;
3590 p.viewPortY := sY;
3591 p.viewPortW := sWidth;
3592 p.viewPortH := sHeight;
3594 {$IFDEF ENABLE_HOLMES}
3595 if (p = gPlayer1) then
3596 begin
3597 g_Holmes_plrViewPos(sX, sY);
3598 g_Holmes_plrViewSize(sWidth, sHeight);
3599 end;
3600 {$ENDIF}
3602 renderMapInternal(-c, -d, true);
3604 if p.FSpectator then
3605 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3606 p.GameY + PLAYER_RECT_CY - 4,
3607 'X', gStdFont, 255, 255, 255, 1, True);
3609 for a := 0 to High(gCollideMap) do
3610 for b := 0 to High(gCollideMap[a]) do
3611 begin
3612 d := 0;
3613 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3614 d := d + 1;
3615 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3616 d := d + 2;
3618 case d of
3619 1: e_DrawPoint(1, b, a, 200, 200, 200);
3620 2: e_DrawPoint(1, b, a, 64, 64, 255);
3621 3: e_DrawPoint(1, b, a, 255, 0, 255);
3622 end;
3623 end;
3626 glPopMatrix();
3628 p.DrawPain();
3629 p.DrawPickup();
3630 p.DrawRulez();
3631 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3632 if g_Debug_Player then
3633 g_Player_DrawDebug(p);
3634 p.DrawGUI();
3635 end;
3637 procedure drawProfilers ();
3638 var
3639 px: Integer = -1;
3640 py: Integer = -1;
3641 begin
3642 if g_profile_frame_draw and (profileFrameDraw <> nil) then px := px-drawProfiles(px, py, profileFrameDraw);
3643 if g_profile_collision and (profMapCollision <> nil) then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3644 if g_profile_los and (profMonsLOS <> nil) then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3645 end;
3647 procedure g_Game_Draw();
3648 var
3649 ID: DWORD;
3650 w, h: Word;
3651 ww, hh: Byte;
3652 Time: Int64;
3653 back: string;
3654 plView1, plView2: TPlayer;
3655 Split: Boolean;
3656 begin
3657 if gExit = EXIT_QUIT then Exit;
3659 Time := GetTimer() {div 1000};
3660 FPSCounter := FPSCounter+1;
3661 if Time - FPSTime >= 1000 then
3662 begin
3663 FPS := FPSCounter;
3664 FPSCounter := 0;
3665 FPSTime := Time;
3666 end;
3668 if gGameOn or (gState = STATE_FOLD) then
3669 begin
3670 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3671 begin
3672 gSpectMode := SPECT_NONE;
3673 if not gRevertPlayers then
3674 begin
3675 plView1 := gPlayer1;
3676 plView2 := gPlayer2;
3677 end
3678 else
3679 begin
3680 plView1 := gPlayer2;
3681 plView2 := gPlayer1;
3682 end;
3683 end
3684 else
3685 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3686 begin
3687 gSpectMode := SPECT_NONE;
3688 if gPlayer2 = nil then
3689 plView1 := gPlayer1
3690 else
3691 plView1 := gPlayer2;
3692 plView2 := nil;
3693 end
3694 else
3695 begin
3696 plView1 := nil;
3697 plView2 := nil;
3698 end;
3700 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3701 gSpectMode := SPECT_STATS;
3703 if gSpectMode = SPECT_PLAYERS then
3704 if gPlayers <> nil then
3705 begin
3706 plView1 := GetActivePlayer_ByID(gSpectPID1);
3707 if plView1 = nil then
3708 begin
3709 gSpectPID1 := GetActivePlayerID_Next();
3710 plView1 := GetActivePlayer_ByID(gSpectPID1);
3711 end;
3712 if gSpectViewTwo then
3713 begin
3714 plView2 := GetActivePlayer_ByID(gSpectPID2);
3715 if plView2 = nil then
3716 begin
3717 gSpectPID2 := GetActivePlayerID_Next();
3718 plView2 := GetActivePlayer_ByID(gSpectPID2);
3719 end;
3720 end;
3721 end;
3723 if gSpectMode = SPECT_MAPVIEW then
3724 begin
3725 // Ðåæèì ïðîñìîòðà êàðòû
3726 Split := False;
3727 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3728 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3729 gHearPoint1.Active := True;
3730 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3731 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3732 gHearPoint2.Active := False;
3733 end
3734 else
3735 begin
3736 Split := (plView1 <> nil) and (plView2 <> nil);
3738 // Òî÷êè ñëóõà èãðîêîâ
3739 if plView1 <> nil then
3740 begin
3741 gHearPoint1.Active := True;
3742 gHearPoint1.Coords.X := plView1.GameX;
3743 gHearPoint1.Coords.Y := plView1.GameY;
3744 end else
3745 gHearPoint1.Active := False;
3746 if plView2 <> nil then
3747 begin
3748 gHearPoint2.Active := True;
3749 gHearPoint2.Coords.X := plView2.GameX;
3750 gHearPoint2.Coords.Y := plView2.GameY;
3751 end else
3752 gHearPoint2.Active := False;
3754 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3755 gPlayerScreenSize.X := gScreenWidth-196;
3756 if Split then
3757 begin
3758 gPlayerScreenSize.Y := gScreenHeight div 2;
3759 if gScreenHeight mod 2 = 0 then
3760 Dec(gPlayerScreenSize.Y);
3761 end
3762 else
3763 gPlayerScreenSize.Y := gScreenHeight;
3765 if Split then
3766 if gScreenHeight mod 2 = 0 then
3767 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3768 else
3769 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3771 DrawPlayer(plView1);
3772 gPlayer1ScreenCoord.X := sX;
3773 gPlayer1ScreenCoord.Y := sY;
3775 if Split then
3776 begin
3777 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3779 DrawPlayer(plView2);
3780 gPlayer2ScreenCoord.X := sX;
3781 gPlayer2ScreenCoord.Y := sY;
3782 end;
3784 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3786 if Split then
3787 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3788 end;
3790 {$IFDEF ENABLE_HOLMES}
3791 // draw inspector
3792 if (g_holmes_enabled) then g_Holmes_Draw();
3793 {$ENDIF}
3795 if MessageText <> '' then
3796 begin
3797 w := 0;
3798 h := 0;
3799 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3800 if Split then
3801 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3802 (gScreenHeight div 2)-(h div 2), MessageText)
3803 else
3804 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3805 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3806 end;
3808 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3810 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) and (not gSpectAuto) then
3811 begin
3812 // Draw spectator GUI
3813 ww := 0;
3814 hh := 0;
3815 e_TextureFontGetSize(gStdFont, ww, hh);
3816 case gSpectMode of
3817 SPECT_STATS:
3818 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3819 SPECT_MAPVIEW:
3820 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3821 SPECT_PLAYERS:
3822 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3823 end;
3824 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3825 if gSpectMode = SPECT_STATS then
3826 begin
3827 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2)*2, 'Autoview', gStdFont, 255, 255, 255, 1);
3828 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2), '< fire >', gStdFont, 255, 255, 255, 1);
3829 end;
3830 if gSpectMode = SPECT_MAPVIEW then
3831 begin
3832 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3833 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3834 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3835 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3836 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3837 end;
3838 if gSpectMode = SPECT_PLAYERS then
3839 begin
3840 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3841 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3842 if gSpectViewTwo then
3843 begin
3844 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3845 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3846 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3847 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3848 end
3849 else
3850 begin
3851 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3852 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3853 end;
3854 end;
3855 end;
3856 end;
3858 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
3859 begin
3860 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3861 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3863 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3864 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3865 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3866 end;
3868 if not gGameOn then
3869 begin
3870 if (gState = STATE_MENU) then
3871 begin
3872 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3873 begin
3874 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3875 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3876 end;
3877 // F3 at menu will show game loading dialog
3878 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3879 if (g_ActiveWindow <> nil) then
3880 begin
3881 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3882 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3883 end
3884 else
3885 begin
3886 // F3 at titlepic will show game loading dialog
3887 if e_KeyPressed(IK_F3) then
3888 begin
3889 g_Menu_Show_LoadMenu(true);
3890 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3891 end;
3892 end;
3893 end;
3895 if gState = STATE_FOLD then
3896 begin
3897 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3898 end;
3900 if gState = STATE_INTERCUSTOM then
3901 begin
3902 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3903 begin
3904 back := 'TEXTURE_endpic';
3905 if not g_Texture_Get(back, ID) then
3906 back := _lc[I_TEXTURE_ENDPIC];
3907 end
3908 else
3909 back := 'INTER';
3911 if g_Texture_Get(back, ID) then
3912 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3913 else
3914 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3916 DrawCustomStat();
3918 if g_ActiveWindow <> nil then
3919 begin
3920 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3921 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3922 end;
3923 end;
3925 if gState = STATE_INTERSINGLE then
3926 begin
3927 if EndingGameCounter > 0 then
3928 begin
3929 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3930 end
3931 else
3932 begin
3933 back := 'INTER';
3935 if g_Texture_Get(back, ID) then
3936 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3937 else
3938 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3940 DrawSingleStat();
3942 if g_ActiveWindow <> nil then
3943 begin
3944 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3945 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3946 end;
3947 end;
3948 end;
3950 if gState = STATE_ENDPIC then
3951 begin
3952 ID := DWORD(-1);
3953 if not g_Texture_Get('TEXTURE_endpic', ID) then
3954 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3956 if ID <> DWORD(-1) then
3957 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3958 else
3959 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3961 if g_ActiveWindow <> nil then
3962 begin
3963 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3964 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3965 end;
3966 end;
3968 if gState = STATE_SLIST then
3969 begin
3970 if g_Texture_Get('MENU_BACKGROUND', ID) then
3971 begin
3972 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3973 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3974 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3975 end;
3976 g_Serverlist_Draw(slCurrent, slTable);
3977 end;
3978 end;
3980 if g_ActiveWindow <> nil then
3981 begin
3982 if gGameOn then
3983 begin
3984 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3985 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3986 end;
3987 g_ActiveWindow.Draw();
3988 end;
3990 g_Console_Draw();
3992 if g_debug_Sounds and gGameOn then
3993 begin
3994 for w := 0 to High(e_SoundsArray) do
3995 for h := 0 to e_SoundsArray[w].nRefs do
3996 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3997 end;
3999 if gShowFPS then
4000 begin
4001 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
4002 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
4003 end;
4005 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
4006 drawTime(gScreenWidth-72, gScreenHeight-16);
4008 if gGameOn then drawProfilers();
4010 {$IFDEF ENABLE_HOLMES}
4011 g_Holmes_DrawUI();
4012 {$ENDIF}
4014 g_Touch_Draw;
4015 end;
4017 procedure g_Game_Quit();
4018 begin
4019 g_Game_StopAllSounds(True);
4020 gMusic.Free();
4021 g_Game_SaveOptions();
4022 g_Game_FreeData();
4023 g_PlayerModel_FreeData();
4024 g_Texture_DeleteAll();
4025 g_Frames_DeleteAll();
4026 //g_Menu_Free(); //k8: this segfaults after resolution change; who cares?
4028 if NetInitDone then g_Net_Free;
4030 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
4031 if gMapToDelete <> '' then
4032 g_Game_DeleteTestMap();
4034 gExit := EXIT_QUIT;
4035 PushExitEvent();
4036 end;
4038 procedure g_FatalError(Text: String);
4039 begin
4040 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
4041 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning);
4043 gExit := EXIT_SIMPLE;
4044 end;
4046 procedure g_SimpleError(Text: String);
4047 begin
4048 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
4049 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
4050 end;
4052 procedure g_Game_SetupScreenSize();
4053 const
4054 RES_FACTOR = 4.0 / 3.0;
4055 var
4056 s: Single;
4057 rf: Single;
4058 bw, bh: Word;
4059 begin
4060 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4061 gPlayerScreenSize.X := gScreenWidth-196;
4062 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
4063 gPlayerScreenSize.Y := gScreenHeight div 2
4064 else
4065 gPlayerScreenSize.Y := gScreenHeight;
4067 // Ðàçìåð çàäíåãî ïëàíà:
4068 if BackID <> DWORD(-1) then
4069 begin
4070 s := SKY_STRETCH;
4071 if (gScreenWidth*s > gMapInfo.Width) or
4072 (gScreenHeight*s > gMapInfo.Height) then
4073 begin
4074 gBackSize.X := gScreenWidth;
4075 gBackSize.Y := gScreenHeight;
4076 end
4077 else
4078 begin
4079 e_GetTextureSize(BackID, @bw, @bh);
4080 rf := Single(bw) / Single(bh);
4081 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
4082 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
4083 s := Max(gScreenWidth / bw, gScreenHeight / bh);
4084 if (s < 1.0) then s := 1.0;
4085 gBackSize.X := Round(bw*s);
4086 gBackSize.Y := Round(bh*s);
4087 end;
4088 end;
4089 end;
4091 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
4092 begin
4093 g_Window_SetSize(newWidth, newHeight, nowFull);
4094 end;
4096 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
4097 begin
4098 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
4099 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
4100 Exit;
4101 if gPlayer1 = nil then
4102 begin
4103 if g_Game_IsClient then
4104 begin
4105 if NetPlrUID1 > -1 then
4106 begin
4107 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
4108 gPlayer1 := g_Player_Get(NetPlrUID1);
4109 end;
4110 Exit;
4111 end;
4113 if not (Team in [TEAM_RED, TEAM_BLUE]) then
4114 Team := gPlayer1Settings.Team;
4116 // Ñîçäàíèå ïåðâîãî èãðîêà:
4117 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4118 gPlayer1Settings.Color,
4119 Team, False));
4120 if gPlayer1 = nil then
4121 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
4122 else
4123 begin
4124 gPlayer1.Name := gPlayer1Settings.Name;
4125 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
4126 if g_Game_IsServer and g_Game_IsNet then
4127 MH_SEND_PlayerCreate(gPlayer1.UID);
4128 gPlayer1.Respawn(False, True);
4130 if g_Game_IsNet and NetUseMaster then
4131 g_Net_Slist_Update;
4132 end;
4134 Exit;
4135 end;
4136 if gPlayer2 = nil then
4137 begin
4138 if g_Game_IsClient then
4139 begin
4140 if NetPlrUID2 > -1 then
4141 gPlayer2 := g_Player_Get(NetPlrUID2);
4142 Exit;
4143 end;
4145 if not (Team in [TEAM_RED, TEAM_BLUE]) then
4146 Team := gPlayer2Settings.Team;
4148 // Ñîçäàíèå âòîðîãî èãðîêà:
4149 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4150 gPlayer2Settings.Color,
4151 Team, False));
4152 if gPlayer2 = nil then
4153 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
4154 else
4155 begin
4156 gPlayer2.Name := gPlayer2Settings.Name;
4157 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
4158 if g_Game_IsServer and g_Game_IsNet then
4159 MH_SEND_PlayerCreate(gPlayer2.UID);
4160 gPlayer2.Respawn(False, True);
4162 if g_Game_IsNet and NetUseMaster then
4163 g_Net_Slist_Update;
4164 end;
4166 Exit;
4167 end;
4168 end;
4170 procedure g_Game_RemovePlayer();
4171 var
4172 Pl: TPlayer;
4173 begin
4174 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
4175 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
4176 Exit;
4177 Pl := gPlayer2;
4178 if Pl <> nil then
4179 begin
4180 if g_Game_IsServer then
4181 begin
4182 Pl.Lives := 0;
4183 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4184 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
4185 g_Player_Remove(Pl.UID);
4187 if g_Game_IsNet and NetUseMaster then
4188 g_Net_Slist_Update;
4189 end else
4190 gPlayer2 := nil;
4191 Exit;
4192 end;
4193 Pl := gPlayer1;
4194 if Pl <> nil then
4195 begin
4196 if g_Game_IsServer then
4197 begin
4198 Pl.Lives := 0;
4199 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4200 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
4201 g_Player_Remove(Pl.UID);
4203 if g_Game_IsNet and NetUseMaster then
4204 g_Net_Slist_Update;
4205 end else
4206 begin
4207 gPlayer1 := nil;
4208 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
4209 end;
4210 Exit;
4211 end;
4212 end;
4214 procedure g_Game_Spectate();
4215 begin
4216 g_Game_RemovePlayer();
4217 if gPlayer1 <> nil then
4218 g_Game_RemovePlayer();
4219 end;
4221 procedure g_Game_SpectateCenterView();
4222 begin
4223 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
4224 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
4225 end;
4227 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
4228 var
4229 i, nPl: Integer;
4230 tmps: AnsiString;
4231 begin
4232 g_Game_Free();
4234 e_WriteLog('Starting singleplayer game...', TMsgType.Notify);
4236 g_Game_ClearLoading();
4238 // Íàñòðîéêè èãðû:
4239 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
4240 gAimLine := False;
4241 gShowMap := False;
4242 gGameSettings.GameType := GT_SINGLE;
4243 gGameSettings.MaxLives := 0;
4244 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
4245 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
4246 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
4247 gSwitchGameMode := GM_SINGLE;
4249 g_Game_ExecuteEvent('ongamestart');
4251 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4252 g_Game_SetupScreenSize();
4254 // Ñîçäàíèå ïåðâîãî èãðîêà:
4255 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4256 gPlayer1Settings.Color,
4257 gPlayer1Settings.Team, False));
4258 if gPlayer1 = nil then
4259 begin
4260 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4261 Exit;
4262 end;
4264 gPlayer1.Name := gPlayer1Settings.Name;
4265 nPl := 1;
4267 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
4268 if TwoPlayers then
4269 begin
4270 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4271 gPlayer2Settings.Color,
4272 gPlayer2Settings.Team, False));
4273 if gPlayer2 = nil then
4274 begin
4275 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4276 Exit;
4277 end;
4279 gPlayer2.Name := gPlayer2Settings.Name;
4280 Inc(nPl);
4281 end;
4283 // Çàãðóçêà è çàïóñê êàðòû:
4284 if not g_Game_StartMap(MAP, True) then
4285 begin
4286 if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP;
4287 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps]));
4288 Exit;
4289 end;
4291 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4292 g_Player_Init();
4294 // Ñîçäàåì áîòîâ:
4295 for i := nPl+1 to nPlayers do
4296 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4297 end;
4299 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
4300 TimeLimit, GoalLimit: Word;
4301 MaxLives: Byte;
4302 Options: LongWord; nPlayers: Byte);
4303 var
4304 i, nPl: Integer;
4305 begin
4306 g_Game_Free();
4308 e_WriteLog('Starting custom game...', TMsgType.Notify);
4310 g_Game_ClearLoading();
4312 // Íàñòðîéêè èãðû:
4313 gGameSettings.GameType := GT_CUSTOM;
4314 gGameSettings.GameMode := GameMode;
4315 gSwitchGameMode := GameMode;
4316 gGameSettings.TimeLimit := TimeLimit;
4317 gGameSettings.GoalLimit := GoalLimit;
4318 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4319 gGameSettings.Options := Options;
4321 gCoopTotalMonstersKilled := 0;
4322 gCoopTotalSecretsFound := 0;
4323 gCoopTotalMonsters := 0;
4324 gCoopTotalSecrets := 0;
4325 gAimLine := False;
4326 gShowMap := False;
4328 g_Game_ExecuteEvent('ongamestart');
4330 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4331 g_Game_SetupScreenSize();
4333 // Ðåæèì íàáëþäàòåëÿ:
4334 if nPlayers = 0 then
4335 begin
4336 gPlayer1 := nil;
4337 gPlayer2 := nil;
4338 end;
4340 nPl := 0;
4341 if nPlayers >= 1 then
4342 begin
4343 // Ñîçäàíèå ïåðâîãî èãðîêà:
4344 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4345 gPlayer1Settings.Color,
4346 gPlayer1Settings.Team, False));
4347 if gPlayer1 = nil then
4348 begin
4349 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4350 Exit;
4351 end;
4353 gPlayer1.Name := gPlayer1Settings.Name;
4354 Inc(nPl);
4355 end;
4357 if nPlayers >= 2 then
4358 begin
4359 // Ñîçäàíèå âòîðîãî èãðîêà:
4360 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4361 gPlayer2Settings.Color,
4362 gPlayer2Settings.Team, False));
4363 if gPlayer2 = nil then
4364 begin
4365 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4366 Exit;
4367 end;
4369 gPlayer2.Name := gPlayer2Settings.Name;
4370 Inc(nPl);
4371 end;
4373 // Çàãðóçêà è çàïóñê êàðòû:
4374 if not g_Game_StartMap(Map, True) then
4375 begin
4376 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4377 Exit;
4378 end;
4380 // Íåò òî÷åê ïîÿâëåíèÿ:
4381 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4382 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4383 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4384 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4385 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4386 begin
4387 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4388 Exit;
4389 end;
4391 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4392 g_Player_Init();
4394 // Ñîçäàåì áîòîâ:
4395 for i := nPl+1 to nPlayers do
4396 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4397 end;
4399 procedure g_Game_StartServer(Map: String; GameMode: Byte;
4400 TimeLimit, GoalLimit: Word; MaxLives: Byte;
4401 Options: LongWord; nPlayers: Byte;
4402 IPAddr: LongWord; Port: Word);
4403 begin
4404 g_Game_Free();
4406 e_WriteLog('Starting net game (server)...', TMsgType.Notify);
4408 g_Game_ClearLoading();
4410 // Íàñòðîéêè èãðû:
4411 gGameSettings.GameType := GT_SERVER;
4412 gGameSettings.GameMode := GameMode;
4413 gSwitchGameMode := GameMode;
4414 gGameSettings.TimeLimit := TimeLimit;
4415 gGameSettings.GoalLimit := GoalLimit;
4416 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4417 gGameSettings.Options := Options;
4419 gCoopTotalMonstersKilled := 0;
4420 gCoopTotalSecretsFound := 0;
4421 gCoopTotalMonsters := 0;
4422 gCoopTotalSecrets := 0;
4423 gAimLine := False;
4424 gShowMap := False;
4426 g_Game_ExecuteEvent('ongamestart');
4428 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
4429 g_Game_SetupScreenSize();
4431 // Ðåæèì íàáëþäàòåëÿ:
4432 if nPlayers = 0 then
4433 begin
4434 gPlayer1 := nil;
4435 gPlayer2 := nil;
4436 end;
4438 if nPlayers >= 1 then
4439 begin
4440 // Ñîçäàíèå ïåðâîãî èãðîêà:
4441 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4442 gPlayer1Settings.Color,
4443 gPlayer1Settings.Team, False));
4444 if gPlayer1 = nil then
4445 begin
4446 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4447 Exit;
4448 end;
4450 gPlayer1.Name := gPlayer1Settings.Name;
4451 end;
4453 if nPlayers >= 2 then
4454 begin
4455 // Ñîçäàíèå âòîðîãî èãðîêà:
4456 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4457 gPlayer2Settings.Color,
4458 gPlayer2Settings.Team, False));
4459 if gPlayer2 = nil then
4460 begin
4461 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4462 Exit;
4463 end;
4465 gPlayer2.Name := gPlayer2Settings.Name;
4466 end;
4468 g_Game_SetLoadingText(_lc[I_LOAD_HOST], 0, False);
4469 if NetForwardPorts then
4470 g_Game_SetLoadingText(_lc[I_LOAD_PORTS], 0, False);
4472 // Ñòàðòóåì ñåðâåð
4473 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
4474 begin
4475 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
4476 Exit;
4477 end;
4479 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
4481 // Çàãðóçêà è çàïóñê êàðòû:
4482 if not g_Game_StartMap(Map, True) then
4483 begin
4484 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4485 Exit;
4486 end;
4488 // Íåò òî÷åê ïîÿâëåíèÿ:
4489 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4490 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4491 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4492 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4493 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4494 begin
4495 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4496 Exit;
4497 end;
4499 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4500 g_Player_Init();
4502 NetState := NET_STATE_GAME;
4503 end;
4505 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
4506 var
4507 Map: String;
4508 WadName: string;
4509 Ptr: Pointer;
4510 T: Cardinal;
4511 MID: Byte;
4512 State: Byte;
4513 OuterLoop: Boolean;
4514 newResPath: string;
4515 InMsg: TMsg;
4516 begin
4517 g_Game_Free();
4519 State := 0;
4520 e_WriteLog('Starting net game (client)...', TMsgType.Notify);
4521 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', TMsgType.Notify);
4523 g_Game_ClearLoading();
4525 // Íàñòðîéêè èãðû:
4526 gGameSettings.GameType := GT_CLIENT;
4528 gCoopTotalMonstersKilled := 0;
4529 gCoopTotalSecretsFound := 0;
4530 gCoopTotalMonsters := 0;
4531 gCoopTotalSecrets := 0;
4532 gAimLine := False;
4533 gShowMap := False;
4535 g_Game_ExecuteEvent('ongamestart');
4537 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4538 g_Game_SetupScreenSize();
4540 NetState := NET_STATE_AUTH;
4542 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4543 // Ñòàðòóåì êëèåíò
4544 if not g_Net_Connect(Addr, Port) then
4545 begin
4546 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4547 NetState := NET_STATE_NONE;
4548 Exit;
4549 end;
4551 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4552 MC_SEND_Info(PW);
4553 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4555 OuterLoop := True;
4556 while OuterLoop do
4557 begin
4558 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4559 begin
4560 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4561 begin
4562 Ptr := NetEvent.packet^.data;
4563 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4564 continue;
4566 MID := InMsg.ReadByte();
4568 if (MID = NET_MSG_INFO) and (State = 0) then
4569 begin
4570 NetMyID := InMsg.ReadByte();
4571 NetPlrUID1 := InMsg.ReadWord();
4573 WadName := InMsg.ReadString();
4574 Map := InMsg.ReadString();
4576 gWADHash := InMsg.ReadMD5();
4578 gGameSettings.GameMode := InMsg.ReadByte();
4579 gSwitchGameMode := gGameSettings.GameMode;
4580 gGameSettings.GoalLimit := InMsg.ReadWord();
4581 gGameSettings.TimeLimit := InMsg.ReadWord();
4582 gGameSettings.MaxLives := InMsg.ReadByte();
4583 gGameSettings.Options := InMsg.ReadLongWord();
4584 T := InMsg.ReadLongWord();
4586 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4587 if newResPath = '' then
4588 begin
4589 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4590 newResPath := g_Res_DownloadWAD(WadName);
4591 if newResPath = '' then
4592 begin
4593 g_FatalError(_lc[I_NET_ERR_HASH]);
4594 enet_packet_destroy(NetEvent.packet);
4595 NetState := NET_STATE_NONE;
4596 Exit;
4597 end;
4598 end;
4599 newResPath := ExtractRelativePath(MapsDir, newResPath);
4601 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4602 gPlayer1Settings.Color,
4603 gPlayer1Settings.Team, False));
4605 if gPlayer1 = nil then
4606 begin
4607 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4609 enet_packet_destroy(NetEvent.packet);
4610 NetState := NET_STATE_NONE;
4611 Exit;
4612 end;
4614 gPlayer1.Name := gPlayer1Settings.Name;
4615 gPlayer1.UID := NetPlrUID1;
4616 gPlayer1.Reset(True);
4618 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4619 begin
4620 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4622 enet_packet_destroy(NetEvent.packet);
4623 NetState := NET_STATE_NONE;
4624 Exit;
4625 end;
4627 gTime := T;
4629 State := 1;
4630 OuterLoop := False;
4631 enet_packet_destroy(NetEvent.packet);
4632 break;
4633 end
4634 else
4635 enet_packet_destroy(NetEvent.packet);
4636 end
4637 else
4638 begin
4639 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4640 begin
4641 State := 0;
4642 if (NetEvent.data <= NET_DISC_MAX) then
4643 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4644 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4645 OuterLoop := False;
4646 Break;
4647 end;
4648 end;
4649 end;
4651 ProcessLoading(true);
4653 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) or e_KeyPressed(VK_ESCAPE) then
4654 begin
4655 State := 0;
4656 break;
4657 end;
4658 end;
4660 if State <> 1 then
4661 begin
4662 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4663 NetState := NET_STATE_NONE;
4664 Exit;
4665 end;
4667 gLMSRespawn := LMS_RESPAWN_NONE;
4668 gLMSRespawnTime := 0;
4670 g_Player_Init();
4671 NetState := NET_STATE_GAME;
4672 MC_SEND_FullStateRequest;
4673 e_WriteLog('NET: Connection successful.', TMsgType.Notify);
4674 end;
4676 procedure g_Game_SaveOptions();
4677 begin
4678 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4679 end;
4681 procedure g_Game_ChangeMap(const MapPath: String);
4682 var
4683 Force: Boolean;
4684 begin
4685 g_Game_ClearLoading();
4687 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4688 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4689 if gExitByTrigger then
4690 begin
4691 Force := False;
4692 gExitByTrigger := False;
4693 end;
4694 if not g_Game_StartMap(MapPath, Force) then
4695 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4696 end;
4698 procedure g_Game_Restart();
4699 var
4700 Map: string;
4701 begin
4702 if g_Game_IsClient then
4703 Exit;
4704 map := g_ExtractFileName(gMapInfo.Map);
4706 MessageTime := 0;
4707 gGameOn := False;
4708 g_Game_ClearLoading();
4709 g_Game_StartMap(Map, True, gCurrentMapFileName);
4710 end;
4712 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4713 var
4714 NewWAD, ResName: String;
4715 I: Integer;
4716 begin
4717 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4718 g_Player_RemoveAllCorpses();
4720 if (not g_Game_IsClient) and
4721 (gSwitchGameMode <> gGameSettings.GameMode) and
4722 (gGameSettings.GameMode <> GM_SINGLE) then
4723 begin
4724 if gSwitchGameMode = GM_CTF then
4725 gGameSettings.MaxLives := 0;
4726 gGameSettings.GameMode := gSwitchGameMode;
4727 Force := True;
4728 end else
4729 gSwitchGameMode := gGameSettings.GameMode;
4731 g_Player_ResetTeams();
4733 if isWadPath(Map) then
4734 begin
4735 NewWAD := g_ExtractWadName(Map);
4736 ResName := g_ExtractFileName(Map);
4737 if g_Game_IsServer then
4738 begin
4739 gWADHash := MD5File(MapsDir + NewWAD);
4740 g_Game_LoadWAD(NewWAD);
4741 end else
4742 // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART
4743 g_Game_ClientWAD(NewWAD, gWADHash);
4744 end else
4745 ResName := Map;
4747 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4748 if Result then
4749 begin
4750 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4752 gState := STATE_NONE;
4753 g_ActiveWindow := nil;
4754 gGameOn := True;
4756 DisableCheats();
4757 ResetTimer();
4759 if gGameSettings.GameMode = GM_CTF then
4760 begin
4761 g_Map_ResetFlag(FLAG_RED);
4762 g_Map_ResetFlag(FLAG_BLUE);
4763 // CTF, à ôëàãîâ íåò:
4764 if not g_Map_HaveFlagPoints() then
4765 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4766 end;
4767 end
4768 else
4769 begin
4770 gState := STATE_MENU;
4771 gGameOn := False;
4772 end;
4774 gExit := 0;
4775 gPauseMain := false;
4776 gPauseHolmes := false;
4777 gTime := 0;
4778 NetTimeToUpdate := 1;
4779 NetTimeToReliable := 0;
4780 NetTimeToMaster := NetMasterRate;
4781 gLMSRespawn := LMS_RESPAWN_NONE;
4782 gLMSRespawnTime := 0;
4783 gMissionFailed := False;
4784 gNextMap := '';
4786 gCoopMonstersKilled := 0;
4787 gCoopSecretsFound := 0;
4789 gVoteInProgress := False;
4790 gVotePassed := False;
4791 gVoteCount := 0;
4792 gVoted := False;
4794 gStatsOff := False;
4796 if not gGameOn then Exit;
4798 g_Game_SpectateCenterView();
4800 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4801 begin
4802 gLMSRespawn := LMS_RESPAWN_WARMUP;
4803 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4804 gLMSSoftSpawn := True;
4805 if NetMode = NET_SERVER then
4806 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4807 else
4808 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4809 end;
4811 if NetMode = NET_SERVER then
4812 begin
4813 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4815 // Ìàñòåðñåðâåð
4816 if NetUseMaster then
4817 begin
4818 if (NetMHost = nil) or (NetMPeer = nil) then
4819 if not g_Net_Slist_Connect then
4820 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4822 g_Net_Slist_Update;
4823 end;
4825 if NetClients <> nil then
4826 for I := 0 to High(NetClients) do
4827 if NetClients[I].Used then
4828 begin
4829 NetClients[I].Voted := False;
4830 if NetClients[I].RequestedFullUpdate then
4831 begin
4832 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4833 NetClients[I].RequestedFullUpdate := False;
4834 end;
4835 end;
4837 g_Net_UnbanNonPermHosts();
4838 end;
4840 if gLastMap then
4841 begin
4842 gCoopTotalMonstersKilled := 0;
4843 gCoopTotalSecretsFound := 0;
4844 gCoopTotalMonsters := 0;
4845 gCoopTotalSecrets := 0;
4846 gLastMap := False;
4847 end;
4849 g_Game_ExecuteEvent('onmapstart');
4850 end;
4852 procedure SetFirstLevel();
4853 begin
4854 gNextMap := '';
4856 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4857 if MapList = nil then
4858 Exit;
4860 SortSArray(MapList);
4861 gNextMap := MapList[Low(MapList)];
4863 MapList := nil;
4864 end;
4866 procedure g_Game_ExitLevel(const Map: AnsiString);
4867 begin
4868 gNextMap := Map;
4870 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4871 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4872 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4873 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4875 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4876 if gGameSettings.GameType = GT_SINGLE then
4877 gExit := EXIT_ENDLEVELSINGLE
4878 else // Âûøëè â âûõîä â Ñâîåé èãðå
4879 begin
4880 gExit := EXIT_ENDLEVELCUSTOM;
4881 if gGameSettings.GameMode = GM_COOP then
4882 g_Player_RememberAll;
4884 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4885 begin
4886 gLastMap := True;
4887 if gGameSettings.GameMode = GM_COOP then
4888 gStatsOff := True;
4890 gStatsPressed := True;
4891 gNextMap := 'MAP01';
4893 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4894 g_Game_NextLevel;
4896 if g_Game_IsNet then
4897 begin
4898 MH_SEND_GameStats();
4899 MH_SEND_CoopStats();
4900 end;
4901 end;
4902 end;
4903 end;
4905 procedure g_Game_RestartLevel();
4906 var
4907 Map: string;
4908 begin
4909 if gGameSettings.GameMode = GM_SINGLE then
4910 begin
4911 g_Game_Restart();
4912 Exit;
4913 end;
4914 gExit := EXIT_ENDLEVELCUSTOM;
4915 Map := g_ExtractFileName(gMapInfo.Map);
4916 gNextMap := Map;
4917 end;
4919 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4920 var
4921 gWAD: String;
4922 begin
4923 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4924 Exit;
4925 if not g_Game_IsClient then
4926 Exit;
4927 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4928 if gWAD = '' then
4929 begin
4930 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4931 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4932 if gWAD = '' then
4933 begin
4934 g_Game_Free();
4935 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4936 Exit;
4937 end;
4938 end;
4939 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4940 g_Game_LoadWAD(NewWAD);
4941 end;
4943 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4944 var
4945 i, n, nb, nr: Integer;
4947 function monRespawn (mon: TMonster): Boolean;
4948 begin
4949 result := false; // don't stop
4950 if not mon.FNoRespawn then mon.Respawn();
4951 end;
4953 begin
4954 if not g_Game_IsServer then Exit;
4955 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4956 gLMSRespawn := LMS_RESPAWN_NONE;
4957 gLMSRespawnTime := 0;
4958 MessageTime := 0;
4960 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4961 begin
4962 gMissionFailed := True;
4963 g_Game_RestartLevel;
4964 Exit;
4965 end;
4967 n := 0; nb := 0; nr := 0;
4968 for i := Low(gPlayers) to High(gPlayers) do
4969 if (gPlayers[i] <> nil) and
4970 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4971 (gPlayers[i] is TBot)) then
4972 begin
4973 Inc(n);
4974 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4975 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4976 end;
4978 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4979 begin
4980 // wait a second until the fuckers finally decide to join
4981 gLMSRespawn := LMS_RESPAWN_WARMUP;
4982 gLMSRespawnTime := gTime + 1000;
4983 gLMSSoftSpawn := NoMapRestart;
4984 Exit;
4985 end;
4987 g_Player_RemoveAllCorpses;
4988 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4989 if g_Game_IsNet then
4990 MH_SEND_GameEvent(NET_EV_LMS_START);
4992 for i := Low(gPlayers) to High(gPlayers) do
4993 begin
4994 if gPlayers[i] = nil then continue;
4995 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4996 // don't touch normal spectators
4997 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4998 begin
4999 gPlayers[i].FNoRespawn := True;
5000 gPlayers[i].Lives := 0;
5001 if g_Game_IsNet then
5002 MH_SEND_PlayerStats(gPlayers[I].UID);
5003 continue;
5004 end;
5005 gPlayers[i].FNoRespawn := False;
5006 gPlayers[i].Lives := gGameSettings.MaxLives;
5007 gPlayers[i].Respawn(False, True);
5008 if gGameSettings.GameMode = GM_COOP then
5009 begin
5010 gPlayers[i].Frags := 0;
5011 gPlayers[i].RecallState;
5012 end;
5013 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
5014 gPlayer1 := g_Player_Get(gLMSPID1);
5015 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
5016 gPlayer2 := g_Player_Get(gLMSPID2);
5017 end;
5019 g_Items_RestartRound();
5022 g_Mons_ForEach(monRespawn);
5024 gLMSSoftSpawn := False;
5025 end;
5027 function g_Game_GetFirstMap(WAD: String): String;
5028 begin
5029 Result := '';
5031 MapList := g_Map_GetMapsList(WAD);
5032 if MapList = nil then
5033 Exit;
5035 SortSArray(MapList);
5036 Result := MapList[Low(MapList)];
5038 if not g_Map_Exist(WAD + ':\' + Result) then
5039 Result := '';
5041 MapList := nil;
5042 end;
5044 function g_Game_GetNextMap(): String;
5045 var
5046 I: Integer;
5047 Map: string;
5048 begin
5049 Result := '';
5051 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
5052 if MapList = nil then
5053 Exit;
5055 Map := g_ExtractFileName(gMapInfo.Map);
5057 SortSArray(MapList);
5058 MapIndex := -255;
5059 for I := Low(MapList) to High(MapList) do
5060 if Map = MapList[I] then
5061 begin
5062 MapIndex := I;
5063 Break;
5064 end;
5066 if MapIndex <> -255 then
5067 begin
5068 if MapIndex = High(MapList) then
5069 Result := MapList[Low(MapList)]
5070 else
5071 Result := MapList[MapIndex + 1];
5073 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
5074 end;
5076 MapList := nil;
5077 end;
5079 procedure g_Game_NextLevel();
5080 begin
5081 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
5082 gExit := EXIT_ENDLEVELCUSTOM
5083 else
5084 begin
5085 gExit := EXIT_ENDLEVELSINGLE;
5086 Exit;
5087 end;
5089 if gNextMap <> '' then Exit;
5090 gNextMap := g_Game_GetNextMap();
5091 end;
5093 function g_Game_IsTestMap(): Boolean;
5094 begin
5095 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
5096 end;
5098 procedure g_Game_DeleteTestMap();
5099 var
5100 a: Integer;
5101 //MapName: AnsiString;
5102 WadName: string;
5104 WAD: TWADFile;
5105 MapList: SSArray;
5106 time: Integer;
5108 begin
5109 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
5110 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
5111 if (a = 0) then exit;
5113 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
5114 WadName := Copy(gMapToDelete, 1, a+3);
5115 Delete(gMapToDelete, 1, a+5);
5116 gMapToDelete := UpperCase(gMapToDelete);
5117 //MapName := '';
5118 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
5121 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
5122 if MapName <> TEST_MAP_NAME then
5123 Exit;
5125 if not gTempDelete then
5126 begin
5127 time := g_GetFileTime(WadName);
5128 WAD := TWADFile.Create();
5130 // ×èòàåì Wad-ôàéë:
5131 if not WAD.ReadFile(WadName) then
5132 begin // Íåò òàêîãî WAD-ôàéëà
5133 WAD.Free();
5134 Exit;
5135 end;
5137 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
5138 WAD.CreateImage();
5139 MapList := WAD.GetResourcesList('');
5141 if MapList <> nil then
5142 for a := 0 to High(MapList) do
5143 if MapList[a] = MapName then
5144 begin
5145 // Óäàëÿåì è ñîõðàíÿåì:
5146 WAD.RemoveResource('', MapName);
5147 WAD.SaveTo(WadName);
5148 Break;
5149 end;
5151 WAD.Free();
5152 g_SetFileTime(WadName, time);
5153 end else
5155 if gTempDelete then DeleteFile(WadName);
5156 end;
5158 procedure GameCVars(P: SSArray);
5159 var
5160 a, b: Integer;
5161 stat: TPlayerStatArray;
5162 cmd, s: string;
5163 config: TConfig;
5164 begin
5165 stat := nil;
5166 cmd := LowerCase(P[0]);
5167 if cmd = 'r_showfps' then
5168 begin
5169 if (Length(P) > 1) and
5170 ((P[1] = '1') or (P[1] = '0')) then
5171 gShowFPS := (P[1][1] = '1');
5173 if gShowFPS then
5174 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
5175 else
5176 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
5177 end
5178 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
5179 begin
5180 with gGameSettings do
5181 begin
5182 if (Length(P) > 1) and
5183 ((P[1] = '1') or (P[1] = '0')) then
5184 begin
5185 if (P[1][1] = '1') then
5186 Options := Options or GAME_OPTION_TEAMDAMAGE
5187 else
5188 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
5189 end;
5191 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
5192 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
5193 else
5194 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
5196 if g_Game_IsNet then MH_SEND_GameSettings;
5197 end;
5198 end
5199 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
5200 begin
5201 with gGameSettings do
5202 begin
5203 if (Length(P) > 1) and
5204 ((P[1] = '1') or (P[1] = '0')) then
5205 begin
5206 if (P[1][1] = '1') then
5207 Options := Options or GAME_OPTION_WEAPONSTAY
5208 else
5209 Options := Options and (not GAME_OPTION_WEAPONSTAY);
5210 end;
5212 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
5213 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
5214 else
5215 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
5217 if g_Game_IsNet then MH_SEND_GameSettings;
5218 end;
5219 end
5220 else if cmd = 'g_gamemode' then
5221 begin
5222 a := g_Game_TextToMode(P[1]);
5223 if a = GM_SINGLE then a := GM_COOP;
5224 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
5225 begin
5226 gSwitchGameMode := a;
5227 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
5228 (gState = STATE_INTERSINGLE) then
5229 gSwitchGameMode := GM_SINGLE;
5230 if not gGameOn then
5231 gGameSettings.GameMode := gSwitchGameMode;
5232 end;
5233 if gSwitchGameMode = gGameSettings.GameMode then
5234 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
5235 [g_Game_ModeToText(gGameSettings.GameMode)]))
5236 else
5237 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
5238 [g_Game_ModeToText(gGameSettings.GameMode),
5239 g_Game_ModeToText(gSwitchGameMode)]));
5240 end
5241 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
5242 begin
5243 with gGameSettings do
5244 begin
5245 if (Length(P) > 1) and
5246 ((P[1] = '1') or (P[1] = '0')) then
5247 begin
5248 if (P[1][1] = '1') then
5249 Options := Options or GAME_OPTION_ALLOWEXIT
5250 else
5251 Options := Options and (not GAME_OPTION_ALLOWEXIT);
5252 end;
5254 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
5255 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
5256 else
5257 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
5258 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5260 if g_Game_IsNet then MH_SEND_GameSettings;
5261 end;
5262 end
5263 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
5264 begin
5265 with gGameSettings do
5266 begin
5267 if (Length(P) > 1) and
5268 ((P[1] = '1') or (P[1] = '0')) then
5269 begin
5270 if (P[1][1] = '1') then
5271 Options := Options or GAME_OPTION_MONSTERS
5272 else
5273 Options := Options and (not GAME_OPTION_MONSTERS);
5274 end;
5276 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
5277 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
5278 else
5279 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
5280 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5282 if g_Game_IsNet then MH_SEND_GameSettings;
5283 end;
5284 end
5285 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
5286 begin
5287 with gGameSettings do
5288 begin
5289 if (Length(P) > 1) and
5290 ((P[1] = '1') or (P[1] = '0')) then
5291 begin
5292 if (P[1][1] = '1') then
5293 Options := Options or GAME_OPTION_BOTVSPLAYER
5294 else
5295 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
5296 end;
5298 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
5299 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
5300 else
5301 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
5303 if g_Game_IsNet then MH_SEND_GameSettings;
5304 end;
5305 end
5306 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
5307 begin
5308 with gGameSettings do
5309 begin
5310 if (Length(P) > 1) and
5311 ((P[1] = '1') or (P[1] = '0')) then
5312 begin
5313 if (P[1][1] = '1') then
5314 Options := Options or GAME_OPTION_BOTVSMONSTER
5315 else
5316 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
5317 end;
5319 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
5320 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
5321 else
5322 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
5324 if g_Game_IsNet then MH_SEND_GameSettings;
5325 end;
5326 end
5327 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
5328 begin
5329 if Length(P) > 1 then
5330 begin
5331 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
5332 gGameSettings.WarmupTime := 30
5333 else
5334 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
5335 end;
5337 g_Console_Add(Format(_lc[I_MSG_WARMUP],
5338 [gGameSettings.WarmupTime]));
5339 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5340 end
5341 else if cmd = 'net_interp' then
5342 begin
5343 if (Length(P) > 1) then
5344 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
5346 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
5347 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5348 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
5349 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5350 config.Free();
5351 end
5352 else if cmd = 'net_forceplayerupdate' then
5353 begin
5354 if (Length(P) > 1) and
5355 ((P[1] = '1') or (P[1] = '0')) then
5356 NetForcePlayerUpdate := (P[1][1] = '1');
5358 if NetForcePlayerUpdate then
5359 g_Console_Add('net_forceplayerupdate = 1')
5360 else
5361 g_Console_Add('net_forceplayerupdate = 0');
5362 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5363 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
5364 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5365 config.Free();
5366 end
5367 else if cmd = 'net_predictself' then
5368 begin
5369 if (Length(P) > 1) and
5370 ((P[1] = '1') or (P[1] = '0')) then
5371 NetPredictSelf := (P[1][1] = '1');
5373 if NetPredictSelf then
5374 g_Console_Add('net_predictself = 1')
5375 else
5376 g_Console_Add('net_predictself = 0');
5377 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5378 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
5379 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5380 config.Free();
5381 end
5382 else if cmd = 'sv_name' then
5383 begin
5384 if (Length(P) > 1) and (Length(P[1]) > 0) then
5385 begin
5386 NetServerName := P[1];
5387 if Length(NetServerName) > 64 then
5388 SetLength(NetServerName, 64);
5389 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5390 g_Net_Slist_Update;
5391 end;
5393 g_Console_Add(cmd + ' = "' + NetServerName + '"');
5394 end
5395 else if cmd = 'sv_passwd' then
5396 begin
5397 if (Length(P) > 1) and (Length(P[1]) > 0) then
5398 begin
5399 NetPassword := P[1];
5400 if Length(NetPassword) > 24 then
5401 SetLength(NetPassword, 24);
5402 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5403 g_Net_Slist_Update;
5404 end;
5406 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
5407 end
5408 else if cmd = 'sv_maxplrs' then
5409 begin
5410 if (Length(P) > 1) then
5411 begin
5412 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
5413 if g_Game_IsServer and g_Game_IsNet then
5414 begin
5415 b := 0;
5416 for a := 0 to High(NetClients) do
5417 if NetClients[a].Used then
5418 begin
5419 Inc(b);
5420 if b > NetMaxClients then
5421 begin
5422 s := g_Player_Get(NetClients[a].Player).Name;
5423 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
5424 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5425 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5426 end;
5427 end;
5428 if NetUseMaster then
5429 g_Net_Slist_Update;
5430 end;
5431 end;
5433 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
5434 end
5435 else if cmd = 'sv_public' then
5436 begin
5437 if (Length(P) > 1) then
5438 begin
5439 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
5440 if g_Game_IsServer and g_Game_IsNet then
5441 if NetUseMaster then
5442 begin
5443 if NetMPeer = nil then
5444 if not g_Net_Slist_Connect() then
5445 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
5446 g_Net_Slist_Update();
5447 end
5448 else
5449 if NetMPeer <> nil then
5450 g_Net_Slist_Disconnect();
5451 end;
5453 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
5454 end
5455 else if cmd = 'sv_intertime' then
5456 begin
5457 if (Length(P) > 1) then
5458 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
5460 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
5461 end
5462 else if cmd = 'p1_name' then
5463 begin
5464 if (Length(P) > 1) and gGameOn then
5465 begin
5466 if g_Game_IsClient then
5467 begin
5468 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5469 MC_SEND_PlayerSettings;
5470 end
5471 else
5472 if gPlayer1 <> nil then
5473 begin
5474 gPlayer1.Name := b_Text_Unformat(P[1]);
5475 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5476 end
5477 else
5478 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5479 end;
5480 end
5481 else if cmd = 'p2_name' then
5482 begin
5483 if (Length(P) > 1) and gGameOn then
5484 begin
5485 if g_Game_IsClient then
5486 begin
5487 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5488 MC_SEND_PlayerSettings;
5489 end
5490 else
5491 if gPlayer2 <> nil then
5492 begin
5493 gPlayer2.Name := b_Text_Unformat(P[1]);
5494 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5495 end
5496 else
5497 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5498 end;
5499 end
5500 else if cmd = 'p1_color' then
5501 begin
5502 if Length(P) > 3 then
5503 if g_Game_IsClient then
5504 begin
5505 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5506 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5507 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5508 MC_SEND_PlayerSettings;
5509 end
5510 else
5511 if gPlayer1 <> nil then
5512 begin
5513 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5514 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5515 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5516 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5517 end
5518 else
5519 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5520 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5521 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5522 end
5523 else if (cmd = 'p2_color') and not g_Game_IsNet then
5524 begin
5525 if Length(P) > 3 then
5526 if g_Game_IsClient then
5527 begin
5528 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5529 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5530 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5531 MC_SEND_PlayerSettings;
5532 end
5533 else
5534 if gPlayer2 <> nil then
5535 begin
5536 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5537 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5538 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5539 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5540 end
5541 else
5542 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5543 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5544 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5545 end
5546 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5547 begin
5548 if cmd = 'r_showtime' then
5549 begin
5550 if (Length(P) > 1) and
5551 ((P[1] = '1') or (P[1] = '0')) then
5552 gShowTime := (P[1][1] = '1');
5554 if gShowTime then
5555 g_Console_Add(_lc[I_MSG_TIME_ON])
5556 else
5557 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5558 end
5559 else if cmd = 'r_showscore' then
5560 begin
5561 if (Length(P) > 1) and
5562 ((P[1] = '1') or (P[1] = '0')) then
5563 gShowGoals := (P[1][1] = '1');
5565 if gShowGoals then
5566 g_Console_Add(_lc[I_MSG_SCORE_ON])
5567 else
5568 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5569 end
5570 else if cmd = 'r_showstat' then
5571 begin
5572 if (Length(P) > 1) and
5573 ((P[1] = '1') or (P[1] = '0')) then
5574 gShowStat := (P[1][1] = '1');
5576 if gShowStat then
5577 g_Console_Add(_lc[I_MSG_STATS_ON])
5578 else
5579 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5580 end
5581 else if cmd = 'r_showkillmsg' then
5582 begin
5583 if (Length(P) > 1) and
5584 ((P[1] = '1') or (P[1] = '0')) then
5585 gShowKillMsg := (P[1][1] = '1');
5587 if gShowKillMsg then
5588 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5589 else
5590 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5591 end
5592 else if cmd = 'r_showlives' then
5593 begin
5594 if (Length(P) > 1) and
5595 ((P[1] = '1') or (P[1] = '0')) then
5596 gShowLives := (P[1][1] = '1');
5598 if gShowLives then
5599 g_Console_Add(_lc[I_MSG_LIVES_ON])
5600 else
5601 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5602 end
5603 else if cmd = 'r_showspect' then
5604 begin
5605 if (Length(P) > 1) and
5606 ((P[1] = '1') or (P[1] = '0')) then
5607 gSpectHUD := (P[1][1] = '1');
5609 if gSpectHUD then
5610 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5611 else
5612 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5613 end
5614 else if cmd = 'r_showping' then
5615 begin
5616 if (Length(P) > 1) and
5617 ((P[1] = '1') or (P[1] = '0')) then
5618 gShowPing := (P[1][1] = '1');
5620 if gShowPing then
5621 g_Console_Add(_lc[I_MSG_PING_ON])
5622 else
5623 g_Console_Add(_lc[I_MSG_PING_OFF]);
5624 end
5625 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5626 begin
5627 if Length(P) > 1 then
5628 begin
5629 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5630 gGameSettings.GoalLimit := 0
5631 else
5632 begin
5633 b := 0;
5635 if gGameSettings.GameMode = GM_DM then
5636 begin // DM
5637 stat := g_Player_GetStats();
5638 if stat <> nil then
5639 for a := 0 to High(stat) do
5640 if stat[a].Frags > b then
5641 b := stat[a].Frags;
5642 end
5643 else // TDM/CTF
5644 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5646 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5647 end;
5649 if g_Game_IsNet then MH_SEND_GameSettings;
5650 end;
5652 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5653 end
5654 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5655 begin
5656 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5657 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5659 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5660 [gGameSettings.TimeLimit div 3600,
5661 (gGameSettings.TimeLimit div 60) mod 60,
5662 gGameSettings.TimeLimit mod 60]));
5663 if g_Game_IsNet then MH_SEND_GameSettings;
5664 end
5665 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5666 begin
5667 if Length(P) > 1 then
5668 begin
5669 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5670 gGameSettings.MaxLives := 0
5671 else
5672 begin
5673 b := 0;
5674 stat := g_Player_GetStats();
5675 if stat <> nil then
5676 for a := 0 to High(stat) do
5677 if stat[a].Lives > b then
5678 b := stat[a].Lives;
5679 gGameSettings.MaxLives :=
5680 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5681 end;
5682 end;
5684 g_Console_Add(Format(_lc[I_MSG_LIVES],
5685 [gGameSettings.MaxLives]));
5686 if g_Game_IsNet then MH_SEND_GameSettings;
5687 end;
5688 end;
5689 end;
5691 procedure PrintHeapStats();
5692 var
5693 hs: TFPCHeapStatus;
5694 begin
5695 hs := GetFPCHeapStatus();
5696 e_LogWriteLn ('v===== heap status =====v');
5697 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
5698 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
5699 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
5700 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
5701 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
5702 e_LogWriteLn ('^=======================^');
5703 end;
5705 procedure DebugCommands(P: SSArray);
5706 var
5707 a, b: Integer;
5708 cmd: string;
5709 //pt: TDFPoint;
5710 mon: TMonster;
5711 begin
5712 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5713 if {gDebugMode}conIsCheatsEnabled then
5714 begin
5715 cmd := LowerCase(P[0]);
5716 if cmd = 'd_window' then
5717 begin
5718 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5719 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5720 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5721 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5722 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5723 end
5724 else if cmd = 'd_sounds' then
5725 begin
5726 if (Length(P) > 1) and
5727 ((P[1] = '1') or (P[1] = '0')) then
5728 g_Debug_Sounds := (P[1][1] = '1');
5730 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5731 end
5732 else if cmd = 'd_frames' then
5733 begin
5734 if (Length(P) > 1) and
5735 ((P[1] = '1') or (P[1] = '0')) then
5736 g_Debug_Frames := (P[1][1] = '1');
5738 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5739 end
5740 else if cmd = 'd_winmsg' then
5741 begin
5742 if (Length(P) > 1) and
5743 ((P[1] = '1') or (P[1] = '0')) then
5744 g_Debug_WinMsgs := (P[1][1] = '1');
5746 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5747 end
5748 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5749 begin
5750 if (Length(P) > 1) and
5751 ((P[1] = '1') or (P[1] = '0')) then
5752 g_Debug_MonsterOff := (P[1][1] = '1');
5754 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5755 end
5756 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5757 begin
5758 if Length(P) > 1 then
5759 case P[1][1] of
5760 '0': g_debug_BotAIOff := 0;
5761 '1': g_debug_BotAIOff := 1;
5762 '2': g_debug_BotAIOff := 2;
5763 '3': g_debug_BotAIOff := 3;
5764 end;
5766 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5767 end
5768 else if cmd = 'd_monster' then
5769 begin
5770 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5771 if Length(P) < 2 then
5772 begin
5773 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5774 g_Console_Add('ID | Name');
5775 for b := MONSTER_DEMON to MONSTER_MAN do
5776 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5777 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
5778 end else
5779 begin
5780 a := StrToIntDef(P[1], 0);
5781 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5782 a := g_Mons_TypeIdByName(P[1]);
5784 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5785 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5786 else
5787 begin
5788 with gPlayer1.Obj do
5789 begin
5790 mon := g_Monsters_Create(a,
5791 X + Rect.X + (Rect.Width div 2),
5792 Y + Rect.Y + Rect.Height,
5793 gPlayer1.Direction, True);
5794 end;
5795 if (Length(P) > 2) and (mon <> nil) then
5796 begin
5797 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
5798 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
5799 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
5800 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
5801 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
5802 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
5803 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
5804 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
5805 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5806 end;
5807 end;
5808 end;
5809 end
5810 else if (cmd = 'd_health') then
5811 begin
5812 if (Length(P) > 1) and
5813 ((P[1] = '1') or (P[1] = '0')) then
5814 g_debug_HealthBar := (P[1][1] = '1');
5816 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5817 end
5818 else if (cmd = 'd_player') then
5819 begin
5820 if (Length(P) > 1) and
5821 ((P[1] = '1') or (P[1] = '0')) then
5822 g_debug_Player := (P[1][1] = '1');
5824 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5825 end
5826 else if (cmd = 'd_joy') then
5827 begin
5828 for a := 1 to 8 do
5829 g_Console_Add(e_JoystickStateToString(a));
5830 end
5831 else if (cmd = 'd_mem') then
5832 begin
5833 PrintHeapStats();
5834 end;
5835 end
5836 else
5837 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5838 end;
5841 procedure GameCheats(P: SSArray);
5842 var
5843 cmd: string;
5844 f, a: Integer;
5845 plr: TPlayer;
5846 begin
5847 if (not gGameOn) or (not conIsCheatsEnabled) then
5848 begin
5849 g_Console_Add('not available');
5850 exit;
5851 end;
5852 plr := gPlayer1;
5853 if plr = nil then
5854 begin
5855 g_Console_Add('where is the player?!');
5856 exit;
5857 end;
5858 cmd := LowerCase(P[0]);
5859 // god
5860 if cmd = 'god' then
5861 begin
5862 plr.GodMode := not plr.GodMode;
5863 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5864 exit;
5865 end;
5866 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5867 if cmd = 'give' then
5868 begin
5869 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5870 for f := 1 to High(P) do
5871 begin
5872 cmd := LowerCase(P[f]);
5873 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5874 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5875 if cmd = 'exit' then
5876 begin
5877 if gTriggers <> nil then
5878 begin
5879 for a := 0 to High(gTriggers) do
5880 begin
5881 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5882 begin
5883 g_Console_Add('player left the map');
5884 gExitByTrigger := True;
5885 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5886 g_Game_ExitLevel(gTriggers[a].tgcMap);
5887 break;
5888 end;
5889 end;
5890 end;
5891 continue;
5892 end;
5894 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5895 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5896 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5897 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5898 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5900 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5901 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5903 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5904 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;
5906 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5907 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5909 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5910 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5912 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5913 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5915 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5916 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5917 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5919 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5920 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5921 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5922 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;
5923 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5924 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5926 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;
5927 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;
5928 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;
5929 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;
5930 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;
5931 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;
5933 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5934 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;
5936 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;
5937 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;
5939 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5941 if cmd = 'ammo' then
5942 begin
5943 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5944 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5945 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5946 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5947 plr.GiveItem(ITEM_AMMO_FUELCAN);
5948 g_Console_Add('player got some ammo');
5949 continue;
5950 end;
5952 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5953 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5955 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5956 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5958 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5959 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5961 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5962 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5964 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5966 if cmd = 'weapons' then
5967 begin
5968 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5969 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5970 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5971 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5972 plr.GiveItem(ITEM_WEAPON_PLASMA);
5973 plr.GiveItem(ITEM_WEAPON_BFG);
5974 g_Console_Add('player got weapons');
5975 continue;
5976 end;
5978 if cmd = 'keys' then
5979 begin
5980 plr.GiveItem(ITEM_KEY_RED);
5981 plr.GiveItem(ITEM_KEY_GREEN);
5982 plr.GiveItem(ITEM_KEY_BLUE);
5983 g_Console_Add('player got all keys');
5984 continue;
5985 end;
5987 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5988 end;
5989 exit;
5990 end;
5991 // open
5992 if cmd = 'open' then
5993 begin
5994 g_Console_Add('player activated sesame');
5995 g_Triggers_OpenAll();
5996 exit;
5997 end;
5998 // fly
5999 if cmd = 'fly' then
6000 begin
6001 gFly := not gFly;
6002 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
6003 exit;
6004 end;
6005 // noclip
6006 if cmd = 'noclip' then
6007 begin
6008 plr.SwitchNoClip;
6009 g_Console_Add('wall hardeness adjusted');
6010 exit;
6011 end;
6012 // notarget
6013 if cmd = 'notarget' then
6014 begin
6015 plr.NoTarget := not plr.NoTarget;
6016 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
6017 exit;
6018 end;
6019 // noreload
6020 if cmd = 'noreload' then
6021 begin
6022 plr.NoReload := not plr.NoReload;
6023 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
6024 exit;
6025 end;
6026 // speedy
6027 if cmd = 'speedy' then
6028 begin
6029 MAX_RUNVEL := 32-MAX_RUNVEL;
6030 g_Console_Add('speed adjusted');
6031 exit;
6032 end;
6033 // jumpy
6034 if cmd = 'jumpy' then
6035 begin
6036 VEL_JUMP := 30-VEL_JUMP;
6037 g_Console_Add('jump height adjusted');
6038 exit;
6039 end;
6040 // automap
6041 if cmd = 'automap' then
6042 begin
6043 gShowMap := not gShowMap;
6044 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
6045 exit;
6046 end;
6047 // aimline
6048 if cmd = 'aimline' then
6049 begin
6050 gAimLine := not gAimLine;
6051 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
6052 exit;
6053 end;
6054 end;
6056 procedure GameCommands(P: SSArray);
6057 var
6058 a, b: Integer;
6059 s, pw: String;
6060 chstr: string;
6061 cmd: string;
6062 pl: pTNetClient = nil;
6063 plr: TPlayer;
6064 prt: Word;
6065 nm: Boolean;
6066 listen: LongWord;
6067 begin
6068 // Îáùèå êîìàíäû:
6069 cmd := LowerCase(P[0]);
6070 chstr := '';
6071 if (cmd = 'quit') or
6072 (cmd = 'exit') then
6073 begin
6074 g_Game_Free();
6075 g_Game_Quit();
6076 Exit;
6077 end
6078 else if cmd = 'pause' then
6079 begin
6080 if (g_ActiveWindow = nil) then
6081 g_Game_Pause(not gPauseMain);
6082 end
6083 else if cmd = 'endgame' then
6084 gExit := EXIT_SIMPLE
6085 else if cmd = 'restart' then
6086 begin
6087 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
6088 begin
6089 if g_Game_IsClient then
6090 begin
6091 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6092 Exit;
6093 end;
6094 g_Game_Restart();
6095 end else
6096 g_Console_Add(_lc[I_MSG_NOT_GAME]);
6097 end
6098 else if cmd = 'kick' then
6099 begin
6100 if g_Game_IsServer then
6101 begin
6102 if Length(P) < 2 then
6103 begin
6104 g_Console_Add('kick <name>');
6105 Exit;
6106 end;
6107 if P[1] = '' then
6108 begin
6109 g_Console_Add('kick <name>');
6110 Exit;
6111 end;
6113 if g_Game_IsNet then
6114 pl := g_Net_Client_ByName(P[1]);
6115 if (pl <> nil) then
6116 begin
6117 s := g_Net_ClientName_ByID(pl^.ID);
6118 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
6119 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
6120 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
6121 if NetUseMaster then
6122 g_Net_Slist_Update;
6123 end else if gPlayers <> nil then
6124 for a := Low(gPlayers) to High(gPlayers) do
6125 if gPlayers[a] <> nil then
6126 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
6127 begin
6128 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
6129 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
6130 continue;
6131 gPlayers[a].Lives := 0;
6132 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
6133 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
6134 g_Player_Remove(gPlayers[a].UID);
6135 if NetUseMaster then
6136 g_Net_Slist_Update;
6137 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
6138 g_Bot_MixNames();
6139 end;
6140 end else
6141 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6142 end
6143 else if cmd = 'kick_id' then
6144 begin
6145 if g_Game_IsServer and g_Game_IsNet then
6146 begin
6147 if Length(P) < 2 then
6148 begin
6149 g_Console_Add('kick_id <client ID>');
6150 Exit;
6151 end;
6152 if P[1] = '' then
6153 begin
6154 g_Console_Add('kick_id <client ID>');
6155 Exit;
6156 end;
6158 a := StrToIntDef(P[1], 0);
6159 if (NetClients <> nil) and (a <= High(NetClients)) then
6160 begin
6161 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6162 begin
6163 s := g_Net_ClientName_ByID(NetClients[a].ID);
6164 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
6165 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
6166 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
6167 if NetUseMaster then
6168 g_Net_Slist_Update;
6169 end;
6170 end;
6171 end else
6172 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6173 end
6174 else if cmd = 'ban' then
6175 begin
6176 if g_Game_IsServer and g_Game_IsNet then
6177 begin
6178 if Length(P) < 2 then
6179 begin
6180 g_Console_Add('ban <name>');
6181 Exit;
6182 end;
6183 if P[1] = '' then
6184 begin
6185 g_Console_Add('ban <name>');
6186 Exit;
6187 end;
6189 pl := g_Net_Client_ByName(P[1]);
6190 if (pl <> nil) then
6191 begin
6192 s := g_Net_ClientName_ByID(pl^.ID);
6193 g_Net_BanHost(pl^.Peer^.address.host, False);
6194 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
6195 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6196 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6197 if NetUseMaster then
6198 g_Net_Slist_Update;
6199 end else
6200 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6201 end else
6202 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6203 end
6204 else if cmd = 'ban_id' then
6205 begin
6206 if g_Game_IsServer and g_Game_IsNet then
6207 begin
6208 if Length(P) < 2 then
6209 begin
6210 g_Console_Add('ban_id <client ID>');
6211 Exit;
6212 end;
6213 if P[1] = '' then
6214 begin
6215 g_Console_Add('ban_id <client ID>');
6216 Exit;
6217 end;
6219 a := StrToIntDef(P[1], 0);
6220 if (NetClients <> nil) and (a <= High(NetClients)) then
6221 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6222 begin
6223 s := g_Net_ClientName_ByID(NetClients[a].ID);
6224 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
6225 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
6226 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6227 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6228 if NetUseMaster then
6229 g_Net_Slist_Update;
6230 end;
6231 end else
6232 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6233 end
6234 else if cmd = 'permban' then
6235 begin
6236 if g_Game_IsServer and g_Game_IsNet then
6237 begin
6238 if Length(P) < 2 then
6239 begin
6240 g_Console_Add('permban <name>');
6241 Exit;
6242 end;
6243 if P[1] = '' then
6244 begin
6245 g_Console_Add('permban <name>');
6246 Exit;
6247 end;
6249 pl := g_Net_Client_ByName(P[1]);
6250 if (pl <> nil) then
6251 begin
6252 s := g_Net_ClientName_ByID(pl^.ID);
6253 g_Net_BanHost(pl^.Peer^.address.host);
6254 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
6255 g_Net_SaveBanList();
6256 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6257 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6258 if NetUseMaster then
6259 g_Net_Slist_Update;
6260 end else
6261 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6262 end else
6263 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6264 end
6265 else if cmd = 'permban_id' then
6266 begin
6267 if g_Game_IsServer and g_Game_IsNet then
6268 begin
6269 if Length(P) < 2 then
6270 begin
6271 g_Console_Add('permban_id <client ID>');
6272 Exit;
6273 end;
6274 if P[1] = '' then
6275 begin
6276 g_Console_Add('permban_id <client ID>');
6277 Exit;
6278 end;
6280 a := StrToIntDef(P[1], 0);
6281 if (NetClients <> nil) and (a <= High(NetClients)) then
6282 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6283 begin
6284 s := g_Net_ClientName_ByID(NetClients[a].ID);
6285 g_Net_BanHost(NetClients[a].Peer^.address.host);
6286 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
6287 g_Net_SaveBanList();
6288 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6289 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6290 if NetUseMaster then
6291 g_Net_Slist_Update;
6292 end;
6293 end else
6294 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6295 end
6296 else if cmd = 'unban' then
6297 begin
6298 if g_Game_IsServer and g_Game_IsNet then
6299 begin
6300 if Length(P) < 2 then
6301 begin
6302 g_Console_Add('unban <IP Address>');
6303 Exit;
6304 end;
6305 if P[1] = '' then
6306 begin
6307 g_Console_Add('unban <IP Address>');
6308 Exit;
6309 end;
6311 if g_Net_UnbanHost(P[1]) then
6312 begin
6313 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
6314 g_Net_SaveBanList();
6315 end else
6316 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
6317 end else
6318 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6319 end
6320 else if cmd = 'clientlist' then
6321 begin
6322 if g_Game_IsServer and g_Game_IsNet then
6323 begin
6324 b := 0;
6325 if NetClients <> nil then
6326 for a := Low(NetClients) to High(NetClients) do
6327 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6328 begin
6329 plr := g_Player_Get(NetClients[a].Player);
6330 if plr = nil then continue;
6331 Inc(b);
6332 g_Console_Add(Format('#%2d: %-15s | %s', [a,
6333 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
6334 end;
6335 if b = 0 then
6336 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
6337 end else
6338 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6339 end
6340 else if cmd = 'connect' then
6341 begin
6342 if (NetMode = NET_NONE) then
6343 begin
6344 if Length(P) < 2 then
6345 begin
6346 g_Console_Add('connect <IP> [port] [password]');
6347 Exit;
6348 end;
6349 if P[1] = '' then
6350 begin
6351 g_Console_Add('connect <IP> [port] [password]');
6352 Exit;
6353 end;
6355 if Length(P) > 2 then
6356 prt := StrToIntDef(P[2], 25666)
6357 else
6358 prt := 25666;
6360 if Length(P) > 3 then
6361 pw := P[3]
6362 else
6363 pw := '';
6365 g_Game_StartClient(P[1], prt, pw);
6366 end;
6367 end
6368 else if cmd = 'disconnect' then
6369 begin
6370 if (NetMode = NET_CLIENT) then
6371 g_Net_Disconnect();
6372 end
6373 else if cmd = 'reconnect' then
6374 begin
6375 if (NetMode = NET_SERVER) then
6376 Exit;
6378 if (NetMode = NET_CLIENT) then
6379 begin
6380 g_Net_Disconnect();
6381 gExit := EXIT_SIMPLE;
6382 EndGame;
6383 end;
6385 //TODO: Use last successful password to reconnect, instead of ''
6386 g_Game_StartClient(NetClientIP, NetClientPort, '');
6387 end
6388 else if (cmd = 'addbot') or
6389 (cmd = 'bot_add') then
6390 begin
6391 if Length(P) > 1 then
6392 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
6393 else
6394 g_Bot_Add(TEAM_NONE, 2);
6395 end
6396 else if cmd = 'bot_addlist' then
6397 begin
6398 if Length(P) > 1 then
6399 if Length(P) = 2 then
6400 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
6401 else
6402 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
6403 end
6404 else if cmd = 'bot_removeall' then
6405 g_Bot_RemoveAll()
6406 else if cmd = 'chat' then
6407 begin
6408 if g_Game_IsNet then
6409 begin
6410 if Length(P) > 1 then
6411 begin
6412 for a := 1 to High(P) do
6413 chstr := chstr + P[a] + ' ';
6415 if Length(chstr) > 200 then SetLength(chstr, 200);
6417 if Length(chstr) < 1 then
6418 begin
6419 g_Console_Add('chat <text>');
6420 Exit;
6421 end;
6423 chstr := b_Text_Format(chstr);
6424 if g_Game_IsClient then
6425 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
6426 else
6427 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
6428 end
6429 else
6430 g_Console_Add('chat <text>');
6431 end else
6432 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6433 end
6434 else if cmd = 'teamchat' then
6435 begin
6436 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
6437 begin
6438 if Length(P) > 1 then
6439 begin
6440 for a := 1 to High(P) do
6441 chstr := chstr + P[a] + ' ';
6443 if Length(chstr) > 200 then SetLength(chstr, 200);
6445 if Length(chstr) < 1 then
6446 begin
6447 g_Console_Add('teamchat <text>');
6448 Exit;
6449 end;
6451 chstr := b_Text_Format(chstr);
6452 if g_Game_IsClient then
6453 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
6454 else
6455 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
6456 gPlayer1Settings.Team);
6457 end
6458 else
6459 g_Console_Add('teamchat <text>');
6460 end else
6461 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6462 end
6463 else if cmd = 'game' then
6464 begin
6465 if gGameSettings.GameType <> GT_NONE then
6466 begin
6467 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6468 Exit;
6469 end;
6470 if Length(P) = 1 then
6471 begin
6472 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
6473 Exit;
6474 end;
6475 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
6476 P[1] := addWadExtension(P[1]);
6477 if FileExists(MapsDir + P[1]) then
6478 begin
6479 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6480 if Length(P) < 3 then
6481 begin
6482 SetLength(P, 3);
6483 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6484 end;
6486 s := P[1] + ':\' + UpperCase(P[2]);
6488 if g_Map_Exist(MapsDir + s) then
6489 begin
6490 // Çàïóñêàåì ñâîþ èãðó
6491 g_Game_Free();
6492 with gGameSettings do
6493 begin
6494 GameMode := g_Game_TextToMode(gcGameMode);
6495 if gSwitchGameMode <> GM_NONE then
6496 GameMode := gSwitchGameMode;
6497 if GameMode = GM_NONE then GameMode := GM_DM;
6498 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6499 b := 1;
6500 if Length(P) >= 4 then
6501 b := StrToIntDef(P[3], 1);
6502 g_Game_StartCustom(s, GameMode, TimeLimit,
6503 GoalLimit, MaxLives, Options, b);
6504 end;
6505 end
6506 else
6507 if P[2] = '' then
6508 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6509 else
6510 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[2]), P[1]]));
6511 end else
6512 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6513 end
6514 else if cmd = 'host' then
6515 begin
6516 if gGameSettings.GameType <> GT_NONE then
6517 begin
6518 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6519 Exit;
6520 end;
6521 if Length(P) < 4 then
6522 begin
6523 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
6524 Exit;
6525 end;
6526 if not StrToIp(P[1], listen) then
6527 Exit;
6528 prt := StrToIntDef(P[2], 25666);
6530 P[3] := addWadExtension(P[3]);
6531 if FileExists(MapsDir + P[3]) then
6532 begin
6533 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6534 if Length(P) < 5 then
6535 begin
6536 SetLength(P, 5);
6537 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
6538 end;
6540 s := P[3] + ':\' + UpperCase(P[4]);
6542 if g_Map_Exist(MapsDir + s) then
6543 begin
6544 // Çàïóñêàåì ñâîþ èãðó
6545 g_Game_Free();
6546 with gGameSettings do
6547 begin
6548 GameMode := g_Game_TextToMode(gcGameMode);
6549 if gSwitchGameMode <> GM_NONE then
6550 GameMode := gSwitchGameMode;
6551 if GameMode = GM_NONE then GameMode := GM_DM;
6552 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6553 b := 0;
6554 if Length(P) >= 6 then
6555 b := StrToIntDef(P[5], 0);
6556 g_Game_StartServer(s, GameMode, TimeLimit,
6557 GoalLimit, MaxLives, Options, b, listen, prt);
6558 end;
6559 end
6560 else
6561 if P[4] = '' then
6562 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6563 else
6564 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]));
6565 end else
6566 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6567 end
6568 else if cmd = 'map' then
6569 begin
6570 if Length(P) = 1 then
6571 begin
6572 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6573 begin
6574 g_Console_Add(cmd + ' <MAP>');
6575 g_Console_Add(cmd + ' <WAD> [MAP]');
6576 end else
6577 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6578 end else
6579 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6580 begin
6581 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6582 if Length(P) < 3 then
6583 begin
6584 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6585 s := UpperCase(P[1]);
6586 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6587 begin // Êàðòà íàøëàñü
6588 gExitByTrigger := False;
6589 if gGameOn then
6590 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6591 gNextMap := s;
6592 gExit := EXIT_ENDLEVELCUSTOM;
6593 end
6594 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6595 g_Game_ChangeMap(s);
6596 end else
6597 begin
6598 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6599 P[1] := addWadExtension(P[1]);
6600 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6601 if FileExists(MapsDir + P[1]) then
6602 begin
6603 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6604 SetLength(P, 3);
6605 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6607 s := P[1] + ':\' + P[2];
6609 if g_Map_Exist(MapsDir + s) then
6610 begin
6611 gExitByTrigger := False;
6612 if gGameOn then
6613 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6614 gNextMap := s;
6615 gExit := EXIT_ENDLEVELCUSTOM;
6616 end
6617 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6618 g_Game_ChangeMap(s);
6619 end else
6620 if P[2] = '' then
6621 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6622 else
6623 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6624 end else
6625 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6626 end;
6627 end else
6628 begin
6629 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6630 P[1] := addWadExtension(P[1]);
6631 if FileExists(MapsDir + P[1]) then
6632 begin
6633 // Íàøëè WAD ôàéë
6634 P[2] := UpperCase(P[2]);
6635 s := P[1] + ':\' + P[2];
6637 if g_Map_Exist(MapsDir + s) then
6638 begin // Íàøëè êàðòó
6639 gExitByTrigger := False;
6640 if gGameOn then
6641 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6642 gNextMap := s;
6643 gExit := EXIT_ENDLEVELCUSTOM;
6644 end
6645 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6646 g_Game_ChangeMap(s);
6647 end else
6648 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6649 end else
6650 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6651 end;
6652 end else
6653 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6654 end
6655 else if cmd = 'nextmap' then
6656 begin
6657 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6658 g_Console_Add(_lc[I_MSG_NOT_GAME])
6659 else begin
6660 nm := True;
6661 if Length(P) = 1 then
6662 begin
6663 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6664 begin
6665 g_Console_Add(cmd + ' <MAP>');
6666 g_Console_Add(cmd + ' <WAD> [MAP]');
6667 end else begin
6668 nm := False;
6669 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6670 end;
6671 end else
6672 begin
6673 nm := False;
6674 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6675 begin
6676 if Length(P) < 3 then
6677 begin
6678 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6679 s := UpperCase(P[1]);
6680 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6681 begin // Êàðòà íàøëàñü
6682 gExitByTrigger := False;
6683 gNextMap := s;
6684 nm := True;
6685 end else
6686 begin
6687 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6688 P[1] := addWadExtension(P[1]);
6689 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6690 if FileExists(MapsDir + P[1]) then
6691 begin
6692 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6693 SetLength(P, 3);
6694 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6696 s := P[1] + ':\' + P[2];
6698 if g_Map_Exist(MapsDir + s) then
6699 begin // Óñòàíàâëèâàåì êàðòó
6700 gExitByTrigger := False;
6701 gNextMap := s;
6702 nm := True;
6703 end else
6704 if P[2] = '' then
6705 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6706 else
6707 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6708 end else
6709 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6710 end;
6711 end else
6712 begin
6713 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6714 P[1] := addWadExtension(P[1]);
6715 if FileExists(MapsDir + P[1]) then
6716 begin
6717 // Íàøëè WAD ôàéë
6718 P[2] := UpperCase(P[2]);
6719 s := P[1] + ':\' + P[2];
6721 if g_Map_Exist(MapsDir + s) then
6722 begin // Íàøëè êàðòó
6723 gExitByTrigger := False;
6724 gNextMap := s;
6725 nm := True;
6726 end else
6727 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6728 end else
6729 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6730 end;
6731 end else
6732 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6733 end;
6734 if nm then
6735 if gNextMap = '' then
6736 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6737 else
6738 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6739 end;
6740 end
6741 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6742 begin
6743 if not gGameOn then
6744 g_Console_Add(_lc[I_MSG_NOT_GAME])
6745 else
6746 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6747 begin
6748 gExitByTrigger := False;
6749 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6750 if (gNextMap = '') and (gTriggers <> nil) then
6751 for a := 0 to High(gTriggers) do
6752 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6753 begin
6754 gExitByTrigger := True;
6755 //gNextMap := gTriggers[a].Data.MapName;
6756 gNextMap := gTriggers[a].tgcMap;
6757 Break;
6758 end;
6759 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6760 if gNextMap = '' then
6761 gNextMap := g_Game_GetNextMap();
6762 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6763 if not isWadPath(gNextMap) then
6764 s := gGameSettings.WAD + ':\' + gNextMap
6765 else
6766 s := gNextMap;
6767 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6768 if g_Map_Exist(MapsDir + s) then
6769 gExit := EXIT_ENDLEVELCUSTOM
6770 else
6771 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6772 end else
6773 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6774 end
6775 else if (cmd = 'event') then
6776 begin
6777 if (Length(P) <= 1) then
6778 begin
6779 for a := 0 to High(gEvents) do
6780 if gEvents[a].Command = '' then
6781 g_Console_Add(gEvents[a].Name + ' <none>')
6782 else
6783 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6784 Exit;
6785 end;
6786 if (Length(P) = 2) then
6787 begin
6788 for a := 0 to High(gEvents) do
6789 if gEvents[a].Name = P[1] then
6790 if gEvents[a].Command = '' then
6791 g_Console_Add(gEvents[a].Name + ' <none>')
6792 else
6793 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6794 Exit;
6795 end;
6796 for a := 0 to High(gEvents) do
6797 if gEvents[a].Name = P[1] then
6798 begin
6799 gEvents[a].Command := '';
6800 for b := 2 to High(P) do
6801 if Pos(' ', P[b]) = 0 then
6802 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6803 else
6804 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6805 gEvents[a].Command := Trim(gEvents[a].Command);
6806 Exit;
6807 end;
6808 end
6809 else if cmd = 'suicide' then
6810 begin
6811 if gGameOn then
6812 begin
6813 if g_Game_IsClient then
6814 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6815 else
6816 begin
6817 if gPlayer1 <> nil then
6818 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6819 if gPlayer2 <> nil then
6820 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6821 end;
6822 end;
6823 end
6824 // Êîìàíäû Ñâîåé èãðû:
6825 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6826 begin
6827 if cmd = 'bot_addred' then
6828 begin
6829 if Length(P) > 1 then
6830 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6831 else
6832 g_Bot_Add(TEAM_RED, 2);
6833 end
6834 else if cmd = 'bot_addblue' then
6835 begin
6836 if Length(P) > 1 then
6837 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6838 else
6839 g_Bot_Add(TEAM_BLUE, 2);
6840 end
6841 else if cmd = 'spectate' then
6842 begin
6843 if not gGameOn then
6844 Exit;
6845 g_Game_Spectate();
6846 end
6847 else if cmd = 'say' then
6848 begin
6849 if g_Game_IsServer and g_Game_IsNet then
6850 begin
6851 if Length(P) > 1 then
6852 begin
6853 chstr := '';
6854 for a := 1 to High(P) do
6855 chstr := chstr + P[a] + ' ';
6857 if Length(chstr) > 200 then SetLength(chstr, 200);
6859 if Length(chstr) < 1 then
6860 begin
6861 g_Console_Add('say <text>');
6862 Exit;
6863 end;
6865 chstr := b_Text_Format(chstr);
6866 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6867 end
6868 else g_Console_Add('say <text>');
6869 end else
6870 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6871 end
6872 else if cmd = 'tell' then
6873 begin
6874 if g_Game_IsServer and g_Game_IsNet then
6875 begin
6876 if (Length(P) > 2) and (P[1] <> '') then
6877 begin
6878 chstr := '';
6879 for a := 2 to High(P) do
6880 chstr := chstr + P[a] + ' ';
6882 if Length(chstr) > 200 then SetLength(chstr, 200);
6884 if Length(chstr) < 1 then
6885 begin
6886 g_Console_Add('tell <playername> <text>');
6887 Exit;
6888 end;
6890 pl := g_Net_Client_ByName(P[1]);
6891 if pl <> nil then
6892 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6893 else
6894 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6895 end
6896 else g_Console_Add('tell <playername> <text>');
6897 end else
6898 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6899 end
6900 else if (cmd = 'overtime') and not g_Game_IsClient then
6901 begin
6902 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6903 Exit;
6904 // Äîïîëíèòåëüíîå âðåìÿ:
6905 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6907 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6908 [gGameSettings.TimeLimit div 3600,
6909 (gGameSettings.TimeLimit div 60) mod 60,
6910 gGameSettings.TimeLimit mod 60]));
6911 if g_Game_IsNet then MH_SEND_GameSettings;
6912 end
6913 else if (cmd = 'rcon_password') and g_Game_IsClient then
6914 begin
6915 if (Length(P) <= 1) then
6916 g_Console_Add('rcon_password <password>')
6917 else
6918 MC_SEND_RCONPassword(P[1]);
6919 end
6920 else if cmd = 'rcon' then
6921 begin
6922 if g_Game_IsClient then
6923 begin
6924 if Length(P) > 1 then
6925 begin
6926 chstr := '';
6927 for a := 1 to High(P) do
6928 chstr := chstr + P[a] + ' ';
6930 if Length(chstr) > 200 then SetLength(chstr, 200);
6932 if Length(chstr) < 1 then
6933 begin
6934 g_Console_Add('rcon <command>');
6935 Exit;
6936 end;
6938 MC_SEND_RCONCommand(chstr);
6939 end
6940 else g_Console_Add('rcon <command>');
6941 end;
6942 end
6943 else if cmd = 'ready' then
6944 begin
6945 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6946 gLMSRespawnTime := gTime + 100;
6947 end
6948 else if (cmd = 'callvote') and g_Game_IsNet then
6949 begin
6950 if Length(P) > 1 then
6951 begin
6952 chstr := '';
6953 for a := 1 to High(P) do begin
6954 if a > 1 then chstr := chstr + ' ';
6955 chstr := chstr + P[a];
6956 end;
6958 if Length(chstr) > 200 then SetLength(chstr, 200);
6960 if Length(chstr) < 1 then
6961 begin
6962 g_Console_Add('callvote <command>');
6963 Exit;
6964 end;
6966 if g_Game_IsClient then
6967 MC_SEND_Vote(True, chstr)
6968 else
6969 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6970 g_Console_Process('vote', True);
6971 end
6972 else
6973 g_Console_Add('callvote <command>');
6974 end
6975 else if (cmd = 'vote') and g_Game_IsNet then
6976 begin
6977 if g_Game_IsClient then
6978 MC_SEND_Vote(False)
6979 else if gVoteInProgress then
6980 begin
6981 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6982 a := Floor((NetClientCount+1)/2.0) + 1
6983 else
6984 a := Floor(NetClientCount/2.0) + 1;
6985 if gVoted then
6986 begin
6987 Dec(gVoteCount);
6988 gVoted := False;
6989 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6990 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6991 end
6992 else
6993 begin
6994 Inc(gVoteCount);
6995 gVoted := True;
6996 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6997 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6998 g_Game_CheckVote;
6999 end;
7000 end;
7001 end
7002 end;
7003 end;
7005 procedure g_TakeScreenShot();
7006 var
7007 a: Word;
7008 FileName: string;
7009 ssdir, t: string;
7010 st: TStream;
7011 ok: Boolean;
7012 begin
7013 if e_NoGraphics then Exit;
7014 ssdir := GameDir+'/screenshots';
7015 if not findFileCI(ssdir, true) then
7016 begin
7017 // try to create dir
7018 try
7019 CreateDir(ssdir);
7020 except
7021 end;
7022 if not findFileCI(ssdir, true) then exit; // alas
7023 end;
7024 try
7025 for a := 1 to High(Word) do
7026 begin
7027 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
7028 t := FileName;
7029 if findFileCI(t, true) then continue;
7030 if not findFileCI(FileName) then
7031 begin
7032 ok := false;
7033 st := createDiskFile(FileName);
7034 try
7035 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
7036 ok := true;
7037 finally
7038 st.Free();
7039 end;
7040 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
7041 break;
7042 end;
7043 end;
7044 except
7045 end;
7046 end;
7048 procedure g_Game_InGameMenu(Show: Boolean);
7049 begin
7050 if (g_ActiveWindow = nil) and Show then
7051 begin
7052 if gGameSettings.GameType = GT_SINGLE then
7053 g_GUI_ShowWindow('GameSingleMenu')
7054 else
7055 begin
7056 if g_Game_IsClient then
7057 g_GUI_ShowWindow('GameClientMenu')
7058 else
7059 if g_Game_IsNet then
7060 g_GUI_ShowWindow('GameServerMenu')
7061 else
7062 g_GUI_ShowWindow('GameCustomMenu');
7063 end;
7064 g_Sound_PlayEx('MENU_OPEN');
7066 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7067 if (not g_Game_IsNet) then
7068 g_Game_Pause(True);
7069 end
7070 else
7071 if (g_ActiveWindow <> nil) and (not Show) then
7072 begin
7073 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7074 if (not g_Game_IsNet) then
7075 g_Game_Pause(False);
7076 end;
7077 end;
7079 procedure g_Game_Pause (Enable: Boolean);
7080 var
7081 oldPause: Boolean;
7082 begin
7083 if not gGameOn then exit;
7085 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
7087 oldPause := gPause;
7088 gPauseMain := Enable;
7090 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
7091 end;
7093 procedure g_Game_HolmesPause (Enable: Boolean);
7094 var
7095 oldPause: Boolean;
7096 begin
7097 if not gGameOn then exit;
7098 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
7100 oldPause := gPause;
7101 gPauseHolmes := Enable;
7103 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
7104 end;
7106 procedure g_Game_PauseAllSounds(Enable: Boolean);
7107 var
7108 i: Integer;
7109 begin
7110 // Òðèããåðû:
7111 if gTriggers <> nil then
7112 for i := 0 to High(gTriggers) do
7113 with gTriggers[i] do
7114 if (TriggerType = TRIGGER_SOUND) and
7115 (Sound <> nil) and
7116 Sound.IsPlaying() then
7117 begin
7118 Sound.Pause(Enable);
7119 end;
7121 // Çâóêè èãðîêîâ:
7122 if gPlayers <> nil then
7123 for i := 0 to High(gPlayers) do
7124 if gPlayers[i] <> nil then
7125 gPlayers[i].PauseSounds(Enable);
7127 // Ìóçûêà:
7128 if gMusic <> nil then
7129 gMusic.Pause(Enable);
7130 end;
7132 procedure g_Game_StopAllSounds(all: Boolean);
7133 var
7134 i: Integer;
7135 begin
7136 if gTriggers <> nil then
7137 for i := 0 to High(gTriggers) do
7138 with gTriggers[i] do
7139 if (TriggerType = TRIGGER_SOUND) and
7140 (Sound <> nil) then
7141 Sound.Stop();
7143 if gMusic <> nil then
7144 gMusic.Stop();
7146 if all then
7147 e_StopChannels();
7148 end;
7150 procedure g_Game_UpdateTriggerSounds();
7151 var
7152 i: Integer;
7153 begin
7154 if gTriggers <> nil then
7155 for i := 0 to High(gTriggers) do
7156 with gTriggers[i] do
7157 if (TriggerType = TRIGGER_SOUND) and
7158 (Sound <> nil) and
7159 (tgcLocal) and
7160 Sound.IsPlaying() then
7161 begin
7162 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
7163 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
7164 begin
7165 Sound.SetPan(0.5 - tgcPan/255.0);
7166 Sound.SetVolume(tgcVolume/255.0);
7167 end
7168 else
7169 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
7170 end;
7171 end;
7173 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
7174 begin
7175 Result := False;
7176 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
7177 begin
7178 Result := True;
7179 Exit;
7180 end;
7181 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
7182 begin
7183 Result := True;
7184 Exit;
7185 end;
7186 if gSpectMode <> SPECT_PLAYERS then
7187 Exit;
7188 if gSpectPID1 = UID then
7189 begin
7190 Result := True;
7191 Exit;
7192 end;
7193 if gSpectViewTwo and (gSpectPID2 = UID) then
7194 begin
7195 Result := True;
7196 Exit;
7197 end;
7198 end;
7200 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
7201 var
7202 Pl: TPlayer;
7203 begin
7204 Result := False;
7205 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
7206 begin
7207 Result := True;
7208 Exit;
7209 end;
7210 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
7211 begin
7212 Result := True;
7213 Exit;
7214 end;
7215 if gSpectMode <> SPECT_PLAYERS then
7216 Exit;
7217 Pl := g_Player_Get(gSpectPID1);
7218 if (Pl <> nil) and (Pl.Team = Team) then
7219 begin
7220 Result := True;
7221 Exit;
7222 end;
7223 if gSpectViewTwo then
7224 begin
7225 Pl := g_Player_Get(gSpectPID2);
7226 if (Pl <> nil) and (Pl.Team = Team) then
7227 begin
7228 Result := True;
7229 Exit;
7230 end;
7231 end;
7232 end;
7234 procedure g_Game_Message(Msg: string; Time: Word);
7235 begin
7236 MessageLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont);
7237 MessageText := b_Text_Wrap(b_Text_Format(Msg), MessageLineLength);
7238 MessageTime := Time;
7239 end;
7241 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
7242 const
7243 punct: Array[0..13] of String =
7244 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
7245 var
7246 i, j: Integer;
7247 ok: Boolean;
7248 fpText: String;
7250 function IsPunctuation(S: String): Boolean;
7251 var
7252 i: Integer;
7253 begin
7254 Result := False;
7255 if Length(S) <> 1 then
7256 Exit;
7257 for i := Low(punct) to High(punct) do
7258 if S = punct[i] then
7259 begin
7260 Result := True;
7261 break;
7262 end;
7263 end;
7264 function FilterPunctuation(S: String): String;
7265 var
7266 i: Integer;
7267 begin
7268 for i := Low(punct) to High(punct) do
7269 S := StringReplace(S, punct[i], ' ', [rfReplaceAll]);
7270 Result := S;
7271 end;
7272 begin
7273 ok := False;
7275 if gUseChatSounds and Taunt and (gChatSounds <> nil) and (Pos(': ', Text) > 0) then
7276 begin
7277 // remove player name
7278 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
7279 // for FullWord check
7280 Text := toLowerCase1251(' ' + Text + ' ');
7281 fpText := FilterPunctuation(Text);
7283 for i := 0 to Length(gChatSounds) - 1 do
7284 begin
7285 ok := True;
7286 for j := 0 to Length(gChatSounds[i].Tags) - 1 do
7287 begin
7288 if gChatSounds[i].FullWord and (not IsPunctuation(gChatSounds[i].Tags[j])) then
7289 ok := Pos(' ' + gChatSounds[i].Tags[j] + ' ', fpText) > 0
7290 else
7291 ok := Pos(gChatSounds[i].Tags[j], Text) > 0;
7292 if not ok then
7293 break;
7294 end;
7295 if ok then
7296 begin
7297 gChatSounds[i].Sound.Play();
7298 break;
7299 end;
7300 end;
7301 end;
7302 if not ok then
7303 g_Sound_PlayEx('SOUND_GAME_RADIO');
7304 end;
7306 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
7307 var
7308 a: Integer;
7309 begin
7310 case gAnnouncer of
7311 ANNOUNCE_NONE:
7312 Exit;
7313 ANNOUNCE_ME,
7314 ANNOUNCE_MEPLUS:
7315 if not g_Game_IsWatchedPlayer(SpawnerUID) then
7316 Exit;
7317 end;
7318 for a := 0 to 3 do
7319 if goodsnd[a].IsPlaying() then
7320 Exit;
7322 goodsnd[Random(4)].Play();
7323 end;
7325 procedure g_Game_Announce_KillCombo(Param: Integer);
7326 var
7327 UID: Word;
7328 c, n: Byte;
7329 Pl: TPlayer;
7330 Name: String;
7331 begin
7332 UID := Param and $FFFF;
7333 c := Param shr 16;
7334 if c < 2 then
7335 Exit;
7337 Pl := g_Player_Get(UID);
7338 if Pl = nil then
7339 Name := '?'
7340 else
7341 Name := Pl.Name;
7343 case c of
7344 2: begin
7345 n := 0;
7346 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
7347 end;
7348 3: begin
7349 n := 1;
7350 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
7351 end;
7352 4: begin
7353 n := 2;
7354 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
7355 end;
7356 else begin
7357 n := 3;
7358 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
7359 end;
7360 end;
7362 case gAnnouncer of
7363 ANNOUNCE_NONE:
7364 Exit;
7365 ANNOUNCE_ME:
7366 if not g_Game_IsWatchedPlayer(UID) then
7367 Exit;
7368 ANNOUNCE_MEPLUS:
7369 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
7370 Exit;
7371 end;
7373 if killsnd[n].IsPlaying() then
7374 killsnd[n].Stop();
7375 killsnd[n].Play();
7376 end;
7378 procedure g_Game_Announce_BodyKill(SpawnerUID: Word);
7379 var
7380 a: Integer;
7381 begin
7382 case gAnnouncer of
7383 ANNOUNCE_NONE:
7384 Exit;
7385 ANNOUNCE_ME,
7386 ANNOUNCE_MEPLUS:
7387 if not g_Game_IsWatchedPlayer(SpawnerUID) then
7388 Exit;
7389 end;
7390 for a := 0 to 2 do
7391 if hahasnd[a].IsPlaying() then
7392 Exit;
7394 hahasnd[Random(3)].Play();
7395 end;
7397 procedure g_Game_StartVote(Command, Initiator: string);
7398 var
7399 Need: Integer;
7400 begin
7401 if not gVotesEnabled then Exit;
7402 if gGameSettings.GameType <> GT_SERVER then Exit;
7403 if gVoteInProgress or gVotePassed then
7404 begin
7405 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
7406 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
7407 Exit;
7408 end;
7409 gVoteInProgress := True;
7410 gVotePassed := False;
7411 gVoteTimer := gTime + gVoteTimeout * 1000;
7412 gVoteCount := 0;
7413 gVoted := False;
7414 gVoteCommand := Command;
7416 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7417 Need := Floor((NetClientCount+1)/2.0)+1
7418 else
7419 Need := Floor(NetClientCount/2.0)+1;
7420 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
7421 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
7422 end;
7424 procedure g_Game_CheckVote;
7425 var
7426 I, Need: Integer;
7427 begin
7428 if gGameSettings.GameType <> GT_SERVER then Exit;
7429 if not gVoteInProgress then Exit;
7431 if (gTime >= gVoteTimer) then
7432 begin
7433 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7434 Need := Floor((NetClientCount+1)/2.0) + 1
7435 else
7436 Need := Floor(NetClientCount/2.0) + 1;
7437 if gVoteCount >= Need then
7438 begin
7439 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7440 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7441 gVotePassed := True;
7442 gVoteCmdTimer := gTime + 5000;
7443 end
7444 else
7445 begin
7446 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
7447 MH_SEND_VoteEvent(NET_VE_FAILED);
7448 end;
7449 if NetClients <> nil then
7450 for I := Low(NetClients) to High(NetClients) do
7451 if NetClients[i].Used then
7452 NetClients[i].Voted := False;
7453 gVoteInProgress := False;
7454 gVoted := False;
7455 gVoteCount := 0;
7456 end
7457 else
7458 begin
7459 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7460 Need := Floor((NetClientCount+1)/2.0) + 1
7461 else
7462 Need := Floor(NetClientCount/2.0) + 1;
7463 if gVoteCount >= Need then
7464 begin
7465 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7466 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7467 gVoteInProgress := False;
7468 gVotePassed := True;
7469 gVoteCmdTimer := gTime + 5000;
7470 gVoted := False;
7471 gVoteCount := 0;
7472 if NetClients <> nil then
7473 for I := Low(NetClients) to High(NetClients) do
7474 if NetClients[i].Used then
7475 NetClients[i].Voted := False;
7476 end;
7477 end;
7478 end;
7480 procedure g_Game_LoadMapList(FileName: string);
7481 var
7482 ListFile: TextFile;
7483 s: string;
7484 begin
7485 MapList := nil;
7486 MapIndex := -1;
7488 if not FileExists(FileName) then Exit;
7490 AssignFile(ListFile, FileName);
7491 Reset(ListFile);
7492 while not EOF(ListFile) do
7493 begin
7494 ReadLn(ListFile, s);
7496 s := Trim(s);
7497 if s = '' then Continue;
7499 SetLength(MapList, Length(MapList)+1);
7500 MapList[High(MapList)] := s;
7501 end;
7502 CloseFile(ListFile);
7503 end;
7505 procedure g_Game_SetDebugMode();
7506 begin
7507 gDebugMode := True;
7508 // ×èòû (äàæå â ñâîåé èãðå):
7509 gCheats := True;
7510 end;
7512 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
7513 var
7514 i: Word;
7515 begin
7516 if Length(LoadingStat.Msgs) = 0 then
7517 Exit;
7519 with LoadingStat do
7520 begin
7521 if not reWrite then
7522 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
7523 if NextMsg = Length(Msgs) then
7524 begin // scroll
7525 for i := 0 to High(Msgs)-1 do
7526 Msgs[i] := Msgs[i+1];
7527 end
7528 else
7529 Inc(NextMsg);
7530 end else
7531 if NextMsg = 0 then
7532 Inc(NextMsg);
7534 Msgs[NextMsg-1] := Text;
7535 CurValue := 0;
7536 MaxValue := Max;
7537 ShowCount := 0;
7538 PBarWasHere := false;
7539 end;
7541 g_ActiveWindow := nil;
7543 ProcessLoading(true);
7544 end;
7546 procedure g_Game_StepLoading(Value: Integer = -1);
7547 begin
7548 with LoadingStat do
7549 begin
7550 if Value = -1 then
7551 begin
7552 Inc(CurValue);
7553 Inc(ShowCount);
7554 end
7555 else
7556 CurValue := Value;
7557 if (ShowCount > LOADING_SHOW_STEP) or (Value > -1) then
7558 begin
7559 ShowCount := 0;
7560 ProcessLoading();
7561 end;
7562 end;
7563 end;
7565 procedure g_Game_ClearLoading();
7566 var
7567 len: Word;
7568 begin
7569 with LoadingStat do
7570 begin
7571 CurValue := 0;
7572 MaxValue := 0;
7573 ShowCount := 0;
7574 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
7575 if len < 1 then len := 1;
7576 SetLength(Msgs, len);
7577 for len := Low(Msgs) to High(Msgs) do
7578 Msgs[len] := '';
7579 NextMsg := 0;
7580 PBarWasHere := false;
7581 end;
7582 end;
7584 procedure Parse_Params(var pars: TParamStrValues);
7585 var
7586 i: Integer;
7587 s: String;
7588 begin
7589 SetLength(pars, 0);
7590 i := 1;
7591 while i <= ParamCount do
7592 begin
7593 s := ParamStr(i);
7594 if (s[1] = '-') and (Length(s) > 1) then
7595 begin
7596 if (s[2] = '-') and (Length(s) > 2) then
7597 begin // Îäèíî÷íûé ïàðàìåòð
7598 SetLength(pars, Length(pars) + 1);
7599 with pars[High(pars)] do
7600 begin
7601 Name := LowerCase(s);
7602 Value := '+';
7603 end;
7604 end
7605 else
7606 if (i < ParamCount) then
7607 begin // Ïàðàìåòð ñî çíà÷åíèåì
7608 Inc(i);
7609 SetLength(pars, Length(pars) + 1);
7610 with pars[High(pars)] do
7611 begin
7612 Name := LowerCase(s);
7613 Value := LowerCase(ParamStr(i));
7614 end;
7615 end;
7616 end;
7618 Inc(i);
7619 end;
7620 end;
7622 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
7623 var
7624 i: Integer;
7625 begin
7626 Result := '';
7627 for i := 0 to High(pars) do
7628 if pars[i].Name = aName then
7629 begin
7630 Result := pars[i].Value;
7631 Break;
7632 end;
7633 end;
7635 procedure g_Game_Process_Params();
7636 var
7637 pars: TParamStrValues;
7638 map: String;
7639 GMode, n: Byte;
7640 LimT, LimS: Integer;
7641 Opt: LongWord;
7642 Lives: Integer;
7643 s: String;
7644 Port: Integer;
7645 ip: String;
7646 F: TextFile;
7647 begin
7648 Parse_Params(pars);
7650 // Debug mode:
7651 s := Find_Param_Value(pars, '--debug');
7652 if (s <> '') then
7653 begin
7654 g_Game_SetDebugMode();
7655 s := Find_Param_Value(pars, '--netdump');
7656 if (s <> '') then
7657 NetDump := True;
7658 end;
7660 // Connect when game loads
7661 ip := Find_Param_Value(pars, '-connect');
7663 if ip <> '' then
7664 begin
7665 s := Find_Param_Value(pars, '-port');
7666 if (s = '') or not TryStrToInt(s, Port) then
7667 Port := 25666;
7669 s := Find_Param_Value(pars, '-pw');
7671 g_Game_StartClient(ip, Port, s);
7672 Exit;
7673 end;
7675 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7676 if (s <> '') then
7677 begin
7678 gDefaultMegawadStart := s;
7679 end;
7681 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7682 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7683 begin
7684 gDefaultMegawadStart := DF_Default_Megawad_Start;
7685 end;
7687 // Start map when game loads:
7688 map := LowerCase(Find_Param_Value(pars, '-map'));
7689 if isWadPath(map) then
7690 begin
7691 // Game mode:
7692 s := Find_Param_Value(pars, '-gm');
7693 GMode := g_Game_TextToMode(s);
7694 if GMode = GM_NONE then GMode := GM_DM;
7695 if GMode = GM_SINGLE then GMode := GM_COOP;
7697 // Time limit:
7698 s := Find_Param_Value(pars, '-limt');
7699 if (s = '') or (not TryStrToInt(s, LimT)) then
7700 LimT := 0;
7701 if LimT < 0 then
7702 LimT := 0;
7704 // Goal limit:
7705 s := Find_Param_Value(pars, '-lims');
7706 if (s = '') or (not TryStrToInt(s, LimS)) then
7707 LimS := 0;
7708 if LimS < 0 then
7709 LimS := 0;
7711 // Lives limit:
7712 s := Find_Param_Value(pars, '-lives');
7713 if (s = '') or (not TryStrToInt(s, Lives)) then
7714 Lives := 0;
7715 if Lives < 0 then
7716 Lives := 0;
7718 // Options:
7719 s := Find_Param_Value(pars, '-opt');
7720 if (s = '') then
7721 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7722 else
7723 Opt := StrToIntDef(s, 0);
7724 if Opt = 0 then
7725 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7727 // Close after map:
7728 s := Find_Param_Value(pars, '--close');
7729 if (s <> '') then
7730 gMapOnce := True;
7732 // Override map to test:
7733 s := LowerCase(Find_Param_Value(pars, '-testmap'));
7734 if s <> '' then
7735 gTestMap := MapsDir + s;
7737 // Delete test map after play:
7738 s := Find_Param_Value(pars, '--testdelete');
7739 if (s <> '') then
7740 begin
7741 gMapToDelete := MapsDir + map;
7742 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
7743 Halt(1);
7744 end;
7746 // Delete temporary WAD after play:
7747 s := Find_Param_Value(pars, '--tempdelete');
7748 if (s <> '') and (gTestMap <> '') then
7749 begin
7750 gMapToDelete := gTestMap;
7751 gTempDelete := True;
7752 end;
7754 // Number of players:
7755 s := Find_Param_Value(pars, '-pl');
7756 if (s = '') then
7757 n := 1
7758 else
7759 n := StrToIntDef(s, 1);
7761 // Start:
7762 s := Find_Param_Value(pars, '-port');
7763 if (s = '') or not TryStrToInt(s, Port) then
7764 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7765 else
7766 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7767 end;
7769 // Execute script when game loads:
7770 s := Find_Param_Value(pars, '-exec');
7771 if s <> '' then
7772 begin
7773 if not isWadPath(s) then
7774 s := GameDir + '/' + s;
7776 {$I-}
7777 AssignFile(F, s);
7778 Reset(F);
7779 if IOResult <> 0 then
7780 begin
7781 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7782 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7783 CloseFile(F);
7784 Exit;
7785 end;
7786 e_WriteLog('Executing script: ' + s, TMsgType.Notify);
7787 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7789 while not EOF(F) do
7790 begin
7791 ReadLn(F, s);
7792 if IOResult <> 0 then
7793 begin
7794 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7795 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7796 CloseFile(F);
7797 Exit;
7798 end;
7799 if Pos('#', s) <> 1 then // script comment
7800 g_Console_Process(s, True);
7801 end;
7803 CloseFile(F);
7804 {$I+}
7805 end;
7807 SetLength(pars, 0);
7808 end;
7810 begin
7811 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7812 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7813 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7814 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7816 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7817 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7818 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7819 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7821 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7822 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7824 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7825 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7827 {$IFDEF ENABLE_HOLMES}
7828 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7829 {$ENDIF}
7831 conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds, 'ignore level bounds', '', false);
7833 conRegVar('r_scale', @g_dbg_scale, 0.01, 100.0, 'render scale', '', false);
7835 conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
7836 conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
7838 conRegVar('r_smallmap_align_h', @r_smallmap_h, 'halign: 0: left; 1: center; 2: right', 'horizontal aligning of small maps');
7839 conRegVar('r_smallmap_align_v', @r_smallmap_v, 'valign: 0: top; 1: center; 2: bottom', 'vertial aligning of small maps');
7840 end.