DEADSOFTWARE

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