DEADSOFTWARE

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