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