DEADSOFTWARE

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