DEADSOFTWARE

5205699d0aa7ba96a3178ac26dc6b7903c69b60a
[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 ProcessPlayerControls2 (plr: TPlayer; p: Integer; var MoveButton: Byte);
1585 var
1586 time: Word;
1587 strafeDir: Byte;
1588 begin
1589 if (plr = nil) then exit;
1590 if (p = 2) then time := 1000 else time := 1;
1591 strafeDir := MoveButton shr 4;
1592 MoveButton := MoveButton and $0F;
1594 if gPlayerAction[p, ACTION_MOVELEFT] and (not gPlayerAction[p, ACTION_MOVERIGHT]) then
1595 MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1596 else if (not gPlayerAction[p, ACTION_MOVELEFT]) and gPlayerAction[p, ACTION_MOVERIGHT] then
1597 MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1598 else if (not gPlayerAction[p, ACTION_MOVELEFT]) and (not gPlayerAction[p, ACTION_MOVERIGHT]) then
1599 MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1601 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1602 if MoveButton = 1 then
1603 plr.PressKey(KEY_LEFT, time)
1604 else if MoveButton = 2 then
1605 plr.PressKey(KEY_RIGHT, time);
1607 // if we have "strafe" key, turn off old strafe mechanics
1608 if gPlayerAction[p, ACTION_STRAFE] then
1609 begin
1610 // new strafe mechanics
1611 if (strafeDir = 0) then
1612 strafeDir := MoveButton; // start strafing
1613 // now set direction according to strafe (reversed)
1614 if (strafeDir = 2) then
1615 plr.SetDirection(TDirection.D_LEFT)
1616 else if (strafeDir = 1) then
1617 plr.SetDirection(TDirection.D_RIGHT)
1618 end
1619 else
1620 begin
1621 strafeDir := 0; // not strafing anymore
1622 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1623 if (MoveButton = 2) and gPlayerAction[p, ACTION_MOVELEFT] then
1624 plr.SetDirection(TDirection.D_LEFT)
1625 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1626 else if (MoveButton = 1) and gPlayerAction[p, ACTION_MOVERIGHT] then
1627 plr.SetDirection(TDirection.D_RIGHT)
1628 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1629 else if MoveButton <> 0 then
1630 plr.SetDirection(TDirection(MoveButton-1))
1631 end;
1633 // fix movebutton state
1634 MoveButton := MoveButton or (strafeDir shl 4);
1636 // Îñòàëüíûå êëàâèøè:
1637 if gPlayerAction[p, ACTION_MOVEUP] then plr.PressKey(KEY_JUMP, time);
1638 if gPlayerAction[p, ACTION_LOOKUP] then plr.PressKey(KEY_UP, time);
1639 if gPlayerAction[p, ACTION_LOOKDOWN] then plr.PressKey(KEY_DOWN, time);
1640 if gPlayerAction[p, ACTION_ATTACK] then plr.PressKey(KEY_FIRE);
1641 if gPlayerAction[p, ACTION_WEAPNEXT] then plr.PressKey(KEY_NEXTWEAPON);
1642 if gPlayerAction[p, ACTION_WEAPPREV] then plr.PressKey(KEY_PREVWEAPON);
1643 if gPlayerAction[p, ACTION_ACTIVATE] then plr.PressKey(KEY_OPEN);
1645 if gPlayerAction[p, ACTION_WEAP1] then plr.QueueWeaponSwitch(WEAPON_KASTET);
1646 if gPlayerAction[p, ACTION_WEAP2] then plr.QueueWeaponSwitch(WEAPON_SAW);
1647 if gPlayerAction[p, ACTION_WEAP3] then plr.QueueWeaponSwitch(WEAPON_PISTOL);
1648 if gPlayerAction[p, ACTION_WEAP4] then plr.QueueWeaponSwitch(WEAPON_SHOTGUN1);
1649 if gPlayerAction[p, ACTION_WEAP5] then plr.QueueWeaponSwitch(WEAPON_SHOTGUN2);
1650 if gPlayerAction[p, ACTION_WEAP6] then plr.QueueWeaponSwitch(WEAPON_CHAINGUN);
1651 if gPlayerAction[p, ACTION_WEAP7] then plr.QueueWeaponSwitch(WEAPON_ROCKETLAUNCHER);
1652 if gPlayerAction[p, ACTION_WEAP8] then plr.QueueWeaponSwitch(WEAPON_PLASMA);
1653 if gPlayerAction[p, ACTION_WEAP9] then plr.QueueWeaponSwitch(WEAPON_BFG);
1654 if gPlayerAction[p, ACTION_WEAP10] then plr.QueueWeaponSwitch(WEAPON_SUPERPULEMET);
1655 if gPlayerAction[p, ACTION_WEAP11] then plr.QueueWeaponSwitch(WEAPON_FLAMETHROWER);
1657 // HACK: add dynlight here
1658 if gwin_k8_enable_light_experiments then
1659 begin
1660 if e_KeyPressed(IK_F8) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1661 begin
1662 g_playerLight := true;
1663 end;
1664 if e_KeyPressed(IK_F9) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1665 begin
1666 g_playerLight := false;
1667 end;
1668 end;
1670 if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
1671 end;
1673 procedure g_Game_Update();
1674 var
1675 Msg: g_gui.TMessage;
1676 Time: Int64;
1677 a: Byte;
1678 w: Word;
1679 i, b: Integer;
1681 function sendMonsPos (mon: TMonster): Boolean;
1682 begin
1683 result := false; // don't stop
1684 // this will also reset "need-send" flag
1685 if mon.gncNeedSend then
1686 begin
1687 MH_SEND_MonsterPos(mon.UID);
1688 end
1689 else if (mon.MonsterType = MONSTER_BARREL) then
1690 begin
1691 if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1692 end
1693 else if (mon.MonsterState <> MONSTATE_SLEEP) then
1694 begin
1695 if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1696 end;
1697 end;
1699 function sendMonsPosUnexpected (mon: TMonster): Boolean;
1700 begin
1701 result := false; // don't stop
1702 // this will also reset "need-send" flag
1703 if mon.gncNeedSend then MH_SEND_MonsterPos(mon.UID);
1704 end;
1706 var
1707 reliableUpdate: Boolean;
1708 begin
1709 g_ResetDynlights();
1710 framePool.reset();
1712 // Ïîðà âûêëþ÷àòü èãðó:
1713 if gExit = EXIT_QUIT then
1714 Exit;
1715 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1716 if gExit <> 0 then
1717 begin
1718 EndGame();
1719 if gExit = EXIT_QUIT then
1720 Exit;
1721 end;
1723 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî
1724 // no need to, as we'll do it in event handler
1726 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1727 g_Console_Update();
1729 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1730 begin
1731 gExit := EXIT_SIMPLE;
1732 EndGame();
1733 Exit;
1734 end;
1736 case gState of
1737 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1738 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1739 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1740 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1741 begin
1742 if g_Game_IsNet and g_Game_IsServer then
1743 begin
1744 gInterTime := gInterTime + GAME_TICK;
1745 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1746 if a <> gServInterTime then
1747 begin
1748 gServInterTime := a;
1749 MH_SEND_TimeSync(gServInterTime);
1750 end;
1751 end;
1753 if (not g_Game_IsClient) and
1757 e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or
1758 e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN)
1760 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1761 and (g_ActiveWindow = nil)
1763 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1765 then
1766 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1767 g_Game_StopAllSounds(True);
1769 if gMapOnce then // Ýòî áûë òåñò
1770 gExit := EXIT_SIMPLE
1771 else
1772 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1773 g_Game_ChangeMap(gNextMap)
1774 else // Ñëåäóþùåé êàðòû íåò
1775 begin
1776 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1777 begin
1778 // Âûõîä â ãëàâíîå ìåíþ:
1779 g_Game_Free;
1780 g_GUI_ShowWindow('MainMenu');
1781 gMusic.SetByName('MUSIC_MENU');
1782 gMusic.Play();
1783 gState := STATE_MENU;
1784 end else
1785 begin
1786 // Ôèíàëüíàÿ êàðòèíêà:
1787 g_Game_ExecuteEvent('onwadend');
1788 g_Game_Free();
1789 if not gMusic.SetByName('MUSIC_endmus') then
1790 gMusic.SetByName('MUSIC_STDENDMUS');
1791 gMusic.Play();
1792 gState := STATE_ENDPIC;
1793 end;
1794 g_Game_ExecuteEvent('ongameend');
1795 end;
1797 Exit;
1798 end;
1800 if gState = STATE_INTERTEXT then
1801 if InterText.counter > 0 then
1802 InterText.counter := InterText.counter - 1;
1803 end;
1805 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1806 begin
1807 if EndingGameCounter = 0 then
1808 begin
1809 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1810 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1811 begin
1812 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1813 begin
1814 g_Game_ExecuteEvent('onwadend');
1815 if not gMusic.SetByName('MUSIC_endmus') then
1816 gMusic.SetByName('MUSIC_STDENDMUS');
1817 end
1818 else
1819 gMusic.SetByName('MUSIC_ROUNDMUS');
1821 gMusic.Play();
1822 gState := STATE_INTERCUSTOM;
1823 e_UnpressAllKeys();
1824 end
1825 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1826 begin
1827 gMusic.SetByName('MUSIC_INTERMUS');
1828 gMusic.Play();
1829 gState := STATE_INTERSINGLE;
1830 e_UnpressAllKeys();
1831 end;
1832 g_Game_ExecuteEvent('oninter');
1833 end
1834 else
1835 DecMin(EndingGameCounter, 6, 0);
1836 end;
1838 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1839 begin
1840 if gMapOnce then // Ýòî áûë òåñò
1841 begin
1842 gExit := EXIT_SIMPLE;
1843 Exit;
1844 end;
1845 end;
1847 STATE_SLIST:
1848 g_Serverlist_Control(slCurrent, slTable);
1849 end;
1851 if g_Game_IsNet then
1852 if not gConsoleShow then
1853 if not gChatShow then
1854 begin
1855 if g_ActiveWindow = nil then
1856 begin
1857 if e_KeyPressed(gGameControls.GameControls.Chat) or e_KeyPressed(VK_CHAT) then
1858 g_Console_Chat_Switch(False)
1859 else if (e_KeyPressed(gGameControls.GameControls.TeamChat) or e_KeyPressed(VK_TEAM)) and
1860 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1861 g_Console_Chat_Switch(True);
1862 end;
1863 end else
1864 if not gChatEnter then
1865 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1866 and (not e_KeyPressed(gGameControls.GameControls.TeamChat))
1867 and (not e_KeyPressed(VK_CHAT))
1868 and (not e_KeyPressed(VK_TEAM)) then
1869 gChatEnter := True;
1871 // Ñòàòèñòèêà ïî Tab:
1872 if gGameOn then
1873 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1874 (gGameSettings.GameType <> GT_SINGLE) and
1875 (e_KeyPressed(gGameControls.GameControls.Stat) or e_KeyPressed(VK_STATUS));
1877 // Èãðà èäåò:
1878 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1879 begin
1880 // Âðåìÿ += 28 ìèëëèñåêóíä:
1881 gTime := gTime + GAME_TICK;
1883 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1884 if MessageTime = 0 then
1885 MessageText := '';
1886 if MessageTime > 0 then
1887 MessageTime := MessageTime - 1;
1889 if (g_Game_IsServer) then
1890 begin
1891 // Áûë çàäàí ëèìèò âðåìåíè:
1892 if (gGameSettings.TimeLimit > 0) then
1893 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1894 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1895 g_Game_NextLevel();
1896 Exit;
1897 end;
1899 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1900 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1901 g_Game_RestartRound(gLMSSoftSpawn);
1903 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1904 if gVoteInProgress and (gVoteTimer < gTime) then
1905 g_Game_CheckVote
1906 else if gVotePassed and (gVoteCmdTimer < gTime) then
1907 begin
1908 g_Console_Process(gVoteCommand);
1909 gVoteCommand := '';
1910 gVotePassed := False;
1911 end;
1913 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1914 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1915 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1916 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1917 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1919 // Áûë çàäàí ëèìèò ïîáåä:
1920 if (gGameSettings.GoalLimit > 0) then
1921 begin
1922 b := 0;
1924 if gGameSettings.GameMode = GM_DM then
1925 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1926 for i := 0 to High(gPlayers) do
1927 if gPlayers[i] <> nil then
1928 if gPlayers[i].Frags > b then
1929 b := gPlayers[i].Frags;
1930 end
1931 else
1932 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1933 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1934 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1935 end;
1937 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1938 if b >= gGameSettings.GoalLimit then
1939 begin
1940 g_Game_NextLevel();
1941 Exit;
1942 end;
1943 end;
1945 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1946 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1947 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1948 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1949 begin
1950 //processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1951 //processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1952 ProcessPlayerControls2(gPlayer1, 0, P1MoveButton);
1953 ProcessPlayerControls2(gPlayer2, 1, P2MoveButton);
1954 end // if not console
1955 else
1956 begin
1957 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1958 end;
1959 // process weapon switch queue
1960 end; // if server
1962 // Íàáëþäàòåëü
1963 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1964 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1965 begin
1966 if not gSpectKeyPress then
1967 begin
1968 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)
1969 and (not gSpectAuto) then
1970 begin
1971 // switch spect mode
1972 case gSpectMode of
1973 SPECT_NONE: ; // not spectator
1974 SPECT_STATS,
1975 SPECT_MAPVIEW: Inc(gSpectMode);
1976 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1977 end;
1978 gSpectKeyPress := True;
1979 end;
1980 if (gSpectMode = SPECT_MAPVIEW)
1981 and (not gSpectAuto) then
1982 begin
1983 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1984 gSpectX := Max(gSpectX - gSpectStep, 0);
1985 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1986 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1987 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1988 gSpectY := Max(gSpectY - gSpectStep, 0);
1989 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1990 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1991 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1992 begin
1993 // decrease step
1994 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1995 gSpectKeyPress := True;
1996 end;
1997 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1998 begin
1999 // increase step
2000 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
2001 gSpectKeyPress := True;
2002 end;
2003 end;
2004 if (gSpectMode = SPECT_PLAYERS)
2005 and (not gSpectAuto) then
2006 begin
2007 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
2008 begin
2009 // add second view
2010 gSpectViewTwo := True;
2011 gSpectKeyPress := True;
2012 end;
2013 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
2014 begin
2015 // remove second view
2016 gSpectViewTwo := False;
2017 gSpectKeyPress := True;
2018 end;
2019 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
2020 begin
2021 // prev player (view 1)
2022 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
2023 gSpectKeyPress := True;
2024 end;
2025 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
2026 begin
2027 // next player (view 1)
2028 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
2029 gSpectKeyPress := True;
2030 end;
2031 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
2032 begin
2033 // prev player (view 2)
2034 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
2035 gSpectKeyPress := True;
2036 end;
2037 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
2038 begin
2039 // next player (view 2)
2040 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
2041 gSpectKeyPress := True;
2042 end;
2043 end;
2044 if isKeyPressed(gGameControls.P1Control.KeyFire, gGameControls.P1Control.KeyFire2) then
2045 begin
2046 if (gSpectMode = SPECT_STATS) and (not gSpectAuto) then
2047 begin
2048 gSpectAuto := True;
2049 gSpectAutoNext := 0;
2050 gSpectViewTwo := False;
2051 gSpectKeyPress := True;
2052 end
2053 else
2054 if gSpectAuto then
2055 begin
2056 gSpectMode := SPECT_STATS;
2057 gSpectAuto := False;
2058 gSpectKeyPress := True;
2059 end;
2060 end;
2061 end
2062 else
2063 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
2064 (not isKeyPressed(gGameControls.P1Control.KeyFire, gGameControls.P1Control.KeyFire2)) and
2065 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
2066 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
2067 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
2068 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
2069 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
2070 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
2071 gSpectKeyPress := False;
2073 if gSpectAuto then
2074 begin
2075 if gSpectMode = SPECT_MAPVIEW then
2076 begin
2077 i := Min(Max(gSpectX + gSpectAutoStepX, 0), gMapInfo.Width - gScreenWidth);
2078 if i = gSpectX then
2079 gSpectAutoNext := gTime
2080 else
2081 gSpectX := i;
2082 i := Min(Max(gSpectY + gSpectAutoStepY, 0), gMapInfo.Height - gScreenHeight);
2083 if i = gSpectY then
2084 gSpectAutoNext := gTime
2085 else
2086 gSpectY := i;
2087 end;
2088 if gSpectAutoNext <= gTime then
2089 begin
2090 if gSpectAutoNext > 0 then
2091 begin
2092 gSpectMode := GetRandomSpectMode(gSpectMode);
2093 case gSpectMode of
2094 SPECT_MAPVIEW:
2095 begin
2096 gSpectX := Random(gMapInfo.Width - gScreenWidth);
2097 gSpectY := Random(gMapInfo.Height - gScreenHeight);
2098 gSpectAutoStepX := Random(9) - 4;
2099 gSpectAutoStepY := Random(9) - 4;
2100 if ((gSpectX < 800) and (gSpectAutoStepX < 0)) or
2101 ((gSpectX > gMapInfo.Width - gScreenWidth - 800) and (gSpectAutoStepX > 0)) then
2102 gSpectAutoStepX := gSpectAutoStepX * -1;
2103 if ((gSpectY < 800) and (gSpectAutoStepY < 0)) or
2104 ((gSpectY > gMapInfo.Height - gScreenHeight - 800) and (gSpectAutoStepY > 0)) then
2105 gSpectAutoStepY := gSpectAutoStepY * -1;
2106 end;
2107 SPECT_PLAYERS:
2108 begin
2109 gSpectPID1 := GetActivePlayerID_Random(gSpectPID1);
2110 end;
2111 end;
2112 end;
2113 case gSpectMode of
2114 SPECT_STATS: gSpectAutoNext := gTime + (Random(3) + 5) * 1000;
2115 SPECT_MAPVIEW: gSpectAutoNext := gTime + (Random(4) + 7) * 1000;
2116 SPECT_PLAYERS: gSpectAutoNext := gTime + (Random(7) + 8) * 1000;
2117 end;
2118 end;
2119 end;
2120 end;
2122 // Îáíîâëÿåì âñå îñòàëüíîå:
2123 g_Map_Update();
2124 g_Items_Update();
2125 g_Triggers_Update();
2126 g_Weapon_Update();
2127 g_Monsters_Update();
2128 g_GFX_Update();
2129 g_Player_UpdateAll();
2130 g_Player_UpdatePhysicalObjects();
2132 // server: send newly spawned monsters unconditionally
2133 if (gGameSettings.GameType = GT_SERVER) then
2134 begin
2135 if (Length(gMonstersSpawned) > 0) then
2136 begin
2137 for I := 0 to High(gMonstersSpawned) do MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
2138 SetLength(gMonstersSpawned, 0);
2139 end;
2140 end;
2142 if (gSoundTriggerTime > 8) then
2143 begin
2144 g_Game_UpdateTriggerSounds();
2145 gSoundTriggerTime := 0;
2146 end
2147 else
2148 begin
2149 Inc(gSoundTriggerTime);
2150 end;
2152 if (NetMode = NET_SERVER) then
2153 begin
2154 Inc(NetTimeToUpdate);
2155 Inc(NetTimeToReliable);
2157 // send monster updates
2158 if (NetTimeToReliable >= NetRelupdRate) or (NetTimeToUpdate >= NetUpdateRate) then
2159 begin
2160 // send all monsters (periodic sync)
2161 reliableUpdate := (NetTimeToReliable >= NetRelupdRate);
2163 for I := 0 to High(gPlayers) do
2164 begin
2165 if (gPlayers[I] <> nil) then MH_SEND_PlayerPos(reliableUpdate, gPlayers[I].UID);
2166 end;
2168 g_Mons_ForEach(sendMonsPos);
2170 if reliableUpdate then
2171 begin
2172 NetTimeToReliable := 0;
2173 NetTimeToUpdate := NetUpdateRate;
2174 end
2175 else
2176 begin
2177 NetTimeToUpdate := 0;
2178 end;
2179 end
2180 else
2181 begin
2182 // send only mosters with some unexpected changes
2183 g_Mons_ForEach(sendMonsPosUnexpected);
2184 end;
2186 // send unexpected platform changes
2187 g_Map_NetSendInterestingPanels();
2189 if NetUseMaster then
2190 begin
2191 if gTime >= NetTimeToMaster then
2192 begin
2193 if (NetMHost = nil) or (NetMPeer = nil) then
2194 begin
2195 if not g_Net_Slist_Connect then g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
2196 end;
2198 g_Net_Slist_Update;
2199 NetTimeToMaster := gTime + NetMasterRate;
2200 end;
2201 end;
2202 end
2203 else if (NetMode = NET_CLIENT) then
2204 begin
2205 MC_SEND_PlayerPos();
2206 end;
2207 end; // if gameOn ...
2209 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
2210 if g_ActiveWindow <> nil then
2211 begin
2212 w := e_GetFirstKeyPressed();
2214 if (w <> IK_INVALID) then
2215 begin
2216 Msg.Msg := MESSAGE_DIKEY;
2217 Msg.wParam := w;
2218 g_ActiveWindow.OnMessage(Msg);
2219 end;
2221 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
2222 if g_ActiveWindow <> nil then
2223 g_ActiveWindow.Update();
2225 // Íóæíî ñìåíèòü ðàçðåøåíèå:
2226 if gResolutionChange then
2227 begin
2228 e_WriteLog('Changing resolution', TMsgType.Notify);
2229 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
2230 gResolutionChange := False;
2231 g_ActiveWindow := nil;
2232 end;
2234 // Íóæíî ñìåíèòü ÿçûê:
2235 if gLanguageChange then
2236 begin
2237 //e_WriteLog('Read language file', MSG_NOTIFY);
2238 //g_Language_Load(DataDir + gLanguage + '.txt');
2239 g_Language_Set(gLanguage);
2240 g_Menu_Reset();
2241 gLanguageChange := False;
2242 end;
2243 end;
2245 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
2246 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) or e_KeyPressed(VK_PRINTSCR) then
2247 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
2248 begin
2249 g_TakeScreenShot();
2250 LastScreenShot := GetTimer();
2251 end;
2253 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
2254 if e_KeyPressed(IK_F10) and
2255 gGameOn and
2256 (not gConsoleShow) and
2257 (g_ActiveWindow = nil) then
2258 begin
2259 KeyPress(IK_F10);
2260 end;
2262 Time := GetTimer() {div 1000};
2264 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
2265 if gDelayedEvents <> nil then
2266 for a := 0 to High(gDelayedEvents) do
2267 if gDelayedEvents[a].Pending and
2269 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
2270 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
2271 ) then
2272 begin
2273 case gDelayedEvents[a].DEType of
2274 DE_GLOBEVENT:
2275 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
2276 DE_BFGHIT:
2277 if gGameOn then
2278 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
2279 DE_KILLCOMBO:
2280 if gGameOn then
2281 begin
2282 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
2283 if g_Game_IsNet and g_Game_IsServer then
2284 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
2285 end;
2286 DE_BODYKILL:
2287 if gGameOn then
2288 g_Game_Announce_BodyKill(gDelayedEvents[a].DENum);
2289 end;
2290 gDelayedEvents[a].Pending := False;
2291 end;
2293 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2294 UPSCounter := UPSCounter + 1;
2295 if Time - UPSTime >= 1000 then
2296 begin
2297 UPS := UPSCounter;
2298 UPSCounter := 0;
2299 UPSTime := Time;
2300 end;
2302 if gGameOn then
2303 begin
2304 g_Weapon_AddDynLights();
2305 g_Items_AddDynLights();
2306 end;
2307 end;
2309 procedure g_Game_LoadChatSounds(Resource: string);
2310 var
2311 WAD: TWADFile;
2312 FileName, Snd: string;
2313 p: Pointer;
2314 len, cnt, tags, i, j: Integer;
2315 cfg: TConfig;
2316 begin
2317 FileName := g_ExtractWadName(Resource);
2319 WAD := TWADFile.Create();
2320 WAD.ReadFile(FileName);
2322 if not WAD.GetResource(g_ExtractFilePathName(Resource), p, len) then
2323 begin
2324 gChatSounds := nil;
2325 WAD.Free();
2326 Exit;
2327 end;
2329 cfg := TConfig.CreateMem(p, len);
2330 cnt := cfg.ReadInt('ChatSounds', 'Count', 0);
2332 SetLength(gChatSounds, cnt);
2333 for i := 0 to Length(gChatSounds) - 1 do
2334 begin
2335 gChatSounds[i].Sound := nil;
2336 Snd := Trim(cfg.ReadStr(IntToStr(i), 'Sound', ''));
2337 tags := cfg.ReadInt(IntToStr(i), 'Tags', 0);
2338 if (Snd = '') or (Tags <= 0) then
2339 continue;
2340 g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i), GameWAD+':'+Snd);
2341 gChatSounds[i].Sound := TPlayableSound.Create();
2342 gChatSounds[i].Sound.SetByName('SOUND_CHAT_MACRO' + IntToStr(i));
2343 SetLength(gChatSounds[i].Tags, tags);
2344 for j := 0 to tags - 1 do
2345 gChatSounds[i].Tags[j] := toLowerCase1251(cfg.ReadStr(IntToStr(i), 'Tag' + IntToStr(j), ''));
2346 gChatSounds[i].FullWord := cfg.ReadBool(IntToStr(i), 'FullWord', False);
2347 end;
2349 cfg.Free();
2350 WAD.Free();
2351 end;
2353 procedure g_Game_FreeChatSounds();
2354 var
2355 i: Integer;
2356 begin
2357 for i := 0 to Length(gChatSounds) - 1 do
2358 begin
2359 gChatSounds[i].Sound.Free();
2360 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i));
2361 end;
2362 SetLength(gChatSounds, 0);
2363 gChatSounds := nil;
2364 end;
2366 procedure g_Game_LoadData();
2367 var
2368 wl, hl: Integer;
2369 wr, hr: Integer;
2370 wb, hb: Integer;
2371 wm, hm: Integer;
2372 begin
2373 if DataLoaded then Exit;
2375 e_WriteLog('Loading game data...', TMsgType.Notify);
2377 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2378 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2379 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2380 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2381 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2382 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2383 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
2384 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
2385 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
2386 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
2387 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
2388 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
2389 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2390 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2391 g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD+':TEXTURES\PLRIND');
2393 hasPBarGfx := true;
2394 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false;
2395 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false;
2396 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false;
2397 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false;
2399 if hasPBarGfx then
2400 begin
2401 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2402 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2403 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2404 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2405 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
2406 begin
2407 // yay!
2408 end
2409 else
2410 begin
2411 hasPBarGfx := false;
2412 end;
2413 end;
2415 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2416 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD+':WEAPONS\PUNCH', 64, 64, 4, False);
2417 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD+':WEAPONS\PUNCH_UP', 64, 64, 4, False);
2418 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD+':WEAPONS\PUNCH_DN', 64, 64, 4, False);
2419 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD+':WEAPONS\PUNCHB', 64, 64, 4, False);
2420 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD+':WEAPONS\PUNCHB_UP', 64, 64, 4, False);
2421 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD+':WEAPONS\PUNCHB_DN', 64, 64, 4, False);
2422 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2423 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2424 g_Sound_CreateWADEx('SOUND_GAME_SECRET', GameWAD+':SOUNDS\SECRET');
2425 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2426 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2427 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2428 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2429 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2430 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2431 g_Sound_CreateWADEx('SOUND_GAME_BURNING', GameWAD+':SOUNDS\BURNING');
2432 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2433 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2434 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2435 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2436 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2437 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2438 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2439 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2440 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2441 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2442 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2443 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD+':SOUNDS\MUHAHA1');
2444 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD+':SOUNDS\MUHAHA2');
2445 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD+':SOUNDS\MUHAHA3');
2446 g_Sound_CreateWADEx('SOUND_CTF_GET1', GameWAD+':SOUNDS\GETFLAG1');
2447 g_Sound_CreateWADEx('SOUND_CTF_GET2', GameWAD+':SOUNDS\GETFLAG2');
2448 g_Sound_CreateWADEx('SOUND_CTF_LOST1', GameWAD+':SOUNDS\LOSTFLG1');
2449 g_Sound_CreateWADEx('SOUND_CTF_LOST2', GameWAD+':SOUNDS\LOSTFLG2');
2450 g_Sound_CreateWADEx('SOUND_CTF_RETURN1', GameWAD+':SOUNDS\RETFLAG1');
2451 g_Sound_CreateWADEx('SOUND_CTF_RETURN2', GameWAD+':SOUNDS\RETFLAG2');
2452 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE1', GameWAD+':SOUNDS\CAPFLAG1');
2453 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE2', GameWAD+':SOUNDS\CAPFLAG2');
2455 goodsnd[0] := TPlayableSound.Create();
2456 goodsnd[1] := TPlayableSound.Create();
2457 goodsnd[2] := TPlayableSound.Create();
2458 goodsnd[3] := TPlayableSound.Create();
2460 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2461 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2462 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2463 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2465 killsnd[0] := TPlayableSound.Create();
2466 killsnd[1] := TPlayableSound.Create();
2467 killsnd[2] := TPlayableSound.Create();
2468 killsnd[3] := TPlayableSound.Create();
2470 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2471 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2472 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2473 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2475 hahasnd[0] := TPlayableSound.Create();
2476 hahasnd[1] := TPlayableSound.Create();
2477 hahasnd[2] := TPlayableSound.Create();
2479 hahasnd[0].SetByName('SOUND_ANNOUNCER_MUHAHA1');
2480 hahasnd[1].SetByName('SOUND_ANNOUNCER_MUHAHA2');
2481 hahasnd[2].SetByName('SOUND_ANNOUNCER_MUHAHA3');
2483 sound_get_flag[0] := TPlayableSound.Create();
2484 sound_get_flag[1] := TPlayableSound.Create();
2485 sound_lost_flag[0] := TPlayableSound.Create();
2486 sound_lost_flag[1] := TPlayableSound.Create();
2487 sound_ret_flag[0] := TPlayableSound.Create();
2488 sound_ret_flag[1] := TPlayableSound.Create();
2489 sound_cap_flag[0] := TPlayableSound.Create();
2490 sound_cap_flag[1] := TPlayableSound.Create();
2492 sound_get_flag[0].SetByName('SOUND_CTF_GET1');
2493 sound_get_flag[1].SetByName('SOUND_CTF_GET2');
2494 sound_lost_flag[0].SetByName('SOUND_CTF_LOST1');
2495 sound_lost_flag[1].SetByName('SOUND_CTF_LOST2');
2496 sound_ret_flag[0].SetByName('SOUND_CTF_RETURN1');
2497 sound_ret_flag[1].SetByName('SOUND_CTF_RETURN2');
2498 sound_cap_flag[0].SetByName('SOUND_CTF_CAPTURE1');
2499 sound_cap_flag[1].SetByName('SOUND_CTF_CAPTURE2');
2501 g_Game_LoadChatSounds(GameWAD+':CHATSND\SNDCFG');
2503 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2504 g_Items_LoadData();
2506 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2507 g_Weapon_LoadData();
2509 g_Monsters_LoadData();
2511 DataLoaded := True;
2512 end;
2514 procedure g_Game_FreeData();
2515 begin
2516 if not DataLoaded then Exit;
2518 g_Items_FreeData();
2519 g_Weapon_FreeData();
2520 g_Monsters_FreeData();
2522 e_WriteLog('Releasing game data...', TMsgType.Notify);
2524 g_Texture_Delete('NOTEXTURE');
2525 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2526 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2527 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2528 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2529 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2530 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2531 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2532 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2533 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2534 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2535 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2536 g_Frames_DeleteByName('FRAMES_TELEPORT');
2537 g_Frames_DeleteByName('FRAMES_PUNCH');
2538 g_Frames_DeleteByName('FRAMES_PUNCH_UP');
2539 g_Frames_DeleteByName('FRAMES_PUNCH_DN');
2540 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK');
2541 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP');
2542 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
2543 g_Sound_Delete('SOUND_GAME_TELEPORT');
2544 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2545 g_Sound_Delete('SOUND_GAME_SECRET');
2546 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2547 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2548 g_Sound_Delete('SOUND_GAME_BULK1');
2549 g_Sound_Delete('SOUND_GAME_BULK2');
2550 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2551 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2552 g_Sound_Delete('SOUND_GAME_BURNING');
2553 g_Sound_Delete('SOUND_GAME_SWITCH1');
2554 g_Sound_Delete('SOUND_GAME_SWITCH0');
2556 goodsnd[0].Free();
2557 goodsnd[1].Free();
2558 goodsnd[2].Free();
2559 goodsnd[3].Free();
2561 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2562 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2563 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2564 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2566 killsnd[0].Free();
2567 killsnd[1].Free();
2568 killsnd[2].Free();
2569 killsnd[3].Free();
2571 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2572 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2573 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2574 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2576 hahasnd[0].Free();
2577 hahasnd[1].Free();
2578 hahasnd[2].Free();
2580 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1');
2581 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2');
2582 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3');
2584 sound_get_flag[0].Free();
2585 sound_get_flag[1].Free();
2586 sound_lost_flag[0].Free();
2587 sound_lost_flag[1].Free();
2588 sound_ret_flag[0].Free();
2589 sound_ret_flag[1].Free();
2590 sound_cap_flag[0].Free();
2591 sound_cap_flag[1].Free();
2593 g_Sound_Delete('SOUND_CTF_GET1');
2594 g_Sound_Delete('SOUND_CTF_GET2');
2595 g_Sound_Delete('SOUND_CTF_LOST1');
2596 g_Sound_Delete('SOUND_CTF_LOST2');
2597 g_Sound_Delete('SOUND_CTF_RETURN1');
2598 g_Sound_Delete('SOUND_CTF_RETURN2');
2599 g_Sound_Delete('SOUND_CTF_CAPTURE1');
2600 g_Sound_Delete('SOUND_CTF_CAPTURE2');
2602 g_Game_FreeChatSounds();
2604 DataLoaded := False;
2605 end;
2607 procedure DrawCustomStat();
2608 var
2609 pc, x, y, w, _y,
2610 w1, w2, w3,
2611 t, p, m: Integer;
2612 ww1, hh1: Word;
2613 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2614 s1, s2, topstr: String;
2615 begin
2616 e_TextureFontGetSize(gStdFont, ww2, hh2);
2618 g_ProcessMessages();
2620 if e_KeyPressed(IK_TAB) or e_KeyPressed(VK_STATUS) then
2621 begin
2622 if not gStatsPressed then
2623 begin
2624 gStatsOff := not gStatsOff;
2625 gStatsPressed := True;
2626 end;
2627 end
2628 else
2629 gStatsPressed := False;
2631 if gStatsOff then
2632 begin
2633 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2634 w := (Length(s1) * ww2) div 2;
2635 x := gScreenWidth div 2 - w;
2636 y := 8;
2637 e_TextureFontPrint(x, y, s1, gStdFont);
2638 Exit;
2639 end;
2641 if (gGameSettings.GameMode = GM_COOP) then
2642 begin
2643 if gMissionFailed then
2644 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2645 else
2646 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2647 end
2648 else
2649 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2651 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2652 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2654 if g_Game_IsNet then
2655 begin
2656 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2657 if not gChatShow then
2658 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2659 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2660 end;
2662 if g_Game_IsClient then
2663 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2664 else
2665 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2666 if not gChatShow then
2667 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2668 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2670 x := 32;
2671 y := 16+hh1+16;
2673 w := gScreenWidth-x*2;
2675 w2 := (w-16) div 6;
2676 w3 := w2;
2677 w1 := w-16-w2-w3;
2679 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2680 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2682 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2684 case CustomStat.GameMode of
2685 GM_DM:
2686 begin
2687 if gGameSettings.MaxLives = 0 then
2688 s1 := _lc[I_GAME_DM]
2689 else
2690 s1 := _lc[I_GAME_LMS];
2691 end;
2692 GM_TDM:
2693 begin
2694 if gGameSettings.MaxLives = 0 then
2695 s1 := _lc[I_GAME_TDM]
2696 else
2697 s1 := _lc[I_GAME_TLMS];
2698 end;
2699 GM_CTF: s1 := _lc[I_GAME_CTF];
2700 GM_COOP:
2701 begin
2702 if gGameSettings.MaxLives = 0 then
2703 s1 := _lc[I_GAME_COOP]
2704 else
2705 s1 := _lc[I_GAME_SURV];
2706 end;
2707 else s1 := '';
2708 end;
2710 _y := y+16;
2711 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2712 _y := _y+8;
2714 _y := _y+16;
2715 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2716 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2718 _y := _y+16;
2719 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2720 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2721 (CustomStat.GameTime div 1000 div 60) mod 60,
2722 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2724 pc := Length(CustomStat.PlayerStat);
2725 if pc = 0 then Exit;
2727 if CustomStat.GameMode = GM_COOP then
2728 begin
2729 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2730 _y := _y+32;
2731 s2 := _lc[I_GAME_MONSTERS];
2732 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2733 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2734 _y := _y+16;
2735 s2 := _lc[I_GAME_SECRETS];
2736 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2737 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2738 if gLastMap then
2739 begin
2740 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2741 _y := _y-16;
2742 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2743 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2744 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2745 _y := _y+16;
2746 s2 := _lc[I_GAME_SECRETS_TOTAL];
2747 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2748 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2749 end;
2750 end;
2752 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2753 begin
2754 _y := _y+16+16;
2756 with CustomStat do
2757 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2758 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2759 else s1 := _lc[I_GAME_WIN_DRAW];
2761 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2762 _y := _y+40;
2764 for t := TEAM_RED to TEAM_BLUE do
2765 begin
2766 if t = TEAM_RED then
2767 begin
2768 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2769 gStdFont, 255, 0, 0, 1);
2770 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2771 gStdFont, 255, 0, 0, 1);
2772 r := 255;
2773 g := 0;
2774 b := 0;
2775 end
2776 else
2777 begin
2778 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2779 gStdFont, 0, 0, 255, 1);
2780 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2781 gStdFont, 0, 0, 255, 1);
2782 r := 0;
2783 g := 0;
2784 b := 255;
2785 end;
2787 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2788 _y := _y+24;
2790 for p := 0 to High(CustomStat.PlayerStat) do
2791 if CustomStat.PlayerStat[p].Team = t then
2792 with CustomStat.PlayerStat[p] do
2793 begin
2794 if Spectator then
2795 begin
2796 rr := r div 2;
2797 gg := g div 2;
2798 bb := b div 2;
2799 end
2800 else
2801 begin
2802 rr := r;
2803 gg := g;
2804 bb := b;
2805 end;
2806 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2807 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2808 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2809 _y := _y+24;
2810 end;
2812 _y := _y+16+16;
2813 end;
2814 end
2815 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2816 begin
2817 _y := _y+40;
2818 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2819 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2820 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2822 _y := _y+24;
2823 for p := 0 to High(CustomStat.PlayerStat) do
2824 with CustomStat.PlayerStat[p] do
2825 begin
2826 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2828 if Spectator then
2829 r := 127
2830 else
2831 r := 255;
2833 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2834 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2835 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2836 _y := _y+24;
2837 end;
2838 end;
2839 end;
2841 procedure DrawSingleStat();
2842 var
2843 tm, key_x, val_x, y: Integer;
2844 w1, w2, h: Word;
2845 s1, s2: String;
2847 procedure player_stat(n: Integer);
2848 var
2849 kpm: Real;
2851 begin
2852 // "Kills: # / #":
2853 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2854 s2 := Format(' %d', [gTotalMonsters]);
2856 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2857 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2858 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2859 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2860 s1 := s1 + '/';
2861 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2862 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2864 // "Kills-per-minute: ##.#":
2865 s1 := _lc[I_MENU_INTER_KPM];
2866 if tm > 0 then
2867 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2868 else
2869 kpm := SingleStat.PlayerStat[n].Kills;
2870 s2 := Format(' %.1f', [kpm]);
2872 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2873 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2875 // "Secrets found: # / #":
2876 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2877 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2879 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2880 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2881 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2882 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2883 s1 := s1 + '/';
2884 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2885 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2886 end;
2888 begin
2889 // "Level Complete":
2890 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2891 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2893 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2894 s1 := _lc[I_MENU_INTER_KPM];
2895 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2896 Inc(w1, 16);
2897 s1 := ' 9999.9';
2898 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2900 key_x := (gScreenWidth-w1-w2) div 2;
2901 val_x := key_x + w1;
2903 // "Time: #:##:##":
2904 tm := SingleStat.GameTime div 1000;
2905 s1 := _lc[I_MENU_INTER_TIME];
2906 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2908 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2909 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2911 if SingleStat.TwoPlayers then
2912 begin
2913 // "Player 1":
2914 s1 := _lc[I_MENU_PLAYER_1];
2915 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2916 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2918 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2919 y := 176;
2920 player_stat(0);
2922 // "Player 2":
2923 s1 := _lc[I_MENU_PLAYER_2];
2924 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2925 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2927 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2928 y := 336;
2929 player_stat(1);
2930 end
2931 else
2932 begin
2933 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2934 y := 128;
2935 player_stat(0);
2936 end;
2937 end;
2939 procedure DrawLoadingStat();
2940 procedure drawRect (x, y, w, h: Integer);
2941 begin
2942 if (w < 1) or (h < 1) then exit;
2943 glBegin(GL_QUADS);
2944 glVertex2f(x+0.375, y+0.375);
2945 glVertex2f(x+w+0.375, y+0.375);
2946 glVertex2f(x+w+0.375, y+h+0.375);
2947 glVertex2f(x+0.375, y+h+0.375);
2948 glEnd();
2949 end;
2951 function drawPBar (cur, total: Integer; washere: Boolean): Boolean;
2952 var
2953 rectW, rectH: Integer;
2954 x0, y0: Integer;
2955 wdt: Integer;
2956 wl, hl: Integer;
2957 wr, hr: Integer;
2958 wb, hb: Integer;
2959 wm, hm: Integer;
2960 idl, idr, idb, idm: LongWord;
2961 f, my: Integer;
2962 begin
2963 result := false;
2964 if (total < 1) then exit;
2965 if (cur < 1) then exit; // don't blink
2966 if (not washere) and (cur >= total) then exit; // don't blink
2967 //if (cur < 0) then cur := 0;
2968 //if (cur > total) then cur := total;
2969 result := true;
2971 if (hasPBarGfx) then
2972 begin
2973 g_Texture_Get('UI_GFX_PBAR_LEFT', idl);
2974 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2975 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr);
2976 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2977 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb);
2978 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2979 g_Texture_Get('UI_GFX_PBAR_MARKER', idm);
2980 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2982 //rectW := gScreenWidth-360;
2983 rectW := trunc(624.0*gScreenWidth/1024.0);
2984 rectH := hl;
2986 x0 := (gScreenWidth-rectW) div 2;
2987 y0 := gScreenHeight-rectH-64;
2988 if (y0 < 2) then y0 := 2;
2990 glEnable(GL_SCISSOR_TEST);
2992 // left and right
2993 glScissor(x0, gScreenHeight-y0-rectH, rectW, rectH);
2994 e_DrawSize(idl, x0, y0, 0, true, false, wl, hl);
2995 e_DrawSize(idr, x0+rectW-wr, y0, 0, true, false, wr, hr);
2997 // body
2998 glScissor(x0+wl, gScreenHeight-y0-rectH, rectW-wl-wr, rectH);
2999 f := x0+wl;
3000 while (f < x0+rectW) do
3001 begin
3002 e_DrawSize(idb, f, y0, 0, true, false, wb, hb);
3003 f += wb;
3004 end;
3006 // filled part
3007 wdt := (rectW-wl-wr)*cur div total;
3008 if (wdt > rectW-wl-wr) then wdt := rectW-wr-wr;
3009 if (wdt > 0) then
3010 begin
3011 my := y0; // don't be so smart, ketmar: +(rectH-wm) div 2;
3012 glScissor(x0+wl, gScreenHeight-my-rectH, wdt, hm);
3013 f := x0+wl;
3014 while (wdt > 0) do
3015 begin
3016 e_DrawSize(idm, f, y0, 0, true, false, wm, hm);
3017 f += wm;
3018 wdt -= wm;
3019 end;
3020 end;
3022 glScissor(0, 0, gScreenWidth, gScreenHeight);
3023 end
3024 else
3025 begin
3026 rectW := gScreenWidth-64;
3027 rectH := 16;
3029 x0 := (gScreenWidth-rectW) div 2;
3030 y0 := gScreenHeight-rectH-64;
3031 if (y0 < 2) then y0 := 2;
3033 glDisable(GL_BLEND);
3034 glDisable(GL_TEXTURE_2D);
3036 //glClearColor(0, 0, 0, 0);
3037 //glClear(GL_COLOR_BUFFER_BIT);
3039 glColor4ub(127, 127, 127, 255);
3040 drawRect(x0-2, y0-2, rectW+4, rectH+4);
3042 glColor4ub(0, 0, 0, 255);
3043 drawRect(x0-1, y0-1, rectW+2, rectH+2);
3045 glColor4ub(127, 127, 127, 255);
3046 wdt := rectW*cur div total;
3047 if (wdt > rectW) then wdt := rectW;
3048 drawRect(x0, y0, wdt, rectH);
3049 end;
3050 end;
3052 var
3053 ww, hh: Word;
3054 xx, yy, i: Integer;
3055 s: String;
3056 begin
3057 if (Length(LoadingStat.Msgs) = 0) then exit;
3059 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
3060 yy := (gScreenHeight div 3);
3061 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
3062 xx := (gScreenWidth div 3);
3064 with LoadingStat do
3065 begin
3066 for i := 0 to NextMsg-1 do
3067 begin
3068 if (i = (NextMsg-1)) and (MaxValue > 0) then
3069 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
3070 else
3071 s := Msgs[i];
3073 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
3074 yy := yy + LOADING_INTERLINE;
3075 PBarWasHere := drawPBar(CurValue, MaxValue, PBarWasHere);
3076 end;
3077 end;
3078 end;
3080 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
3081 var
3082 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
3084 function monDraw (mon: TMonster): Boolean;
3085 begin
3086 result := false; // don't stop
3087 with mon do
3088 begin
3089 if alive then
3090 begin
3091 // Ëåâûé âåðõíèé óãîë
3092 aX := Obj.X div ScaleSz + 1;
3093 aY := Obj.Y div ScaleSz + 1;
3094 // Ðàçìåðû
3095 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
3096 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
3097 // Ïðàâûé íèæíèé óãîë
3098 aX2 := aX + aX2 - 1;
3099 aY2 := aY + aY2 - 1;
3100 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
3101 end;
3102 end;
3103 end;
3105 begin
3106 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
3107 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
3108 begin
3109 Scale := 1;
3110 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
3111 ScaleSz := 16 div Scale;
3112 // Ðàçìåðû ìèíè-êàðòû:
3113 aX := max(gMapInfo.Width div ScaleSz, 1);
3114 aY := max(gMapInfo.Height div ScaleSz, 1);
3115 // Ðàìêà êàðòû:
3116 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
3118 if gWalls <> nil then
3119 begin
3120 // Ðèñóåì ñòåíû:
3121 for a := 0 to High(gWalls) do
3122 with gWalls[a] do
3123 if PanelType <> 0 then
3124 begin
3125 // Ëåâûé âåðõíèé óãîë:
3126 aX := X div ScaleSz;
3127 aY := Y div ScaleSz;
3128 // Ðàçìåðû:
3129 aX2 := max(Width div ScaleSz, 1);
3130 aY2 := max(Height div ScaleSz, 1);
3131 // Ïðàâûé íèæíèé óãîë:
3132 aX2 := aX + aX2 - 1;
3133 aY2 := aY + aY2 - 1;
3135 case PanelType of
3136 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
3137 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
3138 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
3139 end;
3140 end;
3141 end;
3142 if gSteps <> nil then
3143 begin
3144 // Ðèñóåì ñòóïåíè:
3145 for a := 0 to High(gSteps) do
3146 with gSteps[a] do
3147 if PanelType <> 0 then
3148 begin
3149 // Ëåâûé âåðõíèé óãîë:
3150 aX := X div ScaleSz;
3151 aY := Y div ScaleSz;
3152 // Ðàçìåðû:
3153 aX2 := max(Width div ScaleSz, 1);
3154 aY2 := max(Height div ScaleSz, 1);
3155 // Ïðàâûé íèæíèé óãîë:
3156 aX2 := aX + aX2 - 1;
3157 aY2 := aY + aY2 - 1;
3159 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
3160 end;
3161 end;
3162 if gLifts <> nil then
3163 begin
3164 // Ðèñóåì ëèôòû:
3165 for a := 0 to High(gLifts) do
3166 with gLifts[a] do
3167 if PanelType <> 0 then
3168 begin
3169 // Ëåâûé âåðõíèé óãîë:
3170 aX := X div ScaleSz;
3171 aY := Y div ScaleSz;
3172 // Ðàçìåðû:
3173 aX2 := max(Width div ScaleSz, 1);
3174 aY2 := max(Height div ScaleSz, 1);
3175 // Ïðàâûé íèæíèé óãîë:
3176 aX2 := aX + aX2 - 1;
3177 aY2 := aY + aY2 - 1;
3179 case LiftType of
3180 LIFTTYPE_UP: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
3181 LIFTTYPE_DOWN: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
3182 LIFTTYPE_LEFT: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
3183 LIFTTYPE_RIGHT: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
3184 end;
3185 end;
3186 end;
3187 if gWater <> nil then
3188 begin
3189 // Ðèñóåì âîäó:
3190 for a := 0 to High(gWater) do
3191 with gWater[a] do
3192 if PanelType <> 0 then
3193 begin
3194 // Ëåâûé âåðõíèé óãîë:
3195 aX := X div ScaleSz;
3196 aY := Y div ScaleSz;
3197 // Ðàçìåðû:
3198 aX2 := max(Width div ScaleSz, 1);
3199 aY2 := max(Height div ScaleSz, 1);
3200 // Ïðàâûé íèæíèé óãîë:
3201 aX2 := aX + aX2 - 1;
3202 aY2 := aY + aY2 - 1;
3204 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
3205 end;
3206 end;
3207 if gAcid1 <> nil then
3208 begin
3209 // Ðèñóåì êèñëîòó 1:
3210 for a := 0 to High(gAcid1) do
3211 with gAcid1[a] do
3212 if PanelType <> 0 then
3213 begin
3214 // Ëåâûé âåðõíèé óãîë:
3215 aX := X div ScaleSz;
3216 aY := Y div ScaleSz;
3217 // Ðàçìåðû:
3218 aX2 := max(Width div ScaleSz, 1);
3219 aY2 := max(Height div ScaleSz, 1);
3220 // Ïðàâûé íèæíèé óãîë:
3221 aX2 := aX + aX2 - 1;
3222 aY2 := aY + aY2 - 1;
3224 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
3225 end;
3226 end;
3227 if gAcid2 <> nil then
3228 begin
3229 // Ðèñóåì êèñëîòó 2:
3230 for a := 0 to High(gAcid2) do
3231 with gAcid2[a] do
3232 if PanelType <> 0 then
3233 begin
3234 // Ëåâûé âåðõíèé óãîë:
3235 aX := X div ScaleSz;
3236 aY := Y div ScaleSz;
3237 // Ðàçìåðû:
3238 aX2 := max(Width div ScaleSz, 1);
3239 aY2 := max(Height div ScaleSz, 1);
3240 // Ïðàâûé íèæíèé óãîë:
3241 aX2 := aX + aX2 - 1;
3242 aY2 := aY + aY2 - 1;
3244 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
3245 end;
3246 end;
3247 if gPlayers <> nil then
3248 begin
3249 // Ðèñóåì èãðîêîâ:
3250 for a := 0 to High(gPlayers) do
3251 if gPlayers[a] <> nil then with gPlayers[a] do
3252 if alive then begin
3253 // Ëåâûé âåðõíèé óãîë:
3254 aX := Obj.X div ScaleSz + 1;
3255 aY := Obj.Y div ScaleSz + 1;
3256 // Ðàçìåðû:
3257 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
3258 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
3259 // Ïðàâûé íèæíèé óãîë:
3260 aX2 := aX + aX2 - 1;
3261 aY2 := aY + aY2 - 1;
3263 if gPlayers[a] = p then
3264 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
3265 else
3266 case Team of
3267 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
3268 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
3269 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
3270 end;
3271 end;
3272 end;
3273 // Ðèñóåì ìîíñòðîâ
3274 g_Mons_ForEach(monDraw);
3275 end;
3276 end;
3279 procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor);
3280 begin
3281 if not hasAmbient then exit;
3282 e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3283 end;
3286 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3287 //FIXME: broken for splitscreen mode
3288 procedure renderDynLightsInternal ();
3289 var
3290 //hasAmbient: Boolean;
3291 //ambColor: TDFColor;
3292 lln: Integer;
3293 lx, ly, lrad: Integer;
3294 scxywh: array[0..3] of GLint;
3295 wassc: Boolean;
3296 begin
3297 if e_NoGraphics then exit;
3299 //TODO: lights should be in separate grid, i think
3300 // but on the other side: grid may be slower for dynlights, as their lifetime is short
3301 if (not gwin_k8_enable_light_experiments) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit;
3303 // rendering mode
3304 //ambColor := gCurrentMap['light_ambient'].rgba;
3305 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3307 { // this will multiply incoming color to alpha from framebuffer
3308 glEnable(GL_BLEND);
3309 glBlendFunc(GL_DST_ALPHA, GL_ONE);
3312 (*
3313 * light rendering: (INVALID!)
3314 * glStencilFunc(GL_EQUAL, 0, $ff);
3315 * for each light:
3316 * glClear(GL_STENCIL_BUFFER_BIT);
3317 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3318 * draw shadow volume into stencil buffer
3319 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3320 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
3321 * turn off blending
3322 * draw color-less quad with light alpha (WARNING! don't touch color!)
3323 * glEnable(GL_BLEND);
3324 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
3325 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
3326 *)
3327 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
3328 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
3330 // setup OpenGL parameters
3331 glStencilMask($FFFFFFFF);
3332 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
3333 glEnable(GL_STENCIL_TEST);
3334 glEnable(GL_SCISSOR_TEST);
3335 glClear(GL_STENCIL_BUFFER_BIT);
3336 glStencilFunc(GL_EQUAL, 0, $ff);
3338 for lln := 0 to g_dynLightCount-1 do
3339 begin
3340 lx := g_dynLights[lln].x;
3341 ly := g_dynLights[lln].y;
3342 lrad := g_dynLights[lln].radius;
3343 if (lrad < 3) then continue;
3345 if (lx-sX+lrad < 0) then continue;
3346 if (ly-sY+lrad < 0) then continue;
3347 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
3348 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
3350 // set scissor to optimize drawing
3351 if (g_dbg_scale = 1.0) then
3352 begin
3353 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
3354 end
3355 else
3356 begin
3357 glScissor(0, 0, gWinSizeX, gWinSizeY);
3358 end;
3359 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3360 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
3361 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3362 // draw extruded panels
3363 glDisable(GL_TEXTURE_2D);
3364 glDisable(GL_BLEND);
3365 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
3366 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
3367 // render light texture
3368 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3369 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
3370 // blend it
3371 glEnable(GL_BLEND);
3372 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3373 glEnable(GL_TEXTURE_2D);
3374 // color and opacity
3375 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
3376 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
3377 glBegin(GL_QUADS);
3378 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
3379 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
3380 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
3381 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
3382 glEnd();
3383 end;
3385 // done
3386 glDisable(GL_STENCIL_TEST);
3387 glDisable(GL_BLEND);
3388 glDisable(GL_SCISSOR_TEST);
3389 //glScissor(0, 0, sWidth, sHeight);
3391 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
3392 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
3393 end;
3396 function fixViewportForScale (): Boolean;
3397 var
3398 nx0, ny0, nw, nh: Integer;
3399 begin
3400 result := false;
3401 if (g_dbg_scale <> 1.0) then
3402 begin
3403 result := true;
3404 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
3405 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
3406 nw := round(sWidth/g_dbg_scale);
3407 nh := round(sHeight/g_dbg_scale);
3408 sX := nx0;
3409 sY := ny0;
3410 sWidth := nw;
3411 sHeight := nh;
3412 end;
3413 end;
3416 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3417 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3418 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
3419 type
3420 TDrawCB = procedure ();
3422 var
3423 hasAmbient: Boolean;
3424 ambColor: TDFColor;
3425 doAmbient: Boolean = false;
3427 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
3428 var
3429 tagmask: Integer;
3430 pan: TPanel;
3431 begin
3432 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
3433 if gdbg_map_use_accel_render then
3434 begin
3435 tagmask := panelTypeToTag(panType);
3436 while (gDrawPanelList.count > 0) do
3437 begin
3438 pan := TPanel(gDrawPanelList.front());
3439 if ((pan.tag and tagmask) = 0) then break;
3440 if doDraw then pan.Draw(doAmbient, ambColor);
3441 gDrawPanelList.popFront();
3442 end;
3443 end
3444 else
3445 begin
3446 if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor);
3447 end;
3448 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3449 end;
3451 procedure drawOther (profname: AnsiString; cb: TDrawCB);
3452 begin
3453 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
3454 if assigned(cb) then cb();
3455 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3456 end;
3458 begin
3459 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('total');
3461 // our accelerated renderer will collect all panels to gDrawPanelList
3462 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3463 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('collect');
3464 if gdbg_map_use_accel_render then
3465 begin
3466 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
3467 end;
3468 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3470 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('skyback');
3471 g_Map_DrawBack(backXOfs, backYOfs);
3472 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3474 if setTransMatrix then
3475 begin
3476 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3477 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
3478 glTranslatef(-sX, -sY, 0);
3479 end;
3481 // rendering mode
3482 ambColor := gCurrentMap['light_ambient'].rgba;
3483 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3486 if hasAmbient then
3487 begin
3488 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3489 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3490 glClear(GL_COLOR_BUFFER_BIT);
3491 end;
3493 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3496 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
3497 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
3498 drawOther('items', @g_Items_Draw);
3499 drawOther('weapons', @g_Weapon_Draw);
3500 drawOther('shells', @g_Player_DrawShells);
3501 drawOther('drawall', @g_Player_DrawAll);
3502 drawOther('corpses', @g_Player_DrawCorpses);
3503 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
3504 drawOther('monsters', @g_Monsters_Draw);
3505 drawOther('itemdrop', @g_Items_DrawDrop);
3506 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
3507 drawOther('gfx', @g_GFX_Draw);
3508 drawOther('flags', @g_Map_DrawFlags);
3509 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
3510 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
3511 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
3512 drawOther('dynlights', @renderDynLightsInternal);
3514 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3515 begin
3516 renderAmbientQuad(hasAmbient, ambColor);
3517 end;
3519 doAmbient := true;
3520 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
3523 if g_debug_HealthBar then
3524 begin
3525 g_Monsters_DrawHealth();
3526 g_Player_DrawHealth();
3527 end;
3529 if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering
3530 end;
3533 procedure DrawMapView(x, y, w, h: Integer);
3535 var
3536 bx, by: Integer;
3537 begin
3538 glPushMatrix();
3540 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
3541 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
3543 sX := x;
3544 sY := y;
3545 sWidth := w;
3546 sHeight := h;
3548 fixViewportForScale();
3549 renderMapInternal(-bx, -by, true);
3551 glPopMatrix();
3552 end;
3555 procedure DrawPlayer(p: TPlayer);
3556 var
3557 px, py, a, b, c, d: Integer;
3558 //R: TRect;
3559 begin
3560 if (p = nil) or (p.FDummy) then
3561 begin
3562 glPushMatrix();
3563 g_Map_DrawBack(0, 0);
3564 glPopMatrix();
3565 Exit;
3566 end;
3568 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3569 if (profileFrameDraw <> nil) then profileFrameDraw.mainBegin(g_profile_frame_draw);
3571 gPlayerDrawn := p;
3573 glPushMatrix();
3575 px := p.GameX + PLAYER_RECT_CX;
3576 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
3578 if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then
3579 begin
3580 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3581 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3583 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3584 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3586 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3587 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3588 begin
3589 // hcenter
3590 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3591 end;
3593 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3594 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3595 begin
3596 // vcenter
3597 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3598 end;
3599 end
3600 else
3601 begin
3602 // scaled, ignore level bounds
3603 a := -px+(gPlayerScreenSize.X div 2);
3604 b := -py+(gPlayerScreenSize.Y div 2);
3605 end;
3607 if p.IncCam <> 0 then
3608 begin
3609 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
3610 begin
3611 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
3612 begin
3613 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
3614 end;
3615 end;
3617 if py < gPlayerScreenSize.Y div 2 then
3618 begin
3619 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
3620 begin
3621 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
3622 end;
3623 end;
3625 if p.IncCam < 0 then
3626 begin
3627 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
3628 end;
3630 if p.IncCam > 0 then
3631 begin
3632 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
3633 end;
3634 end;
3636 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
3637 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
3638 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
3640 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
3641 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
3642 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
3644 sX := -a;
3645 sY := -(b+p.IncCam);
3646 sWidth := gPlayerScreenSize.X;
3647 sHeight := gPlayerScreenSize.Y;
3649 //glTranslatef(a, b+p.IncCam, 0);
3651 //if (p = gPlayer1) and (g_dbg_scale >= 1.0) then g_Holmes_plrViewSize(sWidth, sHeight);
3653 //conwritefln('OLD: (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3654 fixViewportForScale();
3655 //conwritefln(' (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]);
3657 if (g_dbg_scale <> 1.0) and (not g_dbg_ignore_bounds) then
3658 begin
3659 if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth;
3660 if (sY+sHeight > gMapInfo.Height) then sY := gMapInfo.Height-sHeight;
3661 if (sX < 0) then sX := 0;
3662 if (sY < 0) then sY := 0;
3664 if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth));
3665 if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight));
3666 end;
3668 //r_smallmap_h: 0: left; 1: center; 2: right
3669 //r_smallmap_v: 0: top; 1: center; 2: bottom
3670 // horiz small map?
3671 if (gMapInfo.Width = sWidth) then
3672 begin
3673 sX := 0;
3674 end
3675 else if (gMapInfo.Width < sWidth) then
3676 begin
3677 case r_smallmap_h of
3678 1: sX := -((sWidth-gMapInfo.Width) div 2); // center
3679 2: sX := -(sWidth-gMapInfo.Width); // right
3680 else sX := 0; // left
3681 end;
3682 end;
3683 // vert small map?
3684 if (gMapInfo.Height = sHeight) then
3685 begin
3686 sY := 0;
3687 end
3688 else if (gMapInfo.Height < sHeight) then
3689 begin
3690 case r_smallmap_v of
3691 1: sY := -((sHeight-gMapInfo.Height) div 2); // center
3692 2: sY := -(sHeight-gMapInfo.Height); // bottom
3693 else sY := 0; // top
3694 end;
3695 end;
3697 p.viewPortX := sX;
3698 p.viewPortY := sY;
3699 p.viewPortW := sWidth;
3700 p.viewPortH := sHeight;
3702 {$IFDEF ENABLE_HOLMES}
3703 if (p = gPlayer1) then
3704 begin
3705 g_Holmes_plrViewPos(sX, sY);
3706 g_Holmes_plrViewSize(sWidth, sHeight);
3707 end;
3708 {$ENDIF}
3710 renderMapInternal(-c, -d, true);
3712 if (gGameSettings.GameMode <> GM_SINGLE) and gPlayerIndicator then
3713 p.DrawIndicator();
3714 if p.FSpectator then
3715 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3716 p.GameY + PLAYER_RECT_CY - 4,
3717 'X', gStdFont, 255, 255, 255, 1, True);
3719 for a := 0 to High(gCollideMap) do
3720 for b := 0 to High(gCollideMap[a]) do
3721 begin
3722 d := 0;
3723 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3724 d := d + 1;
3725 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3726 d := d + 2;
3728 case d of
3729 1: e_DrawPoint(1, b, a, 200, 200, 200);
3730 2: e_DrawPoint(1, b, a, 64, 64, 255);
3731 3: e_DrawPoint(1, b, a, 255, 0, 255);
3732 end;
3733 end;
3736 glPopMatrix();
3738 p.DrawPain();
3739 p.DrawPickup();
3740 p.DrawRulez();
3741 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3742 if g_Debug_Player then
3743 g_Player_DrawDebug(p);
3744 p.DrawGUI();
3745 end;
3747 procedure drawProfilers ();
3748 var
3749 px: Integer = -1;
3750 py: Integer = -1;
3751 begin
3752 if g_profile_frame_draw and (profileFrameDraw <> nil) then px := px-drawProfiles(px, py, profileFrameDraw);
3753 if g_profile_collision and (profMapCollision <> nil) then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3754 if g_profile_los and (profMonsLOS <> nil) then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3755 end;
3757 procedure g_Game_Draw();
3758 var
3759 ID: DWORD;
3760 w, h: Word;
3761 ww, hh: Byte;
3762 Time: Int64;
3763 back: string;
3764 plView1, plView2: TPlayer;
3765 Split: Boolean;
3766 begin
3767 if gExit = EXIT_QUIT then Exit;
3769 Time := GetTimer() {div 1000};
3770 FPSCounter := FPSCounter+1;
3771 if Time - FPSTime >= 1000 then
3772 begin
3773 FPS := FPSCounter;
3774 FPSCounter := 0;
3775 FPSTime := Time;
3776 end;
3778 if gGameOn or (gState = STATE_FOLD) then
3779 begin
3780 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3781 begin
3782 gSpectMode := SPECT_NONE;
3783 if not gRevertPlayers then
3784 begin
3785 plView1 := gPlayer1;
3786 plView2 := gPlayer2;
3787 end
3788 else
3789 begin
3790 plView1 := gPlayer2;
3791 plView2 := gPlayer1;
3792 end;
3793 end
3794 else
3795 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3796 begin
3797 gSpectMode := SPECT_NONE;
3798 if gPlayer2 = nil then
3799 plView1 := gPlayer1
3800 else
3801 plView1 := gPlayer2;
3802 plView2 := nil;
3803 end
3804 else
3805 begin
3806 plView1 := nil;
3807 plView2 := nil;
3808 end;
3810 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3811 gSpectMode := SPECT_STATS;
3813 if gSpectMode = SPECT_PLAYERS then
3814 if gPlayers <> nil then
3815 begin
3816 plView1 := GetActivePlayer_ByID(gSpectPID1);
3817 if plView1 = nil then
3818 begin
3819 gSpectPID1 := GetActivePlayerID_Next();
3820 plView1 := GetActivePlayer_ByID(gSpectPID1);
3821 end;
3822 if gSpectViewTwo then
3823 begin
3824 plView2 := GetActivePlayer_ByID(gSpectPID2);
3825 if plView2 = nil then
3826 begin
3827 gSpectPID2 := GetActivePlayerID_Next();
3828 plView2 := GetActivePlayer_ByID(gSpectPID2);
3829 end;
3830 end;
3831 end;
3833 if gSpectMode = SPECT_MAPVIEW then
3834 begin
3835 // Ðåæèì ïðîñìîòðà êàðòû
3836 Split := False;
3837 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3838 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3839 gHearPoint1.Active := True;
3840 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3841 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3842 gHearPoint2.Active := False;
3843 end
3844 else
3845 begin
3846 Split := (plView1 <> nil) and (plView2 <> nil);
3848 // Òî÷êè ñëóõà èãðîêîâ
3849 if plView1 <> nil then
3850 begin
3851 gHearPoint1.Active := True;
3852 gHearPoint1.Coords.X := plView1.GameX;
3853 gHearPoint1.Coords.Y := plView1.GameY;
3854 end else
3855 gHearPoint1.Active := False;
3856 if plView2 <> nil then
3857 begin
3858 gHearPoint2.Active := True;
3859 gHearPoint2.Coords.X := plView2.GameX;
3860 gHearPoint2.Coords.Y := plView2.GameY;
3861 end else
3862 gHearPoint2.Active := False;
3864 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3865 gPlayerScreenSize.X := gScreenWidth-196;
3866 if Split then
3867 begin
3868 gPlayerScreenSize.Y := gScreenHeight div 2;
3869 if gScreenHeight mod 2 = 0 then
3870 Dec(gPlayerScreenSize.Y);
3871 end
3872 else
3873 gPlayerScreenSize.Y := gScreenHeight;
3875 if Split then
3876 if gScreenHeight mod 2 = 0 then
3877 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3878 else
3879 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3881 DrawPlayer(plView1);
3882 gPlayer1ScreenCoord.X := sX;
3883 gPlayer1ScreenCoord.Y := sY;
3885 if Split then
3886 begin
3887 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3889 DrawPlayer(plView2);
3890 gPlayer2ScreenCoord.X := sX;
3891 gPlayer2ScreenCoord.Y := sY;
3892 end;
3894 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3896 if Split then
3897 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3898 end;
3900 {$IFDEF ENABLE_HOLMES}
3901 // draw inspector
3902 if (g_holmes_enabled) then g_Holmes_Draw();
3903 {$ENDIF}
3905 if MessageText <> '' then
3906 begin
3907 w := 0;
3908 h := 0;
3909 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3910 if Split then
3911 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3912 (gScreenHeight div 2)-(h div 2), MessageText)
3913 else
3914 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3915 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3916 end;
3918 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3920 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) and (not gSpectAuto) then
3921 begin
3922 // Draw spectator GUI
3923 ww := 0;
3924 hh := 0;
3925 e_TextureFontGetSize(gStdFont, ww, hh);
3926 case gSpectMode of
3927 SPECT_STATS:
3928 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3929 SPECT_MAPVIEW:
3930 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3931 SPECT_PLAYERS:
3932 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3933 end;
3934 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3935 if gSpectMode = SPECT_STATS then
3936 begin
3937 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2)*2, 'Autoview', gStdFont, 255, 255, 255, 1);
3938 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2), '< fire >', gStdFont, 255, 255, 255, 1);
3939 end;
3940 if gSpectMode = SPECT_MAPVIEW then
3941 begin
3942 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3943 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3944 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3945 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3946 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3947 end;
3948 if gSpectMode = SPECT_PLAYERS then
3949 begin
3950 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3951 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3952 if gSpectViewTwo then
3953 begin
3954 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3955 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3956 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3957 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3958 end
3959 else
3960 begin
3961 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3962 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3963 end;
3964 end;
3965 end;
3966 end;
3968 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
3969 begin
3970 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3971 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3973 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3974 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3975 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3976 end;
3978 if not gGameOn then
3979 begin
3980 if (gState = STATE_MENU) then
3981 begin
3982 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3983 begin
3984 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3985 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3986 end;
3987 // F3 at menu will show game loading dialog
3988 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3989 if (g_ActiveWindow <> nil) then
3990 begin
3991 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3992 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3993 end
3994 else
3995 begin
3996 // F3 at titlepic will show game loading dialog
3997 if e_KeyPressed(IK_F3) then
3998 begin
3999 g_Menu_Show_LoadMenu(true);
4000 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4001 end;
4002 end;
4003 end;
4005 if gState = STATE_FOLD then
4006 begin
4007 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
4008 end;
4010 if gState = STATE_INTERCUSTOM then
4011 begin
4012 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
4013 begin
4014 back := 'TEXTURE_endpic';
4015 if not g_Texture_Get(back, ID) then
4016 back := _lc[I_TEXTURE_ENDPIC];
4017 end
4018 else
4019 back := 'INTER';
4021 if g_Texture_Get(back, ID) then
4022 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
4023 else
4024 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
4026 DrawCustomStat();
4028 if g_ActiveWindow <> nil then
4029 begin
4030 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4031 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4032 end;
4033 end;
4035 if gState = STATE_INTERSINGLE then
4036 begin
4037 if EndingGameCounter > 0 then
4038 begin
4039 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
4040 end
4041 else
4042 begin
4043 back := 'INTER';
4045 if g_Texture_Get(back, ID) then
4046 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
4047 else
4048 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
4050 DrawSingleStat();
4052 if g_ActiveWindow <> nil then
4053 begin
4054 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4055 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4056 end;
4057 end;
4058 end;
4060 if gState = STATE_ENDPIC then
4061 begin
4062 ID := DWORD(-1);
4063 if not g_Texture_Get('TEXTURE_endpic', ID) then
4064 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
4066 if ID <> DWORD(-1) then
4067 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
4068 else
4069 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
4071 if g_ActiveWindow <> nil then
4072 begin
4073 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4074 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4075 end;
4076 end;
4078 if gState = STATE_SLIST then
4079 begin
4080 if g_Texture_Get('MENU_BACKGROUND', ID) then
4081 begin
4082 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
4083 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4084 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4085 end;
4086 g_Serverlist_Draw(slCurrent, slTable);
4087 end;
4088 end;
4090 if g_ActiveWindow <> nil then
4091 begin
4092 if gGameOn then
4093 begin
4094 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4095 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4096 end;
4097 g_ActiveWindow.Draw();
4098 end;
4100 g_Console_Draw();
4102 if g_debug_Sounds and gGameOn then
4103 begin
4104 for w := 0 to High(e_SoundsArray) do
4105 for h := 0 to e_SoundsArray[w].nRefs do
4106 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
4107 end;
4109 if gShowFPS then
4110 begin
4111 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
4112 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
4113 end;
4115 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
4116 drawTime(gScreenWidth-72, gScreenHeight-16);
4118 if gGameOn then drawProfilers();
4120 {$IFDEF ENABLE_HOLMES}
4121 g_Holmes_DrawUI();
4122 {$ENDIF}
4124 g_Touch_Draw;
4125 end;
4127 procedure g_Game_Quit();
4128 begin
4129 g_Game_StopAllSounds(True);
4130 gMusic.Free();
4131 g_Game_SaveOptions();
4132 g_Game_FreeData();
4133 g_PlayerModel_FreeData();
4134 g_Texture_DeleteAll();
4135 g_Frames_DeleteAll();
4136 //g_Menu_Free(); //k8: this segfaults after resolution change; who cares?
4138 if NetInitDone then g_Net_Free;
4140 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
4141 if gMapToDelete <> '' then
4142 g_Game_DeleteTestMap();
4144 gExit := EXIT_QUIT;
4145 PushExitEvent();
4146 end;
4148 procedure g_FatalError(Text: String);
4149 begin
4150 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
4151 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning);
4153 gExit := EXIT_SIMPLE;
4154 end;
4156 procedure g_SimpleError(Text: String);
4157 begin
4158 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
4159 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
4160 end;
4162 procedure g_Game_SetupScreenSize();
4163 const
4164 RES_FACTOR = 4.0 / 3.0;
4165 var
4166 s: Single;
4167 rf: Single;
4168 bw, bh: Word;
4169 begin
4170 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4171 gPlayerScreenSize.X := gScreenWidth-196;
4172 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
4173 gPlayerScreenSize.Y := gScreenHeight div 2
4174 else
4175 gPlayerScreenSize.Y := gScreenHeight;
4177 // Ðàçìåð çàäíåãî ïëàíà:
4178 if BackID <> DWORD(-1) then
4179 begin
4180 s := SKY_STRETCH;
4181 if (gScreenWidth*s > gMapInfo.Width) or
4182 (gScreenHeight*s > gMapInfo.Height) then
4183 begin
4184 gBackSize.X := gScreenWidth;
4185 gBackSize.Y := gScreenHeight;
4186 end
4187 else
4188 begin
4189 e_GetTextureSize(BackID, @bw, @bh);
4190 rf := Single(bw) / Single(bh);
4191 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
4192 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
4193 s := Max(gScreenWidth / bw, gScreenHeight / bh);
4194 if (s < 1.0) then s := 1.0;
4195 gBackSize.X := Round(bw*s);
4196 gBackSize.Y := Round(bh*s);
4197 end;
4198 end;
4199 end;
4201 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
4202 begin
4203 g_Window_SetSize(newWidth, newHeight, nowFull);
4204 end;
4206 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
4207 begin
4208 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
4209 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
4210 Exit;
4211 if gPlayer1 = nil then
4212 begin
4213 if g_Game_IsClient then
4214 begin
4215 if NetPlrUID1 > -1 then
4216 begin
4217 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
4218 gPlayer1 := g_Player_Get(NetPlrUID1);
4219 end;
4220 Exit;
4221 end;
4223 if not (Team in [TEAM_RED, TEAM_BLUE]) then
4224 Team := gPlayer1Settings.Team;
4226 // Ñîçäàíèå ïåðâîãî èãðîêà:
4227 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4228 gPlayer1Settings.Color,
4229 Team, False));
4230 if gPlayer1 = nil then
4231 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
4232 else
4233 begin
4234 gPlayer1.Name := gPlayer1Settings.Name;
4235 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
4236 if g_Game_IsServer and g_Game_IsNet then
4237 MH_SEND_PlayerCreate(gPlayer1.UID);
4238 gPlayer1.Respawn(False, True);
4240 if g_Game_IsNet and NetUseMaster then
4241 g_Net_Slist_Update;
4242 end;
4244 Exit;
4245 end;
4246 if gPlayer2 = nil then
4247 begin
4248 if g_Game_IsClient then
4249 begin
4250 if NetPlrUID2 > -1 then
4251 gPlayer2 := g_Player_Get(NetPlrUID2);
4252 Exit;
4253 end;
4255 if not (Team in [TEAM_RED, TEAM_BLUE]) then
4256 Team := gPlayer2Settings.Team;
4258 // Ñîçäàíèå âòîðîãî èãðîêà:
4259 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4260 gPlayer2Settings.Color,
4261 Team, False));
4262 if gPlayer2 = nil then
4263 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
4264 else
4265 begin
4266 gPlayer2.Name := gPlayer2Settings.Name;
4267 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
4268 if g_Game_IsServer and g_Game_IsNet then
4269 MH_SEND_PlayerCreate(gPlayer2.UID);
4270 gPlayer2.Respawn(False, True);
4272 if g_Game_IsNet and NetUseMaster then
4273 g_Net_Slist_Update;
4274 end;
4276 Exit;
4277 end;
4278 end;
4280 procedure g_Game_RemovePlayer();
4281 var
4282 Pl: TPlayer;
4283 begin
4284 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
4285 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
4286 Exit;
4287 Pl := gPlayer2;
4288 if Pl <> nil then
4289 begin
4290 if g_Game_IsServer then
4291 begin
4292 Pl.Lives := 0;
4293 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4294 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
4295 g_Player_Remove(Pl.UID);
4297 if g_Game_IsNet and NetUseMaster then
4298 g_Net_Slist_Update;
4299 end else
4300 gPlayer2 := nil;
4301 Exit;
4302 end;
4303 Pl := gPlayer1;
4304 if Pl <> nil then
4305 begin
4306 if g_Game_IsServer then
4307 begin
4308 Pl.Lives := 0;
4309 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4310 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
4311 g_Player_Remove(Pl.UID);
4313 if g_Game_IsNet and NetUseMaster then
4314 g_Net_Slist_Update;
4315 end else
4316 begin
4317 gPlayer1 := nil;
4318 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
4319 end;
4320 Exit;
4321 end;
4322 end;
4324 procedure g_Game_Spectate();
4325 begin
4326 g_Game_RemovePlayer();
4327 if gPlayer1 <> nil then
4328 g_Game_RemovePlayer();
4329 end;
4331 procedure g_Game_SpectateCenterView();
4332 begin
4333 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
4334 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
4335 end;
4337 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
4338 var
4339 i, nPl: Integer;
4340 tmps: AnsiString;
4341 begin
4342 g_Game_Free();
4344 e_WriteLog('Starting singleplayer game...', TMsgType.Notify);
4346 g_Game_ClearLoading();
4348 // Íàñòðîéêè èãðû:
4349 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
4350 gAimLine := False;
4351 gShowMap := False;
4352 gGameSettings.GameType := GT_SINGLE;
4353 gGameSettings.MaxLives := 0;
4354 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
4355 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
4356 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
4357 gSwitchGameMode := GM_SINGLE;
4359 g_Game_ExecuteEvent('ongamestart');
4361 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4362 g_Game_SetupScreenSize();
4364 // Ñîçäàíèå ïåðâîãî èãðîêà:
4365 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4366 gPlayer1Settings.Color,
4367 gPlayer1Settings.Team, False));
4368 if gPlayer1 = nil then
4369 begin
4370 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4371 Exit;
4372 end;
4374 gPlayer1.Name := gPlayer1Settings.Name;
4375 nPl := 1;
4377 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
4378 if TwoPlayers then
4379 begin
4380 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4381 gPlayer2Settings.Color,
4382 gPlayer2Settings.Team, False));
4383 if gPlayer2 = nil then
4384 begin
4385 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4386 Exit;
4387 end;
4389 gPlayer2.Name := gPlayer2Settings.Name;
4390 Inc(nPl);
4391 end;
4393 // Çàãðóçêà è çàïóñê êàðòû:
4394 if not g_Game_StartMap(MAP, True) then
4395 begin
4396 if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP;
4397 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps]));
4398 Exit;
4399 end;
4401 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4402 g_Player_Init();
4404 // Ñîçäàåì áîòîâ:
4405 for i := nPl+1 to nPlayers do
4406 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4407 end;
4409 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
4410 TimeLimit, GoalLimit: Word;
4411 MaxLives: Byte;
4412 Options: LongWord; nPlayers: Byte);
4413 var
4414 i, nPl: Integer;
4415 begin
4416 g_Game_Free();
4418 e_WriteLog('Starting custom game...', TMsgType.Notify);
4420 g_Game_ClearLoading();
4422 // Íàñòðîéêè èãðû:
4423 gGameSettings.GameType := GT_CUSTOM;
4424 gGameSettings.GameMode := GameMode;
4425 gSwitchGameMode := GameMode;
4426 gGameSettings.TimeLimit := TimeLimit;
4427 gGameSettings.GoalLimit := GoalLimit;
4428 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4429 gGameSettings.Options := Options;
4431 gCoopTotalMonstersKilled := 0;
4432 gCoopTotalSecretsFound := 0;
4433 gCoopTotalMonsters := 0;
4434 gCoopTotalSecrets := 0;
4435 gAimLine := False;
4436 gShowMap := False;
4438 g_Game_ExecuteEvent('ongamestart');
4440 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4441 g_Game_SetupScreenSize();
4443 // Ðåæèì íàáëþäàòåëÿ:
4444 if nPlayers = 0 then
4445 begin
4446 gPlayer1 := nil;
4447 gPlayer2 := nil;
4448 end;
4450 nPl := 0;
4451 if nPlayers >= 1 then
4452 begin
4453 // Ñîçäàíèå ïåðâîãî èãðîêà:
4454 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4455 gPlayer1Settings.Color,
4456 gPlayer1Settings.Team, False));
4457 if gPlayer1 = nil then
4458 begin
4459 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4460 Exit;
4461 end;
4463 gPlayer1.Name := gPlayer1Settings.Name;
4464 Inc(nPl);
4465 end;
4467 if nPlayers >= 2 then
4468 begin
4469 // Ñîçäàíèå âòîðîãî èãðîêà:
4470 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4471 gPlayer2Settings.Color,
4472 gPlayer2Settings.Team, False));
4473 if gPlayer2 = nil then
4474 begin
4475 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4476 Exit;
4477 end;
4479 gPlayer2.Name := gPlayer2Settings.Name;
4480 Inc(nPl);
4481 end;
4483 // Çàãðóçêà è çàïóñê êàðòû:
4484 if not g_Game_StartMap(Map, True) then
4485 begin
4486 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4487 Exit;
4488 end;
4490 // Íåò òî÷åê ïîÿâëåíèÿ:
4491 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4492 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4493 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4494 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4495 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4496 begin
4497 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4498 Exit;
4499 end;
4501 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4502 g_Player_Init();
4504 // Ñîçäàåì áîòîâ:
4505 for i := nPl+1 to nPlayers do
4506 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4507 end;
4509 procedure g_Game_StartServer(Map: String; GameMode: Byte;
4510 TimeLimit, GoalLimit: Word; MaxLives: Byte;
4511 Options: LongWord; nPlayers: Byte;
4512 IPAddr: LongWord; Port: Word);
4513 begin
4514 g_Game_Free();
4516 e_WriteLog('Starting net game (server)...', TMsgType.Notify);
4518 g_Game_ClearLoading();
4520 // Íàñòðîéêè èãðû:
4521 gGameSettings.GameType := GT_SERVER;
4522 gGameSettings.GameMode := GameMode;
4523 gSwitchGameMode := GameMode;
4524 gGameSettings.TimeLimit := TimeLimit;
4525 gGameSettings.GoalLimit := GoalLimit;
4526 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4527 gGameSettings.Options := Options;
4529 gCoopTotalMonstersKilled := 0;
4530 gCoopTotalSecretsFound := 0;
4531 gCoopTotalMonsters := 0;
4532 gCoopTotalSecrets := 0;
4533 gAimLine := False;
4534 gShowMap := False;
4536 g_Game_ExecuteEvent('ongamestart');
4538 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
4539 g_Game_SetupScreenSize();
4541 // Ðåæèì íàáëþäàòåëÿ:
4542 if nPlayers = 0 then
4543 begin
4544 gPlayer1 := nil;
4545 gPlayer2 := nil;
4546 end;
4548 if nPlayers >= 1 then
4549 begin
4550 // Ñîçäàíèå ïåðâîãî èãðîêà:
4551 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4552 gPlayer1Settings.Color,
4553 gPlayer1Settings.Team, False));
4554 if gPlayer1 = nil then
4555 begin
4556 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4557 Exit;
4558 end;
4560 gPlayer1.Name := gPlayer1Settings.Name;
4561 end;
4563 if nPlayers >= 2 then
4564 begin
4565 // Ñîçäàíèå âòîðîãî èãðîêà:
4566 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4567 gPlayer2Settings.Color,
4568 gPlayer2Settings.Team, False));
4569 if gPlayer2 = nil then
4570 begin
4571 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4572 Exit;
4573 end;
4575 gPlayer2.Name := gPlayer2Settings.Name;
4576 end;
4578 g_Game_SetLoadingText(_lc[I_LOAD_HOST], 0, False);
4579 if NetForwardPorts then
4580 g_Game_SetLoadingText(_lc[I_LOAD_PORTS], 0, False);
4582 // Ñòàðòóåì ñåðâåð
4583 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
4584 begin
4585 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
4586 Exit;
4587 end;
4589 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
4591 // Çàãðóçêà è çàïóñê êàðòû:
4592 if not g_Game_StartMap(Map, True) then
4593 begin
4594 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4595 Exit;
4596 end;
4598 // Íåò òî÷åê ïîÿâëåíèÿ:
4599 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4600 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4601 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4602 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4603 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4604 begin
4605 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4606 Exit;
4607 end;
4609 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4610 g_Player_Init();
4612 NetState := NET_STATE_GAME;
4613 end;
4615 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
4616 var
4617 Map: String;
4618 WadName: string;
4619 Ptr: Pointer;
4620 T: Cardinal;
4621 MID: Byte;
4622 State: Byte;
4623 OuterLoop: Boolean;
4624 newResPath: string;
4625 InMsg: TMsg;
4626 begin
4627 g_Game_Free();
4629 State := 0;
4630 e_WriteLog('Starting net game (client)...', TMsgType.Notify);
4631 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', TMsgType.Notify);
4633 g_Game_ClearLoading();
4635 // Íàñòðîéêè èãðû:
4636 gGameSettings.GameType := GT_CLIENT;
4638 gCoopTotalMonstersKilled := 0;
4639 gCoopTotalSecretsFound := 0;
4640 gCoopTotalMonsters := 0;
4641 gCoopTotalSecrets := 0;
4642 gAimLine := False;
4643 gShowMap := False;
4645 g_Game_ExecuteEvent('ongamestart');
4647 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4648 g_Game_SetupScreenSize();
4650 NetState := NET_STATE_AUTH;
4652 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4653 // Ñòàðòóåì êëèåíò
4654 if not g_Net_Connect(Addr, Port) then
4655 begin
4656 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4657 NetState := NET_STATE_NONE;
4658 Exit;
4659 end;
4661 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4662 MC_SEND_Info(PW);
4663 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4665 OuterLoop := True;
4666 while OuterLoop do
4667 begin
4668 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4669 begin
4670 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4671 begin
4672 Ptr := NetEvent.packet^.data;
4673 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4674 continue;
4676 MID := InMsg.ReadByte();
4678 if (MID = NET_MSG_INFO) and (State = 0) then
4679 begin
4680 NetMyID := InMsg.ReadByte();
4681 NetPlrUID1 := InMsg.ReadWord();
4683 WadName := InMsg.ReadString();
4684 Map := InMsg.ReadString();
4686 gWADHash := InMsg.ReadMD5();
4688 gGameSettings.GameMode := InMsg.ReadByte();
4689 gSwitchGameMode := gGameSettings.GameMode;
4690 gGameSettings.GoalLimit := InMsg.ReadWord();
4691 gGameSettings.TimeLimit := InMsg.ReadWord();
4692 gGameSettings.MaxLives := InMsg.ReadByte();
4693 gGameSettings.Options := InMsg.ReadLongWord();
4694 T := InMsg.ReadLongWord();
4696 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4697 if newResPath = '' then
4698 begin
4699 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4700 newResPath := g_Res_DownloadWAD(WadName);
4701 if newResPath = '' then
4702 begin
4703 g_FatalError(_lc[I_NET_ERR_HASH]);
4704 enet_packet_destroy(NetEvent.packet);
4705 NetState := NET_STATE_NONE;
4706 Exit;
4707 end;
4708 end;
4709 newResPath := ExtractRelativePath(MapsDir, newResPath);
4711 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4712 gPlayer1Settings.Color,
4713 gPlayer1Settings.Team, False));
4715 if gPlayer1 = nil then
4716 begin
4717 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4719 enet_packet_destroy(NetEvent.packet);
4720 NetState := NET_STATE_NONE;
4721 Exit;
4722 end;
4724 gPlayer1.Name := gPlayer1Settings.Name;
4725 gPlayer1.UID := NetPlrUID1;
4726 gPlayer1.Reset(True);
4728 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4729 begin
4730 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4732 enet_packet_destroy(NetEvent.packet);
4733 NetState := NET_STATE_NONE;
4734 Exit;
4735 end;
4737 gTime := T;
4739 State := 1;
4740 OuterLoop := False;
4741 enet_packet_destroy(NetEvent.packet);
4742 break;
4743 end
4744 else
4745 enet_packet_destroy(NetEvent.packet);
4746 end
4747 else
4748 begin
4749 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4750 begin
4751 State := 0;
4752 if (NetEvent.data <= NET_DISC_MAX) then
4753 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4754 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4755 OuterLoop := False;
4756 Break;
4757 end;
4758 end;
4759 end;
4761 ProcessLoading(true);
4763 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) or e_KeyPressed(VK_ESCAPE) then
4764 begin
4765 State := 0;
4766 break;
4767 end;
4768 end;
4770 if State <> 1 then
4771 begin
4772 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4773 NetState := NET_STATE_NONE;
4774 Exit;
4775 end;
4777 gLMSRespawn := LMS_RESPAWN_NONE;
4778 gLMSRespawnTime := 0;
4780 g_Player_Init();
4781 NetState := NET_STATE_GAME;
4782 MC_SEND_FullStateRequest;
4783 e_WriteLog('NET: Connection successful.', TMsgType.Notify);
4784 end;
4786 procedure g_Game_SaveOptions();
4787 begin
4788 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4789 end;
4791 procedure g_Game_ChangeMap(const MapPath: String);
4792 var
4793 Force: Boolean;
4794 begin
4795 g_Game_ClearLoading();
4797 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4798 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4799 if gExitByTrigger then
4800 begin
4801 Force := False;
4802 gExitByTrigger := False;
4803 end;
4804 if not g_Game_StartMap(MapPath, Force) then
4805 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4806 end;
4808 procedure g_Game_Restart();
4809 var
4810 Map: string;
4811 begin
4812 if g_Game_IsClient then
4813 Exit;
4814 map := g_ExtractFileName(gMapInfo.Map);
4816 MessageTime := 0;
4817 gGameOn := False;
4818 g_Game_ClearLoading();
4819 g_Game_StartMap(Map, True, gCurrentMapFileName);
4820 end;
4822 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4823 var
4824 NewWAD, ResName: String;
4825 I: Integer;
4826 begin
4827 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4828 g_Player_RemoveAllCorpses();
4830 if (not g_Game_IsClient) and
4831 (gSwitchGameMode <> gGameSettings.GameMode) and
4832 (gGameSettings.GameMode <> GM_SINGLE) then
4833 begin
4834 if gSwitchGameMode = GM_CTF then
4835 gGameSettings.MaxLives := 0;
4836 gGameSettings.GameMode := gSwitchGameMode;
4837 Force := True;
4838 end else
4839 gSwitchGameMode := gGameSettings.GameMode;
4841 g_Player_ResetTeams();
4843 if isWadPath(Map) then
4844 begin
4845 NewWAD := g_ExtractWadName(Map);
4846 ResName := g_ExtractFileName(Map);
4847 if g_Game_IsServer then
4848 begin
4849 gWADHash := MD5File(MapsDir + NewWAD);
4850 g_Game_LoadWAD(NewWAD);
4851 end else
4852 // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART
4853 g_Game_ClientWAD(NewWAD, gWADHash);
4854 end else
4855 ResName := Map;
4857 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4858 if Result then
4859 begin
4860 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4862 gState := STATE_NONE;
4863 g_ActiveWindow := nil;
4864 gGameOn := True;
4866 DisableCheats();
4867 ResetTimer();
4869 if gGameSettings.GameMode = GM_CTF then
4870 begin
4871 g_Map_ResetFlag(FLAG_RED);
4872 g_Map_ResetFlag(FLAG_BLUE);
4873 // CTF, à ôëàãîâ íåò:
4874 if not g_Map_HaveFlagPoints() then
4875 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4876 end;
4877 end
4878 else
4879 begin
4880 gState := STATE_MENU;
4881 gGameOn := False;
4882 end;
4884 gExit := 0;
4885 gPauseMain := false;
4886 gPauseHolmes := false;
4887 gTime := 0;
4888 NetTimeToUpdate := 1;
4889 NetTimeToReliable := 0;
4890 NetTimeToMaster := NetMasterRate;
4891 gLMSRespawn := LMS_RESPAWN_NONE;
4892 gLMSRespawnTime := 0;
4893 gMissionFailed := False;
4894 gNextMap := '';
4896 gCoopMonstersKilled := 0;
4897 gCoopSecretsFound := 0;
4899 gVoteInProgress := False;
4900 gVotePassed := False;
4901 gVoteCount := 0;
4902 gVoted := False;
4904 gStatsOff := False;
4906 if not gGameOn then Exit;
4908 g_Game_SpectateCenterView();
4910 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4911 begin
4912 gLMSRespawn := LMS_RESPAWN_WARMUP;
4913 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4914 gLMSSoftSpawn := True;
4915 if NetMode = NET_SERVER then
4916 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4917 else
4918 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4919 end;
4921 if NetMode = NET_SERVER then
4922 begin
4923 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4925 // Ìàñòåðñåðâåð
4926 if NetUseMaster then
4927 begin
4928 if (NetMHost = nil) or (NetMPeer = nil) then
4929 if not g_Net_Slist_Connect then
4930 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4932 g_Net_Slist_Update;
4933 end;
4935 if NetClients <> nil then
4936 for I := 0 to High(NetClients) do
4937 if NetClients[I].Used then
4938 begin
4939 NetClients[I].Voted := False;
4940 if NetClients[I].RequestedFullUpdate then
4941 begin
4942 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4943 NetClients[I].RequestedFullUpdate := False;
4944 end;
4945 end;
4947 g_Net_UnbanNonPermHosts();
4948 end;
4950 if gLastMap then
4951 begin
4952 gCoopTotalMonstersKilled := 0;
4953 gCoopTotalSecretsFound := 0;
4954 gCoopTotalMonsters := 0;
4955 gCoopTotalSecrets := 0;
4956 gLastMap := False;
4957 end;
4959 g_Game_ExecuteEvent('onmapstart');
4960 end;
4962 procedure SetFirstLevel();
4963 begin
4964 gNextMap := '';
4966 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4967 if MapList = nil then
4968 Exit;
4970 SortSArray(MapList);
4971 gNextMap := MapList[Low(MapList)];
4973 MapList := nil;
4974 end;
4976 procedure g_Game_ExitLevel(const Map: AnsiString);
4977 begin
4978 gNextMap := Map;
4980 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4981 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4982 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4983 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4985 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4986 if gGameSettings.GameType = GT_SINGLE then
4987 gExit := EXIT_ENDLEVELSINGLE
4988 else // Âûøëè â âûõîä â Ñâîåé èãðå
4989 begin
4990 gExit := EXIT_ENDLEVELCUSTOM;
4991 if gGameSettings.GameMode = GM_COOP then
4992 g_Player_RememberAll;
4994 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4995 begin
4996 gLastMap := True;
4997 if gGameSettings.GameMode = GM_COOP then
4998 gStatsOff := True;
5000 gStatsPressed := True;
5001 gNextMap := 'MAP01';
5003 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
5004 g_Game_NextLevel;
5006 if g_Game_IsNet then
5007 begin
5008 MH_SEND_GameStats();
5009 MH_SEND_CoopStats();
5010 end;
5011 end;
5012 end;
5013 end;
5015 procedure g_Game_RestartLevel();
5016 var
5017 Map: string;
5018 begin
5019 if gGameSettings.GameMode = GM_SINGLE then
5020 begin
5021 g_Game_Restart();
5022 Exit;
5023 end;
5024 gExit := EXIT_ENDLEVELCUSTOM;
5025 Map := g_ExtractFileName(gMapInfo.Map);
5026 gNextMap := Map;
5027 end;
5029 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
5030 var
5031 gWAD: String;
5032 begin
5033 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
5034 Exit;
5035 if not g_Game_IsClient then
5036 Exit;
5037 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
5038 if gWAD = '' then
5039 begin
5040 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
5041 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
5042 if gWAD = '' then
5043 begin
5044 g_Game_Free();
5045 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
5046 Exit;
5047 end;
5048 end;
5049 NewWAD := ExtractRelativePath(MapsDir, gWAD);
5050 g_Game_LoadWAD(NewWAD);
5051 end;
5053 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
5054 var
5055 i, n, nb, nr: Integer;
5057 function monRespawn (mon: TMonster): Boolean;
5058 begin
5059 result := false; // don't stop
5060 if not mon.FNoRespawn then mon.Respawn();
5061 end;
5063 begin
5064 if not g_Game_IsServer then Exit;
5065 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
5066 gLMSRespawn := LMS_RESPAWN_NONE;
5067 gLMSRespawnTime := 0;
5068 MessageTime := 0;
5070 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
5071 begin
5072 gMissionFailed := True;
5073 g_Game_RestartLevel;
5074 Exit;
5075 end;
5077 n := 0; nb := 0; nr := 0;
5078 for i := Low(gPlayers) to High(gPlayers) do
5079 if (gPlayers[i] <> nil) and
5080 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
5081 (gPlayers[i] is TBot)) then
5082 begin
5083 Inc(n);
5084 if gPlayers[i].Team = TEAM_RED then Inc(nr)
5085 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
5086 end;
5088 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
5089 begin
5090 // wait a second until the fuckers finally decide to join
5091 gLMSRespawn := LMS_RESPAWN_WARMUP;
5092 gLMSRespawnTime := gTime + 1000;
5093 gLMSSoftSpawn := NoMapRestart;
5094 Exit;
5095 end;
5097 g_Player_RemoveAllCorpses;
5098 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
5099 if g_Game_IsNet then
5100 MH_SEND_GameEvent(NET_EV_LMS_START);
5102 for i := Low(gPlayers) to High(gPlayers) do
5103 begin
5104 if gPlayers[i] = nil then continue;
5105 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
5106 // don't touch normal spectators
5107 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
5108 begin
5109 gPlayers[i].FNoRespawn := True;
5110 gPlayers[i].Lives := 0;
5111 if g_Game_IsNet then
5112 MH_SEND_PlayerStats(gPlayers[I].UID);
5113 continue;
5114 end;
5115 gPlayers[i].FNoRespawn := False;
5116 gPlayers[i].Lives := gGameSettings.MaxLives;
5117 gPlayers[i].Respawn(False, True);
5118 if gGameSettings.GameMode = GM_COOP then
5119 begin
5120 gPlayers[i].Frags := 0;
5121 gPlayers[i].RecallState;
5122 end;
5123 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
5124 gPlayer1 := g_Player_Get(gLMSPID1);
5125 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
5126 gPlayer2 := g_Player_Get(gLMSPID2);
5127 end;
5129 g_Items_RestartRound();
5132 g_Mons_ForEach(monRespawn);
5134 gLMSSoftSpawn := False;
5135 end;
5137 function g_Game_GetFirstMap(WAD: String): String;
5138 begin
5139 Result := '';
5141 MapList := g_Map_GetMapsList(WAD);
5142 if MapList = nil then
5143 Exit;
5145 SortSArray(MapList);
5146 Result := MapList[Low(MapList)];
5148 if not g_Map_Exist(WAD + ':\' + Result) then
5149 Result := '';
5151 MapList := nil;
5152 end;
5154 function g_Game_GetNextMap(): String;
5155 var
5156 I: Integer;
5157 Map: string;
5158 begin
5159 Result := '';
5161 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
5162 if MapList = nil then
5163 Exit;
5165 Map := g_ExtractFileName(gMapInfo.Map);
5167 SortSArray(MapList);
5168 MapIndex := -255;
5169 for I := Low(MapList) to High(MapList) do
5170 if Map = MapList[I] then
5171 begin
5172 MapIndex := I;
5173 Break;
5174 end;
5176 if MapIndex <> -255 then
5177 begin
5178 if MapIndex = High(MapList) then
5179 Result := MapList[Low(MapList)]
5180 else
5181 Result := MapList[MapIndex + 1];
5183 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
5184 end;
5186 MapList := nil;
5187 end;
5189 procedure g_Game_NextLevel();
5190 begin
5191 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
5192 gExit := EXIT_ENDLEVELCUSTOM
5193 else
5194 begin
5195 gExit := EXIT_ENDLEVELSINGLE;
5196 Exit;
5197 end;
5199 if gNextMap <> '' then Exit;
5200 gNextMap := g_Game_GetNextMap();
5201 end;
5203 function g_Game_IsTestMap(): Boolean;
5204 begin
5205 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
5206 end;
5208 procedure g_Game_DeleteTestMap();
5209 var
5210 a: Integer;
5211 //MapName: AnsiString;
5212 WadName: string;
5214 WAD: TWADFile;
5215 MapList: SSArray;
5216 time: Integer;
5218 begin
5219 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
5220 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
5221 if (a = 0) then exit;
5223 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
5224 WadName := Copy(gMapToDelete, 1, a+3);
5225 Delete(gMapToDelete, 1, a+5);
5226 gMapToDelete := UpperCase(gMapToDelete);
5227 //MapName := '';
5228 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
5231 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
5232 if MapName <> TEST_MAP_NAME then
5233 Exit;
5235 if not gTempDelete then
5236 begin
5237 time := g_GetFileTime(WadName);
5238 WAD := TWADFile.Create();
5240 // ×èòàåì Wad-ôàéë:
5241 if not WAD.ReadFile(WadName) then
5242 begin // Íåò òàêîãî WAD-ôàéëà
5243 WAD.Free();
5244 Exit;
5245 end;
5247 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
5248 WAD.CreateImage();
5249 MapList := WAD.GetResourcesList('');
5251 if MapList <> nil then
5252 for a := 0 to High(MapList) do
5253 if MapList[a] = MapName then
5254 begin
5255 // Óäàëÿåì è ñîõðàíÿåì:
5256 WAD.RemoveResource('', MapName);
5257 WAD.SaveTo(WadName);
5258 Break;
5259 end;
5261 WAD.Free();
5262 g_SetFileTime(WadName, time);
5263 end else
5265 if gTempDelete then DeleteFile(WadName);
5266 end;
5268 procedure GameCVars(P: SSArray);
5269 var
5270 a, b: Integer;
5271 stat: TPlayerStatArray;
5272 cmd, s: string;
5273 config: TConfig;
5274 begin
5275 stat := nil;
5276 cmd := LowerCase(P[0]);
5277 if cmd = 'r_showfps' then
5278 begin
5279 if (Length(P) > 1) and
5280 ((P[1] = '1') or (P[1] = '0')) then
5281 gShowFPS := (P[1][1] = '1');
5283 if gShowFPS then
5284 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
5285 else
5286 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
5287 end
5288 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
5289 begin
5290 with gGameSettings do
5291 begin
5292 if (Length(P) > 1) and
5293 ((P[1] = '1') or (P[1] = '0')) then
5294 begin
5295 if (P[1][1] = '1') then
5296 Options := Options or GAME_OPTION_TEAMDAMAGE
5297 else
5298 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
5299 end;
5301 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
5302 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
5303 else
5304 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
5306 if g_Game_IsNet then MH_SEND_GameSettings;
5307 end;
5308 end
5309 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
5310 begin
5311 with gGameSettings do
5312 begin
5313 if (Length(P) > 1) and
5314 ((P[1] = '1') or (P[1] = '0')) then
5315 begin
5316 if (P[1][1] = '1') then
5317 Options := Options or GAME_OPTION_WEAPONSTAY
5318 else
5319 Options := Options and (not GAME_OPTION_WEAPONSTAY);
5320 end;
5322 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
5323 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
5324 else
5325 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
5327 if g_Game_IsNet then MH_SEND_GameSettings;
5328 end;
5329 end
5330 else if cmd = 'g_gamemode' then
5331 begin
5332 a := g_Game_TextToMode(P[1]);
5333 if a = GM_SINGLE then a := GM_COOP;
5334 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
5335 begin
5336 gSwitchGameMode := a;
5337 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
5338 (gState = STATE_INTERSINGLE) then
5339 gSwitchGameMode := GM_SINGLE;
5340 if not gGameOn then
5341 gGameSettings.GameMode := gSwitchGameMode;
5342 end;
5343 if gSwitchGameMode = gGameSettings.GameMode then
5344 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
5345 [g_Game_ModeToText(gGameSettings.GameMode)]))
5346 else
5347 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
5348 [g_Game_ModeToText(gGameSettings.GameMode),
5349 g_Game_ModeToText(gSwitchGameMode)]));
5350 end
5351 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
5352 begin
5353 with gGameSettings do
5354 begin
5355 if (Length(P) > 1) and
5356 ((P[1] = '1') or (P[1] = '0')) then
5357 begin
5358 if (P[1][1] = '1') then
5359 Options := Options or GAME_OPTION_ALLOWEXIT
5360 else
5361 Options := Options and (not GAME_OPTION_ALLOWEXIT);
5362 end;
5364 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
5365 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
5366 else
5367 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
5368 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5370 if g_Game_IsNet then MH_SEND_GameSettings;
5371 end;
5372 end
5373 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
5374 begin
5375 with gGameSettings do
5376 begin
5377 if (Length(P) > 1) and
5378 ((P[1] = '1') or (P[1] = '0')) then
5379 begin
5380 if (P[1][1] = '1') then
5381 Options := Options or GAME_OPTION_MONSTERS
5382 else
5383 Options := Options and (not GAME_OPTION_MONSTERS);
5384 end;
5386 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
5387 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
5388 else
5389 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
5390 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5392 if g_Game_IsNet then MH_SEND_GameSettings;
5393 end;
5394 end
5395 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
5396 begin
5397 with gGameSettings do
5398 begin
5399 if (Length(P) > 1) and
5400 ((P[1] = '1') or (P[1] = '0')) then
5401 begin
5402 if (P[1][1] = '1') then
5403 Options := Options or GAME_OPTION_BOTVSPLAYER
5404 else
5405 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
5406 end;
5408 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
5409 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
5410 else
5411 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
5413 if g_Game_IsNet then MH_SEND_GameSettings;
5414 end;
5415 end
5416 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
5417 begin
5418 with gGameSettings do
5419 begin
5420 if (Length(P) > 1) and
5421 ((P[1] = '1') or (P[1] = '0')) then
5422 begin
5423 if (P[1][1] = '1') then
5424 Options := Options or GAME_OPTION_BOTVSMONSTER
5425 else
5426 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
5427 end;
5429 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
5430 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
5431 else
5432 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
5434 if g_Game_IsNet then MH_SEND_GameSettings;
5435 end;
5436 end
5437 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
5438 begin
5439 if Length(P) > 1 then
5440 begin
5441 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
5442 gGameSettings.WarmupTime := 30
5443 else
5444 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
5445 end;
5447 g_Console_Add(Format(_lc[I_MSG_WARMUP],
5448 [gGameSettings.WarmupTime]));
5449 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5450 end
5451 else if cmd = 'net_interp' then
5452 begin
5453 if (Length(P) > 1) then
5454 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
5456 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
5457 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5458 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
5459 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5460 config.Free();
5461 end
5462 else if cmd = 'net_forceplayerupdate' then
5463 begin
5464 if (Length(P) > 1) and
5465 ((P[1] = '1') or (P[1] = '0')) then
5466 NetForcePlayerUpdate := (P[1][1] = '1');
5468 if NetForcePlayerUpdate then
5469 g_Console_Add('net_forceplayerupdate = 1')
5470 else
5471 g_Console_Add('net_forceplayerupdate = 0');
5472 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5473 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
5474 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5475 config.Free();
5476 end
5477 else if cmd = 'net_predictself' then
5478 begin
5479 if (Length(P) > 1) and
5480 ((P[1] = '1') or (P[1] = '0')) then
5481 NetPredictSelf := (P[1][1] = '1');
5483 if NetPredictSelf then
5484 g_Console_Add('net_predictself = 1')
5485 else
5486 g_Console_Add('net_predictself = 0');
5487 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
5488 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
5489 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
5490 config.Free();
5491 end
5492 else if cmd = 'sv_name' then
5493 begin
5494 if (Length(P) > 1) and (Length(P[1]) > 0) then
5495 begin
5496 NetServerName := P[1];
5497 if Length(NetServerName) > 64 then
5498 SetLength(NetServerName, 64);
5499 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5500 g_Net_Slist_Update;
5501 end;
5503 g_Console_Add(cmd + ' = "' + NetServerName + '"');
5504 end
5505 else if cmd = 'sv_passwd' then
5506 begin
5507 if (Length(P) > 1) and (Length(P[1]) > 0) then
5508 begin
5509 NetPassword := P[1];
5510 if Length(NetPassword) > 24 then
5511 SetLength(NetPassword, 24);
5512 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
5513 g_Net_Slist_Update;
5514 end;
5516 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
5517 end
5518 else if cmd = 'sv_maxplrs' then
5519 begin
5520 if (Length(P) > 1) then
5521 begin
5522 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
5523 if g_Game_IsServer and g_Game_IsNet then
5524 begin
5525 b := 0;
5526 for a := 0 to High(NetClients) do
5527 if NetClients[a].Used then
5528 begin
5529 Inc(b);
5530 if b > NetMaxClients then
5531 begin
5532 s := g_Player_Get(NetClients[a].Player).Name;
5533 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
5534 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5535 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5536 end;
5537 end;
5538 if NetUseMaster then
5539 g_Net_Slist_Update;
5540 end;
5541 end;
5543 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
5544 end
5545 else if cmd = 'sv_public' then
5546 begin
5547 if (Length(P) > 1) then
5548 begin
5549 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
5550 if g_Game_IsServer and g_Game_IsNet then
5551 if NetUseMaster then
5552 begin
5553 if NetMPeer = nil then
5554 if not g_Net_Slist_Connect() then
5555 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
5556 g_Net_Slist_Update();
5557 end
5558 else
5559 if NetMPeer <> nil then
5560 g_Net_Slist_Disconnect();
5561 end;
5563 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
5564 end
5565 else if cmd = 'sv_intertime' then
5566 begin
5567 if (Length(P) > 1) then
5568 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
5570 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
5571 end
5572 else if cmd = 'p1_name' then
5573 begin
5574 if (Length(P) > 1) and gGameOn then
5575 begin
5576 if g_Game_IsClient then
5577 begin
5578 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5579 MC_SEND_PlayerSettings;
5580 end
5581 else
5582 if gPlayer1 <> nil then
5583 begin
5584 gPlayer1.Name := b_Text_Unformat(P[1]);
5585 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5586 end
5587 else
5588 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5589 end;
5590 end
5591 else if cmd = 'p2_name' then
5592 begin
5593 if (Length(P) > 1) and gGameOn then
5594 begin
5595 if g_Game_IsClient then
5596 begin
5597 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5598 MC_SEND_PlayerSettings;
5599 end
5600 else
5601 if gPlayer2 <> nil then
5602 begin
5603 gPlayer2.Name := b_Text_Unformat(P[1]);
5604 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5605 end
5606 else
5607 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5608 end;
5609 end
5610 else if cmd = 'p1_color' then
5611 begin
5612 if Length(P) > 3 then
5613 if g_Game_IsClient then
5614 begin
5615 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5616 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5617 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5618 MC_SEND_PlayerSettings;
5619 end
5620 else
5621 if gPlayer1 <> nil then
5622 begin
5623 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5624 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5625 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5626 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5627 end
5628 else
5629 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5630 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5631 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5632 end
5633 else if (cmd = 'p2_color') and not g_Game_IsNet then
5634 begin
5635 if Length(P) > 3 then
5636 if g_Game_IsClient then
5637 begin
5638 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5639 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5640 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5641 MC_SEND_PlayerSettings;
5642 end
5643 else
5644 if gPlayer2 <> nil then
5645 begin
5646 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5647 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5648 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5649 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5650 end
5651 else
5652 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5653 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5654 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5655 end
5656 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5657 begin
5658 if cmd = 'r_showtime' then
5659 begin
5660 if (Length(P) > 1) and
5661 ((P[1] = '1') or (P[1] = '0')) then
5662 gShowTime := (P[1][1] = '1');
5664 if gShowTime then
5665 g_Console_Add(_lc[I_MSG_TIME_ON])
5666 else
5667 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5668 end
5669 else if cmd = 'r_showscore' then
5670 begin
5671 if (Length(P) > 1) and
5672 ((P[1] = '1') or (P[1] = '0')) then
5673 gShowGoals := (P[1][1] = '1');
5675 if gShowGoals then
5676 g_Console_Add(_lc[I_MSG_SCORE_ON])
5677 else
5678 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5679 end
5680 else if cmd = 'r_showstat' then
5681 begin
5682 if (Length(P) > 1) and
5683 ((P[1] = '1') or (P[1] = '0')) then
5684 gShowStat := (P[1][1] = '1');
5686 if gShowStat then
5687 g_Console_Add(_lc[I_MSG_STATS_ON])
5688 else
5689 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5690 end
5691 else if cmd = 'r_showkillmsg' then
5692 begin
5693 if (Length(P) > 1) and
5694 ((P[1] = '1') or (P[1] = '0')) then
5695 gShowKillMsg := (P[1][1] = '1');
5697 if gShowKillMsg then
5698 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5699 else
5700 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5701 end
5702 else if cmd = 'r_showlives' then
5703 begin
5704 if (Length(P) > 1) and
5705 ((P[1] = '1') or (P[1] = '0')) then
5706 gShowLives := (P[1][1] = '1');
5708 if gShowLives then
5709 g_Console_Add(_lc[I_MSG_LIVES_ON])
5710 else
5711 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5712 end
5713 else if cmd = 'r_showspect' then
5714 begin
5715 if (Length(P) > 1) and
5716 ((P[1] = '1') or (P[1] = '0')) then
5717 gSpectHUD := (P[1][1] = '1');
5719 if gSpectHUD then
5720 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5721 else
5722 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5723 end
5724 else if cmd = 'r_showping' then
5725 begin
5726 if (Length(P) > 1) and
5727 ((P[1] = '1') or (P[1] = '0')) then
5728 gShowPing := (P[1][1] = '1');
5730 if gShowPing then
5731 g_Console_Add(_lc[I_MSG_PING_ON])
5732 else
5733 g_Console_Add(_lc[I_MSG_PING_OFF]);
5734 end
5735 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5736 begin
5737 if Length(P) > 1 then
5738 begin
5739 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5740 gGameSettings.GoalLimit := 0
5741 else
5742 begin
5743 b := 0;
5745 if gGameSettings.GameMode = GM_DM then
5746 begin // DM
5747 stat := g_Player_GetStats();
5748 if stat <> nil then
5749 for a := 0 to High(stat) do
5750 if stat[a].Frags > b then
5751 b := stat[a].Frags;
5752 end
5753 else // TDM/CTF
5754 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5756 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5757 end;
5759 if g_Game_IsNet then MH_SEND_GameSettings;
5760 end;
5762 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5763 end
5764 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5765 begin
5766 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5767 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5769 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5770 [gGameSettings.TimeLimit div 3600,
5771 (gGameSettings.TimeLimit div 60) mod 60,
5772 gGameSettings.TimeLimit mod 60]));
5773 if g_Game_IsNet then MH_SEND_GameSettings;
5774 end
5775 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5776 begin
5777 if Length(P) > 1 then
5778 begin
5779 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5780 gGameSettings.MaxLives := 0
5781 else
5782 begin
5783 b := 0;
5784 stat := g_Player_GetStats();
5785 if stat <> nil then
5786 for a := 0 to High(stat) do
5787 if stat[a].Lives > b then
5788 b := stat[a].Lives;
5789 gGameSettings.MaxLives :=
5790 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5791 end;
5792 end;
5794 g_Console_Add(Format(_lc[I_MSG_LIVES],
5795 [gGameSettings.MaxLives]));
5796 if g_Game_IsNet then MH_SEND_GameSettings;
5797 end;
5798 end;
5799 end;
5801 procedure PrintHeapStats();
5802 var
5803 hs: TFPCHeapStatus;
5804 begin
5805 hs := GetFPCHeapStatus();
5806 e_LogWriteLn ('v===== heap status =====v');
5807 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
5808 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
5809 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
5810 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
5811 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
5812 e_LogWriteLn ('^=======================^');
5813 end;
5815 procedure DebugCommands(P: SSArray);
5816 var
5817 a, b: Integer;
5818 cmd: string;
5819 //pt: TDFPoint;
5820 mon: TMonster;
5821 begin
5822 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5823 if {gDebugMode}conIsCheatsEnabled then
5824 begin
5825 cmd := LowerCase(P[0]);
5826 if cmd = 'd_window' then
5827 begin
5828 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5829 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5830 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5831 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5832 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5833 end
5834 else if cmd = 'd_sounds' then
5835 begin
5836 if (Length(P) > 1) and
5837 ((P[1] = '1') or (P[1] = '0')) then
5838 g_Debug_Sounds := (P[1][1] = '1');
5840 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5841 end
5842 else if cmd = 'd_frames' then
5843 begin
5844 if (Length(P) > 1) and
5845 ((P[1] = '1') or (P[1] = '0')) then
5846 g_Debug_Frames := (P[1][1] = '1');
5848 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5849 end
5850 else if cmd = 'd_winmsg' then
5851 begin
5852 if (Length(P) > 1) and
5853 ((P[1] = '1') or (P[1] = '0')) then
5854 g_Debug_WinMsgs := (P[1][1] = '1');
5856 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5857 end
5858 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5859 begin
5860 if (Length(P) > 1) and
5861 ((P[1] = '1') or (P[1] = '0')) then
5862 g_Debug_MonsterOff := (P[1][1] = '1');
5864 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5865 end
5866 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5867 begin
5868 if Length(P) > 1 then
5869 case P[1][1] of
5870 '0': g_debug_BotAIOff := 0;
5871 '1': g_debug_BotAIOff := 1;
5872 '2': g_debug_BotAIOff := 2;
5873 '3': g_debug_BotAIOff := 3;
5874 end;
5876 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5877 end
5878 else if cmd = 'd_monster' then
5879 begin
5880 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5881 if Length(P) < 2 then
5882 begin
5883 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5884 g_Console_Add('ID | Name');
5885 for b := MONSTER_DEMON to MONSTER_MAN do
5886 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5887 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
5888 end else
5889 begin
5890 a := StrToIntDef(P[1], 0);
5891 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5892 a := g_Mons_TypeIdByName(P[1]);
5894 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5895 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5896 else
5897 begin
5898 with gPlayer1.Obj do
5899 begin
5900 mon := g_Monsters_Create(a,
5901 X + Rect.X + (Rect.Width div 2),
5902 Y + Rect.Y + Rect.Height,
5903 gPlayer1.Direction, True);
5904 end;
5905 if (Length(P) > 2) and (mon <> nil) then
5906 begin
5907 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
5908 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
5909 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
5910 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
5911 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
5912 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
5913 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
5914 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
5915 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5916 end;
5917 end;
5918 end;
5919 end
5920 else if (cmd = 'd_health') then
5921 begin
5922 if (Length(P) > 1) and
5923 ((P[1] = '1') or (P[1] = '0')) then
5924 g_debug_HealthBar := (P[1][1] = '1');
5926 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5927 end
5928 else if (cmd = 'd_player') then
5929 begin
5930 if (Length(P) > 1) and
5931 ((P[1] = '1') or (P[1] = '0')) then
5932 g_debug_Player := (P[1][1] = '1');
5934 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5935 end
5936 else if (cmd = 'd_joy') then
5937 begin
5938 for a := 1 to 8 do
5939 g_Console_Add(e_JoystickStateToString(a));
5940 end
5941 else if (cmd = 'd_mem') then
5942 begin
5943 PrintHeapStats();
5944 end;
5945 end
5946 else
5947 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5948 end;
5951 procedure GameCheats(P: SSArray);
5952 var
5953 cmd: string;
5954 f, a: Integer;
5955 plr: TPlayer;
5956 begin
5957 if (not gGameOn) or (not conIsCheatsEnabled) then
5958 begin
5959 g_Console_Add('not available');
5960 exit;
5961 end;
5962 plr := gPlayer1;
5963 if plr = nil then
5964 begin
5965 g_Console_Add('where is the player?!');
5966 exit;
5967 end;
5968 cmd := LowerCase(P[0]);
5969 // god
5970 if cmd = 'god' then
5971 begin
5972 plr.GodMode := not plr.GodMode;
5973 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5974 exit;
5975 end;
5976 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5977 if cmd = 'give' then
5978 begin
5979 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5980 for f := 1 to High(P) do
5981 begin
5982 cmd := LowerCase(P[f]);
5983 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5984 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5985 if cmd = 'exit' then
5986 begin
5987 if gTriggers <> nil then
5988 begin
5989 for a := 0 to High(gTriggers) do
5990 begin
5991 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5992 begin
5993 g_Console_Add('player left the map');
5994 gExitByTrigger := True;
5995 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5996 g_Game_ExitLevel(gTriggers[a].tgcMap);
5997 break;
5998 end;
5999 end;
6000 end;
6001 continue;
6002 end;
6004 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
6005 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
6006 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
6007 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
6008 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
6010 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
6011 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
6013 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
6014 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;
6016 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
6017 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
6019 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
6020 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
6022 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
6023 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
6025 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
6026 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
6027 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
6029 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
6030 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
6031 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
6032 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;
6033 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
6034 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
6036 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;
6037 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;
6038 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;
6039 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;
6040 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;
6041 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;
6043 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
6044 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;
6046 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;
6047 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;
6049 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
6051 if cmd = 'ammo' then
6052 begin
6053 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
6054 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
6055 plr.GiveItem(ITEM_AMMO_CELL_BIG);
6056 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
6057 plr.GiveItem(ITEM_AMMO_FUELCAN);
6058 g_Console_Add('player got some ammo');
6059 continue;
6060 end;
6062 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
6063 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
6065 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
6066 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
6068 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
6069 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
6071 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
6072 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
6074 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
6076 if cmd = 'weapons' then
6077 begin
6078 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
6079 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
6080 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
6081 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
6082 plr.GiveItem(ITEM_WEAPON_PLASMA);
6083 plr.GiveItem(ITEM_WEAPON_BFG);
6084 g_Console_Add('player got weapons');
6085 continue;
6086 end;
6088 if cmd = 'keys' then
6089 begin
6090 plr.GiveItem(ITEM_KEY_RED);
6091 plr.GiveItem(ITEM_KEY_GREEN);
6092 plr.GiveItem(ITEM_KEY_BLUE);
6093 g_Console_Add('player got all keys');
6094 continue;
6095 end;
6097 g_Console_Add('i don''t know how to give '''+cmd+'''!');
6098 end;
6099 exit;
6100 end;
6101 // open
6102 if cmd = 'open' then
6103 begin
6104 g_Console_Add('player activated sesame');
6105 g_Triggers_OpenAll();
6106 exit;
6107 end;
6108 // fly
6109 if cmd = 'fly' then
6110 begin
6111 gFly := not gFly;
6112 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
6113 exit;
6114 end;
6115 // noclip
6116 if cmd = 'noclip' then
6117 begin
6118 plr.SwitchNoClip;
6119 g_Console_Add('wall hardeness adjusted');
6120 exit;
6121 end;
6122 // notarget
6123 if cmd = 'notarget' then
6124 begin
6125 plr.NoTarget := not plr.NoTarget;
6126 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
6127 exit;
6128 end;
6129 // noreload
6130 if cmd = 'noreload' then
6131 begin
6132 plr.NoReload := not plr.NoReload;
6133 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
6134 exit;
6135 end;
6136 // speedy
6137 if cmd = 'speedy' then
6138 begin
6139 MAX_RUNVEL := 32-MAX_RUNVEL;
6140 g_Console_Add('speed adjusted');
6141 exit;
6142 end;
6143 // jumpy
6144 if cmd = 'jumpy' then
6145 begin
6146 VEL_JUMP := 30-VEL_JUMP;
6147 g_Console_Add('jump height adjusted');
6148 exit;
6149 end;
6150 // automap
6151 if cmd = 'automap' then
6152 begin
6153 gShowMap := not gShowMap;
6154 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
6155 exit;
6156 end;
6157 // aimline
6158 if cmd = 'aimline' then
6159 begin
6160 gAimLine := not gAimLine;
6161 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
6162 exit;
6163 end;
6164 end;
6166 procedure GameCommands(P: SSArray);
6167 var
6168 a, b: Integer;
6169 s, pw: String;
6170 chstr: string;
6171 cmd: string;
6172 pl: pTNetClient = nil;
6173 plr: TPlayer;
6174 prt: Word;
6175 nm: Boolean;
6176 listen: LongWord;
6177 begin
6178 // Îáùèå êîìàíäû:
6179 cmd := LowerCase(P[0]);
6180 chstr := '';
6181 if (cmd = 'quit') or
6182 (cmd = 'exit') then
6183 begin
6184 g_Game_Free();
6185 g_Game_Quit();
6186 Exit;
6187 end
6188 else if cmd = 'pause' then
6189 begin
6190 if (g_ActiveWindow = nil) then
6191 g_Game_Pause(not gPauseMain);
6192 end
6193 else if cmd = 'endgame' then
6194 gExit := EXIT_SIMPLE
6195 else if cmd = 'restart' then
6196 begin
6197 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
6198 begin
6199 if g_Game_IsClient then
6200 begin
6201 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6202 Exit;
6203 end;
6204 g_Game_Restart();
6205 end else
6206 g_Console_Add(_lc[I_MSG_NOT_GAME]);
6207 end
6208 else if cmd = 'kick' then
6209 begin
6210 if g_Game_IsServer then
6211 begin
6212 if Length(P) < 2 then
6213 begin
6214 g_Console_Add('kick <name>');
6215 Exit;
6216 end;
6217 if P[1] = '' then
6218 begin
6219 g_Console_Add('kick <name>');
6220 Exit;
6221 end;
6223 if g_Game_IsNet then
6224 pl := g_Net_Client_ByName(P[1]);
6225 if (pl <> nil) then
6226 begin
6227 s := g_Net_ClientName_ByID(pl^.ID);
6228 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
6229 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
6230 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
6231 if NetUseMaster then
6232 g_Net_Slist_Update;
6233 end else if gPlayers <> nil then
6234 for a := Low(gPlayers) to High(gPlayers) do
6235 if gPlayers[a] <> nil then
6236 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
6237 begin
6238 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
6239 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
6240 continue;
6241 gPlayers[a].Lives := 0;
6242 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
6243 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
6244 g_Player_Remove(gPlayers[a].UID);
6245 if NetUseMaster then
6246 g_Net_Slist_Update;
6247 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
6248 g_Bot_MixNames();
6249 end;
6250 end else
6251 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6252 end
6253 else if cmd = 'kick_id' then
6254 begin
6255 if g_Game_IsServer and g_Game_IsNet then
6256 begin
6257 if Length(P) < 2 then
6258 begin
6259 g_Console_Add('kick_id <client ID>');
6260 Exit;
6261 end;
6262 if P[1] = '' then
6263 begin
6264 g_Console_Add('kick_id <client ID>');
6265 Exit;
6266 end;
6268 a := StrToIntDef(P[1], 0);
6269 if (NetClients <> nil) and (a <= High(NetClients)) then
6270 begin
6271 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6272 begin
6273 s := g_Net_ClientName_ByID(NetClients[a].ID);
6274 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
6275 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
6276 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
6277 if NetUseMaster then
6278 g_Net_Slist_Update;
6279 end;
6280 end;
6281 end else
6282 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6283 end
6284 else if cmd = 'ban' then
6285 begin
6286 if g_Game_IsServer and g_Game_IsNet then
6287 begin
6288 if Length(P) < 2 then
6289 begin
6290 g_Console_Add('ban <name>');
6291 Exit;
6292 end;
6293 if P[1] = '' then
6294 begin
6295 g_Console_Add('ban <name>');
6296 Exit;
6297 end;
6299 pl := g_Net_Client_ByName(P[1]);
6300 if (pl <> nil) then
6301 begin
6302 s := g_Net_ClientName_ByID(pl^.ID);
6303 g_Net_BanHost(pl^.Peer^.address.host, False);
6304 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
6305 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6306 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6307 if NetUseMaster then
6308 g_Net_Slist_Update;
6309 end else
6310 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6311 end else
6312 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6313 end
6314 else if cmd = 'ban_id' then
6315 begin
6316 if g_Game_IsServer and g_Game_IsNet then
6317 begin
6318 if Length(P) < 2 then
6319 begin
6320 g_Console_Add('ban_id <client ID>');
6321 Exit;
6322 end;
6323 if P[1] = '' then
6324 begin
6325 g_Console_Add('ban_id <client ID>');
6326 Exit;
6327 end;
6329 a := StrToIntDef(P[1], 0);
6330 if (NetClients <> nil) and (a <= High(NetClients)) then
6331 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6332 begin
6333 s := g_Net_ClientName_ByID(NetClients[a].ID);
6334 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
6335 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
6336 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6337 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6338 if NetUseMaster then
6339 g_Net_Slist_Update;
6340 end;
6341 end else
6342 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6343 end
6344 else if cmd = 'permban' then
6345 begin
6346 if g_Game_IsServer and g_Game_IsNet then
6347 begin
6348 if Length(P) < 2 then
6349 begin
6350 g_Console_Add('permban <name>');
6351 Exit;
6352 end;
6353 if P[1] = '' then
6354 begin
6355 g_Console_Add('permban <name>');
6356 Exit;
6357 end;
6359 pl := g_Net_Client_ByName(P[1]);
6360 if (pl <> nil) then
6361 begin
6362 s := g_Net_ClientName_ByID(pl^.ID);
6363 g_Net_BanHost(pl^.Peer^.address.host);
6364 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
6365 g_Net_SaveBanList();
6366 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6367 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6368 if NetUseMaster then
6369 g_Net_Slist_Update;
6370 end else
6371 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6372 end else
6373 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6374 end
6375 else if cmd = 'permban_id' then
6376 begin
6377 if g_Game_IsServer and g_Game_IsNet then
6378 begin
6379 if Length(P) < 2 then
6380 begin
6381 g_Console_Add('permban_id <client ID>');
6382 Exit;
6383 end;
6384 if P[1] = '' then
6385 begin
6386 g_Console_Add('permban_id <client ID>');
6387 Exit;
6388 end;
6390 a := StrToIntDef(P[1], 0);
6391 if (NetClients <> nil) and (a <= High(NetClients)) then
6392 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6393 begin
6394 s := g_Net_ClientName_ByID(NetClients[a].ID);
6395 g_Net_BanHost(NetClients[a].Peer^.address.host);
6396 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
6397 g_Net_SaveBanList();
6398 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6399 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6400 if NetUseMaster then
6401 g_Net_Slist_Update;
6402 end;
6403 end else
6404 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6405 end
6406 else if cmd = 'unban' then
6407 begin
6408 if g_Game_IsServer and g_Game_IsNet then
6409 begin
6410 if Length(P) < 2 then
6411 begin
6412 g_Console_Add('unban <IP Address>');
6413 Exit;
6414 end;
6415 if P[1] = '' then
6416 begin
6417 g_Console_Add('unban <IP Address>');
6418 Exit;
6419 end;
6421 if g_Net_UnbanHost(P[1]) then
6422 begin
6423 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
6424 g_Net_SaveBanList();
6425 end else
6426 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
6427 end else
6428 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6429 end
6430 else if cmd = 'clientlist' then
6431 begin
6432 if g_Game_IsServer and g_Game_IsNet then
6433 begin
6434 b := 0;
6435 if NetClients <> nil then
6436 for a := Low(NetClients) to High(NetClients) do
6437 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6438 begin
6439 plr := g_Player_Get(NetClients[a].Player);
6440 if plr = nil then continue;
6441 Inc(b);
6442 g_Console_Add(Format('#%2d: %-15s | %s', [a,
6443 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
6444 end;
6445 if b = 0 then
6446 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
6447 end else
6448 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6449 end
6450 else if cmd = 'connect' then
6451 begin
6452 if (NetMode = NET_NONE) then
6453 begin
6454 if Length(P) < 2 then
6455 begin
6456 g_Console_Add('connect <IP> [port] [password]');
6457 Exit;
6458 end;
6459 if P[1] = '' then
6460 begin
6461 g_Console_Add('connect <IP> [port] [password]');
6462 Exit;
6463 end;
6465 if Length(P) > 2 then
6466 prt := StrToIntDef(P[2], 25666)
6467 else
6468 prt := 25666;
6470 if Length(P) > 3 then
6471 pw := P[3]
6472 else
6473 pw := '';
6475 g_Game_StartClient(P[1], prt, pw);
6476 end;
6477 end
6478 else if cmd = 'disconnect' then
6479 begin
6480 if (NetMode = NET_CLIENT) then
6481 g_Net_Disconnect();
6482 end
6483 else if cmd = 'reconnect' then
6484 begin
6485 if (NetMode = NET_SERVER) then
6486 Exit;
6488 if (NetMode = NET_CLIENT) then
6489 begin
6490 g_Net_Disconnect();
6491 gExit := EXIT_SIMPLE;
6492 EndGame;
6493 end;
6495 //TODO: Use last successful password to reconnect, instead of ''
6496 g_Game_StartClient(NetClientIP, NetClientPort, '');
6497 end
6498 else if (cmd = 'addbot') or
6499 (cmd = 'bot_add') then
6500 begin
6501 if Length(P) > 1 then
6502 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
6503 else
6504 g_Bot_Add(TEAM_NONE, 2);
6505 end
6506 else if cmd = 'bot_addlist' then
6507 begin
6508 if Length(P) > 1 then
6509 if Length(P) = 2 then
6510 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
6511 else
6512 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
6513 end
6514 else if cmd = 'bot_removeall' then
6515 g_Bot_RemoveAll()
6516 else if cmd = 'chat' then
6517 begin
6518 if g_Game_IsNet then
6519 begin
6520 if Length(P) > 1 then
6521 begin
6522 for a := 1 to High(P) do
6523 chstr := chstr + P[a] + ' ';
6525 if Length(chstr) > 200 then SetLength(chstr, 200);
6527 if Length(chstr) < 1 then
6528 begin
6529 g_Console_Add('chat <text>');
6530 Exit;
6531 end;
6533 chstr := b_Text_Format(chstr);
6534 if g_Game_IsClient then
6535 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
6536 else
6537 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
6538 end
6539 else
6540 g_Console_Add('chat <text>');
6541 end else
6542 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6543 end
6544 else if cmd = 'teamchat' then
6545 begin
6546 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
6547 begin
6548 if Length(P) > 1 then
6549 begin
6550 for a := 1 to High(P) do
6551 chstr := chstr + P[a] + ' ';
6553 if Length(chstr) > 200 then SetLength(chstr, 200);
6555 if Length(chstr) < 1 then
6556 begin
6557 g_Console_Add('teamchat <text>');
6558 Exit;
6559 end;
6561 chstr := b_Text_Format(chstr);
6562 if g_Game_IsClient then
6563 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
6564 else
6565 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
6566 gPlayer1Settings.Team);
6567 end
6568 else
6569 g_Console_Add('teamchat <text>');
6570 end else
6571 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6572 end
6573 else if cmd = 'game' then
6574 begin
6575 if gGameSettings.GameType <> GT_NONE then
6576 begin
6577 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6578 Exit;
6579 end;
6580 if Length(P) = 1 then
6581 begin
6582 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
6583 Exit;
6584 end;
6585 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
6586 P[1] := addWadExtension(P[1]);
6587 if FileExists(MapsDir + P[1]) then
6588 begin
6589 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6590 if Length(P) < 3 then
6591 begin
6592 SetLength(P, 3);
6593 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6594 end;
6596 s := P[1] + ':\' + UpperCase(P[2]);
6598 if g_Map_Exist(MapsDir + s) then
6599 begin
6600 // Çàïóñêàåì ñâîþ èãðó
6601 g_Game_Free();
6602 with gGameSettings do
6603 begin
6604 GameMode := g_Game_TextToMode(gcGameMode);
6605 if gSwitchGameMode <> GM_NONE then
6606 GameMode := gSwitchGameMode;
6607 if GameMode = GM_NONE then GameMode := GM_DM;
6608 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6609 b := 1;
6610 if Length(P) >= 4 then
6611 b := StrToIntDef(P[3], 1);
6612 g_Game_StartCustom(s, GameMode, TimeLimit,
6613 GoalLimit, MaxLives, Options, b);
6614 end;
6615 end
6616 else
6617 if P[2] = '' then
6618 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6619 else
6620 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[2]), P[1]]));
6621 end else
6622 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6623 end
6624 else if cmd = 'host' then
6625 begin
6626 if gGameSettings.GameType <> GT_NONE then
6627 begin
6628 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6629 Exit;
6630 end;
6631 if Length(P) < 4 then
6632 begin
6633 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
6634 Exit;
6635 end;
6636 if not StrToIp(P[1], listen) then
6637 Exit;
6638 prt := StrToIntDef(P[2], 25666);
6640 P[3] := addWadExtension(P[3]);
6641 if FileExists(MapsDir + P[3]) then
6642 begin
6643 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6644 if Length(P) < 5 then
6645 begin
6646 SetLength(P, 5);
6647 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
6648 end;
6650 s := P[3] + ':\' + UpperCase(P[4]);
6652 if g_Map_Exist(MapsDir + s) then
6653 begin
6654 // Çàïóñêàåì ñâîþ èãðó
6655 g_Game_Free();
6656 with gGameSettings do
6657 begin
6658 GameMode := g_Game_TextToMode(gcGameMode);
6659 if gSwitchGameMode <> GM_NONE then
6660 GameMode := gSwitchGameMode;
6661 if GameMode = GM_NONE then GameMode := GM_DM;
6662 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6663 b := 0;
6664 if Length(P) >= 6 then
6665 b := StrToIntDef(P[5], 0);
6666 g_Game_StartServer(s, GameMode, TimeLimit,
6667 GoalLimit, MaxLives, Options, b, listen, prt);
6668 end;
6669 end
6670 else
6671 if P[4] = '' then
6672 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6673 else
6674 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]));
6675 end else
6676 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6677 end
6678 else if cmd = 'map' then
6679 begin
6680 if Length(P) = 1 then
6681 begin
6682 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6683 begin
6684 g_Console_Add(cmd + ' <MAP>');
6685 g_Console_Add(cmd + ' <WAD> [MAP]');
6686 end else
6687 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6688 end else
6689 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6690 begin
6691 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6692 if Length(P) < 3 then
6693 begin
6694 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6695 s := UpperCase(P[1]);
6696 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6697 begin // Êàðòà íàøëàñü
6698 gExitByTrigger := False;
6699 if gGameOn then
6700 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6701 gNextMap := s;
6702 gExit := EXIT_ENDLEVELCUSTOM;
6703 end
6704 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6705 g_Game_ChangeMap(s);
6706 end else
6707 begin
6708 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6709 P[1] := addWadExtension(P[1]);
6710 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6711 if FileExists(MapsDir + P[1]) then
6712 begin
6713 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6714 SetLength(P, 3);
6715 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6717 s := P[1] + ':\' + P[2];
6719 if g_Map_Exist(MapsDir + s) then
6720 begin
6721 gExitByTrigger := False;
6722 if gGameOn then
6723 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6724 gNextMap := s;
6725 gExit := EXIT_ENDLEVELCUSTOM;
6726 end
6727 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6728 g_Game_ChangeMap(s);
6729 end else
6730 if P[2] = '' then
6731 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6732 else
6733 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6734 end else
6735 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6736 end;
6737 end else
6738 begin
6739 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6740 P[1] := addWadExtension(P[1]);
6741 if FileExists(MapsDir + P[1]) then
6742 begin
6743 // Íàøëè WAD ôàéë
6744 P[2] := UpperCase(P[2]);
6745 s := P[1] + ':\' + P[2];
6747 if g_Map_Exist(MapsDir + s) then
6748 begin // Íàøëè êàðòó
6749 gExitByTrigger := False;
6750 if gGameOn then
6751 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6752 gNextMap := s;
6753 gExit := EXIT_ENDLEVELCUSTOM;
6754 end
6755 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6756 g_Game_ChangeMap(s);
6757 end else
6758 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6759 end else
6760 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6761 end;
6762 end else
6763 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6764 end
6765 else if cmd = 'nextmap' then
6766 begin
6767 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6768 g_Console_Add(_lc[I_MSG_NOT_GAME])
6769 else begin
6770 nm := True;
6771 if Length(P) = 1 then
6772 begin
6773 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6774 begin
6775 g_Console_Add(cmd + ' <MAP>');
6776 g_Console_Add(cmd + ' <WAD> [MAP]');
6777 end else begin
6778 nm := False;
6779 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6780 end;
6781 end else
6782 begin
6783 nm := False;
6784 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6785 begin
6786 if Length(P) < 3 then
6787 begin
6788 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6789 s := UpperCase(P[1]);
6790 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6791 begin // Êàðòà íàøëàñü
6792 gExitByTrigger := False;
6793 gNextMap := s;
6794 nm := True;
6795 end else
6796 begin
6797 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6798 P[1] := addWadExtension(P[1]);
6799 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
6800 if FileExists(MapsDir + P[1]) then
6801 begin
6802 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6803 SetLength(P, 3);
6804 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6806 s := P[1] + ':\' + P[2];
6808 if g_Map_Exist(MapsDir + s) then
6809 begin // Óñòàíàâëèâàåì êàðòó
6810 gExitByTrigger := False;
6811 gNextMap := s;
6812 nm := True;
6813 end else
6814 if P[2] = '' then
6815 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6816 else
6817 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6818 end else
6819 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6820 end;
6821 end else
6822 begin
6823 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6824 P[1] := addWadExtension(P[1]);
6825 if FileExists(MapsDir + P[1]) then
6826 begin
6827 // Íàøëè WAD ôàéë
6828 P[2] := UpperCase(P[2]);
6829 s := P[1] + ':\' + P[2];
6831 if g_Map_Exist(MapsDir + s) then
6832 begin // Íàøëè êàðòó
6833 gExitByTrigger := False;
6834 gNextMap := s;
6835 nm := True;
6836 end else
6837 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6838 end else
6839 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6840 end;
6841 end else
6842 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6843 end;
6844 if nm then
6845 if gNextMap = '' then
6846 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6847 else
6848 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6849 end;
6850 end
6851 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6852 begin
6853 if not gGameOn then
6854 g_Console_Add(_lc[I_MSG_NOT_GAME])
6855 else
6856 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6857 begin
6858 gExitByTrigger := False;
6859 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6860 if (gNextMap = '') and (gTriggers <> nil) then
6861 for a := 0 to High(gTriggers) do
6862 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6863 begin
6864 gExitByTrigger := True;
6865 //gNextMap := gTriggers[a].Data.MapName;
6866 gNextMap := gTriggers[a].tgcMap;
6867 Break;
6868 end;
6869 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6870 if gNextMap = '' then
6871 gNextMap := g_Game_GetNextMap();
6872 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6873 if not isWadPath(gNextMap) then
6874 s := gGameSettings.WAD + ':\' + gNextMap
6875 else
6876 s := gNextMap;
6877 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6878 if g_Map_Exist(MapsDir + s) then
6879 gExit := EXIT_ENDLEVELCUSTOM
6880 else
6881 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6882 end else
6883 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6884 end
6885 else if (cmd = 'event') then
6886 begin
6887 if (Length(P) <= 1) then
6888 begin
6889 for a := 0 to High(gEvents) do
6890 if gEvents[a].Command = '' then
6891 g_Console_Add(gEvents[a].Name + ' <none>')
6892 else
6893 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6894 Exit;
6895 end;
6896 if (Length(P) = 2) then
6897 begin
6898 for a := 0 to High(gEvents) do
6899 if gEvents[a].Name = P[1] then
6900 if gEvents[a].Command = '' then
6901 g_Console_Add(gEvents[a].Name + ' <none>')
6902 else
6903 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6904 Exit;
6905 end;
6906 for a := 0 to High(gEvents) do
6907 if gEvents[a].Name = P[1] then
6908 begin
6909 gEvents[a].Command := '';
6910 for b := 2 to High(P) do
6911 if Pos(' ', P[b]) = 0 then
6912 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6913 else
6914 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6915 gEvents[a].Command := Trim(gEvents[a].Command);
6916 Exit;
6917 end;
6918 end
6919 else if cmd = 'suicide' then
6920 begin
6921 if gGameOn then
6922 begin
6923 if g_Game_IsClient then
6924 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6925 else
6926 begin
6927 if gPlayer1 <> nil then
6928 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6929 if gPlayer2 <> nil then
6930 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6931 end;
6932 end;
6933 end
6934 // Êîìàíäû Ñâîåé èãðû:
6935 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6936 begin
6937 if cmd = 'bot_addred' then
6938 begin
6939 if Length(P) > 1 then
6940 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6941 else
6942 g_Bot_Add(TEAM_RED, 2);
6943 end
6944 else if cmd = 'bot_addblue' then
6945 begin
6946 if Length(P) > 1 then
6947 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6948 else
6949 g_Bot_Add(TEAM_BLUE, 2);
6950 end
6951 else if cmd = 'spectate' then
6952 begin
6953 if not gGameOn then
6954 Exit;
6955 g_Game_Spectate();
6956 end
6957 else if cmd = 'say' then
6958 begin
6959 if g_Game_IsServer and g_Game_IsNet then
6960 begin
6961 if Length(P) > 1 then
6962 begin
6963 chstr := '';
6964 for a := 1 to High(P) do
6965 chstr := chstr + P[a] + ' ';
6967 if Length(chstr) > 200 then SetLength(chstr, 200);
6969 if Length(chstr) < 1 then
6970 begin
6971 g_Console_Add('say <text>');
6972 Exit;
6973 end;
6975 chstr := b_Text_Format(chstr);
6976 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6977 end
6978 else g_Console_Add('say <text>');
6979 end else
6980 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6981 end
6982 else if cmd = 'tell' then
6983 begin
6984 if g_Game_IsServer and g_Game_IsNet then
6985 begin
6986 if (Length(P) > 2) and (P[1] <> '') then
6987 begin
6988 chstr := '';
6989 for a := 2 to High(P) do
6990 chstr := chstr + P[a] + ' ';
6992 if Length(chstr) > 200 then SetLength(chstr, 200);
6994 if Length(chstr) < 1 then
6995 begin
6996 g_Console_Add('tell <playername> <text>');
6997 Exit;
6998 end;
7000 pl := g_Net_Client_ByName(P[1]);
7001 if pl <> nil then
7002 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
7003 else
7004 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
7005 end
7006 else g_Console_Add('tell <playername> <text>');
7007 end else
7008 g_Console_Add(_lc[I_MSG_SERVERONLY]);
7009 end
7010 else if (cmd = 'overtime') and not g_Game_IsClient then
7011 begin
7012 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
7013 Exit;
7014 // Äîïîëíèòåëüíîå âðåìÿ:
7015 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
7017 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
7018 [gGameSettings.TimeLimit div 3600,
7019 (gGameSettings.TimeLimit div 60) mod 60,
7020 gGameSettings.TimeLimit mod 60]));
7021 if g_Game_IsNet then MH_SEND_GameSettings;
7022 end
7023 else if (cmd = 'rcon_password') and g_Game_IsClient then
7024 begin
7025 if (Length(P) <= 1) then
7026 g_Console_Add('rcon_password <password>')
7027 else
7028 MC_SEND_RCONPassword(P[1]);
7029 end
7030 else if cmd = 'rcon' then
7031 begin
7032 if g_Game_IsClient then
7033 begin
7034 if Length(P) > 1 then
7035 begin
7036 chstr := '';
7037 for a := 1 to High(P) do
7038 chstr := chstr + P[a] + ' ';
7040 if Length(chstr) > 200 then SetLength(chstr, 200);
7042 if Length(chstr) < 1 then
7043 begin
7044 g_Console_Add('rcon <command>');
7045 Exit;
7046 end;
7048 MC_SEND_RCONCommand(chstr);
7049 end
7050 else g_Console_Add('rcon <command>');
7051 end;
7052 end
7053 else if cmd = 'ready' then
7054 begin
7055 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
7056 gLMSRespawnTime := gTime + 100;
7057 end
7058 else if (cmd = 'callvote') and g_Game_IsNet then
7059 begin
7060 if Length(P) > 1 then
7061 begin
7062 chstr := '';
7063 for a := 1 to High(P) do begin
7064 if a > 1 then chstr := chstr + ' ';
7065 chstr := chstr + P[a];
7066 end;
7068 if Length(chstr) > 200 then SetLength(chstr, 200);
7070 if Length(chstr) < 1 then
7071 begin
7072 g_Console_Add('callvote <command>');
7073 Exit;
7074 end;
7076 if g_Game_IsClient then
7077 MC_SEND_Vote(True, chstr)
7078 else
7079 g_Game_StartVote(chstr, gPlayer1Settings.Name);
7080 g_Console_Process('vote', True);
7081 end
7082 else
7083 g_Console_Add('callvote <command>');
7084 end
7085 else if (cmd = 'vote') and g_Game_IsNet then
7086 begin
7087 if g_Game_IsClient then
7088 MC_SEND_Vote(False)
7089 else if gVoteInProgress then
7090 begin
7091 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7092 a := Floor((NetClientCount+1)/2.0) + 1
7093 else
7094 a := Floor(NetClientCount/2.0) + 1;
7095 if gVoted then
7096 begin
7097 Dec(gVoteCount);
7098 gVoted := False;
7099 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
7100 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
7101 end
7102 else
7103 begin
7104 Inc(gVoteCount);
7105 gVoted := True;
7106 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
7107 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
7108 g_Game_CheckVote;
7109 end;
7110 end;
7111 end
7112 end;
7113 end;
7115 procedure g_TakeScreenShot();
7116 var
7117 a: Word;
7118 FileName: string;
7119 ssdir, t: string;
7120 st: TStream;
7121 ok: Boolean;
7122 begin
7123 if e_NoGraphics then Exit;
7124 ssdir := GameDir+'/screenshots';
7125 if not findFileCI(ssdir, true) then
7126 begin
7127 // try to create dir
7128 try
7129 CreateDir(ssdir);
7130 except
7131 end;
7132 if not findFileCI(ssdir, true) then exit; // alas
7133 end;
7134 try
7135 for a := 1 to High(Word) do
7136 begin
7137 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
7138 t := FileName;
7139 if findFileCI(t, true) then continue;
7140 if not findFileCI(FileName) then
7141 begin
7142 ok := false;
7143 st := createDiskFile(FileName);
7144 try
7145 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
7146 ok := true;
7147 finally
7148 st.Free();
7149 end;
7150 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
7151 break;
7152 end;
7153 end;
7154 except
7155 end;
7156 end;
7158 procedure g_Game_InGameMenu(Show: Boolean);
7159 begin
7160 if (g_ActiveWindow = nil) and Show then
7161 begin
7162 if gGameSettings.GameType = GT_SINGLE then
7163 g_GUI_ShowWindow('GameSingleMenu')
7164 else
7165 begin
7166 if g_Game_IsClient then
7167 g_GUI_ShowWindow('GameClientMenu')
7168 else
7169 if g_Game_IsNet then
7170 g_GUI_ShowWindow('GameServerMenu')
7171 else
7172 g_GUI_ShowWindow('GameCustomMenu');
7173 end;
7174 g_Sound_PlayEx('MENU_OPEN');
7176 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7177 if (not g_Game_IsNet) then
7178 g_Game_Pause(True);
7179 end
7180 else
7181 if (g_ActiveWindow <> nil) and (not Show) then
7182 begin
7183 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7184 if (not g_Game_IsNet) then
7185 g_Game_Pause(False);
7186 end;
7187 end;
7189 procedure g_Game_Pause (Enable: Boolean);
7190 var
7191 oldPause: Boolean;
7192 begin
7193 if not gGameOn then exit;
7195 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
7197 oldPause := gPause;
7198 gPauseMain := Enable;
7200 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
7201 end;
7203 procedure g_Game_HolmesPause (Enable: Boolean);
7204 var
7205 oldPause: Boolean;
7206 begin
7207 if not gGameOn then exit;
7208 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
7210 oldPause := gPause;
7211 gPauseHolmes := Enable;
7213 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
7214 end;
7216 procedure g_Game_PauseAllSounds(Enable: Boolean);
7217 var
7218 i: Integer;
7219 begin
7220 // Òðèããåðû:
7221 if gTriggers <> nil then
7222 for i := 0 to High(gTriggers) do
7223 with gTriggers[i] do
7224 if (TriggerType = TRIGGER_SOUND) and
7225 (Sound <> nil) and
7226 Sound.IsPlaying() then
7227 begin
7228 Sound.Pause(Enable);
7229 end;
7231 // Çâóêè èãðîêîâ:
7232 if gPlayers <> nil then
7233 for i := 0 to High(gPlayers) do
7234 if gPlayers[i] <> nil then
7235 gPlayers[i].PauseSounds(Enable);
7237 // Ìóçûêà:
7238 if gMusic <> nil then
7239 gMusic.Pause(Enable);
7240 end;
7242 procedure g_Game_StopAllSounds(all: Boolean);
7243 var
7244 i: Integer;
7245 begin
7246 if gTriggers <> nil then
7247 for i := 0 to High(gTriggers) do
7248 with gTriggers[i] do
7249 if (TriggerType = TRIGGER_SOUND) and
7250 (Sound <> nil) then
7251 Sound.Stop();
7253 if gMusic <> nil then
7254 gMusic.Stop();
7256 if all then
7257 e_StopChannels();
7258 end;
7260 procedure g_Game_UpdateTriggerSounds();
7261 var
7262 i: Integer;
7263 begin
7264 if gTriggers <> nil then
7265 for i := 0 to High(gTriggers) do
7266 with gTriggers[i] do
7267 if (TriggerType = TRIGGER_SOUND) and
7268 (Sound <> nil) and
7269 (tgcLocal) and
7270 Sound.IsPlaying() then
7271 begin
7272 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
7273 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
7274 begin
7275 Sound.SetPan(0.5 - tgcPan/255.0);
7276 Sound.SetVolume(tgcVolume/255.0);
7277 end
7278 else
7279 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
7280 end;
7281 end;
7283 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
7284 begin
7285 Result := False;
7286 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
7287 begin
7288 Result := True;
7289 Exit;
7290 end;
7291 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
7292 begin
7293 Result := True;
7294 Exit;
7295 end;
7296 if gSpectMode <> SPECT_PLAYERS then
7297 Exit;
7298 if gSpectPID1 = UID then
7299 begin
7300 Result := True;
7301 Exit;
7302 end;
7303 if gSpectViewTwo and (gSpectPID2 = UID) then
7304 begin
7305 Result := True;
7306 Exit;
7307 end;
7308 end;
7310 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
7311 var
7312 Pl: TPlayer;
7313 begin
7314 Result := False;
7315 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
7316 begin
7317 Result := True;
7318 Exit;
7319 end;
7320 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
7321 begin
7322 Result := True;
7323 Exit;
7324 end;
7325 if gSpectMode <> SPECT_PLAYERS then
7326 Exit;
7327 Pl := g_Player_Get(gSpectPID1);
7328 if (Pl <> nil) and (Pl.Team = Team) then
7329 begin
7330 Result := True;
7331 Exit;
7332 end;
7333 if gSpectViewTwo then
7334 begin
7335 Pl := g_Player_Get(gSpectPID2);
7336 if (Pl <> nil) and (Pl.Team = Team) then
7337 begin
7338 Result := True;
7339 Exit;
7340 end;
7341 end;
7342 end;
7344 procedure g_Game_Message(Msg: string; Time: Word);
7345 begin
7346 MessageLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont);
7347 MessageText := b_Text_Wrap(b_Text_Format(Msg), MessageLineLength);
7348 MessageTime := Time;
7349 end;
7351 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
7352 const
7353 punct: Array[0..13] of String =
7354 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
7355 var
7356 i, j: Integer;
7357 ok: Boolean;
7358 fpText: String;
7360 function IsPunctuation(S: String): Boolean;
7361 var
7362 i: Integer;
7363 begin
7364 Result := False;
7365 if Length(S) <> 1 then
7366 Exit;
7367 for i := Low(punct) to High(punct) do
7368 if S = punct[i] then
7369 begin
7370 Result := True;
7371 break;
7372 end;
7373 end;
7374 function FilterPunctuation(S: String): String;
7375 var
7376 i: Integer;
7377 begin
7378 for i := Low(punct) to High(punct) do
7379 S := StringReplace(S, punct[i], ' ', [rfReplaceAll]);
7380 Result := S;
7381 end;
7382 begin
7383 ok := False;
7385 if gUseChatSounds and Taunt and (gChatSounds <> nil) and (Pos(': ', Text) > 0) then
7386 begin
7387 // remove player name
7388 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
7389 // for FullWord check
7390 Text := toLowerCase1251(' ' + Text + ' ');
7391 fpText := FilterPunctuation(Text);
7393 for i := 0 to Length(gChatSounds) - 1 do
7394 begin
7395 ok := True;
7396 for j := 0 to Length(gChatSounds[i].Tags) - 1 do
7397 begin
7398 if gChatSounds[i].FullWord and (not IsPunctuation(gChatSounds[i].Tags[j])) then
7399 ok := Pos(' ' + gChatSounds[i].Tags[j] + ' ', fpText) > 0
7400 else
7401 ok := Pos(gChatSounds[i].Tags[j], Text) > 0;
7402 if not ok then
7403 break;
7404 end;
7405 if ok then
7406 begin
7407 gChatSounds[i].Sound.Play();
7408 break;
7409 end;
7410 end;
7411 end;
7412 if not ok then
7413 g_Sound_PlayEx('SOUND_GAME_RADIO');
7414 end;
7416 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
7417 var
7418 a: Integer;
7419 begin
7420 case gAnnouncer of
7421 ANNOUNCE_NONE:
7422 Exit;
7423 ANNOUNCE_ME,
7424 ANNOUNCE_MEPLUS:
7425 if not g_Game_IsWatchedPlayer(SpawnerUID) then
7426 Exit;
7427 end;
7428 for a := 0 to 3 do
7429 if goodsnd[a].IsPlaying() then
7430 Exit;
7432 goodsnd[Random(4)].Play();
7433 end;
7435 procedure g_Game_Announce_KillCombo(Param: Integer);
7436 var
7437 UID: Word;
7438 c, n: Byte;
7439 Pl: TPlayer;
7440 Name: String;
7441 begin
7442 UID := Param and $FFFF;
7443 c := Param shr 16;
7444 if c < 2 then
7445 Exit;
7447 Pl := g_Player_Get(UID);
7448 if Pl = nil then
7449 Name := '?'
7450 else
7451 Name := Pl.Name;
7453 case c of
7454 2: begin
7455 n := 0;
7456 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
7457 end;
7458 3: begin
7459 n := 1;
7460 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
7461 end;
7462 4: begin
7463 n := 2;
7464 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
7465 end;
7466 else begin
7467 n := 3;
7468 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
7469 end;
7470 end;
7472 case gAnnouncer of
7473 ANNOUNCE_NONE:
7474 Exit;
7475 ANNOUNCE_ME:
7476 if not g_Game_IsWatchedPlayer(UID) then
7477 Exit;
7478 ANNOUNCE_MEPLUS:
7479 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
7480 Exit;
7481 end;
7483 if killsnd[n].IsPlaying() then
7484 killsnd[n].Stop();
7485 killsnd[n].Play();
7486 end;
7488 procedure g_Game_Announce_BodyKill(SpawnerUID: Word);
7489 var
7490 a: Integer;
7491 begin
7492 case gAnnouncer of
7493 ANNOUNCE_NONE:
7494 Exit;
7495 ANNOUNCE_ME,
7496 ANNOUNCE_MEPLUS:
7497 if not g_Game_IsWatchedPlayer(SpawnerUID) then
7498 Exit;
7499 end;
7500 for a := 0 to 2 do
7501 if hahasnd[a].IsPlaying() then
7502 Exit;
7504 hahasnd[Random(3)].Play();
7505 end;
7507 procedure g_Game_StartVote(Command, Initiator: string);
7508 var
7509 Need: Integer;
7510 begin
7511 if not gVotesEnabled then Exit;
7512 if gGameSettings.GameType <> GT_SERVER then Exit;
7513 if gVoteInProgress or gVotePassed then
7514 begin
7515 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
7516 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
7517 Exit;
7518 end;
7519 gVoteInProgress := True;
7520 gVotePassed := False;
7521 gVoteTimer := gTime + gVoteTimeout * 1000;
7522 gVoteCount := 0;
7523 gVoted := False;
7524 gVoteCommand := Command;
7526 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7527 Need := Floor((NetClientCount+1)/2.0)+1
7528 else
7529 Need := Floor(NetClientCount/2.0)+1;
7530 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
7531 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
7532 end;
7534 procedure g_Game_CheckVote;
7535 var
7536 I, Need: Integer;
7537 begin
7538 if gGameSettings.GameType <> GT_SERVER then Exit;
7539 if not gVoteInProgress then Exit;
7541 if (gTime >= gVoteTimer) then
7542 begin
7543 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7544 Need := Floor((NetClientCount+1)/2.0) + 1
7545 else
7546 Need := Floor(NetClientCount/2.0) + 1;
7547 if gVoteCount >= Need then
7548 begin
7549 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7550 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7551 gVotePassed := True;
7552 gVoteCmdTimer := gTime + 5000;
7553 end
7554 else
7555 begin
7556 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
7557 MH_SEND_VoteEvent(NET_VE_FAILED);
7558 end;
7559 if NetClients <> nil then
7560 for I := Low(NetClients) to High(NetClients) do
7561 if NetClients[i].Used then
7562 NetClients[i].Voted := False;
7563 gVoteInProgress := False;
7564 gVoted := False;
7565 gVoteCount := 0;
7566 end
7567 else
7568 begin
7569 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7570 Need := Floor((NetClientCount+1)/2.0) + 1
7571 else
7572 Need := Floor(NetClientCount/2.0) + 1;
7573 if gVoteCount >= Need then
7574 begin
7575 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
7576 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
7577 gVoteInProgress := False;
7578 gVotePassed := True;
7579 gVoteCmdTimer := gTime + 5000;
7580 gVoted := False;
7581 gVoteCount := 0;
7582 if NetClients <> nil then
7583 for I := Low(NetClients) to High(NetClients) do
7584 if NetClients[i].Used then
7585 NetClients[i].Voted := False;
7586 end;
7587 end;
7588 end;
7590 procedure g_Game_LoadMapList(FileName: string);
7591 var
7592 ListFile: TextFile;
7593 s: string;
7594 begin
7595 MapList := nil;
7596 MapIndex := -1;
7598 if not FileExists(FileName) then Exit;
7600 AssignFile(ListFile, FileName);
7601 Reset(ListFile);
7602 while not EOF(ListFile) do
7603 begin
7604 ReadLn(ListFile, s);
7606 s := Trim(s);
7607 if s = '' then Continue;
7609 SetLength(MapList, Length(MapList)+1);
7610 MapList[High(MapList)] := s;
7611 end;
7612 CloseFile(ListFile);
7613 end;
7615 procedure g_Game_SetDebugMode();
7616 begin
7617 gDebugMode := True;
7618 // ×èòû (äàæå â ñâîåé èãðå):
7619 gCheats := True;
7620 end;
7622 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
7623 var
7624 i: Word;
7625 begin
7626 if Length(LoadingStat.Msgs) = 0 then
7627 Exit;
7629 with LoadingStat do
7630 begin
7631 if not reWrite then
7632 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
7633 if NextMsg = Length(Msgs) then
7634 begin // scroll
7635 for i := 0 to High(Msgs)-1 do
7636 Msgs[i] := Msgs[i+1];
7637 end
7638 else
7639 Inc(NextMsg);
7640 end else
7641 if NextMsg = 0 then
7642 Inc(NextMsg);
7644 Msgs[NextMsg-1] := Text;
7645 CurValue := 0;
7646 MaxValue := Max;
7647 ShowCount := 0;
7648 PBarWasHere := false;
7649 end;
7651 g_ActiveWindow := nil;
7653 ProcessLoading(true);
7654 end;
7656 procedure g_Game_StepLoading(Value: Integer = -1);
7657 begin
7658 with LoadingStat do
7659 begin
7660 if Value = -1 then
7661 begin
7662 Inc(CurValue);
7663 Inc(ShowCount);
7664 end
7665 else
7666 CurValue := Value;
7667 if (ShowCount > LOADING_SHOW_STEP) or (Value > -1) then
7668 begin
7669 ShowCount := 0;
7670 ProcessLoading();
7671 end;
7672 end;
7673 end;
7675 procedure g_Game_ClearLoading();
7676 var
7677 len: Word;
7678 begin
7679 with LoadingStat do
7680 begin
7681 CurValue := 0;
7682 MaxValue := 0;
7683 ShowCount := 0;
7684 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
7685 if len < 1 then len := 1;
7686 SetLength(Msgs, len);
7687 for len := Low(Msgs) to High(Msgs) do
7688 Msgs[len] := '';
7689 NextMsg := 0;
7690 PBarWasHere := false;
7691 end;
7692 end;
7694 procedure Parse_Params(var pars: TParamStrValues);
7695 var
7696 i: Integer;
7697 s: String;
7698 begin
7699 SetLength(pars, 0);
7700 i := 1;
7701 while i <= ParamCount do
7702 begin
7703 s := ParamStr(i);
7704 if (s[1] = '-') and (Length(s) > 1) then
7705 begin
7706 if (s[2] = '-') and (Length(s) > 2) then
7707 begin // Îäèíî÷íûé ïàðàìåòð
7708 SetLength(pars, Length(pars) + 1);
7709 with pars[High(pars)] do
7710 begin
7711 Name := LowerCase(s);
7712 Value := '+';
7713 end;
7714 end
7715 else
7716 if (i < ParamCount) then
7717 begin // Ïàðàìåòð ñî çíà÷åíèåì
7718 Inc(i);
7719 SetLength(pars, Length(pars) + 1);
7720 with pars[High(pars)] do
7721 begin
7722 Name := LowerCase(s);
7723 Value := LowerCase(ParamStr(i));
7724 end;
7725 end;
7726 end;
7728 Inc(i);
7729 end;
7730 end;
7732 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
7733 var
7734 i: Integer;
7735 begin
7736 Result := '';
7737 for i := 0 to High(pars) do
7738 if pars[i].Name = aName then
7739 begin
7740 Result := pars[i].Value;
7741 Break;
7742 end;
7743 end;
7745 procedure g_Game_Process_Params();
7746 var
7747 pars: TParamStrValues;
7748 map: String;
7749 GMode, n: Byte;
7750 LimT, LimS: Integer;
7751 Opt: LongWord;
7752 Lives: Integer;
7753 s: String;
7754 Port: Integer;
7755 ip: String;
7756 F: TextFile;
7757 begin
7758 Parse_Params(pars);
7760 // Debug mode:
7761 s := Find_Param_Value(pars, '--debug');
7762 if (s <> '') then
7763 begin
7764 g_Game_SetDebugMode();
7765 s := Find_Param_Value(pars, '--netdump');
7766 if (s <> '') then
7767 NetDump := True;
7768 end;
7770 // Connect when game loads
7771 ip := Find_Param_Value(pars, '-connect');
7773 if ip <> '' then
7774 begin
7775 s := Find_Param_Value(pars, '-port');
7776 if (s = '') or not TryStrToInt(s, Port) then
7777 Port := 25666;
7779 s := Find_Param_Value(pars, '-pw');
7781 g_Game_StartClient(ip, Port, s);
7782 Exit;
7783 end;
7785 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7786 if (s <> '') then
7787 begin
7788 gDefaultMegawadStart := s;
7789 end;
7791 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7792 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7793 begin
7794 gDefaultMegawadStart := DF_Default_Megawad_Start;
7795 end;
7797 // Start map when game loads:
7798 map := LowerCase(Find_Param_Value(pars, '-map'));
7799 if isWadPath(map) then
7800 begin
7801 // Game mode:
7802 s := Find_Param_Value(pars, '-gm');
7803 GMode := g_Game_TextToMode(s);
7804 if GMode = GM_NONE then GMode := GM_DM;
7805 if GMode = GM_SINGLE then GMode := GM_COOP;
7807 // Time limit:
7808 s := Find_Param_Value(pars, '-limt');
7809 if (s = '') or (not TryStrToInt(s, LimT)) then
7810 LimT := 0;
7811 if LimT < 0 then
7812 LimT := 0;
7814 // Goal limit:
7815 s := Find_Param_Value(pars, '-lims');
7816 if (s = '') or (not TryStrToInt(s, LimS)) then
7817 LimS := 0;
7818 if LimS < 0 then
7819 LimS := 0;
7821 // Lives limit:
7822 s := Find_Param_Value(pars, '-lives');
7823 if (s = '') or (not TryStrToInt(s, Lives)) then
7824 Lives := 0;
7825 if Lives < 0 then
7826 Lives := 0;
7828 // Options:
7829 s := Find_Param_Value(pars, '-opt');
7830 if (s = '') then
7831 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7832 else
7833 Opt := StrToIntDef(s, 0);
7834 if Opt = 0 then
7835 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7837 // Close after map:
7838 s := Find_Param_Value(pars, '--close');
7839 if (s <> '') then
7840 gMapOnce := True;
7842 // Override map to test:
7843 s := LowerCase(Find_Param_Value(pars, '-testmap'));
7844 if s <> '' then
7845 gTestMap := MapsDir + s;
7847 // Delete test map after play:
7848 s := Find_Param_Value(pars, '--testdelete');
7849 if (s <> '') then
7850 begin
7851 gMapToDelete := MapsDir + map;
7852 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
7853 Halt(1);
7854 end;
7856 // Delete temporary WAD after play:
7857 s := Find_Param_Value(pars, '--tempdelete');
7858 if (s <> '') and (gTestMap <> '') then
7859 begin
7860 gMapToDelete := gTestMap;
7861 gTempDelete := True;
7862 end;
7864 // Number of players:
7865 s := Find_Param_Value(pars, '-pl');
7866 if (s = '') then
7867 n := 1
7868 else
7869 n := StrToIntDef(s, 1);
7871 // Start:
7872 s := Find_Param_Value(pars, '-port');
7873 if (s = '') or not TryStrToInt(s, Port) then
7874 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7875 else
7876 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7877 end;
7879 // Execute script when game loads:
7880 s := Find_Param_Value(pars, '-exec');
7881 if s <> '' then
7882 begin
7883 if not isWadPath(s) then
7884 s := GameDir + '/' + s;
7886 {$I-}
7887 AssignFile(F, s);
7888 Reset(F);
7889 if IOResult <> 0 then
7890 begin
7891 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7892 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7893 CloseFile(F);
7894 Exit;
7895 end;
7896 e_WriteLog('Executing script: ' + s, TMsgType.Notify);
7897 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7899 while not EOF(F) do
7900 begin
7901 ReadLn(F, s);
7902 if IOResult <> 0 then
7903 begin
7904 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
7905 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7906 CloseFile(F);
7907 Exit;
7908 end;
7909 if Pos('#', s) <> 1 then // script comment
7910 g_Console_Process(s, True);
7911 end;
7913 CloseFile(F);
7914 {$I+}
7915 end;
7917 SetLength(pars, 0);
7918 end;
7920 begin
7921 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7922 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7923 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7924 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7926 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7927 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7928 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7929 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7931 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7932 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7934 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7935 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7937 {$IFDEF ENABLE_HOLMES}
7938 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7939 {$ENDIF}
7941 conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds, 'ignore level bounds', '', false);
7943 conRegVar('r_scale', @g_dbg_scale, 0.01, 100.0, 'render scale', '', false);
7945 conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
7946 conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
7948 conRegVar('r_smallmap_align_h', @r_smallmap_h, 'halign: 0: left; 1: center; 2: right', 'horizontal aligning of small maps');
7949 conRegVar('r_smallmap_align_v', @r_smallmap_v, 'valign: 0: top; 1: center; 2: bottom', 'vertial aligning of small maps');
7950 end.