DEADSOFTWARE

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