DEADSOFTWARE

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