DEADSOFTWARE

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