DEADSOFTWARE

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