DEADSOFTWARE

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