DEADSOFTWARE

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