DEADSOFTWARE

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