DEADSOFTWARE

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