DEADSOFTWARE

cleanup: move init and main loop code from g_window
[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, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_game;
18 interface
20 uses
21 SysUtils, Classes,
22 MAPDEF,
23 g_base, g_basic, g_player, r_graphics, g_res_downloader,
24 g_sound, g_gui, utils, md5, mempool, xprofiler,
25 g_touch, g_weapons;
27 type
28 TGameSettings = record
29 GameType: Byte;
30 GameMode: Byte;
31 TimeLimit: Word;
32 GoalLimit: Word;
33 WarmupTime: Word;
34 SpawnInvul: Word;
35 ItemRespawnTime: Word;
36 MaxLives: Byte;
37 Options: LongWord;
38 WAD: String;
39 end;
41 TGameEvent = record
42 Name: String;
43 Command: String;
44 end;
46 TDelayedEvent = record
47 Pending: Boolean;
48 Time: LongWord;
49 DEType: Byte;
50 DENum: Integer;
51 DEStr: String;
52 end;
54 TChatSound = record
55 Sound: TPlayableSound;
56 Tags: Array of String;
57 FullWord: Boolean;
58 end;
60 TPlayerSettings = record
61 Name: String;
62 Model: String;
63 Color: TRGB;
64 Team: Byte;
65 end;
67 TMegaWADInfo = record
68 Name: String;
69 Description: String;
70 Author: String;
71 Pic: String;
72 end;
74 THearPoint = record
75 Active: Boolean;
76 Coords: TDFPoint;
77 end;
79 function g_Game_IsNet(): Boolean;
80 function g_Game_IsServer(): Boolean;
81 function g_Game_IsClient(): Boolean;
82 procedure g_Game_Init();
83 procedure g_Game_Free (freeTextures: Boolean=true);
84 procedure g_Game_LoadData();
85 procedure g_Game_FreeData();
86 procedure g_Game_Update();
87 procedure g_Game_PreUpdate();
88 procedure g_Game_Quit();
89 procedure g_Game_SetupScreenSize();
90 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
91 function g_Game_ModeToText(Mode: Byte): string;
92 function g_Game_TextToMode(Mode: string): Byte;
93 procedure g_Game_ExecuteEvent(Name: String);
94 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
95 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
96 procedure g_Game_RemovePlayer();
97 procedure g_Game_Spectate();
98 procedure g_Game_SpectateCenterView();
99 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
100 procedure g_Game_StartCustom(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte);
101 procedure g_Game_StartServer(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte; IPAddr: LongWord; Port: Word);
102 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
103 procedure g_Game_Restart();
104 procedure g_Game_RestartLevel();
105 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
106 function g_Game_ClientWAD (NewWAD: String; const WHash: TMD5Digest): AnsiString;
107 function g_Game_StartMap(asMegawad: Boolean; 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(Filename: string = '');
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 PlayerSettingsCVars(P: SSArray);
137 procedure SystemCommands(P: SSArray);
138 procedure GameCommands(P: SSArray);
139 procedure GameCheats(P: SSArray);
140 procedure DebugCommands(P: SSArray);
141 procedure g_Game_Process_Params;
142 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
143 procedure g_Game_StepLoading(Value: Integer = -1);
144 procedure g_Game_ClearLoading();
145 procedure g_Game_SetDebugMode();
147 function IsActivePlayer(p: TPlayer): Boolean;
148 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
149 procedure SortGameStat(var stat: TPlayerStatArray);
151 procedure KeyPress (K: Word);
152 procedure CharPress (C: AnsiChar);
154 { procedure SetWinPause(Enable: Boolean); }
156 const
157 GAME_TICK = 28;
159 LOADING_SHOW_STEP = 100;
160 LOADING_INTERLINE = 20;
162 GT_NONE = 0;
163 GT_SINGLE = 1;
164 GT_CUSTOM = 2;
165 GT_SERVER = 3;
166 GT_CLIENT = 4;
168 GM_NONE = 0;
169 GM_DM = 1;
170 GM_TDM = 2;
171 GM_CTF = 3;
172 GM_COOP = 4;
173 GM_SINGLE = 5;
175 MESSAGE_DIKEY = WM_USER + 1;
177 EXIT_QUIT = 1;
178 EXIT_SIMPLE = 2;
179 EXIT_RESTART = 3;
180 EXIT_ENDLEVELSINGLE = 4;
181 EXIT_ENDLEVELCUSTOM = 5;
183 GAME_OPTION_RESERVED = 1;
184 GAME_OPTION_TEAMDAMAGE = 2;
185 GAME_OPTION_ALLOWEXIT = 4;
186 GAME_OPTION_WEAPONSTAY = 8;
187 GAME_OPTION_MONSTERS = 16;
188 GAME_OPTION_BOTVSPLAYER = 32;
189 GAME_OPTION_BOTVSMONSTER = 64;
190 GAME_OPTION_DMKEYS = 128;
191 GAME_OPTION_TEAMHITTRACE = 256;
192 GAME_OPTION_TEAMHITPROJECTILE = 512;
193 GAME_OPTION_TEAMABSORBDAMAGE = 1024;
195 STATE_NONE = 0;
196 STATE_MENU = 1;
197 STATE_FOLD = 2;
198 STATE_INTERCUSTOM = 3;
199 STATE_INTERSINGLE = 4;
200 STATE_INTERTEXT = 5;
201 STATE_INTERPIC = 6;
202 STATE_ENDPIC = 7;
203 STATE_SLIST = 8;
205 LMS_RESPAWN_NONE = 0;
206 LMS_RESPAWN_WARMUP = 1;
207 LMS_RESPAWN_FINAL = 2;
209 SPECT_NONE = 0;
210 SPECT_STATS = 1;
211 SPECT_MAPVIEW = 2;
212 SPECT_PLAYERS = 3;
214 DE_GLOBEVENT = 0;
215 DE_BFGHIT = 1;
216 DE_KILLCOMBO = 2;
217 DE_BODYKILL = 3;
219 ANNOUNCE_NONE = 0;
220 ANNOUNCE_ME = 1;
221 ANNOUNCE_MEPLUS = 2;
222 ANNOUNCE_ALL = 3;
224 CONFIG_FILENAME = 'Doom2DF.cfg';
226 TEST_MAP_NAME = '$$$_TEST_$$$';
228 STD_PLAYER_MODEL = 'Doomer';
230 {$IFDEF HEADLESS}
231 DEFAULT_PLAYERS = 0;
232 {$ELSE}
233 DEFAULT_PLAYERS = 1;
234 {$ENDIF}
236 STATFILE_VERSION = $03;
238 var
239 gStdFont: DWORD;
240 gGameSettings: TGameSettings;
241 gPlayer1Settings: TPlayerSettings;
242 gPlayer2Settings: TPlayerSettings;
243 gGameOn: Boolean;
244 gPlayerScreenSize: TDFPoint;
245 gPlayer1ScreenCoord: TDFPoint;
246 gPlayer2ScreenCoord: TDFPoint;
247 gPlayer1: TPlayer = nil;
248 gPlayer2: TPlayer = nil;
249 gPlayerDrawn: TPlayer = nil;
250 gTime: LongWord;
251 gLerpFactor: Single = 1.0;
252 gSwitchGameMode: Byte = GM_DM;
253 gHearPoint1, gHearPoint2: THearPoint;
254 gSoundEffectsDF: Boolean = False;
255 gSoundTriggerTime: Word = 0;
256 gAnnouncer: Integer = ANNOUNCE_NONE;
257 goodsnd: array[0..3] of TPlayableSound;
258 killsnd: array[0..3] of TPlayableSound;
259 hahasnd: array[0..2] of TPlayableSound;
260 sound_get_flag: array[0..1] of TPlayableSound;
261 sound_lost_flag: array[0..1] of TPlayableSound;
262 sound_ret_flag: array[0..1] of TPlayableSound;
263 sound_cap_flag: array[0..1] of TPlayableSound;
264 gBodyKillEvent: Integer = -1;
265 gDefInterTime: ShortInt = -1;
266 gInterEndTime: LongWord = 0;
267 gInterTime: LongWord = 0;
268 gServInterTime: Byte = 0;
269 gGameStartTime: LongWord = 0;
270 gTotalMonsters: Integer = 0;
271 gPauseMain: Boolean = false;
272 gPauseHolmes: Boolean = false;
273 gShowTime: Boolean = False;
274 gShowFPS: Boolean = False;
275 gShowGoals: Boolean = True;
276 gShowStat: Boolean = True;
277 gShowPIDs: Boolean = False;
278 gShowKillMsg: Boolean = True;
279 gShowLives: Boolean = True;
280 gShowPing: Boolean = False;
281 gShowMap: Boolean = False;
282 gExit: Byte = 0;
283 gState: Byte = STATE_NONE;
284 sX, sY: Integer;
285 sWidth, sHeight: Word;
286 gSpectMode: Byte = SPECT_NONE;
287 gSpectHUD: Boolean = True;
288 gSpectKeyPress: Boolean = False;
289 gSpectX: Integer = 0;
290 gSpectY: Integer = 0;
291 gSpectStep: Byte = 8;
292 gSpectViewTwo: Boolean = False;
293 gSpectPID1: Integer = -1;
294 gSpectPID2: Integer = -1;
295 gSpectAuto: Boolean = False;
296 gSpectAutoNext: LongWord;
297 gSpectAutoStepX: Integer;
298 gSpectAutoStepY: Integer;
299 gMusic: TMusic = nil;
300 gLoadGameMode: Boolean;
301 gCheats: Boolean = False;
302 gMapOnce: Boolean = False;
303 gMapToDelete: String;
304 gTempDelete: Boolean = False;
305 gLastMap: Boolean = False;
306 gScreenWidth: Word;
307 gScreenHeight: Word;
308 gResolutionChange: Boolean = False;
309 gRC_Width, gRC_Height: Integer;
310 gRC_FullScreen, gRC_Maximized: Boolean;
311 gLanguageChange: Boolean = False;
312 gDebugMode: Boolean = False;
313 g_debug_Sounds: Boolean = False;
314 g_debug_Frames: Boolean = False;
315 g_debug_WinMsgs: Boolean = False;
316 g_debug_MonsterOff: Boolean = False;
317 g_debug_BotAIOff: Byte = 0;
318 g_debug_HealthBar: Boolean = False;
319 g_Debug_Player: Boolean = False;
320 gCoopMonstersKilled: Word = 0;
321 gCoopSecretsFound: Word = 0;
322 gCoopTotalMonstersKilled: Word = 0;
323 gCoopTotalSecretsFound: Word = 0;
324 gCoopTotalMonsters: Word = 0;
325 gCoopTotalSecrets: Word = 0;
326 gStatsOff: Boolean = False;
327 gStatsPressed: Boolean = False;
328 gExitByTrigger: Boolean = False;
329 gNextMap: String = '';
330 gLMSRespawn: Byte = LMS_RESPAWN_NONE;
331 gLMSRespawnTime: Cardinal = 0;
332 gLMSSoftSpawn: Boolean = False;
333 gMissionFailed: Boolean = False;
334 gVoteInProgress: Boolean = False;
335 gVotePassed: Boolean = False;
336 gVoteCommand: string = '';
337 gVoteTimer: Cardinal = 0;
338 gVoteCmdTimer: Cardinal = 0;
339 gVoteCount: Integer = 0;
340 gVoteTimeout: Cardinal = 30;
341 gVoted: Boolean = False;
342 gVotesEnabled: Boolean = True;
343 gEvents: Array of TGameEvent;
344 gDelayedEvents: Array of TDelayedEvent;
345 gUseChatSounds: Boolean = True;
346 gChatSounds: Array of TChatSound;
347 gSelectWeapon: Array [0..1, WP_FIRST..WP_LAST] of Boolean; // [player, weapon]
348 gInterReadyCount: Integer = 0;
350 g_dbg_ignore_bounds: Boolean = false;
351 r_smallmap_h: Integer = 0; // 0: left; 1: center; 2: right
352 r_smallmap_v: Integer = 2; // 0: top; 1: center; 2: bottom
354 // move button values:
355 // bits 0-1: l/r state:
356 // 0: neither left, nor right pressed
357 // 1: left pressed
358 // 2: right pressed
359 // bits 4-5: l/r state when strafe was pressed
360 P1MoveButton: Byte = 0;
361 P2MoveButton: Byte = 0;
363 g_profile_frame_update: Boolean = false;
364 g_profile_frame_draw: Boolean = false;
365 g_profile_collision: Boolean = false;
366 g_profile_los: Boolean = false;
367 g_profile_history_size: Integer = 1000;
369 g_rlayer_back: Boolean = true;
370 g_rlayer_step: Boolean = true;
371 g_rlayer_wall: Boolean = true;
372 g_rlayer_door: Boolean = true;
373 g_rlayer_acid1: Boolean = true;
374 g_rlayer_acid2: Boolean = true;
375 g_rlayer_water: Boolean = true;
376 g_rlayer_fore: Boolean = true;
378 wNeedTimeReset: Boolean = false;
380 procedure g_ResetDynlights ();
381 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
382 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
384 function conIsCheatsEnabled (): Boolean; inline;
385 function gPause (): Boolean; inline;
387 type (* private state *)
388 TEndCustomGameStat = record
389 PlayerStat: TPlayerStatArray;
390 TeamStat: TTeamStat;
391 GameTime: LongWord;
392 GameMode: Byte;
393 Map, MapName: String;
394 end;
396 TEndSingleGameStat = record
397 PlayerStat: Array [0..1] of record
398 Kills: Integer;
399 Secrets: Integer;
400 end;
401 GameTime: LongWord;
402 TwoPlayers: Boolean;
403 TotalSecrets: Integer;
404 end;
406 TLoadingStat = record
407 CurValue: Integer;
408 MaxValue: Integer;
409 ShowCount: Integer;
410 Msgs: Array of String;
411 NextMsg: Word;
412 PBarWasHere: Boolean; // did we draw a progress bar for this message?
413 end;
415 TDynLight = record
416 x, y, radius: Integer;
417 r, g, b, a: Single;
418 exploCount: Integer;
419 exploRadius: Integer;
420 end;
422 var (* private state *)
423 CustomStat: TEndCustomGameStat;
424 StatShotDone: Boolean;
425 StatFilename: string = ''; // used by stat screenshot to save with the same name as the csv
426 SingleStat: TEndSingleGameStat;
427 hasPBarGfx: Boolean;
428 LoadingStat: TLoadingStat;
429 MessageText: String;
430 IsDrawStat: Boolean;
431 EndingGameCounter: Byte;
432 UPS: Word;
433 g_playerLight: Boolean;
434 g_dynLights: array of TDynLight = nil;
435 g_dynLightCount: Integer = 0;
437 implementation
439 uses
440 {$IFDEF ENABLE_HOLMES}
441 g_holmes,
442 {$ENDIF}
443 e_res, g_textures, g_window, g_menu,
444 e_input, e_log, g_console, r_console, g_items, g_map, g_panel,
445 g_playermodel, g_gfx, g_options, Math,
446 g_triggers, g_monsters, e_sound, CONFIG,
447 g_language, g_net, g_phys,
448 ENet, e_msg, g_netmsg, g_netmaster,
449 sfs, wadreader, g_system, r_playermodel;
451 var
452 charbuff: packed array [0..15] of AnsiChar = (
453 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
454 );
456 function Translit (const S: AnsiString): AnsiString;
457 var
458 i: Integer;
459 begin
460 Result := S;
461 for i := 1 to Length(Result) do
462 begin
463 case Result[i] of
464 #$C9: Result[i] := 'Q';
465 #$D6: Result[i] := 'W';
466 #$D3: Result[i] := 'E';
467 #$CA: Result[i] := 'R';
468 #$C5: Result[i] := 'T';
469 #$CD: Result[i] := 'Y';
470 #$C3: Result[i] := 'U';
471 #$D8: Result[i] := 'I';
472 #$D9: Result[i] := 'O';
473 #$C7: Result[i] := 'P';
474 #$D5: Result[i] := '['; //Chr(219);
475 #$DA: Result[i] := ']'; //Chr(221);
476 #$D4: Result[i] := 'A';
477 #$DB: Result[i] := 'S';
478 #$C2: Result[i] := 'D';
479 #$C0: Result[i] := 'F';
480 #$CF: Result[i] := 'G';
481 #$D0: Result[i] := 'H';
482 #$CE: Result[i] := 'J';
483 #$CB: Result[i] := 'K';
484 #$C4: Result[i] := 'L';
485 #$C6: Result[i] := ';'; //Chr(186);
486 #$DD: Result[i] := #39; //Chr(222);
487 #$DF: Result[i] := 'Z';
488 #$D7: Result[i] := 'X';
489 #$D1: Result[i] := 'C';
490 #$CC: Result[i] := 'V';
491 #$C8: Result[i] := 'B';
492 #$D2: Result[i] := 'N';
493 #$DC: Result[i] := 'M';
494 #$C1: Result[i] := ','; //Chr(188);
495 #$DE: Result[i] := '.'; //Chr(190);
496 end;
497 end;
498 end;
501 function CheckCheat (ct: TStrings_Locale; eofs: Integer=0): Boolean;
502 var
503 ls1, ls2: string;
504 begin
505 ls1 := CheatEng[ct];
506 ls2 := Translit(CheatRus[ct]);
507 if length(ls1) = 0 then ls1 := '~';
508 if length(ls2) = 0 then ls2 := '~';
509 result :=
510 (Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1)) = ls1) or
511 (Translit(Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1))) = ls1) or
512 (Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2)) = ls2) or
513 (Translit(Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2))) = ls2);
515 if ct = I_GAME_CHEAT_JETPACK then
516 begin
517 e_WriteLog('ls1: ['+ls1+']', MSG_NOTIFY);
518 e_WriteLog('ls2: ['+ls2+']', MSG_NOTIFY);
519 e_WriteLog('bf0: ['+Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1))+']', MSG_NOTIFY);
520 e_WriteLog('bf1: ['+Translit(Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1)))+']', MSG_NOTIFY);
521 e_WriteLog('bf2: ['+Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2))+']', MSG_NOTIFY);
522 e_WriteLog('bf3: ['+Translit(Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2)))+']', MSG_NOTIFY);
523 end;
525 end;
527 procedure Cheat ();
528 const
529 CHEAT_DAMAGE = 500;
530 label
531 Cheated;
532 var
533 s, s2: string;
534 c: ShortString;
535 a: Integer;
536 begin
538 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
539 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode))
540 or g_Game_IsNet then Exit;
542 if not gGameOn then exit;
543 if not conIsCheatsEnabled then exit;
545 s := 'SOUND_GAME_RADIO';
547 //
548 if CheckCheat(I_GAME_CHEAT_GODMODE) then
549 begin
550 if gPlayer1 <> nil then gPlayer1.GodMode := not gPlayer1.GodMode;
551 if gPlayer2 <> nil then gPlayer2.GodMode := not gPlayer2.GodMode;
552 goto Cheated;
553 end;
554 // RAMBO
555 if CheckCheat(I_GAME_CHEAT_WEAPONS) then
556 begin
557 if gPlayer1 <> nil then gPlayer1.AllRulez(False);
558 if gPlayer2 <> nil then gPlayer2.AllRulez(False);
559 goto Cheated;
560 end;
561 // TANK
562 if CheckCheat(I_GAME_CHEAT_HEALTH) then
563 begin
564 if gPlayer1 <> nil then gPlayer1.AllRulez(True);
565 if gPlayer2 <> nil then gPlayer2.AllRulez(True);
566 goto Cheated;
567 end;
568 // IDDQD
569 if CheckCheat(I_GAME_CHEAT_DEATH) then
570 begin
571 if gPlayer1 <> nil then gPlayer1.Damage(CHEAT_DAMAGE, 0, 0, 0, HIT_TRAP);
572 if gPlayer2 <> nil then gPlayer2.Damage(CHEAT_DAMAGE, 0, 0, 0, HIT_TRAP);
573 s := 'SOUND_MONSTER_HAHA';
574 goto Cheated;
575 end;
576 //
577 if CheckCheat(I_GAME_CHEAT_DOORS) then
578 begin
579 g_Triggers_OpenAll();
580 goto Cheated;
581 end;
582 // GOODBYE
583 if CheckCheat(I_GAME_CHEAT_NEXTMAP) then
584 begin
585 if gTriggers <> nil then
586 for a := 0 to High(gTriggers) do
587 if gTriggers[a].TriggerType = TRIGGER_EXIT then
588 begin
589 gExitByTrigger := True;
590 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
591 g_Game_ExitLevel(gTriggers[a].tgcMap);
592 Break;
593 end;
594 goto Cheated;
595 end;
596 //
597 s2 := Copy(charbuff, 15, 2);
598 if CheckCheat(I_GAME_CHEAT_CHANGEMAP, 2) and (s2[1] >= '0') and (s2[1] <= '9') and (s2[2] >= '0') and (s2[2] <= '9') then
599 begin
600 if g_Map_Exist(gGameSettings.WAD + ':\MAP' + s2) then
601 begin
602 c := 'MAP' + s2;
603 g_Game_ExitLevel(c);
604 end;
605 goto Cheated;
606 end;
607 //
608 if CheckCheat(I_GAME_CHEAT_FLY) then
609 begin
610 gFly := not gFly;
611 goto Cheated;
612 end;
613 // BULLFROG
614 if CheckCheat(I_GAME_CHEAT_JUMPS) then
615 begin
616 VEL_JUMP := 30-VEL_JUMP;
617 goto Cheated;
618 end;
619 // FORMULA1
620 if CheckCheat(I_GAME_CHEAT_SPEED) then
621 begin
622 MAX_RUNVEL := 32-MAX_RUNVEL;
623 goto Cheated;
624 end;
625 // CONDOM
626 if CheckCheat(I_GAME_CHEAT_SUIT) then
627 begin
628 if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_SUIT);
629 if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_SUIT);
630 goto Cheated;
631 end;
632 //
633 if CheckCheat(I_GAME_CHEAT_AIR) then
634 begin
635 if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_OXYGEN);
636 if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_OXYGEN);
637 goto Cheated;
638 end;
639 // PURELOVE
640 if CheckCheat(I_GAME_CHEAT_BERSERK) then
641 begin
642 if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_MEDKIT_BLACK);
643 if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_MEDKIT_BLACK);
644 goto Cheated;
645 end;
646 //
647 if CheckCheat(I_GAME_CHEAT_JETPACK) then
648 begin
649 if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_JETPACK);
650 if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_JETPACK);
651 goto Cheated;
652 end;
653 // CASPER
654 if CheckCheat(I_GAME_CHEAT_NOCLIP) then
655 begin
656 if gPlayer1 <> nil then gPlayer1.SwitchNoClip;
657 if gPlayer2 <> nil then gPlayer2.SwitchNoClip;
658 goto Cheated;
659 end;
660 //
661 if CheckCheat(I_GAME_CHEAT_NOTARGET) then
662 begin
663 if gPlayer1 <> nil then gPlayer1.NoTarget := not gPlayer1.NoTarget;
664 if gPlayer2 <> nil then gPlayer2.NoTarget := not gPlayer2.NoTarget;
665 goto Cheated;
666 end;
667 // INFERNO
668 if CheckCheat(I_GAME_CHEAT_NORELOAD) then
669 begin
670 if gPlayer1 <> nil then gPlayer1.NoReload := not gPlayer1.NoReload;
671 if gPlayer2 <> nil then gPlayer2.NoReload := not gPlayer2.NoReload;
672 goto Cheated;
673 end;
674 if CheckCheat(I_GAME_CHEAT_AIMLINE) then
675 begin
676 gAimLine := not gAimLine;
677 goto Cheated;
678 end;
679 if CheckCheat(I_GAME_CHEAT_AUTOMAP) then
680 begin
681 gShowMap := not gShowMap;
682 goto Cheated;
683 end;
684 Exit;
686 Cheated:
687 g_Sound_PlayEx(s);
688 end;
691 procedure KeyPress (K: Word);
692 {$IFNDEF HEADLESS}
693 var
694 Msg: g_gui.TMessage;
695 {$ENDIF}
696 begin
697 {$IFNDEF HEADLESS}
698 case K of
699 VK_ESCAPE: // <Esc>:
700 begin
701 if (g_ActiveWindow <> nil) then
702 begin
703 Msg.Msg := WM_KEYDOWN;
704 Msg.WParam := VK_ESCAPE;
705 g_ActiveWindow.OnMessage(Msg);
706 if (not g_Game_IsNet) and (g_ActiveWindow = nil) then g_Game_Pause(false); //Fn loves to do this
707 end
708 else if (gState <> STATE_FOLD) then
709 begin
710 if gGameOn or (gState = STATE_INTERSINGLE) or (gState = STATE_INTERCUSTOM) then
711 begin
712 g_Game_InGameMenu(True);
713 end
714 else if (gExit = 0) and (gState <> STATE_SLIST) then
715 begin
716 if (gState <> STATE_MENU) then
717 begin
718 if (NetMode <> NET_NONE) then
719 begin
720 g_Game_StopAllSounds(True);
721 g_Game_Free;
722 gState := STATE_MENU;
723 Exit;
724 end;
725 end;
726 g_GUI_ShowWindow('MainMenu');
727 g_Sound_PlayEx('MENU_OPEN');
728 end;
729 end;
730 end;
732 IK_F2, IK_F3, IK_F4, IK_F5, IK_F6, IK_F7, IK_F10:
733 begin // <F2> .. <F6> � <F12>
734 if gGameOn and (not gConsoleShow) and (not gChatShow) then
735 begin
736 while (g_ActiveWindow <> nil) do g_GUI_HideWindow(False);
737 if (not g_Game_IsNet) then g_Game_Pause(True);
738 case K of
739 IK_F2: g_Menu_Show_SaveMenu();
740 IK_F3: g_Menu_Show_LoadMenu();
741 IK_F4: g_Menu_Show_GameSetGame();
742 IK_F5: g_Menu_Show_OptionsVideo();
743 IK_F6: g_Menu_Show_OptionsSound();
744 IK_F7: g_Menu_Show_EndGameMenu();
745 IK_F10: g_Menu_Show_QuitGameMenu();
746 end;
747 end;
748 end;
750 else
751 begin
752 gJustChatted := False;
753 if gConsoleShow or gChatShow then
754 begin
755 g_Console_Control(K);
756 end
757 else if (g_ActiveWindow <> nil) then
758 begin
759 Msg.Msg := WM_KEYDOWN;
760 Msg.WParam := K;
761 g_ActiveWindow.OnMessage(Msg);
762 end
763 else if (gState = STATE_MENU) then
764 begin
765 g_GUI_ShowWindow('MainMenu');
766 g_Sound_PlayEx('MENU_OPEN');
767 end;
768 end;
769 end;
770 {$ENDIF}
771 end;
773 procedure CharPress (C: AnsiChar);
774 var
775 Msg: g_gui.TMessage;
776 a: Integer;
777 begin
778 if gConsoleShow or gChatShow then
779 begin
780 g_Console_Char(C)
781 end
782 else if (g_ActiveWindow <> nil) then
783 begin
784 Msg.Msg := WM_CHAR;
785 Msg.WParam := Ord(C);
786 g_ActiveWindow.OnMessage(Msg);
787 end
788 else
789 begin
790 for a := 0 to 14 do charbuff[a] := charbuff[a+1];
791 charbuff[15] := upcase1251(C);
792 Cheat();
793 end;
794 end;
797 // ////////////////////////////////////////////////////////////////////////// //
798 function gPause (): Boolean; inline; begin result := gPauseMain or gPauseHolmes; end;
800 procedure g_ResetDynlights ();
801 var
802 lnum, idx: Integer;
803 begin
804 if not gwin_has_stencil then begin g_dynLightCount := 0; exit; end;
805 lnum := 0;
806 for idx := 0 to g_dynLightCount-1 do
807 begin
808 if g_dynLights[idx].exploCount = -666 then
809 begin
810 // skip it
811 end
812 else
813 begin
814 // explosion
815 Inc(g_dynLights[idx].exploCount);
816 if (g_dynLights[idx].exploCount < 10) then
817 begin
818 g_dynLights[idx].radius := g_dynLights[idx].exploRadius+g_dynLights[idx].exploCount*8;
819 g_dynLights[idx].a := 0.4+g_dynLights[idx].exploCount/10;
820 if (g_dynLights[idx].a > 0.8) then g_dynLights[idx].a := 0.8;
821 if lnum <> idx then g_dynLights[lnum] := g_dynLights[idx];
822 Inc(lnum);
823 end;
824 end;
825 end;
826 g_dynLightCount := lnum;
827 end;
829 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
830 begin
831 if not gwin_has_stencil then exit;
832 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
833 g_dynLights[g_dynLightCount].x := x;
834 g_dynLights[g_dynLightCount].y := y;
835 g_dynLights[g_dynLightCount].radius := radius;
836 g_dynLights[g_dynLightCount].r := r;
837 g_dynLights[g_dynLightCount].g := g;
838 g_dynLights[g_dynLightCount].b := b;
839 g_dynLights[g_dynLightCount].a := a;
840 g_dynLights[g_dynLightCount].exploCount := -666;
841 Inc(g_dynLightCount);
842 end;
844 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
845 begin
846 if not gwin_has_stencil then exit;
847 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
848 g_dynLights[g_dynLightCount].x := x;
849 g_dynLights[g_dynLightCount].y := y;
850 g_dynLights[g_dynLightCount].radius := 0;
851 g_dynLights[g_dynLightCount].exploRadius := radius;
852 g_dynLights[g_dynLightCount].r := r;
853 g_dynLights[g_dynLightCount].g := g;
854 g_dynLights[g_dynLightCount].b := b;
855 g_dynLights[g_dynLightCount].a := 0;
856 g_dynLights[g_dynLightCount].exploCount := 0;
857 Inc(g_dynLightCount);
858 end;
860 // ////////////////////////////////////////////////////////////////////////// //
861 function conIsCheatsEnabled (): Boolean; inline;
862 begin
863 result := false;
864 if g_Game_IsNet then exit;
865 if not gDebugMode then
866 begin
867 //if not gCheats then exit;
868 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
869 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then exit;
870 end;
871 result := true;
872 end;
874 // ////////////////////////////////////////////////////////////////////////// //
875 type
876 TParamStrValue = record
877 Name: String;
878 Value: String;
879 end;
881 TParamStrValues = Array of TParamStrValue;
883 const
884 INTER_ACTION_TEXT = 1;
885 INTER_ACTION_PIC = 2;
886 INTER_ACTION_MUSIC = 3;
888 var
889 UPSCounter: Word;
890 UPSTime: LongWord;
891 DataLoaded: Boolean = False;
892 MessageTime: Word;
893 MessageLineLength: Integer = 80;
894 MapList: SSArray = nil;
895 MapIndex: Integer = -1;
896 InterReadyTime: Integer = -1;
897 StatDate: string = '';
898 MegaWAD: record
899 info: TMegaWADInfo;
900 endpic: String;
901 endmus: String;
902 res: record
903 text: Array of ShortString;
904 anim: Array of ShortString;
905 pic: Array of ShortString;
906 mus: Array of ShortString;
907 end;
908 triggers: Array of record
909 event: ShortString;
910 actions: Array of record
911 action, p1, p2: Integer;
912 end;
913 end;
914 cur_trigger: Integer;
915 cur_action: Integer;
916 end;
917 //InterPic: String;
918 InterText: record
919 lines: SSArray;
920 img: String;
921 cur_line: Integer;
922 cur_char: Integer;
923 counter: Integer;
924 endtext: Boolean;
925 end;
927 function Compare(a, b: TPlayerStat): Integer;
928 begin
929 if a.Spectator then Result := 1
930 else if b.Spectator then Result := -1
931 else if a.Frags < b.Frags then Result := 1
932 else if a.Frags > b.Frags then Result := -1
933 else if a.Deaths < b.Deaths then Result := -1
934 else if a.Deaths > b.Deaths then Result := 1
935 else if a.Kills < b.Kills then Result := -1
936 else Result := 1;
937 end;
939 procedure SortGameStat(var stat: TPlayerStatArray);
940 var
941 I, J: Integer;
942 T: TPlayerStat;
943 begin
944 if stat = nil then Exit;
946 for I := High(stat) downto Low(stat) do
947 for J := Low(stat) to High(stat) - 1 do
948 if Compare(stat[J], stat[J + 1]) = 1 then
949 begin
950 T := stat[J];
951 stat[J] := stat[J + 1];
952 stat[J + 1] := T;
953 end;
954 end;
956 // saves a shitty CSV containing the game stats passed to it
957 procedure SaveGameStat(Stat: TEndCustomGameStat; Path: string);
958 var
959 s: TextFile;
960 dir, fname, map, mode, etime: String;
961 I: Integer;
962 begin
963 try
964 dir := e_GetWriteableDir(StatsDirs);
965 // stats are placed in stats/yy/mm/dd/*.csv
966 fname := e_CatPath(dir, Path);
967 ForceDirectories(fname); // ensure yy/mm/dd exists within the stats dir
968 fname := e_CatPath(fname, StatFilename + '.csv');
969 AssignFile(s, fname);
970 try
971 Rewrite(s);
972 // line 1: stats ver, datetime, server name, map name, game mode, time limit, score limit, dmflags, game time, num players
973 if g_Game_IsNet then fname := NetServerName else fname := '';
974 map := g_ExtractWadNameNoPath(gMapInfo.Map) + ':/' + g_ExtractFileName(gMapInfo.Map);
975 mode := g_Game_ModeToText(Stat.GameMode);
976 etime := Format('%d:%.2d:%.2d', [
977 Stat.GameTime div 1000 div 3600,
978 (Stat.GameTime div 1000 div 60) mod 60,
979 Stat.GameTime div 1000 mod 60
980 ]);
981 WriteLn(s, 'stats_ver,datetime,server,map,mode,timelimit,scorelimit,dmflags,time,num_players');
982 WriteLn(s, Format('%d,%s,%s,%s,%s,%u,%u,%u,%s,%d', [
983 STATFILE_VERSION,
984 StatDate,
985 dquoteStr(fname),
986 dquoteStr(map),
987 mode,
988 gGameSettings.TimeLimit,
989 gGameSettings.GoalLimit,
990 gGameSettings.Options,
991 etime,
992 Length(Stat.PlayerStat)
993 ]));
994 // line 2: game specific shit
995 // if it's a team game: red score, blue score
996 // if it's a coop game: monsters killed, monsters total, secrets found, secrets total
997 // otherwise nothing
998 if Stat.GameMode in [GM_TDM, GM_CTF] then
999 WriteLn(s,
1000 Format('red_score,blue_score' + LineEnding + '%d,%d', [Stat.TeamStat[TEAM_RED].Goals, Stat.TeamStat[TEAM_BLUE].Goals]))
1001 else if Stat.GameMode in [GM_COOP, GM_SINGLE] then
1002 WriteLn(s,
1003 Format('mon_killed,mon_total,secrets_found,secrets_total' + LineEnding + '%d,%d,%d,%d',[gCoopMonstersKilled, gTotalMonsters, gCoopSecretsFound, gSecretsCount]));
1004 // lines 3-...: team, player name, frags, deaths
1005 WriteLn(s, 'team,name,frags,deaths');
1006 for I := Low(Stat.PlayerStat) to High(Stat.PlayerStat) do
1007 with Stat.PlayerStat[I] do
1008 WriteLn(s, Format('%d,%s,%d,%d', [Team, dquoteStr(Name), Frags, Deaths]));
1009 except
1010 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [fname]));
1011 end;
1012 except
1013 g_Console_Add('could not create gamestats file "' + fname + '"');
1014 end;
1015 CloseFile(s);
1016 end;
1018 function g_Game_ModeToText(Mode: Byte): string;
1019 begin
1020 Result := '';
1021 case Mode of
1022 GM_DM: Result := _lc[I_MENU_GAME_TYPE_DM];
1023 GM_TDM: Result := _lc[I_MENU_GAME_TYPE_TDM];
1024 GM_CTF: Result := _lc[I_MENU_GAME_TYPE_CTF];
1025 GM_COOP: Result := _lc[I_MENU_GAME_TYPE_COOP];
1026 GM_SINGLE: Result := _lc[I_MENU_GAME_TYPE_SINGLE];
1027 end;
1028 end;
1030 function g_Game_TextToMode(Mode: string): Byte;
1031 begin
1032 Result := GM_NONE;
1033 Mode := UpperCase(Mode);
1034 if Mode = _lc[I_MENU_GAME_TYPE_DM] then
1035 begin
1036 Result := GM_DM;
1037 Exit;
1038 end;
1039 if Mode = _lc[I_MENU_GAME_TYPE_TDM] then
1040 begin
1041 Result := GM_TDM;
1042 Exit;
1043 end;
1044 if Mode = _lc[I_MENU_GAME_TYPE_CTF] then
1045 begin
1046 Result := GM_CTF;
1047 Exit;
1048 end;
1049 if Mode = _lc[I_MENU_GAME_TYPE_COOP] then
1050 begin
1051 Result := GM_COOP;
1052 Exit;
1053 end;
1054 if Mode = _lc[I_MENU_GAME_TYPE_SINGLE] then
1055 begin
1056 Result := GM_SINGLE;
1057 Exit;
1058 end;
1059 end;
1061 function g_Game_IsNet(): Boolean;
1062 begin
1063 Result := (gGameSettings.GameType in [GT_SERVER, GT_CLIENT]);
1064 end;
1066 function g_Game_IsServer(): Boolean;
1067 begin
1068 Result := (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM, GT_SERVER]);
1069 end;
1071 function g_Game_IsClient(): Boolean;
1072 begin
1073 Result := (gGameSettings.GameType = GT_CLIENT);
1074 end;
1076 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
1077 var
1078 w: TWADFile;
1079 cfg: TConfig;
1080 p: Pointer;
1081 len: Integer;
1082 begin
1083 Result.name := ExtractFileName(WAD);
1084 Result.description := '';
1085 Result.author := '';
1087 w := TWADFile.Create();
1088 w.ReadFile(WAD);
1090 if not w.GetResource('INTERSCRIPT', p, len) then
1091 begin
1092 w.Free();
1093 Exit;
1094 end;
1096 cfg := TConfig.CreateMem(p, len);
1097 Result.name := cfg.ReadStr('megawad', 'name', ExtractFileName(WAD));
1098 Result.description := cfg.ReadStr('megawad', 'description', '');
1099 Result.author := cfg.ReadStr('megawad', 'author', '');
1100 Result.pic := cfg.ReadStr('megawad', 'pic', '');
1101 cfg.Free();
1103 FreeMem(p);
1104 end;
1106 procedure g_Game_FreeWAD();
1107 var
1108 a: Integer;
1109 begin
1110 for a := 0 to High(MegaWAD.res.pic) do
1111 if MegaWAD.res.pic[a] <> '' then
1112 g_Texture_Delete(MegaWAD.res.pic[a]);
1114 for a := 0 to High(MegaWAD.res.mus) do
1115 if MegaWAD.res.mus[a] <> '' then
1116 g_Sound_Delete(MegaWAD.res.mus[a]);
1118 MegaWAD.res.pic := nil;
1119 MegaWAD.res.text := nil;
1120 MegaWAD.res.anim := nil;
1121 MegaWAD.res.mus := nil;
1122 MegaWAD.triggers := nil;
1124 g_Texture_Delete('TEXTURE_endpic');
1125 g_Sound_Delete('MUSIC_endmus');
1127 ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
1128 gGameSettings.WAD := '';
1129 end;
1131 procedure g_Game_LoadWAD(WAD: string);
1132 var
1133 w: TWADFile;
1134 cfg: TConfig;
1135 p: Pointer;
1136 {b, }len: Integer;
1137 s: AnsiString;
1138 begin
1139 g_Game_FreeWAD();
1140 gGameSettings.WAD := WAD;
1141 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then
1142 Exit;
1144 MegaWAD.info := g_Game_GetMegaWADInfo(WAD);
1146 w := TWADFile.Create();
1147 w.ReadFile(WAD);
1149 if not w.GetResource('INTERSCRIPT', p, len) then
1150 begin
1151 w.Free();
1152 Exit;
1153 end;
1155 cfg := TConfig.CreateMem(p, len);
1157 {b := 1;
1158 while True do
1159 begin
1160 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
1161 if s = '' then Break;
1162 b := b+1;
1164 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
1165 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
1167 g_Texture_CreateWADEx(s, s);
1168 end;
1170 b := 1;
1171 while True do
1172 begin
1173 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
1174 if s = '' then Break;
1175 b := b+1;
1177 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
1178 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
1180 g_Music_CreateWADEx(s, s);
1181 end;}
1183 MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
1184 if MegaWAD.endpic <> '' then
1185 begin
1186 s := e_GetResourcePath(WadDirs, MegaWAD.endpic, WAD);
1187 g_Texture_CreateWADEx('TEXTURE_endpic', s, gTextureFilter);
1188 end;
1189 MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\КОНЕЦ');
1190 if MegaWAD.endmus <> '' then
1191 begin
1192 s := e_GetResourcePath(WadDirs, MegaWAD.endmus, WAD);
1193 g_Sound_CreateWADEx('MUSIC_endmus', s, True);
1194 end;
1196 cfg.Free();
1197 FreeMem(p);
1198 w.Free();
1199 end;
1201 {procedure start_trigger(t: string);
1202 begin
1203 end;
1205 function next_trigger(): Boolean;
1206 begin
1207 end;}
1209 procedure DisableCheats();
1210 begin
1211 MAX_RUNVEL := 8;
1212 VEL_JUMP := 10;
1213 gFly := False;
1215 if gPlayer1 <> nil then gPlayer1.GodMode := False;
1216 if gPlayer2 <> nil then gPlayer2.GodMode := False;
1217 if gPlayer1 <> nil then gPlayer1.NoTarget := False;
1218 if gPlayer2 <> nil then gPlayer2.NoTarget := False;
1220 {$IF DEFINED(D2F_DEBUG)}
1221 if gPlayer1 <> nil then gPlayer1.NoTarget := True;
1222 gAimLine := g_dbg_aimline_on;
1223 {$ENDIF}
1224 end;
1226 procedure g_Game_ExecuteEvent(Name: String);
1227 var
1228 a: Integer;
1229 begin
1230 if Name = '' then
1231 Exit;
1232 if gEvents = nil then
1233 Exit;
1234 for a := 0 to High(gEvents) do
1235 if gEvents[a].Name = Name then
1236 begin
1237 if gEvents[a].Command <> '' then
1238 g_Console_Process(gEvents[a].Command, True);
1239 break;
1240 end;
1241 end;
1243 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
1244 var
1245 a, n: Integer;
1246 begin
1247 n := -1;
1248 if gDelayedEvents <> nil then
1249 for a := 0 to High(gDelayedEvents) do
1250 if not gDelayedEvents[a].Pending then
1251 begin
1252 n := a;
1253 break;
1254 end;
1255 if n = -1 then
1256 begin
1257 SetLength(gDelayedEvents, Length(gDelayedEvents) + 1);
1258 n := High(gDelayedEvents);
1259 end;
1260 gDelayedEvents[n].Pending := True;
1261 gDelayedEvents[n].DEType := DEType;
1262 gDelayedEvents[n].DENum := Num;
1263 gDelayedEvents[n].DEStr := Str;
1264 if DEType = DE_GLOBEVENT then
1265 gDelayedEvents[n].Time := (sys_GetTicks() {div 1000}) + Time
1266 else
1267 gDelayedEvents[n].Time := gTime + Time;
1268 Result := n;
1269 end;
1271 procedure EndGame();
1272 var
1273 a: Integer;
1274 FileName: string;
1275 t: TDateTime;
1276 begin
1277 if g_Game_IsNet and g_Game_IsServer then
1278 MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed));
1280 // Стоп игра:
1281 gPauseMain := false;
1282 gPauseHolmes := false;
1283 gGameOn := false;
1285 g_Game_StopAllSounds(False);
1287 MessageTime := 0;
1288 MessageText := '';
1290 EndingGameCounter := 0;
1291 g_ActiveWindow := nil;
1293 gLMSRespawn := LMS_RESPAWN_NONE;
1294 gLMSRespawnTime := 0;
1296 case gExit of
1297 EXIT_SIMPLE: // Выход через меню или конец теста
1298 begin
1299 g_Game_Free();
1301 if gMapOnce then
1302 begin // Это был тест
1303 g_Game_Quit();
1304 end
1305 else
1306 begin // Выход в главное меню
1307 gMusic.SetByName('MUSIC_MENU');
1308 gMusic.Play();
1309 if gState <> STATE_SLIST then
1310 begin
1311 g_GUI_ShowWindow('MainMenu');
1312 gState := STATE_MENU;
1313 end else
1314 begin
1315 // Обновляем список серверов
1316 slReturnPressed := True;
1317 if g_Net_Slist_Fetch(slCurrent) then
1318 begin
1319 if slCurrent = nil then
1320 slWaitStr := _lc[I_NET_SLIST_NOSERVERS];
1321 end
1322 else
1323 slWaitStr := _lc[I_NET_SLIST_ERROR];
1324 g_Serverlist_GenerateTable(slCurrent, slTable);
1325 end;
1327 g_Game_ExecuteEvent('ongameend');
1328 end;
1329 end;
1331 EXIT_RESTART: // Начать уровень сначала
1332 begin
1333 if not g_Game_IsClient then g_Game_Restart();
1334 end;
1336 EXIT_ENDLEVELCUSTOM: // Закончился уровень в Своей игре
1337 begin
1338 // Статистика Своей игры:
1339 FileName := g_ExtractWadName(gMapInfo.Map);
1341 CustomStat.GameTime := gTime;
1342 CustomStat.Map := ExtractFileName(FileName)+':'+g_ExtractFileName(gMapInfo.Map); //ResName;
1343 CustomStat.MapName := gMapInfo.Name;
1344 CustomStat.GameMode := gGameSettings.GameMode;
1345 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1346 CustomStat.TeamStat := gTeamStat;
1348 CustomStat.PlayerStat := nil;
1350 // Статистика игроков:
1351 if gPlayers <> nil then
1352 begin
1353 for a := 0 to High(gPlayers) do
1354 if gPlayers[a] <> nil then
1355 begin
1356 SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1);
1357 with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do
1358 begin
1359 Num := a;
1360 Name := gPlayers[a].Name;
1361 Frags := gPlayers[a].Frags;
1362 Deaths := gPlayers[a].Death;
1363 Kills := gPlayers[a].Kills;
1364 Team := gPlayers[a].Team;
1365 Color := gPlayers[a].Model.Color;
1366 Spectator := gPlayers[a].FSpectator;
1367 end;
1368 end;
1370 SortGameStat(CustomStat.PlayerStat);
1372 if (gSaveStats or gScreenshotStats) and (Length(CustomStat.PlayerStat) > 1) then
1373 begin
1374 t := Now;
1375 if g_Game_IsNet then StatFilename := NetServerName else StatFilename := 'local';
1376 StatDate := FormatDateTime('yymmdd_hhnnss', t);
1377 StatFilename := StatFilename + '_' + CustomStat.Map + '_' + g_Game_ModeToText(CustomStat.GameMode);
1378 StatFilename := sanitizeFilename(StatFilename) + '_' + StatDate;
1379 if gSaveStats then
1380 SaveGameStat(CustomStat, FormatDateTime('yyyy"/"mm"/"dd', t));
1381 end;
1383 StatShotDone := False;
1384 end;
1386 g_Game_ExecuteEvent('onmapend');
1387 if not g_Game_IsClient then g_Player_ResetReady;
1388 gInterReadyCount := 0;
1390 // Затухающий экран:
1391 EndingGameCounter := 255;
1392 gState := STATE_FOLD;
1393 gInterTime := 0;
1394 if gDefInterTime < 0 then
1395 gInterEndTime := IfThen((gGameSettings.GameType = GT_SERVER) and (gPlayer1 = nil), 15000, 25000)
1396 else
1397 gInterEndTime := gDefInterTime * 1000;
1398 end;
1400 EXIT_ENDLEVELSINGLE: // Закончился уровень в Одиночной игре
1401 begin
1402 // Статистика Одиночной игры:
1403 SingleStat.GameTime := gTime;
1404 SingleStat.TwoPlayers := gPlayer2 <> nil;
1405 SingleStat.TotalSecrets := gSecretsCount;
1406 // Статистика первого игрока:
1407 SingleStat.PlayerStat[0].Kills := gPlayer1.MonsterKills;
1408 SingleStat.PlayerStat[0].Secrets := gPlayer1.Secrets;
1409 // Статистика второго игрока (если есть):
1410 if SingleStat.TwoPlayers then
1411 begin
1412 SingleStat.PlayerStat[1].Kills := gPlayer2.MonsterKills;
1413 SingleStat.PlayerStat[1].Secrets := gPlayer2.Secrets;
1414 end;
1416 g_Game_ExecuteEvent('onmapend');
1418 // Есть еще карты:
1419 if gNextMap <> '' then
1420 begin
1421 gMusic.SetByName('MUSIC_INTERMUS');
1422 gMusic.Play();
1423 gState := STATE_INTERSINGLE;
1424 e_UnpressAllKeys();
1426 g_Game_ExecuteEvent('oninter');
1427 end
1428 else // Больше нет карт
1429 begin
1430 // Затухающий экран:
1431 EndingGameCounter := 255;
1432 gState := STATE_FOLD;
1433 end;
1434 end;
1435 end;
1437 // Окончание обработано:
1438 if gExit <> EXIT_QUIT then
1439 gExit := 0;
1440 end;
1442 procedure g_Game_Init();
1443 var
1444 SR: TSearchRec;
1445 knownFiles: array of AnsiString = nil;
1446 found: Boolean;
1447 wext, s: AnsiString;
1448 f: Integer;
1449 begin
1450 gExit := 0;
1451 gMapToDelete := '';
1452 gTempDelete := False;
1454 sfsGCDisable(); // temporary disable removing of temporary volumes
1456 try
1457 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE', gTextureFilter);
1458 g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER', gTextureFilter);
1459 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN', gTextureFilter);
1460 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU', gTextureFilter);
1462 LoadStdFont('STDTXT', 'STDFONT', gStdFont);
1463 LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
1464 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
1466 g_Game_ClearLoading();
1467 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
1468 g_Game_SetLoadingText('', 0, False);
1470 g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
1471 r_Console_Init;
1472 g_Console_Init();
1474 g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
1475 r_PlayerModel_Initialize;
1477 // load models from all possible wad types, in all known directories
1478 // this does a loosy job (linear search, ooph!), but meh
1479 for wext in wadExtensions do
1480 begin
1481 for f := High(ModelDirs) downto Low(ModelDirs) do
1482 begin
1483 if (FindFirst(ModelDirs[f]+DirectorySeparator+'*'+wext, faAnyFile, SR) = 0) then
1484 begin
1485 repeat
1486 found := false;
1487 for s in knownFiles do
1488 begin
1489 if (strEquCI1251(forceFilenameExt(SR.Name, ''), forceFilenameExt(ExtractFileName(s), ''))) then
1490 begin
1491 found := true;
1492 break;
1493 end;
1494 end;
1495 if not found then
1496 begin
1497 SetLength(knownFiles, length(knownFiles)+1);
1498 knownFiles[High(knownFiles)] := ModelDirs[f]+DirectorySeparator+SR.Name;
1499 end;
1500 until (FindNext(SR) <> 0);
1501 end;
1502 FindClose(SR);
1503 end;
1504 end;
1506 if (length(knownFiles) = 0) then raise Exception.Create('no player models found!');
1508 if (length(knownFiles) = 1) then e_LogWriteln('1 player model found.', TMsgType.Notify) else e_LogWritefln('%d player models found.', [Integer(length(knownFiles))], TMsgType.Notify);
1509 for s in knownFiles do
1510 begin
1511 if not g_PlayerModel_Load(s) then e_LogWritefln('Error loading model "%s"', [s], TMsgType.Warning);
1512 end;
1514 gGameOn := false;
1515 gPauseMain := false;
1516 gPauseHolmes := false;
1517 gTime := 0;
1519 {e_MouseInfo.Accel := 1.0;}
1521 g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
1522 g_Game_LoadData();
1524 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1525 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
1526 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
1527 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True, True);
1528 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
1530 {$IFNDEF HEADLESS}
1531 g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
1532 g_Menu_Init();
1533 {$ENDIF}
1535 gMusic := TMusic.Create();
1536 gMusic.SetByName('MUSIC_MENU');
1537 gMusic.Play();
1539 gGameSettings.WarmupTime := 30;
1541 gState := STATE_MENU;
1543 SetLength(gEvents, 6);
1544 gEvents[0].Name := 'ongamestart';
1545 gEvents[1].Name := 'ongameend';
1546 gEvents[2].Name := 'onmapstart';
1547 gEvents[3].Name := 'onmapend';
1548 gEvents[4].Name := 'oninter';
1549 gEvents[5].Name := 'onwadend';
1550 finally
1551 sfsGCEnable(); // enable releasing unused volumes
1552 end;
1553 end;
1555 procedure g_Game_Free(freeTextures: Boolean=true);
1556 begin
1557 if NetMode = NET_CLIENT then g_Net_Disconnect();
1558 if NetMode = NET_SERVER then g_Net_Host_Die();
1560 g_Map_Free(freeTextures);
1561 g_Player_Free();
1562 g_Player_RemoveAllCorpses();
1564 gGameSettings.GameType := GT_NONE;
1565 if gGameSettings.GameMode = GM_SINGLE then
1566 gGameSettings.GameMode := GM_DM;
1567 gSwitchGameMode := gGameSettings.GameMode;
1569 gChatShow := False;
1570 gExitByTrigger := False;
1571 end;
1573 function IsActivePlayer(p: TPlayer): Boolean;
1574 begin
1575 Result := False;
1576 if p = nil then
1577 Exit;
1578 Result := (not p.FDummy) and (not p.FSpectator);
1579 end;
1581 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
1582 var
1583 a, idx: Integer;
1584 ids: Array of Word;
1585 begin
1586 Result := -1;
1587 if gPlayers = nil then
1588 Exit;
1589 SetLength(ids, 0);
1590 idx := -1;
1591 for a := Low(gPlayers) to High(gPlayers) do
1592 if IsActivePlayer(gPlayers[a]) then
1593 begin
1594 SetLength(ids, Length(ids) + 1);
1595 ids[High(ids)] := gPlayers[a].UID;
1596 if gPlayers[a].UID = Skip then
1597 idx := High(ids);
1598 end;
1599 if Length(ids) = 0 then
1600 Exit;
1601 if idx = -1 then
1602 Result := ids[0]
1603 else
1604 Result := ids[(idx + 1) mod Length(ids)];
1605 end;
1607 function GetActivePlayerID_Prev(Skip: Integer = -1): Integer;
1608 var
1609 a, idx: Integer;
1610 ids: Array of Word;
1611 begin
1612 Result := -1;
1613 if gPlayers = nil then
1614 Exit;
1615 SetLength(ids, 0);
1616 idx := -1;
1617 for a := Low(gPlayers) to High(gPlayers) do
1618 if IsActivePlayer(gPlayers[a]) then
1619 begin
1620 SetLength(ids, Length(ids) + 1);
1621 ids[High(ids)] := gPlayers[a].UID;
1622 if gPlayers[a].UID = Skip then
1623 idx := High(ids);
1624 end;
1625 if Length(ids) = 0 then
1626 Exit;
1627 if idx = -1 then
1628 Result := ids[Length(ids) - 1]
1629 else
1630 Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
1631 end;
1633 function GetActivePlayerID_Random(Skip: Integer = -1): Integer;
1634 var
1635 a, idx: Integer;
1636 ids: Array of Word;
1637 begin
1638 Result := -1;
1639 if gPlayers = nil then
1640 Exit;
1641 SetLength(ids, 0);
1642 idx := -1;
1643 for a := Low(gPlayers) to High(gPlayers) do
1644 if IsActivePlayer(gPlayers[a]) then
1645 begin
1646 SetLength(ids, Length(ids) + 1);
1647 ids[High(ids)] := gPlayers[a].UID;
1648 if gPlayers[a].UID = Skip then
1649 idx := High(ids);
1650 end;
1651 if Length(ids) = 0 then
1652 Exit;
1653 if Length(ids) = 1 then
1654 begin
1655 Result := ids[0];
1656 Exit;
1657 end;
1658 Result := ids[Random(Length(ids))];
1659 a := 10;
1660 while (idx <> -1) and (Result = Skip) and (a > 0) do
1661 begin
1662 Result := ids[Random(Length(ids))];
1663 Dec(a);
1664 end;
1665 end;
1667 function GetRandomSpectMode(Current: Byte): Byte;
1668 label
1669 retry;
1670 begin
1671 Result := Current;
1672 retry:
1673 case Random(7) of
1674 0: Result := SPECT_STATS;
1675 1: Result := SPECT_MAPVIEW;
1676 2: Result := SPECT_MAPVIEW;
1677 3: Result := SPECT_PLAYERS;
1678 4: Result := SPECT_PLAYERS;
1679 5: Result := SPECT_PLAYERS;
1680 6: Result := SPECT_PLAYERS;
1681 end;
1682 if (Current in [SPECT_STATS, SPECT_MAPVIEW]) and (Current = Result) then
1683 goto retry;
1684 end;
1686 procedure ProcessPlayerControls (plr: TPlayer; p: Integer; var MoveButton: Byte);
1687 var
1688 time: Word;
1689 strafeDir: Byte;
1690 i: Integer;
1691 begin
1692 if (plr = nil) then exit;
1693 if (p = 2) then time := 1000 else time := 1;
1694 strafeDir := MoveButton shr 4;
1695 MoveButton := MoveButton and $0F;
1697 if gPlayerAction[p, ACTION_MOVELEFT] and (not gPlayerAction[p, ACTION_MOVERIGHT]) then
1698 MoveButton := 1 // Нажата только "Влево"
1699 else if (not gPlayerAction[p, ACTION_MOVELEFT]) and gPlayerAction[p, ACTION_MOVERIGHT] then
1700 MoveButton := 2 // Нажата только "Вправо"
1701 else if (not gPlayerAction[p, ACTION_MOVELEFT]) and (not gPlayerAction[p, ACTION_MOVERIGHT]) then
1702 MoveButton := 0; // Не нажаты ни "Влево", ни "Вправо"
1704 // Сейчас или раньше были нажаты "Влево"/"Вправо" => передаем игроку:
1705 if MoveButton = 1 then
1706 plr.PressKey(KEY_LEFT, time)
1707 else if MoveButton = 2 then
1708 plr.PressKey(KEY_RIGHT, time);
1710 // if we have "strafe" key, turn off old strafe mechanics
1711 if gPlayerAction[p, ACTION_STRAFE] then
1712 begin
1713 // new strafe mechanics
1714 if (strafeDir = 0) then
1715 strafeDir := MoveButton; // start strafing
1716 // now set direction according to strafe (reversed)
1717 if (strafeDir = 2) then
1718 plr.SetDirection(TDirection.D_LEFT)
1719 else if (strafeDir = 1) then
1720 plr.SetDirection(TDirection.D_RIGHT)
1721 end
1722 else
1723 begin
1724 strafeDir := 0; // not strafing anymore
1725 // Раньше была нажата "Вправо", а сейчас "Влево" => бежим вправо, смотрим влево:
1726 if (MoveButton = 2) and gPlayerAction[p, ACTION_MOVELEFT] then
1727 plr.SetDirection(TDirection.D_LEFT)
1728 // Раньше была нажата "Влево", а сейчас "Вправо" => бежим влево, смотрим вправо:
1729 else if (MoveButton = 1) and gPlayerAction[p, ACTION_MOVERIGHT] then
1730 plr.SetDirection(TDirection.D_RIGHT)
1731 // Что-то было нажато и не изменилось => куда бежим, туда и смотрим:
1732 else if MoveButton <> 0 then
1733 plr.SetDirection(TDirection(MoveButton-1))
1734 end;
1736 // fix movebutton state
1737 MoveButton := MoveButton or (strafeDir shl 4);
1739 // Остальные клавиши:
1740 if gPlayerAction[p, ACTION_JUMP] then plr.PressKey(KEY_JUMP, time);
1741 if gPlayerAction[p, ACTION_LOOKUP] then plr.PressKey(KEY_UP, time);
1742 if gPlayerAction[p, ACTION_LOOKDOWN] then plr.PressKey(KEY_DOWN, time);
1743 if gPlayerAction[p, ACTION_ATTACK] then plr.PressKey(KEY_FIRE);
1744 if gPlayerAction[p, ACTION_WEAPNEXT] then plr.PressKey(KEY_NEXTWEAPON);
1745 if gPlayerAction[p, ACTION_WEAPPREV] then plr.PressKey(KEY_PREVWEAPON);
1746 if gPlayerAction[p, ACTION_ACTIVATE] then plr.PressKey(KEY_OPEN);
1748 gPlayerAction[p, ACTION_WEAPNEXT] := False; // HACK, remove after readyweaon&pendinweapon implementation
1749 gPlayerAction[p, ACTION_WEAPPREV] := False; // HACK, remove after readyweaon&pendinweapon implementation
1751 for i := WP_FIRST to WP_LAST do
1752 begin
1753 if gSelectWeapon[p, i] then
1754 begin
1755 plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
1756 gSelectWeapon[p, i] := False
1757 end
1758 end;
1760 // HACK: add dynlight here
1761 if gwin_k8_enable_light_experiments then
1762 begin
1763 if e_KeyPressed(IK_F8) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1764 begin
1765 g_playerLight := true;
1766 end;
1767 if e_KeyPressed(IK_F9) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1768 begin
1769 g_playerLight := false;
1770 end;
1771 end;
1773 if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
1774 end;
1776 // HACK: don't have a "key was pressed" function
1777 procedure InterReady();
1778 begin
1779 if InterReadyTime > gTime then Exit;
1780 InterReadyTime := gTime + 3000;
1781 MC_SEND_CheatRequest(NET_CHEAT_READY);
1782 end;
1784 procedure g_Game_PreUpdate();
1785 begin
1786 // these are in separate PreUpdate functions because they can interact during Update()
1787 // and are synced over the net
1788 // we don't care that much about corpses and gibs
1789 g_Player_PreUpdate();
1790 g_Monsters_PreUpdate();
1791 g_Items_PreUpdate();
1792 g_Weapon_PreUpdate();
1793 end;
1795 procedure g_Game_Update();
1796 var
1797 Msg: g_gui.TMessage;
1798 Time: Int64;
1799 a: Byte;
1800 w: Word;
1801 i, b: Integer;
1803 function sendMonsPos (mon: TMonster): Boolean;
1804 begin
1805 result := false; // don't stop
1806 // this will also reset "need-send" flag
1807 if mon.gncNeedSend then
1808 begin
1809 MH_SEND_MonsterPos(mon.UID);
1810 end
1811 else if (mon.MonsterType = MONSTER_BARREL) then
1812 begin
1813 if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1814 end
1815 else if (mon.MonsterState <> MONSTATE_SLEEP) then
1816 begin
1817 if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1818 end;
1819 end;
1821 function sendMonsPosUnexpected (mon: TMonster): Boolean;
1822 begin
1823 result := false; // don't stop
1824 // this will also reset "need-send" flag
1825 if mon.gncNeedSend then MH_SEND_MonsterPos(mon.UID);
1826 end;
1828 var
1829 reliableUpdate: Boolean;
1830 begin
1831 g_ResetDynlights();
1832 framePool.reset();
1834 // Пора выключать игру:
1835 if gExit = EXIT_QUIT then
1836 Exit;
1837 // Игра закончилась - обрабатываем:
1838 if gExit <> 0 then
1839 begin
1840 EndGame();
1841 if gExit = EXIT_QUIT then
1842 Exit;
1843 end;
1845 // Читаем клавиатуру и джойстик, если окно активно
1846 // no need to, as we'll do it in event handler
1848 // Обновляем консоль (движение и сообщения):
1849 r_Console_Update;
1850 g_Console_Update();
1852 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1853 begin
1854 gExit := EXIT_SIMPLE;
1855 EndGame();
1856 Exit;
1857 end;
1859 // process master server communications
1860 g_Net_Slist_Pulse();
1862 case gState of
1863 STATE_INTERSINGLE, // Статистка после прохождения уровня в Одиночной игре
1864 STATE_INTERCUSTOM, // Статистка после прохождения уровня в Своей игре
1865 STATE_INTERTEXT, // Текст между уровнями
1866 STATE_INTERPIC: // Картинка между уровнями
1867 begin
1868 if g_Game_IsNet and g_Game_IsServer then
1869 begin
1870 gInterTime := gInterTime + GAME_TICK;
1871 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1872 if a <> gServInterTime then
1873 begin
1874 gServInterTime := a;
1875 MH_SEND_TimeSync(gServInterTime);
1876 end;
1877 end;
1879 if (not g_Game_IsClient) and
1883 e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or
1884 e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) or
1885 e_KeyPressed(JOY0_ATTACK) or e_KeyPressed(JOY1_ATTACK) or
1886 e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK)
1888 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1889 and (g_ActiveWindow = nil)
1891 or (g_Game_IsNet and ((gInterTime > gInterEndTime) or ((gInterReadyCount >= NetClientCount) and (NetClientCount > 0))))
1893 then
1894 begin // Нажали <Enter>/<Пробел> или прошло достаточно времени:
1895 g_Game_StopAllSounds(True);
1897 if gMapOnce then // Это был тест
1898 gExit := EXIT_SIMPLE
1899 else
1900 if gNextMap <> '' then // Переходим на следующую карту
1901 g_Game_ChangeMap(gNextMap)
1902 else // Следующей карты нет
1903 begin
1904 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1905 begin
1906 // Выход в главное меню:
1907 g_Game_Free;
1908 g_GUI_ShowWindow('MainMenu');
1909 gMusic.SetByName('MUSIC_MENU');
1910 gMusic.Play();
1911 gState := STATE_MENU;
1912 end else
1913 begin
1914 // Финальная картинка:
1915 g_Game_ExecuteEvent('onwadend');
1916 g_Game_Free();
1917 if not gMusic.SetByName('MUSIC_endmus') then
1918 gMusic.SetByName('MUSIC_STDENDMUS');
1919 gMusic.Play();
1920 gState := STATE_ENDPIC;
1921 end;
1922 g_Game_ExecuteEvent('ongameend');
1923 end;
1925 Exit;
1926 end
1927 else if g_Game_IsClient and
1930 e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or
1931 e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) or
1932 e_KeyPressed(JOY0_ATTACK) or e_KeyPressed(JOY1_ATTACK) or
1933 e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK)
1935 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1936 and (g_ActiveWindow = nil)
1938 then
1939 begin
1940 // ready / unready
1941 InterReady();
1942 end;
1944 if gState = STATE_INTERTEXT then
1945 if InterText.counter > 0 then
1946 InterText.counter := InterText.counter - 1;
1947 end;
1949 STATE_FOLD: // Затухание экрана
1950 begin
1951 if EndingGameCounter = 0 then
1952 begin
1953 // Закончился уровень в Своей игре:
1954 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1955 begin
1956 InterReadyTime := -1;
1957 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1958 begin
1959 g_Game_ExecuteEvent('onwadend');
1960 if not gMusic.SetByName('MUSIC_endmus') then
1961 gMusic.SetByName('MUSIC_STDENDMUS');
1962 end
1963 else
1964 gMusic.SetByName('MUSIC_ROUNDMUS');
1966 gMusic.Play();
1967 gState := STATE_INTERCUSTOM;
1968 e_UnpressAllKeys();
1969 end
1970 else // Закончилась последняя карта в Одиночной игре
1971 begin
1972 gMusic.SetByName('MUSIC_INTERMUS');
1973 gMusic.Play();
1974 gState := STATE_INTERSINGLE;
1975 e_UnpressAllKeys();
1976 end;
1977 g_Game_ExecuteEvent('oninter');
1978 end
1979 else
1980 DecMin(EndingGameCounter, 6, 0);
1981 end;
1983 STATE_ENDPIC: // Картинка окончания мегаВада
1984 begin
1985 if gMapOnce then // Это был тест
1986 begin
1987 gExit := EXIT_SIMPLE;
1988 Exit;
1989 end;
1990 end;
1992 STATE_SLIST:
1993 g_Serverlist_Control(slCurrent, slTable);
1994 end;
1996 // Статистика по Tab:
1997 if gGameOn then
1998 IsDrawStat := (not gConsoleShow) and (not gChatShow) and (gGameSettings.GameType <> GT_SINGLE) and g_Console_Action(ACTION_SCORES);
2000 // Игра идет:
2001 if gGameOn and not gPause and (gState <> STATE_FOLD) then
2002 begin
2003 // Время += 28 миллисекунд:
2004 gTime := gTime + GAME_TICK;
2006 // Сообщение посередине экрана:
2007 if MessageTime = 0 then
2008 MessageText := '';
2009 if MessageTime > 0 then
2010 MessageTime := MessageTime - 1;
2012 if (g_Game_IsServer) then
2013 begin
2014 // Был задан лимит времени:
2015 if (gGameSettings.TimeLimit > 0) then
2016 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
2017 begin // Он прошел => конец уровня
2018 g_Game_NextLevel();
2019 Exit;
2020 end;
2022 // Надо респавнить игроков в LMS:
2023 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
2024 g_Game_RestartRound(gLMSSoftSpawn);
2026 // Проверим результат голосования, если время прошло
2027 if gVoteInProgress and (gVoteTimer < gTime) then
2028 g_Game_CheckVote
2029 else if gVotePassed and (gVoteCmdTimer < gTime) then
2030 begin
2031 g_Console_Process(gVoteCommand);
2032 gVoteCommand := '';
2033 gVotePassed := False;
2034 end;
2036 // Замеряем время захвата флагов
2037 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
2038 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
2039 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
2040 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
2042 // Был задан лимит побед:
2043 if (gGameSettings.GoalLimit > 0) then
2044 begin
2045 b := 0;
2047 if gGameSettings.GameMode = GM_DM then
2048 begin // В DM ищем игрока с max фрагами
2049 for i := 0 to High(gPlayers) do
2050 if gPlayers[i] <> nil then
2051 if gPlayers[i].Frags > b then
2052 b := gPlayers[i].Frags;
2053 end
2054 else
2055 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2056 begin // В CTF/TDM выбираем команду с наибольшим счетом
2057 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
2058 end;
2060 // Лимит побед набран => конец уровня:
2061 if b >= gGameSettings.GoalLimit then
2062 begin
2063 g_Game_NextLevel();
2064 Exit;
2065 end;
2066 end;
2068 // Обрабатываем клавиши игроков:
2069 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
2070 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
2071 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
2072 begin
2073 ProcessPlayerControls(gPlayer1, 0, P1MoveButton);
2074 ProcessPlayerControls(gPlayer2, 1, P2MoveButton);
2075 end // if not console
2076 else
2077 begin
2078 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
2079 end;
2080 // process weapon switch queue
2081 end; // if server
2083 // Наблюдатель
2084 if (gPlayer1 = nil) and (gPlayer2 = nil) and
2085 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
2086 begin
2087 if not gSpectKeyPress then
2088 begin
2089 if gPlayerAction[0, ACTION_JUMP] and (not gSpectAuto) then
2090 begin
2091 // switch spect mode
2092 case gSpectMode of
2093 SPECT_NONE: ; // not spectator
2094 SPECT_STATS,
2095 SPECT_MAPVIEW: Inc(gSpectMode);
2096 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
2097 end;
2098 gSpectKeyPress := True;
2099 end;
2100 if (gSpectMode = SPECT_MAPVIEW)
2101 and (not gSpectAuto) then
2102 begin
2103 if gPlayerAction[0, ACTION_MOVELEFT] then
2104 gSpectX := Max(gSpectX - gSpectStep, 0);
2105 if gPlayerAction[0, ACTION_MOVERIGHT] then
2106 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
2107 if gPlayerAction[0, ACTION_LOOKUP] then
2108 gSpectY := Max(gSpectY - gSpectStep, 0);
2109 if gPlayerAction[0, ACTION_LOOKDOWN] then
2110 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
2111 if gPlayerAction[0, ACTION_WEAPPREV] then
2112 begin
2113 // decrease step
2114 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
2115 gSpectKeyPress := True;
2116 end;
2117 if gPlayerAction[0, ACTION_WEAPNEXT] then
2118 begin
2119 // increase step
2120 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
2121 gSpectKeyPress := True;
2122 end;
2123 end;
2124 if (gSpectMode = SPECT_PLAYERS)
2125 and (not gSpectAuto) then
2126 begin
2127 if gPlayerAction[0, ACTION_LOOKUP] then
2128 begin
2129 // add second view
2130 gSpectViewTwo := True;
2131 gSpectKeyPress := True;
2132 end;
2133 if gPlayerAction[0, ACTION_LOOKDOWN] then
2134 begin
2135 // remove second view
2136 gSpectViewTwo := False;
2137 gSpectKeyPress := True;
2138 end;
2139 if gPlayerAction[0, ACTION_MOVELEFT] then
2140 begin
2141 // prev player (view 1)
2142 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
2143 gSpectKeyPress := True;
2144 end;
2145 if gPlayerAction[0, ACTION_MOVERIGHT] then
2146 begin
2147 // next player (view 1)
2148 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
2149 gSpectKeyPress := True;
2150 end;
2151 if gPlayerAction[0, ACTION_WEAPPREV] then
2152 begin
2153 // prev player (view 2)
2154 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
2155 gSpectKeyPress := True;
2156 end;
2157 if gPlayerAction[0, ACTION_WEAPNEXT] then
2158 begin
2159 // next player (view 2)
2160 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
2161 gSpectKeyPress := True;
2162 end;
2163 end;
2164 if gPlayerAction[0, ACTION_ATTACK] then
2165 begin
2166 if (gSpectMode = SPECT_STATS) and (not gSpectAuto) then
2167 begin
2168 gSpectAuto := True;
2169 gSpectAutoNext := 0;
2170 gSpectViewTwo := False;
2171 gSpectKeyPress := True;
2172 end
2173 else
2174 if gSpectAuto then
2175 begin
2176 gSpectMode := SPECT_STATS;
2177 gSpectAuto := False;
2178 gSpectKeyPress := True;
2179 end;
2180 end;
2181 end
2182 else
2183 if (not gPlayerAction[0, ACTION_JUMP]) and
2184 (not gPlayerAction[0, ACTION_ATTACK]) and
2185 (not gPlayerAction[0, ACTION_MOVELEFT]) and
2186 (not gPlayerAction[0, ACTION_MOVERIGHT]) and
2187 (not gPlayerAction[0, ACTION_LOOKUP]) and
2188 (not gPlayerAction[0, ACTION_LOOKDOWN]) and
2189 (not gPlayerAction[0, ACTION_WEAPPREV]) and
2190 (not gPlayerAction[0, ACTION_WEAPNEXT]) then
2191 gSpectKeyPress := False;
2193 if gSpectAuto then
2194 begin
2195 if gSpectMode = SPECT_MAPVIEW then
2196 begin
2197 i := Min(Max(gSpectX + gSpectAutoStepX, 0), gMapInfo.Width - gScreenWidth);
2198 if i = gSpectX then
2199 gSpectAutoNext := gTime
2200 else
2201 gSpectX := i;
2202 i := Min(Max(gSpectY + gSpectAutoStepY, 0), gMapInfo.Height - gScreenHeight);
2203 if i = gSpectY then
2204 gSpectAutoNext := gTime
2205 else
2206 gSpectY := i;
2207 end;
2208 if gSpectAutoNext <= gTime then
2209 begin
2210 if gSpectAutoNext > 0 then
2211 begin
2212 gSpectMode := GetRandomSpectMode(gSpectMode);
2213 case gSpectMode of
2214 SPECT_MAPVIEW:
2215 begin
2216 gSpectX := Random(gMapInfo.Width - gScreenWidth);
2217 gSpectY := Random(gMapInfo.Height - gScreenHeight);
2218 gSpectAutoStepX := Random(9) - 4;
2219 gSpectAutoStepY := Random(9) - 4;
2220 if ((gSpectX < 800) and (gSpectAutoStepX < 0)) or
2221 ((gSpectX > gMapInfo.Width - gScreenWidth - 800) and (gSpectAutoStepX > 0)) then
2222 gSpectAutoStepX := gSpectAutoStepX * -1;
2223 if ((gSpectY < 800) and (gSpectAutoStepY < 0)) or
2224 ((gSpectY > gMapInfo.Height - gScreenHeight - 800) and (gSpectAutoStepY > 0)) then
2225 gSpectAutoStepY := gSpectAutoStepY * -1;
2226 end;
2227 SPECT_PLAYERS:
2228 begin
2229 gSpectPID1 := GetActivePlayerID_Random(gSpectPID1);
2230 end;
2231 end;
2232 end;
2233 case gSpectMode of
2234 SPECT_STATS: gSpectAutoNext := gTime + (Random(3) + 5) * 1000;
2235 SPECT_MAPVIEW: gSpectAutoNext := gTime + (Random(4) + 7) * 1000;
2236 SPECT_PLAYERS: gSpectAutoNext := gTime + (Random(7) + 8) * 1000;
2237 end;
2238 end;
2239 end;
2240 end;
2242 // Обновляем все остальное:
2243 g_Map_Update();
2244 g_Items_Update();
2245 g_Triggers_Update();
2246 g_Weapon_Update();
2247 g_Monsters_Update();
2248 g_GFX_Update();
2249 g_Player_UpdateAll();
2250 g_Player_UpdatePhysicalObjects();
2252 // server: send newly spawned monsters unconditionally
2253 if (gGameSettings.GameType = GT_SERVER) then
2254 begin
2255 if (Length(gMonstersSpawned) > 0) then
2256 begin
2257 for I := 0 to High(gMonstersSpawned) do MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
2258 SetLength(gMonstersSpawned, 0);
2259 end;
2260 end;
2262 if (gSoundTriggerTime > 8) then
2263 begin
2264 g_Game_UpdateTriggerSounds();
2265 gSoundTriggerTime := 0;
2266 end
2267 else
2268 begin
2269 Inc(gSoundTriggerTime);
2270 end;
2272 if (NetMode = NET_SERVER) then
2273 begin
2274 Inc(NetTimeToUpdate);
2275 Inc(NetTimeToReliable);
2277 // send monster updates
2278 if (NetTimeToReliable >= NetRelupdRate) or (NetTimeToUpdate >= NetUpdateRate) then
2279 begin
2280 // send all monsters (periodic sync)
2281 reliableUpdate := (NetTimeToReliable >= NetRelupdRate);
2283 for I := 0 to High(gPlayers) do
2284 begin
2285 if (gPlayers[I] <> nil) then MH_SEND_PlayerPos(reliableUpdate, gPlayers[I].UID);
2286 end;
2288 g_Mons_ForEach(sendMonsPos);
2290 if reliableUpdate then
2291 begin
2292 NetTimeToReliable := 0;
2293 NetTimeToUpdate := NetUpdateRate;
2294 end
2295 else
2296 begin
2297 NetTimeToUpdate := 0;
2298 end;
2299 end
2300 else
2301 begin
2302 // send only mosters with some unexpected changes
2303 g_Mons_ForEach(sendMonsPosUnexpected);
2304 end;
2306 // send unexpected platform changes
2307 g_Map_NetSendInterestingPanels();
2309 g_Net_Slist_ServerUpdate();
2311 if NetUseMaster then
2312 begin
2313 if (gTime >= NetTimeToMaster) or g_Net_Slist_IsConnectionInProgress then
2314 begin
2315 if (not g_Net_Slist_IsConnectionActive) then g_Net_Slist_Connect(false); // non-blocking connection to the master
2316 g_Net_Slist_Update;
2317 NetTimeToMaster := gTime + NetMasterRate;
2318 end;
2319 end;
2321 end
2322 else if (NetMode = NET_CLIENT) then
2323 begin
2324 MC_SEND_PlayerPos();
2325 end;
2326 end; // if gameOn ...
2328 // Активно окно интерфейса - передаем клавиши ему:
2329 if g_ActiveWindow <> nil then
2330 begin
2331 w := e_GetFirstKeyPressed();
2333 if (w <> IK_INVALID) then
2334 begin
2335 Msg.Msg := MESSAGE_DIKEY;
2336 Msg.wParam := w;
2337 g_ActiveWindow.OnMessage(Msg);
2338 end;
2340 // Если оно от этого не закрылось, то обновляем:
2341 if g_ActiveWindow <> nil then
2342 g_ActiveWindow.Update();
2344 // Нужно сменить разрешение:
2345 if gResolutionChange then
2346 begin
2347 e_WriteLog('Changing resolution', TMsgType.Notify);
2348 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
2349 gResolutionChange := False;
2350 g_ActiveWindow := nil;
2351 end;
2353 // Нужно сменить язык:
2354 if gLanguageChange then
2355 begin
2356 //e_WriteLog('Read language file', MSG_NOTIFY);
2357 //g_Language_Load(DataDir + gLanguage + '.txt');
2358 g_Language_Set(gLanguage);
2359 {$IFNDEF HEADLESS}
2360 g_Menu_Reset();
2361 {$ENDIF}
2362 gLanguageChange := False;
2363 end;
2364 end;
2366 // Горячая клавиша для вызова меню выхода из игры (F10):
2367 if e_KeyPressed(IK_F10) and
2368 gGameOn and
2369 (not gConsoleShow) and
2370 (g_ActiveWindow = nil) then
2371 begin
2372 KeyPress(IK_F10);
2373 end;
2375 Time := sys_GetTicks() {div 1000};
2377 // Обработка отложенных событий:
2378 if gDelayedEvents <> nil then
2379 for a := 0 to High(gDelayedEvents) do
2380 if gDelayedEvents[a].Pending and
2382 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
2383 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
2384 ) then
2385 begin
2386 case gDelayedEvents[a].DEType of
2387 DE_GLOBEVENT:
2388 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
2389 DE_BFGHIT:
2390 if gGameOn then
2391 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
2392 DE_KILLCOMBO:
2393 if gGameOn then
2394 begin
2395 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
2396 if g_Game_IsNet and g_Game_IsServer then
2397 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
2398 end;
2399 DE_BODYKILL:
2400 if gGameOn then
2401 g_Game_Announce_BodyKill(gDelayedEvents[a].DENum);
2402 end;
2403 gDelayedEvents[a].Pending := False;
2404 end;
2406 // Каждую секунду обновляем счетчик обновлений:
2407 UPSCounter := UPSCounter + 1;
2408 if Time - UPSTime >= 1000 then
2409 begin
2410 UPS := UPSCounter;
2411 UPSCounter := 0;
2412 UPSTime := Time;
2413 end;
2415 if gGameOn then
2416 begin
2417 g_Weapon_AddDynLights();
2418 g_Items_AddDynLights();
2419 end;
2420 end;
2422 procedure g_Game_LoadChatSounds(Resource: string);
2423 var
2424 WAD: TWADFile;
2425 FileName, Snd: string;
2426 p: Pointer;
2427 len, cnt, tags, i, j: Integer;
2428 cfg: TConfig;
2429 begin
2430 FileName := g_ExtractWadName(Resource);
2432 WAD := TWADFile.Create();
2433 WAD.ReadFile(FileName);
2435 if not WAD.GetResource(g_ExtractFilePathName(Resource), p, len) then
2436 begin
2437 gChatSounds := nil;
2438 WAD.Free();
2439 Exit;
2440 end;
2442 cfg := TConfig.CreateMem(p, len);
2443 cnt := cfg.ReadInt('ChatSounds', 'Count', 0);
2445 SetLength(gChatSounds, cnt);
2446 for i := 0 to Length(gChatSounds) - 1 do
2447 begin
2448 gChatSounds[i].Sound := nil;
2449 Snd := Trim(cfg.ReadStr(IntToStr(i), 'Sound', ''));
2450 tags := cfg.ReadInt(IntToStr(i), 'Tags', 0);
2451 if (Snd = '') or (Tags <= 0) then
2452 continue;
2453 g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i), GameWAD+':'+Snd);
2454 gChatSounds[i].Sound := TPlayableSound.Create();
2455 gChatSounds[i].Sound.SetByName('SOUND_CHAT_MACRO' + IntToStr(i));
2456 SetLength(gChatSounds[i].Tags, tags);
2457 for j := 0 to tags - 1 do
2458 gChatSounds[i].Tags[j] := toLowerCase1251(cfg.ReadStr(IntToStr(i), 'Tag' + IntToStr(j), ''));
2459 gChatSounds[i].FullWord := cfg.ReadBool(IntToStr(i), 'FullWord', False);
2460 end;
2462 cfg.Free();
2463 WAD.Free();
2464 end;
2466 procedure g_Game_FreeChatSounds();
2467 var
2468 i: Integer;
2469 begin
2470 for i := 0 to Length(gChatSounds) - 1 do
2471 begin
2472 gChatSounds[i].Sound.Free();
2473 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i));
2474 end;
2475 SetLength(gChatSounds, 0);
2476 gChatSounds := nil;
2477 end;
2479 procedure g_Game_LoadData();
2480 var
2481 wl, hl: Integer;
2482 wr, hr: Integer;
2483 wb, hb: Integer;
2484 wm, hm: Integer;
2485 begin
2486 if DataLoaded then Exit;
2488 e_WriteLog('Loading game data...', TMsgType.Notify);
2490 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2491 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2492 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2493 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2494 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2495 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2496 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
2497 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
2498 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
2499 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
2500 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
2501 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
2502 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2503 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2504 g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD+':TEXTURES\PLRIND');
2506 hasPBarGfx := true;
2507 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false;
2508 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false;
2509 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false;
2510 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false;
2512 if hasPBarGfx then
2513 begin
2514 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2515 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2516 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2517 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2518 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
2519 begin
2520 // yay!
2521 end
2522 else
2523 begin
2524 hasPBarGfx := false;
2525 end;
2526 end;
2528 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2529 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD+':WEAPONS\PUNCH', 64, 64, 4, False);
2530 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD+':WEAPONS\PUNCH_UP', 64, 64, 4, False);
2531 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD+':WEAPONS\PUNCH_DN', 64, 64, 4, False);
2532 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD+':WEAPONS\PUNCHB', 64, 64, 4, False);
2533 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD+':WEAPONS\PUNCHB_UP', 64, 64, 4, False);
2534 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD+':WEAPONS\PUNCHB_DN', 64, 64, 4, False);
2535 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2536 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2537 g_Sound_CreateWADEx('SOUND_GAME_SECRET', GameWAD+':SOUNDS\SECRET');
2538 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2539 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2540 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2541 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2542 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2543 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2544 g_Sound_CreateWADEx('SOUND_GAME_BURNING', GameWAD+':SOUNDS\BURNING');
2545 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2546 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2547 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2548 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2549 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2550 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2551 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2552 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2553 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2554 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2555 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2556 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD+':SOUNDS\MUHAHA1');
2557 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD+':SOUNDS\MUHAHA2');
2558 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD+':SOUNDS\MUHAHA3');
2559 g_Sound_CreateWADEx('SOUND_CTF_GET1', GameWAD+':SOUNDS\GETFLAG1');
2560 g_Sound_CreateWADEx('SOUND_CTF_GET2', GameWAD+':SOUNDS\GETFLAG2');
2561 g_Sound_CreateWADEx('SOUND_CTF_LOST1', GameWAD+':SOUNDS\LOSTFLG1');
2562 g_Sound_CreateWADEx('SOUND_CTF_LOST2', GameWAD+':SOUNDS\LOSTFLG2');
2563 g_Sound_CreateWADEx('SOUND_CTF_RETURN1', GameWAD+':SOUNDS\RETFLAG1');
2564 g_Sound_CreateWADEx('SOUND_CTF_RETURN2', GameWAD+':SOUNDS\RETFLAG2');
2565 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE1', GameWAD+':SOUNDS\CAPFLAG1');
2566 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE2', GameWAD+':SOUNDS\CAPFLAG2');
2568 goodsnd[0] := TPlayableSound.Create();
2569 goodsnd[1] := TPlayableSound.Create();
2570 goodsnd[2] := TPlayableSound.Create();
2571 goodsnd[3] := TPlayableSound.Create();
2573 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2574 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2575 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2576 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2578 killsnd[0] := TPlayableSound.Create();
2579 killsnd[1] := TPlayableSound.Create();
2580 killsnd[2] := TPlayableSound.Create();
2581 killsnd[3] := TPlayableSound.Create();
2583 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2584 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2585 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2586 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2588 hahasnd[0] := TPlayableSound.Create();
2589 hahasnd[1] := TPlayableSound.Create();
2590 hahasnd[2] := TPlayableSound.Create();
2592 hahasnd[0].SetByName('SOUND_ANNOUNCER_MUHAHA1');
2593 hahasnd[1].SetByName('SOUND_ANNOUNCER_MUHAHA2');
2594 hahasnd[2].SetByName('SOUND_ANNOUNCER_MUHAHA3');
2596 sound_get_flag[0] := TPlayableSound.Create();
2597 sound_get_flag[1] := TPlayableSound.Create();
2598 sound_lost_flag[0] := TPlayableSound.Create();
2599 sound_lost_flag[1] := TPlayableSound.Create();
2600 sound_ret_flag[0] := TPlayableSound.Create();
2601 sound_ret_flag[1] := TPlayableSound.Create();
2602 sound_cap_flag[0] := TPlayableSound.Create();
2603 sound_cap_flag[1] := TPlayableSound.Create();
2605 sound_get_flag[0].SetByName('SOUND_CTF_GET1');
2606 sound_get_flag[1].SetByName('SOUND_CTF_GET2');
2607 sound_lost_flag[0].SetByName('SOUND_CTF_LOST1');
2608 sound_lost_flag[1].SetByName('SOUND_CTF_LOST2');
2609 sound_ret_flag[0].SetByName('SOUND_CTF_RETURN1');
2610 sound_ret_flag[1].SetByName('SOUND_CTF_RETURN2');
2611 sound_cap_flag[0].SetByName('SOUND_CTF_CAPTURE1');
2612 sound_cap_flag[1].SetByName('SOUND_CTF_CAPTURE2');
2614 g_Game_LoadChatSounds(GameWAD+':CHATSND\SNDCFG');
2616 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2617 g_Items_LoadData();
2619 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2620 g_Weapon_LoadData();
2622 g_Monsters_LoadData();
2624 DataLoaded := True;
2625 end;
2627 procedure g_Game_Quit();
2628 begin
2629 g_Game_StopAllSounds(True);
2630 gMusic.Free();
2631 g_Game_FreeData();
2632 r_PlayerModel_Finalize;
2633 g_PlayerModel_FreeData();
2634 g_Texture_DeleteAll();
2635 g_Frames_DeleteAll();
2636 {$IFNDEF HEADLESS}
2637 //g_Menu_Free(); //k8: this segfaults after resolution change; who cares?
2638 {$ENDIF}
2640 if NetInitDone then g_Net_Free;
2642 // remove map after test
2643 if gMapToDelete <> '' then
2644 g_Game_DeleteTestMap();
2646 gExit := EXIT_QUIT;
2647 sys_RequestQuit;
2648 end;
2650 procedure g_Game_FreeData();
2651 begin
2652 if not DataLoaded then Exit;
2654 g_Items_FreeData();
2655 g_Weapon_FreeData();
2656 g_Monsters_FreeData();
2658 e_WriteLog('Releasing game data...', TMsgType.Notify);
2660 g_Texture_Delete('NOTEXTURE');
2661 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2662 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2663 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2664 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2665 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2666 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2667 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2668 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2669 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2670 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2671 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2672 g_Frames_DeleteByName('FRAMES_TELEPORT');
2673 g_Frames_DeleteByName('FRAMES_PUNCH');
2674 g_Frames_DeleteByName('FRAMES_PUNCH_UP');
2675 g_Frames_DeleteByName('FRAMES_PUNCH_DN');
2676 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK');
2677 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP');
2678 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
2679 g_Sound_Delete('SOUND_GAME_TELEPORT');
2680 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2681 g_Sound_Delete('SOUND_GAME_SECRET');
2682 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2683 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2684 g_Sound_Delete('SOUND_GAME_BULK1');
2685 g_Sound_Delete('SOUND_GAME_BULK2');
2686 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2687 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2688 g_Sound_Delete('SOUND_GAME_BURNING');
2689 g_Sound_Delete('SOUND_GAME_SWITCH1');
2690 g_Sound_Delete('SOUND_GAME_SWITCH0');
2692 goodsnd[0].Free();
2693 goodsnd[1].Free();
2694 goodsnd[2].Free();
2695 goodsnd[3].Free();
2697 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2698 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2699 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2700 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2702 killsnd[0].Free();
2703 killsnd[1].Free();
2704 killsnd[2].Free();
2705 killsnd[3].Free();
2707 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2708 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2709 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2710 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2712 hahasnd[0].Free();
2713 hahasnd[1].Free();
2714 hahasnd[2].Free();
2716 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1');
2717 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2');
2718 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3');
2720 sound_get_flag[0].Free();
2721 sound_get_flag[1].Free();
2722 sound_lost_flag[0].Free();
2723 sound_lost_flag[1].Free();
2724 sound_ret_flag[0].Free();
2725 sound_ret_flag[1].Free();
2726 sound_cap_flag[0].Free();
2727 sound_cap_flag[1].Free();
2729 g_Sound_Delete('SOUND_CTF_GET1');
2730 g_Sound_Delete('SOUND_CTF_GET2');
2731 g_Sound_Delete('SOUND_CTF_LOST1');
2732 g_Sound_Delete('SOUND_CTF_LOST2');
2733 g_Sound_Delete('SOUND_CTF_RETURN1');
2734 g_Sound_Delete('SOUND_CTF_RETURN2');
2735 g_Sound_Delete('SOUND_CTF_CAPTURE1');
2736 g_Sound_Delete('SOUND_CTF_CAPTURE2');
2738 g_Game_FreeChatSounds();
2740 DataLoaded := False;
2741 end;
2743 procedure g_FatalError(Text: String);
2744 begin
2745 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
2746 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning);
2748 gExit := EXIT_SIMPLE;
2749 if gGameOn then EndGame;
2750 end;
2752 procedure g_SimpleError(Text: String);
2753 begin
2754 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
2755 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
2756 end;
2758 procedure g_Game_SetupScreenSize();
2759 const
2760 RES_FACTOR = 4.0 / 3.0;
2761 var
2762 s: Single;
2763 rf: Single;
2764 bw, bh: Word;
2765 begin
2766 // Размер экранов игроков:
2767 gPlayerScreenSize.X := gScreenWidth-196;
2768 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
2769 gPlayerScreenSize.Y := gScreenHeight div 2
2770 else
2771 gPlayerScreenSize.Y := gScreenHeight;
2773 // Размер заднего плана:
2774 if BackID <> DWORD(-1) then
2775 begin
2776 s := SKY_STRETCH;
2777 if (gScreenWidth*s > gMapInfo.Width) or
2778 (gScreenHeight*s > gMapInfo.Height) then
2779 begin
2780 gBackSize.X := gScreenWidth;
2781 gBackSize.Y := gScreenHeight;
2782 end
2783 else
2784 begin
2785 e_GetTextureSize(BackID, @bw, @bh);
2786 rf := Single(bw) / Single(bh);
2787 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
2788 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
2789 s := Max(gScreenWidth / bw, gScreenHeight / bh);
2790 if (s < 1.0) then s := 1.0;
2791 gBackSize.X := Round(bw*s);
2792 gBackSize.Y := Round(bh*s);
2793 end;
2794 end;
2795 end;
2797 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
2798 begin
2799 sys_SetDisplayMode(newWidth, newHeight, gBPP, nowFull, nowMax);
2800 end;
2802 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
2803 begin
2804 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
2805 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
2806 Exit;
2808 if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then
2809 Exit;
2811 if gPlayer1 = nil then
2812 begin
2813 if g_Game_IsClient then
2814 begin
2815 if NetPlrUID1 > -1 then
2816 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
2817 Exit;
2818 end;
2820 if not (Team in [TEAM_RED, TEAM_BLUE]) then
2821 Team := gPlayer1Settings.Team;
2823 // Создание первого игрока:
2824 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
2825 gPlayer1Settings.Color,
2826 Team, False));
2827 if gPlayer1 = nil then
2828 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
2829 else
2830 begin
2831 gPlayer1.Name := gPlayer1Settings.Name;
2832 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
2833 if g_Game_IsServer and g_Game_IsNet then
2834 MH_SEND_PlayerCreate(gPlayer1.UID);
2835 gPlayer1.Respawn(False, True);
2836 g_Net_Slist_ServerPlayerComes();
2837 end;
2839 Exit;
2840 end;
2841 if gPlayer2 = nil then
2842 begin
2843 if g_Game_IsClient then
2844 begin
2845 if NetPlrUID2 > -1 then
2846 gPlayer2 := g_Player_Get(NetPlrUID2);
2847 Exit;
2848 end;
2850 if not (Team in [TEAM_RED, TEAM_BLUE]) then
2851 Team := gPlayer2Settings.Team;
2853 // Создание второго игрока:
2854 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
2855 gPlayer2Settings.Color,
2856 Team, False));
2857 if gPlayer2 = nil then
2858 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
2859 else
2860 begin
2861 gPlayer2.Name := gPlayer2Settings.Name;
2862 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
2863 if g_Game_IsServer and g_Game_IsNet then
2864 MH_SEND_PlayerCreate(gPlayer2.UID);
2865 gPlayer2.Respawn(False, True);
2866 g_Net_Slist_ServerPlayerComes();
2867 end;
2869 Exit;
2870 end;
2871 end;
2873 procedure g_Game_RemovePlayer();
2874 var
2875 Pl: TPlayer;
2876 begin
2877 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
2878 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
2879 Exit;
2880 Pl := gPlayer2;
2881 if Pl <> nil then
2882 begin
2883 if g_Game_IsServer then
2884 begin
2885 Pl.Lives := 0;
2886 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
2887 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
2888 g_Player_Remove(Pl.UID);
2889 g_Net_Slist_ServerPlayerLeaves();
2890 end
2891 else
2892 begin
2893 gSpectLatchPID2 := Pl.UID;
2894 gPlayer2 := nil;
2895 end;
2896 Exit;
2897 end;
2898 Pl := gPlayer1;
2899 if Pl <> nil then
2900 begin
2901 if g_Game_IsServer then
2902 begin
2903 Pl.Lives := 0;
2904 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
2905 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
2906 g_Player_Remove(Pl.UID);
2907 g_Net_Slist_ServerPlayerLeaves();
2908 end else
2909 begin
2910 gSpectLatchPID1 := Pl.UID;
2911 gPlayer1 := nil;
2912 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
2913 end;
2914 Exit;
2915 end;
2916 g_Net_Slist_ServerPlayerLeaves();
2917 end;
2919 procedure g_Game_Spectate();
2920 begin
2921 g_Game_RemovePlayer();
2922 if gPlayer1 <> nil then
2923 g_Game_RemovePlayer();
2924 end;
2926 procedure g_Game_SpectateCenterView();
2927 begin
2928 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
2929 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
2930 end;
2932 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
2933 var
2934 i, nPl: Integer;
2935 tmps: AnsiString;
2936 begin
2937 g_Game_Free();
2939 e_WriteLog('Starting singleplayer game...', TMsgType.Notify);
2941 g_Game_ClearLoading();
2943 // Настройки игры:
2944 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
2945 gAimLine := False;
2946 gShowMap := False;
2947 gGameSettings.GameType := GT_SINGLE;
2948 gGameSettings.MaxLives := 0;
2949 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
2950 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
2951 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
2952 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_TEAMHITPROJECTILE;
2953 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_TEAMHITTRACE;
2954 gSwitchGameMode := GM_SINGLE;
2956 gLMSRespawn := LMS_RESPAWN_NONE;
2957 gLMSRespawnTime := 0;
2958 gSpectLatchPID1 := 0;
2959 gSpectLatchPID2 := 0;
2961 g_Game_ExecuteEvent('ongamestart');
2963 // Установка размеров окон игроков:
2964 g_Game_SetupScreenSize();
2966 // Создание первого игрока:
2967 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
2968 gPlayer1Settings.Color,
2969 gPlayer1Settings.Team, False));
2970 if gPlayer1 = nil then
2971 begin
2972 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
2973 Exit;
2974 end;
2976 gPlayer1.Name := gPlayer1Settings.Name;
2977 nPl := 1;
2979 // Создание второго игрока, если есть:
2980 if TwoPlayers then
2981 begin
2982 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
2983 gPlayer2Settings.Color,
2984 gPlayer2Settings.Team, False));
2985 if gPlayer2 = nil then
2986 begin
2987 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
2988 Exit;
2989 end;
2991 gPlayer2.Name := gPlayer2Settings.Name;
2992 Inc(nPl);
2993 end;
2995 // Загрузка и запуск карты:
2996 if not g_Game_StartMap(false{asMegawad}, MAP, True) then
2997 begin
2998 if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP;
2999 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps]));
3000 Exit;
3001 end;
3003 // Настройки игроков и ботов:
3004 g_Player_Init();
3006 // Создаем ботов:
3007 for i := nPl+1 to nPlayers do
3008 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3009 end;
3011 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3012 TimeLimit, GoalLimit: Word;
3013 MaxLives: Byte;
3014 Options: LongWord; nPlayers: Byte);
3015 var
3016 i, nPl: Integer;
3017 begin
3018 g_Game_Free();
3020 e_WriteLog('Starting custom game...', TMsgType.Notify);
3022 g_Game_ClearLoading();
3024 // Настройки игры:
3025 gGameSettings.GameType := GT_CUSTOM;
3026 gGameSettings.GameMode := GameMode;
3027 gSwitchGameMode := GameMode;
3028 gGameSettings.TimeLimit := TimeLimit;
3029 gGameSettings.GoalLimit := GoalLimit;
3030 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3031 gGameSettings.Options := Options;
3033 gCoopTotalMonstersKilled := 0;
3034 gCoopTotalSecretsFound := 0;
3035 gCoopTotalMonsters := 0;
3036 gCoopTotalSecrets := 0;
3037 gAimLine := False;
3038 gShowMap := False;
3040 gLMSRespawn := LMS_RESPAWN_NONE;
3041 gLMSRespawnTime := 0;
3042 gSpectLatchPID1 := 0;
3043 gSpectLatchPID2 := 0;
3045 g_Game_ExecuteEvent('ongamestart');
3047 // Установка размеров окон игроков:
3048 g_Game_SetupScreenSize();
3050 // Режим наблюдателя:
3051 if nPlayers = 0 then
3052 begin
3053 gPlayer1 := nil;
3054 gPlayer2 := nil;
3055 end;
3057 nPl := 0;
3058 if nPlayers >= 1 then
3059 begin
3060 // Создание первого игрока:
3061 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3062 gPlayer1Settings.Color,
3063 gPlayer1Settings.Team, False));
3064 if gPlayer1 = nil then
3065 begin
3066 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3067 Exit;
3068 end;
3070 gPlayer1.Name := gPlayer1Settings.Name;
3071 Inc(nPl);
3072 end;
3074 if nPlayers >= 2 then
3075 begin
3076 // Создание второго игрока:
3077 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3078 gPlayer2Settings.Color,
3079 gPlayer2Settings.Team, False));
3080 if gPlayer2 = nil then
3081 begin
3082 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3083 Exit;
3084 end;
3086 gPlayer2.Name := gPlayer2Settings.Name;
3087 Inc(nPl);
3088 end;
3090 // Загрузка и запуск карты:
3091 if not g_Game_StartMap(true{asMegawad}, Map, True) then
3092 begin
3093 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3094 Exit;
3095 end;
3097 // Нет точек появления:
3098 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3099 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3100 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3101 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3102 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3103 begin
3104 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3105 Exit;
3106 end;
3108 // Настройки игроков и ботов:
3109 g_Player_Init();
3111 // Создаем ботов:
3112 for i := nPl+1 to nPlayers do
3113 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3114 end;
3116 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3117 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3118 Options: LongWord; nPlayers: Byte;
3119 IPAddr: LongWord; Port: Word);
3120 begin
3121 g_Game_Free();
3122 g_Net_Slist_ServerClosed();
3124 e_WriteLog('Starting net game (server)...', TMsgType.Notify);
3126 g_Game_ClearLoading();
3128 // Настройки игры:
3129 gGameSettings.GameType := GT_SERVER;
3130 gGameSettings.GameMode := GameMode;
3131 gSwitchGameMode := GameMode;
3132 gGameSettings.TimeLimit := TimeLimit;
3133 gGameSettings.GoalLimit := GoalLimit;
3134 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3135 gGameSettings.Options := Options;
3137 gCoopTotalMonstersKilled := 0;
3138 gCoopTotalSecretsFound := 0;
3139 gCoopTotalMonsters := 0;
3140 gCoopTotalSecrets := 0;
3141 gAimLine := False;
3142 gShowMap := False;
3144 gLMSRespawn := LMS_RESPAWN_NONE;
3145 gLMSRespawnTime := 0;
3146 gSpectLatchPID1 := 0;
3147 gSpectLatchPID2 := 0;
3149 g_Game_ExecuteEvent('ongamestart');
3151 // Установка размеров окна игрока
3152 g_Game_SetupScreenSize();
3154 // Режим наблюдателя:
3155 if nPlayers = 0 then
3156 begin
3157 gPlayer1 := nil;
3158 gPlayer2 := nil;
3159 end;
3161 if nPlayers >= 1 then
3162 begin
3163 // Создание первого игрока:
3164 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3165 gPlayer1Settings.Color,
3166 gPlayer1Settings.Team, False));
3167 if gPlayer1 = nil then
3168 begin
3169 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3170 Exit;
3171 end;
3173 gPlayer1.Name := gPlayer1Settings.Name;
3174 end;
3176 if nPlayers >= 2 then
3177 begin
3178 // Создание второго игрока:
3179 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3180 gPlayer2Settings.Color,
3181 gPlayer2Settings.Team, False));
3182 if gPlayer2 = nil then
3183 begin
3184 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3185 Exit;
3186 end;
3188 gPlayer2.Name := gPlayer2Settings.Name;
3189 end;
3191 g_Game_SetLoadingText(_lc[I_LOAD_HOST], 0, False);
3192 if NetForwardPorts then
3193 g_Game_SetLoadingText(_lc[I_LOAD_PORTS], 0, False);
3195 // Стартуем сервер
3196 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3197 begin
3198 g_FatalError(_lc[I_NET_MSG] + Format(_lc[I_NET_ERR_HOST], [Port]));
3199 Exit;
3200 end;
3202 g_Net_Slist_Set(NetMasterList);
3204 g_Net_Slist_ServerStarted();
3206 // Загрузка и запуск карты:
3207 if not g_Game_StartMap(false{asMegawad}, Map, True) then
3208 begin
3209 g_Net_Slist_ServerClosed();
3210 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3211 Exit;
3212 end;
3214 // Нет точек появления:
3215 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3216 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3217 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3218 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3219 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3220 begin
3221 g_Net_Slist_ServerClosed();
3222 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3223 Exit;
3224 end;
3226 // Настройки игроков и ботов:
3227 g_Player_Init();
3229 g_Net_Slist_ServerMapStarted();
3230 NetState := NET_STATE_GAME;
3231 end;
3233 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3234 var
3235 Map: String;
3236 WadName: string;
3237 Ptr: Pointer;
3238 T: Cardinal;
3239 MID: Byte;
3240 State: Byte;
3241 OuterLoop: Boolean;
3242 newResPath: string;
3243 InMsg: TMsg;
3244 begin
3245 g_Game_Free();
3247 State := 0;
3248 e_WriteLog('Starting net game (client)...', TMsgType.Notify);
3249 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', TMsgType.Notify);
3251 g_Game_ClearLoading();
3253 // Настройки игры:
3254 gGameSettings.GameType := GT_CLIENT;
3256 gCoopTotalMonstersKilled := 0;
3257 gCoopTotalSecretsFound := 0;
3258 gCoopTotalMonsters := 0;
3259 gCoopTotalSecrets := 0;
3260 gAimLine := False;
3261 gShowMap := False;
3263 g_Game_ExecuteEvent('ongamestart');
3265 // Установка размеров окон игроков:
3266 g_Game_SetupScreenSize();
3268 NetState := NET_STATE_AUTH;
3270 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3272 // create (or update) map/resource databases
3273 g_Res_CreateDatabases(true);
3275 gLMSRespawn := LMS_RESPAWN_NONE;
3276 gLMSRespawnTime := 0;
3277 gSpectLatchPID1 := 0;
3278 gSpectLatchPID2 := 0;
3280 // Стартуем клиент
3281 if not g_Net_Connect(Addr, Port) then
3282 begin
3283 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3284 NetState := NET_STATE_NONE;
3285 Exit;
3286 end;
3288 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3289 MC_SEND_Info(PW);
3290 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3292 OuterLoop := True;
3293 while OuterLoop do
3294 begin
3295 // fuck! https://www.mail-archive.com/enet-discuss@cubik.org/msg00852.html
3296 // tl;dr: on shitdows, we can get -1 sometimes, and it is *NOT* a failure.
3297 // thank you, enet. let's ignore failures altogether then.
3298 while (enet_host_service(NetHost, @NetEvent, 50) > 0) do
3299 begin
3300 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3301 begin
3302 if (NetEvent.channelID = NET_CHAN_DOWNLOAD_EX) then
3303 begin
3304 // ignore all download packets, they're processed by separate code
3305 enet_packet_destroy(NetEvent.packet);
3306 continue;
3307 end;
3308 Ptr := NetEvent.packet^.data;
3309 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
3310 begin
3311 enet_packet_destroy(NetEvent.packet);
3312 continue;
3313 end;
3315 InMsg.ReadLongWord(); // skip size
3316 MID := InMsg.ReadByte();
3318 if (MID = NET_MSG_INFO) and (State = 0) then
3319 begin
3320 NetMyID := InMsg.ReadByte();
3321 NetPlrUID1 := InMsg.ReadWord();
3323 WadName := InMsg.ReadString();
3324 Map := InMsg.ReadString();
3326 gWADHash := InMsg.ReadMD5();
3328 gGameSettings.GameMode := InMsg.ReadByte();
3329 gSwitchGameMode := gGameSettings.GameMode;
3330 gGameSettings.GoalLimit := InMsg.ReadWord();
3331 gGameSettings.TimeLimit := InMsg.ReadWord();
3332 gGameSettings.MaxLives := InMsg.ReadByte();
3333 gGameSettings.Options := InMsg.ReadLongWord();
3334 T := InMsg.ReadLongWord();
3336 //newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3337 //if newResPath = '' then
3338 begin
3339 //g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3340 newResPath := g_Res_DownloadMapWAD(ExtractFileName(WadName), gWADHash);
3341 if newResPath = '' then
3342 begin
3343 g_FatalError(_lc[I_NET_ERR_HASH]);
3344 enet_packet_destroy(NetEvent.packet);
3345 NetState := NET_STATE_NONE;
3346 Exit;
3347 end;
3348 e_LogWritefln('using downloaded map wad [%s] for [%s]`', [newResPath, WadName], TMsgType.Notify);
3349 end;
3350 //newResPath := ExtractRelativePath(MapsDir, newResPath);
3353 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3354 gPlayer1Settings.Color,
3355 gPlayer1Settings.Team, False));
3357 if gPlayer1 = nil then
3358 begin
3359 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3361 enet_packet_destroy(NetEvent.packet);
3362 NetState := NET_STATE_NONE;
3363 Exit;
3364 end;
3366 gPlayer1.Name := gPlayer1Settings.Name;
3367 gPlayer1.UID := NetPlrUID1;
3368 gPlayer1.Reset(True);
3370 if not g_Game_StartMap(false{asMegawad}, newResPath + ':\' + Map, True) then
3371 begin
3372 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3374 enet_packet_destroy(NetEvent.packet);
3375 NetState := NET_STATE_NONE;
3376 Exit;
3377 end;
3379 gTime := T;
3381 State := 1;
3382 OuterLoop := False;
3383 enet_packet_destroy(NetEvent.packet);
3384 break;
3385 end
3386 else
3387 enet_packet_destroy(NetEvent.packet);
3388 end
3389 else
3390 begin
3391 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3392 begin
3393 State := 0;
3394 if (NetEvent.data <= NET_DISC_MAX) then
3395 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3396 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3397 OuterLoop := False;
3398 Break;
3399 end;
3400 end;
3401 end;
3403 ProcessLoading(true);
3405 if g_Net_UserRequestExit() then
3406 begin
3407 State := 0;
3408 break;
3409 end;
3410 end;
3412 if State <> 1 then
3413 begin
3414 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3415 NetState := NET_STATE_NONE;
3416 Exit;
3417 end;
3419 g_Player_Init();
3420 NetState := NET_STATE_GAME;
3421 MC_SEND_FullStateRequest;
3422 e_WriteLog('NET: Connection successful.', TMsgType.Notify);
3423 end;
3425 var
3426 lastAsMegaWad: Boolean = false;
3428 procedure g_Game_ChangeMap(const MapPath: String);
3429 var
3430 Force: Boolean;
3431 begin
3432 g_Game_ClearLoading();
3434 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3435 // Если уровень завершился по триггеру Выход, не очищать инвентарь
3436 if gExitByTrigger then
3437 begin
3438 Force := False;
3439 gExitByTrigger := False;
3440 end;
3441 if not g_Game_StartMap(lastAsMegaWad, MapPath, Force) then
3442 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
3443 end;
3445 procedure g_Game_Restart();
3446 var
3447 Map: string;
3448 begin
3449 if g_Game_IsClient then
3450 Exit;
3451 map := g_ExtractFileName(gMapInfo.Map);
3452 e_LogWritefln('g_Game_Restart: map = "%s" gCurrentMapFileName = "%s"', [map, gCurrentMapFileName]);
3454 MessageTime := 0;
3455 gGameOn := False;
3456 g_Game_ClearLoading();
3457 g_Game_StartMap(lastAsMegaWad, Map, True, gCurrentMapFileName);
3458 end;
3460 function g_Game_StartMap (asMegawad: Boolean; Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
3461 var
3462 NewWAD, ResName: String;
3463 I: Integer;
3464 nws: AnsiString;
3465 begin
3466 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
3467 g_Player_RemoveAllCorpses();
3469 if (not g_Game_IsClient) and
3470 (gSwitchGameMode <> gGameSettings.GameMode) and
3471 (gGameSettings.GameMode <> GM_SINGLE) then
3472 begin
3473 if gSwitchGameMode = GM_CTF then
3474 gGameSettings.MaxLives := 0;
3475 gGameSettings.GameMode := gSwitchGameMode;
3476 Force := True;
3477 end else
3478 gSwitchGameMode := gGameSettings.GameMode;
3480 g_Player_ResetTeams();
3482 lastAsMegaWad := asMegawad;
3483 if isWadPath(Map) then
3484 begin
3485 NewWAD := g_ExtractWadName(Map);
3486 ResName := g_ExtractFileName(Map);
3487 if g_Game_IsServer then
3488 begin
3489 nws := findDiskWad(NewWAD);
3490 //writeln('000: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']');
3491 if (asMegawad) then
3492 begin
3493 if (length(nws) = 0) then nws := e_FindWad(MegawadDirs, NewWAD);
3494 if (length(nws) = 0) then nws := e_FindWad(MapDirs, NewWAD);
3495 end
3496 else
3497 begin
3498 if (length(nws) = 0) then nws := e_FindWad(MapDirs, NewWAD);
3499 if (length(nws) = 0) then nws := e_FindWad(MegawadDirs, NewWAD);
3500 end;
3501 //if (length(nws) = 0) then nws := e_FindWad(MapDownloadDirs, NewWAD);
3502 //writeln('001: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']');
3503 //nws := NewWAD;
3504 if (length(nws) = 0) then
3505 begin
3506 ResName := ''; // failed
3507 end
3508 else
3509 begin
3510 NewWAD := nws;
3511 if (g_Game_IsNet) then gWADHash := MD5File(nws);
3512 //writeln('********: nws=', nws, ' : Map=', Map, ' : nw=', NewWAD, ' : resname=', ResName);
3513 g_Game_LoadWAD(NewWAD);
3514 end;
3515 end
3516 else
3517 begin
3518 // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART
3519 NewWAD := g_Game_ClientWAD(NewWAD, gWADHash);
3520 end;
3521 end
3522 else
3523 begin
3524 NewWAD := gGameSettings.WAD;
3525 ResName := Map;
3526 end;
3528 gTime := 0;
3530 //writeln('********: gsw=', gGameSettings.WAD, '; rn=', ResName);
3531 result := false;
3532 if (ResName <> '') and (NewWAD <> '') then
3533 begin
3534 //result := g_Map_Load(gGameSettings.WAD + ':\' + ResName);
3535 result := g_Map_Load(NewWAD+':\'+ResName);
3536 end;
3537 if Result then
3538 begin
3539 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
3541 gState := STATE_NONE;
3542 g_ActiveWindow := nil;
3543 gGameOn := True;
3545 DisableCheats();
3546 wNeedTimeReset := True;
3548 if gGameSettings.GameMode = GM_CTF then
3549 begin
3550 g_Map_ResetFlag(FLAG_RED);
3551 g_Map_ResetFlag(FLAG_BLUE);
3552 // CTF, а флагов нет:
3553 if not g_Map_HaveFlagPoints() then
3554 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
3555 end;
3556 end
3557 else
3558 begin
3559 gState := STATE_MENU;
3560 gGameOn := False;
3561 end;
3563 gExit := 0;
3564 gPauseMain := false;
3565 gPauseHolmes := false;
3566 NetTimeToUpdate := 1;
3567 NetTimeToReliable := 0;
3568 NetTimeToMaster := NetMasterRate;
3569 gSpectLatchPID1 := 0;
3570 gSpectLatchPID2 := 0;
3571 gMissionFailed := False;
3572 gNextMap := '';
3574 gCoopMonstersKilled := 0;
3575 gCoopSecretsFound := 0;
3577 gVoteInProgress := False;
3578 gVotePassed := False;
3579 gVoteCount := 0;
3580 gVoted := False;
3582 gStatsOff := False;
3584 if not gGameOn then Exit;
3586 g_Game_SpectateCenterView();
3588 if g_Game_IsServer then
3589 begin
3590 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
3591 begin
3592 gLMSRespawn := LMS_RESPAWN_WARMUP;
3593 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
3594 gLMSSoftSpawn := True;
3595 if g_Game_IsNet then
3596 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime);
3597 end
3598 else
3599 begin
3600 gLMSRespawn := LMS_RESPAWN_NONE;
3601 gLMSRespawnTime := 0;
3602 end;
3603 end;
3605 if NetMode = NET_SERVER then
3606 begin
3607 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
3609 // Мастерсервер
3610 g_Net_Slist_ServerMapStarted();
3612 if NetClients <> nil then
3613 for I := 0 to High(NetClients) do
3614 if NetClients[I].Used then
3615 begin
3616 NetClients[I].Voted := False;
3617 if NetClients[I].RequestedFullUpdate then
3618 begin
3619 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
3620 NetClients[I].RequestedFullUpdate := False;
3621 end;
3622 end;
3624 g_Net_UnbanNonPermHosts();
3625 end;
3627 if gLastMap then
3628 begin
3629 gCoopTotalMonstersKilled := 0;
3630 gCoopTotalSecretsFound := 0;
3631 gCoopTotalMonsters := 0;
3632 gCoopTotalSecrets := 0;
3633 gLastMap := False;
3634 end;
3636 g_Game_ExecuteEvent('onmapstart');
3637 end;
3639 procedure SetFirstLevel;
3640 begin
3641 gNextMap := '';
3643 MapList := g_Map_GetMapsList(gGameSettings.WAD);
3644 if MapList = nil then
3645 Exit;
3647 SortSArray(MapList);
3648 gNextMap := MapList[Low(MapList)];
3650 MapList := nil;
3651 end;
3653 procedure g_Game_ExitLevel(const Map: AnsiString);
3654 begin
3655 gNextMap := Map;
3657 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
3658 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
3659 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
3660 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
3662 // Вышли в выход в Одиночной игре:
3663 if gGameSettings.GameType = GT_SINGLE then
3664 gExit := EXIT_ENDLEVELSINGLE
3665 else // Вышли в выход в Своей игре
3666 begin
3667 gExit := EXIT_ENDLEVELCUSTOM;
3668 if gGameSettings.GameMode = GM_COOP then
3669 g_Player_RememberAll;
3671 if not g_Map_Exist(gGameSettings.WAD + ':\' + gNextMap) then
3672 begin
3673 gLastMap := True;
3674 if gGameSettings.GameMode = GM_COOP then
3675 gStatsOff := True;
3677 gStatsPressed := True;
3678 gNextMap := 'MAP01';
3680 if not g_Map_Exist(gGameSettings.WAD + ':\' + gNextMap) then
3681 g_Game_NextLevel;
3683 if g_Game_IsNet then
3684 begin
3685 MH_SEND_GameStats();
3686 MH_SEND_CoopStats();
3687 end;
3688 end;
3689 end;
3690 end;
3692 procedure g_Game_RestartLevel();
3693 var
3694 Map: string;
3695 begin
3696 if gGameSettings.GameMode = GM_SINGLE then
3697 begin
3698 g_Game_Restart();
3699 Exit;
3700 end;
3701 gExit := EXIT_ENDLEVELCUSTOM;
3702 Map := g_ExtractFileName(gMapInfo.Map);
3703 gNextMap := Map;
3704 end;
3706 function g_Game_ClientWAD (NewWAD: String; const WHash: TMD5Digest): AnsiString;
3707 var
3708 gWAD{, xwad}: String;
3709 begin
3710 result := NewWAD;
3711 if not g_Game_IsClient then Exit;
3712 //e_LogWritefln('*** g_Game_ClientWAD: `%s`', [NewWAD]);
3714 gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash);
3715 if gWAD = '' then
3716 begin
3717 result := '';
3718 g_Game_Free();
3719 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
3720 Exit;
3721 end;
3723 e_LogWritefln('using downloaded client map wad [%s] for [%s]', [gWAD, NewWAD], TMsgType.Notify);
3724 NewWAD := gWAD;
3726 g_Game_LoadWAD(NewWAD);
3727 result := NewWAD;
3730 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then Exit;
3731 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
3732 if gWAD = '' then
3733 begin
3734 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3735 gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash);
3736 if gWAD = '' then
3737 begin
3738 g_Game_Free();
3739 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
3740 Exit;
3741 end;
3742 end;
3743 NewWAD := ExtractRelativePath(MapsDir, gWAD);
3744 g_Game_LoadWAD(NewWAD);
3746 end;
3748 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
3749 var
3750 i, n, nb, nr: Integer;
3751 begin
3752 if not g_Game_IsServer then Exit;
3753 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
3754 gLMSRespawn := LMS_RESPAWN_NONE;
3755 gLMSRespawnTime := 0;
3756 MessageTime := 0;
3758 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
3759 begin
3760 gMissionFailed := True;
3761 g_Game_RestartLevel;
3762 Exit;
3763 end;
3765 n := 0; nb := 0; nr := 0;
3766 for i := Low(gPlayers) to High(gPlayers) do
3767 if (gPlayers[i] <> nil) and
3768 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
3769 (gPlayers[i] is TBot)) then
3770 begin
3771 Inc(n);
3772 if gPlayers[i].Team = TEAM_RED then Inc(nr)
3773 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
3774 end;
3776 if (n < 1) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
3777 begin
3778 // wait a second until the fuckers finally decide to join
3779 gLMSRespawn := LMS_RESPAWN_WARMUP;
3780 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
3781 gLMSSoftSpawn := NoMapRestart;
3782 if g_Game_IsNet then
3783 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime);
3784 Exit;
3785 end;
3787 g_Player_RemoveAllCorpses;
3788 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
3789 if g_Game_IsNet then
3790 MH_SEND_GameEvent(NET_EV_LMS_START);
3792 for i := Low(gPlayers) to High(gPlayers) do
3793 begin
3794 if gPlayers[i] = nil then continue;
3795 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
3796 // don't touch normal spectators
3797 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
3798 begin
3799 gPlayers[i].FNoRespawn := True;
3800 gPlayers[i].Lives := 0;
3801 if g_Game_IsNet then
3802 MH_SEND_PlayerStats(gPlayers[I].UID);
3803 continue;
3804 end;
3805 gPlayers[i].FNoRespawn := False;
3806 gPlayers[i].Lives := gGameSettings.MaxLives;
3807 gPlayers[i].Respawn(False, True);
3808 if gGameSettings.GameMode = GM_COOP then
3809 begin
3810 gPlayers[i].Frags := 0;
3811 gPlayers[i].RecallState;
3812 end;
3813 if (gPlayer1 = nil) and (gSpectLatchPID1 > 0) then
3814 gPlayer1 := g_Player_Get(gSpectLatchPID1);
3815 if (gPlayer2 = nil) and (gSpectLatchPID2 > 0) then
3816 gPlayer2 := g_Player_Get(gSpectLatchPID2);
3817 end;
3819 g_Items_RestartRound();
3821 gLMSSoftSpawn := False;
3822 end;
3824 function g_Game_GetFirstMap(WAD: String): String;
3825 begin
3826 Result := '';
3828 MapList := g_Map_GetMapsList(WAD);
3829 if MapList = nil then
3830 Exit;
3832 SortSArray(MapList);
3833 Result := MapList[Low(MapList)];
3835 if not g_Map_Exist(WAD + ':\' + Result) then
3836 Result := '';
3838 MapList := nil;
3839 end;
3841 function g_Game_GetNextMap(): String;
3842 var
3843 I: Integer;
3844 Map: string;
3845 begin
3846 Result := '';
3848 MapList := g_Map_GetMapsList(gGameSettings.WAD);
3849 if MapList = nil then
3850 Exit;
3852 Map := g_ExtractFileName(gMapInfo.Map);
3854 SortSArray(MapList);
3855 MapIndex := -255;
3856 for I := Low(MapList) to High(MapList) do
3857 if Map = MapList[I] then
3858 begin
3859 MapIndex := I;
3860 Break;
3861 end;
3863 if MapIndex <> -255 then
3864 begin
3865 if MapIndex = High(MapList) then
3866 Result := MapList[Low(MapList)]
3867 else
3868 Result := MapList[MapIndex + 1];
3870 if not g_Map_Exist(gGameSettings.WAD + ':\' + Result) then Result := Map;
3871 end;
3873 MapList := nil;
3874 end;
3876 procedure g_Game_NextLevel();
3877 begin
3878 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
3879 gExit := EXIT_ENDLEVELCUSTOM
3880 else
3881 begin
3882 gExit := EXIT_ENDLEVELSINGLE;
3883 Exit;
3884 end;
3886 if gNextMap <> '' then Exit;
3887 gNextMap := g_Game_GetNextMap();
3888 end;
3890 function g_Game_IsTestMap(): Boolean;
3891 begin
3892 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
3893 end;
3895 procedure g_Game_DeleteTestMap();
3896 var
3897 a: Integer;
3898 //MapName: AnsiString;
3899 WadName: string;
3901 WAD: TWADFile;
3902 MapList: SSArray;
3903 time: Integer;
3905 begin
3906 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
3907 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
3908 if (a = 0) then exit;
3910 // Выделяем имя wad-файла и имя карты
3911 WadName := Copy(gMapToDelete, 1, a+3);
3912 Delete(gMapToDelete, 1, a+5);
3913 gMapToDelete := UpperCase(gMapToDelete);
3914 //MapName := '';
3915 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
3918 // Имя карты не стандартное тестовое:
3919 if MapName <> TEST_MAP_NAME then
3920 Exit;
3922 if not gTempDelete then
3923 begin
3924 time := g_GetFileTime(WadName);
3925 WAD := TWADFile.Create();
3927 // Читаем Wad-файл:
3928 if not WAD.ReadFile(WadName) then
3929 begin // Нет такого WAD-файла
3930 WAD.Free();
3931 Exit;
3932 end;
3934 // Составляем список карт и ищем нужную:
3935 WAD.CreateImage();
3936 MapList := WAD.GetResourcesList('');
3938 if MapList <> nil then
3939 for a := 0 to High(MapList) do
3940 if MapList[a] = MapName then
3941 begin
3942 // Удаляем и сохраняем:
3943 WAD.RemoveResource('', MapName);
3944 WAD.SaveTo(WadName);
3945 Break;
3946 end;
3948 WAD.Free();
3949 g_SetFileTime(WadName, time);
3950 end else
3952 if gTempDelete then DeleteFile(WadName);
3953 end;
3955 procedure GameCVars(P: SSArray);
3956 var
3957 a, b: Integer;
3958 stat: TPlayerStatArray;
3959 cmd: string;
3961 procedure ParseGameFlag(Flag: LongWord; OffMsg, OnMsg: TStrings_Locale; OnMapChange: Boolean = False);
3962 var
3963 x: Boolean;
3964 begin
3965 if Length(P) > 1 then
3966 begin
3967 x := P[1] = '1';
3969 if x then
3970 gsGameFlags := gsGameFlags or Flag
3971 else
3972 gsGameFlags := gsGameFlags and (not Flag);
3974 if g_Game_IsServer then
3975 begin
3976 if x then
3977 gGameSettings.Options := gGameSettings.Options or Flag
3978 else
3979 gGameSettings.Options := gGameSettings.Options and (not Flag);
3980 if g_Game_IsNet then MH_SEND_GameSettings;
3981 end;
3982 end;
3984 if LongBool(gsGameFlags and Flag) then
3985 g_Console_Add(_lc[OnMsg])
3986 else
3987 g_Console_Add(_lc[OffMsg]);
3989 if OnMapChange and g_Game_IsServer then
3990 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
3991 end;
3993 begin
3994 stat := nil;
3995 cmd := LowerCase(P[0]);
3997 if cmd = 'g_gamemode' then
3998 begin
3999 if (Length(P) > 1) then
4000 begin
4001 a := g_Game_TextToMode(P[1]);
4002 if a = GM_SINGLE then a := GM_COOP;
4003 gsGameMode := g_Game_ModeToText(a);
4004 if g_Game_IsServer then
4005 begin
4006 gSwitchGameMode := a;
4007 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4008 (gState = STATE_INTERSINGLE) then
4009 gSwitchGameMode := GM_SINGLE;
4010 if not gGameOn then
4011 gGameSettings.GameMode := gSwitchGameMode;
4012 end;
4013 end;
4015 if gSwitchGameMode = gGameSettings.GameMode then
4016 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4017 [g_Game_ModeToText(gGameSettings.GameMode)]))
4018 else
4019 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4020 [g_Game_ModeToText(gGameSettings.GameMode),
4021 g_Game_ModeToText(gSwitchGameMode)]));
4022 end
4023 else if cmd = 'g_friendlyfire' then
4024 begin
4025 ParseGameFlag(GAME_OPTION_TEAMDAMAGE, I_MSG_FRIENDLY_FIRE_OFF, I_MSG_FRIENDLY_FIRE_ON);
4026 end
4027 else if cmd = 'g_friendly_absorb_damage' then
4028 begin
4029 ParseGameFlag(GAME_OPTION_TEAMABSORBDAMAGE, I_MSG_FRIENDLY_FIRE_OFF, I_MSG_FRIENDLY_FIRE_ON);
4030 end
4031 else if cmd = 'g_friendly_hit_trace' then
4032 begin
4033 ParseGameFlag(GAME_OPTION_TEAMHITTRACE, I_MSG_FRIENDLY_FIRE_OFF, I_MSG_FRIENDLY_FIRE_ON);
4034 end
4035 else if cmd = 'g_friendly_hit_projectile' then
4036 begin
4037 ParseGameFlag(GAME_OPTION_TEAMHITPROJECTILE, I_MSG_FRIENDLY_FIRE_OFF, I_MSG_FRIENDLY_FIRE_ON);
4038 end
4039 else if cmd = 'g_weaponstay' then
4040 begin
4041 ParseGameFlag(GAME_OPTION_WEAPONSTAY, I_MSG_WEAPONSTAY_OFF, I_MSG_WEAPONSTAY_ON);
4042 end
4043 else if cmd = 'g_allow_exit' then
4044 begin
4045 ParseGameFlag(GAME_OPTION_ALLOWEXIT, I_MSG_ALLOWEXIT_OFF, I_MSG_ALLOWEXIT_ON, True);
4046 end
4047 else if cmd = 'g_allow_monsters' then
4048 begin
4049 ParseGameFlag(GAME_OPTION_MONSTERS, I_MSG_ALLOWMON_OFF, I_MSG_ALLOWMON_ON, True);
4050 end
4051 else if cmd = 'g_bot_vsplayers' then
4052 begin
4053 ParseGameFlag(GAME_OPTION_BOTVSPLAYER, I_MSG_BOTSVSPLAYERS_OFF, I_MSG_BOTSVSPLAYERS_ON);
4054 end
4055 else if cmd = 'g_bot_vsmonsters' then
4056 begin
4057 ParseGameFlag(GAME_OPTION_BOTVSMONSTER, I_MSG_BOTSVSMONSTERS_OFF, I_MSG_BOTSVSMONSTERS_ON);
4058 end
4059 else if cmd = 'g_dm_keys' then
4060 begin
4061 ParseGameFlag(GAME_OPTION_DMKEYS, I_MSG_DMKEYS_OFF, I_MSG_DMKEYS_ON, True);
4062 end
4063 else if cmd = 'g_gameflags' then
4064 begin
4065 if Length(P) > 1 then
4066 begin
4067 gsGameFlags := StrToDWordDef(P[1], gsGameFlags);
4068 if g_Game_IsServer then
4069 begin
4070 gGameSettings.Options := gsGameFlags;
4071 if g_Game_IsNet then MH_SEND_GameSettings;
4072 end;
4073 end;
4075 g_Console_Add(Format('%s %u', [cmd, gsGameFlags]));
4076 end
4077 else if cmd = 'g_warmup_time' then
4078 begin
4079 if Length(P) > 1 then
4080 begin
4081 gsWarmupTime := nclamp(StrToIntDef(P[1], gsWarmupTime), 0, $FFFF);
4082 if g_Game_IsServer then
4083 begin
4084 gGameSettings.WarmupTime := gsWarmupTime;
4085 // extend warmup if it's already going
4086 if gLMSRespawn = LMS_RESPAWN_WARMUP then
4087 begin
4088 gLMSRespawnTime := gTime + gsWarmupTime * 1000;
4089 if g_Game_IsNet then MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime);
4090 end;
4091 if g_Game_IsNet then MH_SEND_GameSettings;
4092 end;
4093 end;
4095 g_Console_Add(Format(_lc[I_MSG_WARMUP], [Integer(gsWarmupTime)]));
4096 if g_Game_IsServer then g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4097 end
4098 else if cmd = 'g_spawn_invul' then
4099 begin
4100 if Length(P) > 1 then
4101 begin
4102 gsSpawnInvul := nclamp(StrToIntDef(P[1], gsSpawnInvul), 0, $FFFF);
4103 if g_Game_IsServer then
4104 begin
4105 gGameSettings.SpawnInvul := gsSpawnInvul;
4106 if g_Game_IsNet then MH_SEND_GameSettings;
4107 end;
4108 end;
4110 g_Console_Add(Format('%s %d', [cmd, Integer(gsSpawnInvul)]));
4111 end
4112 else if cmd = 'g_item_respawn_time' then
4113 begin
4114 if Length(P) > 1 then
4115 begin
4116 gsItemRespawnTime := nclamp(StrToIntDef(P[1], gsItemRespawnTime), 0, $FFFF);
4117 if g_Game_IsServer then
4118 begin
4119 gGameSettings.ItemRespawnTime := gsItemRespawnTime;
4120 if g_Game_IsNet then MH_SEND_GameSettings;
4121 end;
4122 end;
4124 g_Console_Add(Format('%s %d', [cmd, Integer(gsItemRespawnTime)]));
4125 if g_Game_IsServer then g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4126 end
4127 else if cmd = 'sv_intertime' then
4128 begin
4129 if (Length(P) > 1) then
4130 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4132 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4133 end
4134 else if cmd = 'g_max_particles' then
4135 begin
4136 if Length(p) = 2 then
4137 begin
4138 a := Max(0, StrToInt(p[1]));
4139 g_GFX_SetMax(a)
4140 end
4141 else if Length(p) = 1 then
4142 begin
4143 e_LogWritefln('%s', [g_GFX_GetMax()])
4144 end
4145 else
4146 begin
4147 e_LogWritefln('usage: %s <n>', [cmd])
4148 end
4149 end
4150 else if cmd = 'g_max_shells' then
4151 begin
4152 if Length(p) = 2 then
4153 begin
4154 a := Max(0, StrToInt(p[1]));
4155 g_Shells_SetMax(a)
4156 end
4157 else if Length(p) = 1 then
4158 begin
4159 e_LogWritefln('%s', [g_Shells_GetMax()])
4160 end
4161 else
4162 begin
4163 e_LogWritefln('usage: %s <n>', [cmd])
4164 end
4165 end
4166 else if cmd = 'g_max_gibs' then
4167 begin
4168 if Length(p) = 2 then
4169 begin
4170 a := Max(0, StrToInt(p[1]));
4171 g_Gibs_SetMax(a)
4172 end
4173 else if Length(p) = 1 then
4174 begin
4175 e_LogWritefln('%s', [g_Gibs_GetMax()])
4176 end
4177 else
4178 begin
4179 e_LogWritefln('usage: %s <n>', [cmd])
4180 end
4181 end
4182 else if cmd = 'g_max_corpses' then
4183 begin
4184 if Length(p) = 2 then
4185 begin
4186 a := Max(0, StrToInt(p[1]));
4187 g_Corpses_SetMax(a)
4188 end
4189 else if Length(p) = 1 then
4190 begin
4191 e_LogWritefln('%s', [g_Corpses_GetMax()])
4192 end
4193 else
4194 begin
4195 e_LogWritefln('usage: %s <n>', [cmd])
4196 end
4197 end
4198 else if cmd = 'g_scorelimit' then
4199 begin
4200 if Length(P) > 1 then
4201 begin
4202 gsGoalLimit := nclamp(StrToIntDef(P[1], gsGoalLimit), 0, $FFFF);
4204 if g_Game_IsServer then
4205 begin
4206 b := 0;
4207 if gGameSettings.GameMode = GM_DM then
4208 begin // DM
4209 stat := g_Player_GetStats();
4210 if stat <> nil then
4211 for a := 0 to High(stat) do
4212 if stat[a].Frags > b then
4213 b := stat[a].Frags;
4214 end
4215 else // TDM/CTF
4216 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4218 // if someone has a higher score, set it to that instead
4219 gsGoalLimit := max(gsGoalLimit, b);
4220 gGameSettings.GoalLimit := gsGoalLimit;
4222 if g_Game_IsNet then MH_SEND_GameSettings;
4223 end;
4224 end;
4226 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [Integer(gsGoalLimit)]));
4227 end
4228 else if cmd = 'g_timelimit' then
4229 begin
4230 if Length(P) > 1 then
4231 begin
4232 gsTimeLimit := nclamp(StrToIntDef(P[1], gsTimeLimit), 0, $FFFF);
4233 if g_Game_IsServer then
4234 begin
4235 gGameSettings.TimeLimit := gsTimeLimit;
4236 if g_Game_IsNet then MH_SEND_GameSettings;
4237 end;
4238 end;
4239 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4240 [gsTimeLimit div 3600,
4241 (gsTimeLimit div 60) mod 60,
4242 gsTimeLimit mod 60]));
4243 end
4244 else if cmd = 'g_maxlives' then
4245 begin
4246 if Length(P) > 1 then
4247 begin
4248 gsMaxLives := nclamp(StrToIntDef(P[1], gsMaxLives), 0, $FFFF);
4249 if g_Game_IsServer then
4250 begin
4251 gGameSettings.MaxLives := gsMaxLives;
4252 if g_Game_IsNet then MH_SEND_GameSettings;
4253 end;
4254 end;
4256 g_Console_Add(Format(_lc[I_MSG_LIVES], [Integer(gsMaxLives)]));
4257 end;
4258 end;
4260 procedure PlayerSettingsCVars(P: SSArray);
4261 var
4262 cmd: string;
4263 team: Byte;
4265 function ParseTeam(s: string): Byte;
4266 begin
4267 result := 0;
4268 case s of
4269 'red', '1': result := TEAM_RED;
4270 'blue', '2': result := TEAM_BLUE;
4271 else result := TEAM_NONE;
4272 end;
4273 end;
4274 begin
4275 cmd := LowerCase(P[0]);
4276 case cmd of
4277 'p1_name':
4278 begin
4279 if (Length(P) > 1) then
4280 begin
4281 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4282 if g_Game_IsClient then
4283 MC_SEND_PlayerSettings
4284 else if gGameOn and (gPlayer1 <> nil) then
4285 begin
4286 gPlayer1.Name := b_Text_Unformat(P[1]);
4287 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4288 end;
4289 end;
4290 end;
4291 'p2_name':
4292 begin
4293 if (Length(P) > 1) then
4294 begin
4295 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4296 if g_Game_IsClient then
4297 MC_SEND_PlayerSettings
4298 else if gGameOn and (gPlayer2 <> nil) then
4299 begin
4300 gPlayer2.Name := b_Text_Unformat(P[1]);
4301 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4302 end;
4303 end;
4304 end;
4305 'p1_color':
4306 begin
4307 if Length(P) > 3 then
4308 begin
4309 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4310 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4311 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4312 if g_Game_IsClient then
4313 MC_SEND_PlayerSettings
4314 else if gGameOn and (gPlayer1 <> nil) then
4315 begin
4316 gPlayer1.SetColor(gPlayer1Settings.Color);
4317 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4318 end;
4319 end;
4320 end;
4321 'p2_color':
4322 begin
4323 if Length(P) > 3 then
4324 begin
4325 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4326 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4327 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4328 if g_Game_IsClient then
4329 MC_SEND_PlayerSettings
4330 else if gGameOn and (gPlayer2 <> nil) then
4331 begin
4332 gPlayer2.SetColor(gPlayer2Settings.Color);
4333 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4334 end;
4335 end;
4336 end;
4337 'p1_model':
4338 begin
4339 if (Length(P) > 1) then
4340 begin
4341 gPlayer1Settings.Model := P[1];
4342 if g_Game_IsClient then
4343 MC_SEND_PlayerSettings
4344 else if gGameOn and (gPlayer1 <> nil) then
4345 begin
4346 gPlayer1.FActualModelName := gPlayer1Settings.Model;
4347 gPlayer1.SetModel(gPlayer1Settings.Model);
4348 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4349 end;
4350 end;
4351 end;
4352 'p2_model':
4353 begin
4354 if (Length(P) > 1) then
4355 begin
4356 gPlayer2Settings.Model := P[1];
4357 if g_Game_IsClient then
4358 MC_SEND_PlayerSettings
4359 else if gGameOn and (gPlayer2 <> nil) then
4360 begin
4361 gPlayer2.FActualModelName := gPlayer2Settings.Model;
4362 gPlayer2.SetModel(gPlayer2Settings.Model);
4363 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4364 end;
4365 end;
4366 end;
4367 'p1_team':
4368 begin
4369 // TODO: switch teams if in game or store this separately
4370 if (Length(P) > 1) then
4371 begin
4372 team := ParseTeam(P[1]);
4373 if team = TEAM_NONE then
4374 g_Console_Add('expected ''red'', ''blue'', 1 or 2')
4375 else if not gGameOn and not g_Game_IsNet then
4376 gPlayer1Settings.Team := team
4377 else
4378 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4379 end;
4380 end;
4381 'p2_team':
4382 begin
4383 // TODO: switch teams if in game or store this separately
4384 if (Length(P) > 1) then
4385 begin
4386 team := ParseTeam(P[1]);
4387 if team = TEAM_NONE then
4388 g_Console_Add('expected ''red'', ''blue'', 1 or 2')
4389 else if not gGameOn and not g_Game_IsNet then
4390 gPlayer2Settings.Team := team
4391 else
4392 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4393 end;
4394 end;
4395 end;
4396 end;
4398 procedure PrintHeapStats();
4399 var
4400 hs: TFPCHeapStatus;
4401 begin
4402 hs := GetFPCHeapStatus();
4403 e_LogWriteLn ('v===== heap status =====v');
4404 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
4405 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
4406 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
4407 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
4408 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
4409 e_LogWriteLn ('^=======================^');
4410 end;
4412 procedure DebugCommands(P: SSArray);
4413 var
4414 a, b: Integer;
4415 cmd: string;
4416 //pt: TDFPoint;
4417 mon: TMonster;
4418 begin
4419 // Команды отладочного режима:
4420 if {gDebugMode}conIsCheatsEnabled then
4421 begin
4422 cmd := LowerCase(P[0]);
4423 if cmd = 'd_window' then
4424 begin
4425 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
4426 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
4427 end
4428 else if cmd = 'd_sounds' then
4429 begin
4430 if (Length(P) > 1) and
4431 ((P[1] = '1') or (P[1] = '0')) then
4432 g_Debug_Sounds := (P[1][1] = '1');
4434 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
4435 end
4436 else if cmd = 'd_frames' then
4437 begin
4438 if (Length(P) > 1) and
4439 ((P[1] = '1') or (P[1] = '0')) then
4440 g_Debug_Frames := (P[1][1] = '1');
4442 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
4443 end
4444 else if cmd = 'd_winmsg' then
4445 begin
4446 if (Length(P) > 1) and
4447 ((P[1] = '1') or (P[1] = '0')) then
4448 g_Debug_WinMsgs := (P[1][1] = '1');
4450 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
4451 end
4452 else if (cmd = 'd_monoff') and not g_Game_IsNet then
4453 begin
4454 if (Length(P) > 1) and
4455 ((P[1] = '1') or (P[1] = '0')) then
4456 g_Debug_MonsterOff := (P[1][1] = '1');
4458 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
4459 end
4460 else if (cmd = 'd_botoff') and not g_Game_IsNet then
4461 begin
4462 if Length(P) > 1 then
4463 case P[1][1] of
4464 '0': g_debug_BotAIOff := 0;
4465 '1': g_debug_BotAIOff := 1;
4466 '2': g_debug_BotAIOff := 2;
4467 '3': g_debug_BotAIOff := 3;
4468 end;
4470 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
4471 end
4472 else if cmd = 'd_monster' then
4473 begin
4474 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
4475 if Length(P) < 2 then
4476 begin
4477 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
4478 g_Console_Add('ID | Name');
4479 for b := MONSTER_DEMON to MONSTER_MAN do
4480 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
4481 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
4482 end else
4483 begin
4484 a := StrToIntDef(P[1], 0);
4485 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4486 a := g_Mons_TypeIdByName(P[1]);
4488 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4489 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
4490 else
4491 begin
4492 with gPlayer1.Obj do
4493 begin
4494 mon := g_Monsters_Create(a,
4495 X + Rect.X + (Rect.Width div 2),
4496 Y + Rect.Y + Rect.Height,
4497 gPlayer1.Direction, True);
4498 end;
4499 if (Length(P) > 2) and (mon <> nil) then
4500 begin
4501 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
4502 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
4503 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
4504 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
4505 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
4506 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
4507 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
4508 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
4509 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
4510 end;
4511 end;
4512 end;
4513 end
4514 else if (cmd = 'd_health') then
4515 begin
4516 if (Length(P) > 1) and
4517 ((P[1] = '1') or (P[1] = '0')) then
4518 g_debug_HealthBar := (P[1][1] = '1');
4520 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
4521 end
4522 else if (cmd = 'd_player') then
4523 begin
4524 if (Length(P) > 1) and
4525 ((P[1] = '1') or (P[1] = '0')) then
4526 g_debug_Player := (P[1][1] = '1');
4528 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
4529 end
4530 else if (cmd = 'd_mem') then
4531 begin
4532 PrintHeapStats();
4533 end;
4534 end
4535 else
4536 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
4537 end;
4540 procedure GameCheats(P: SSArray);
4541 var
4542 cmd: string;
4543 f, a: Integer;
4544 plr: TPlayer;
4545 begin
4546 if (not gGameOn) or (not conIsCheatsEnabled) then
4547 begin
4548 g_Console_Add('not available');
4549 exit;
4550 end;
4551 plr := gPlayer1;
4552 if plr = nil then
4553 begin
4554 g_Console_Add('where is the player?!');
4555 exit;
4556 end;
4557 cmd := LowerCase(P[0]);
4558 // god
4559 if cmd = 'god' then
4560 begin
4561 plr.GodMode := not plr.GodMode;
4562 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
4563 exit;
4564 end;
4565 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
4566 if cmd = 'give' then
4567 begin
4568 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
4569 for f := 1 to High(P) do
4570 begin
4571 cmd := LowerCase(P[f]);
4572 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
4573 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
4574 if cmd = 'exit' then
4575 begin
4576 if gTriggers <> nil then
4577 begin
4578 for a := 0 to High(gTriggers) do
4579 begin
4580 if gTriggers[a].TriggerType = TRIGGER_EXIT then
4581 begin
4582 g_Console_Add('player left the map');
4583 gExitByTrigger := True;
4584 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
4585 g_Game_ExitLevel(gTriggers[a].tgcMap);
4586 break;
4587 end;
4588 end;
4589 end;
4590 continue;
4591 end;
4593 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
4594 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
4595 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
4596 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
4597 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
4599 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
4600 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
4602 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
4603 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;
4605 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
4606 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
4608 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
4609 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
4611 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
4612 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
4614 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
4615 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
4616 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
4618 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
4619 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
4620 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
4621 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;
4622 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
4623 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
4625 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;
4626 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;
4627 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;
4628 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;
4629 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;
4630 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;
4632 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
4633 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;
4635 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;
4636 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;
4638 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
4640 if cmd = 'ammo' then
4641 begin
4642 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
4643 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
4644 plr.GiveItem(ITEM_AMMO_CELL_BIG);
4645 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
4646 plr.GiveItem(ITEM_AMMO_FUELCAN);
4647 g_Console_Add('player got some ammo');
4648 continue;
4649 end;
4651 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
4652 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
4654 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
4655 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
4657 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
4658 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
4660 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
4661 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
4663 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
4665 if cmd = 'weapons' then
4666 begin
4667 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
4668 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
4669 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
4670 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
4671 plr.GiveItem(ITEM_WEAPON_PLASMA);
4672 plr.GiveItem(ITEM_WEAPON_BFG);
4673 g_Console_Add('player got weapons');
4674 continue;
4675 end;
4677 if cmd = 'keys' then
4678 begin
4679 plr.GiveItem(ITEM_KEY_RED);
4680 plr.GiveItem(ITEM_KEY_GREEN);
4681 plr.GiveItem(ITEM_KEY_BLUE);
4682 g_Console_Add('player got all keys');
4683 continue;
4684 end;
4686 g_Console_Add('i don''t know how to give '''+cmd+'''!');
4687 end;
4688 exit;
4689 end;
4690 // open
4691 if cmd = 'open' then
4692 begin
4693 g_Console_Add('player activated sesame');
4694 g_Triggers_OpenAll();
4695 exit;
4696 end;
4697 // fly
4698 if cmd = 'fly' then
4699 begin
4700 gFly := not gFly;
4701 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
4702 exit;
4703 end;
4704 // noclip
4705 if cmd = 'noclip' then
4706 begin
4707 plr.SwitchNoClip;
4708 g_Console_Add('wall hardeness adjusted');
4709 exit;
4710 end;
4711 // notarget
4712 if cmd = 'notarget' then
4713 begin
4714 plr.NoTarget := not plr.NoTarget;
4715 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
4716 exit;
4717 end;
4718 // noreload
4719 if cmd = 'noreload' then
4720 begin
4721 plr.NoReload := not plr.NoReload;
4722 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
4723 exit;
4724 end;
4725 // speedy
4726 if cmd = 'speedy' then
4727 begin
4728 MAX_RUNVEL := 32-MAX_RUNVEL;
4729 g_Console_Add('speed adjusted');
4730 exit;
4731 end;
4732 // jumpy
4733 if cmd = 'jumpy' then
4734 begin
4735 VEL_JUMP := 30-VEL_JUMP;
4736 g_Console_Add('jump height adjusted');
4737 exit;
4738 end;
4739 // automap
4740 if cmd = 'automap' then
4741 begin
4742 gShowMap := not gShowMap;
4743 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
4744 exit;
4745 end;
4746 // aimline
4747 if cmd = 'aimline' then
4748 begin
4749 gAimLine := not gAimLine;
4750 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
4751 exit;
4752 end;
4753 end;
4755 procedure GameCommands(P: SSArray);
4756 var
4757 a, b: Integer;
4758 s, pw: String;
4759 chstr: string;
4760 cmd: string;
4761 pl: pTNetClient = nil;
4762 plr: TPlayer;
4763 prt: Word;
4764 nm: Boolean;
4765 listen: LongWord;
4766 found: Boolean;
4767 begin
4768 // Общие команды:
4769 cmd := LowerCase(P[0]);
4770 chstr := '';
4771 if cmd = 'pause' then
4772 begin
4773 if (g_ActiveWindow = nil) then
4774 g_Game_Pause(not gPauseMain);
4775 end
4776 else if cmd = 'endgame' then
4777 gExit := EXIT_SIMPLE
4778 else if cmd = 'restart' then
4779 begin
4780 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
4781 begin
4782 if g_Game_IsClient then
4783 begin
4784 g_Console_Add(_lc[I_MSG_SERVERONLY]);
4785 Exit;
4786 end;
4787 g_Game_Restart();
4788 end else
4789 g_Console_Add(_lc[I_MSG_NOT_GAME]);
4790 end
4791 else if cmd = 'kick' then
4792 begin
4793 if g_Game_IsServer then
4794 begin
4795 if Length(P) < 2 then
4796 begin
4797 g_Console_Add('kick <name>');
4798 Exit;
4799 end;
4800 if P[1] = '' then
4801 begin
4802 g_Console_Add('kick <name>');
4803 Exit;
4804 end;
4806 if g_Game_IsNet then
4807 pl := g_Net_Client_ByName(P[1]);
4808 if (pl <> nil) then
4809 begin
4810 s := g_Net_ClientName_ByID(pl^.ID);
4811 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
4812 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4813 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4814 g_Net_Slist_ServerPlayerLeaves();
4815 end else if gPlayers <> nil then
4816 for a := Low(gPlayers) to High(gPlayers) do
4817 if gPlayers[a] <> nil then
4818 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
4819 begin
4820 // Не отключать основных игроков в сингле
4821 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
4822 continue;
4823 gPlayers[a].Lives := 0;
4824 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4825 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
4826 g_Player_Remove(gPlayers[a].UID);
4827 g_Net_Slist_ServerPlayerLeaves();
4828 // Если не перемешать, при добавлении новых ботов появятся старые
4829 g_Bot_MixNames();
4830 end;
4831 end else
4832 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
4833 end
4834 else if cmd = 'kick_id' then
4835 begin
4836 if g_Game_IsServer and g_Game_IsNet then
4837 begin
4838 if Length(P) < 2 then
4839 begin
4840 g_Console_Add('kick_id <client ID>');
4841 Exit;
4842 end;
4843 if P[1] = '' then
4844 begin
4845 g_Console_Add('kick_id <client ID>');
4846 Exit;
4847 end;
4849 a := StrToIntDef(P[1], 0);
4850 if (NetClients <> nil) and (a <= High(NetClients)) then
4851 begin
4852 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
4853 begin
4854 s := g_Net_ClientName_ByID(NetClients[a].ID);
4855 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
4856 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4857 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4858 g_Net_Slist_ServerPlayerLeaves();
4859 end;
4860 end;
4861 end else
4862 g_Console_Add(_lc[I_MSG_SERVERONLY]);
4863 end
4864 else if cmd = 'kick_pid' then
4865 begin
4866 if g_Game_IsServer and g_Game_IsNet then
4867 begin
4868 if Length(P) < 2 then
4869 begin
4870 g_Console_Add('kick_pid <player ID>');
4871 Exit;
4872 end;
4873 if P[1] = '' then
4874 begin
4875 g_Console_Add('kick_pid <player ID>');
4876 Exit;
4877 end;
4879 a := StrToIntDef(P[1], 0);
4880 pl := g_Net_Client_ByPlayer(a);
4881 if (pl <> nil) and pl^.Used and (pl^.Peer <> nil) then
4882 begin
4883 s := g_Net_ClientName_ByID(pl^.ID);
4884 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
4885 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4886 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4887 g_Net_Slist_ServerPlayerLeaves();
4888 end;
4889 end else
4890 g_Console_Add(_lc[I_MSG_SERVERONLY]);
4891 end
4892 else if cmd = 'ban' then
4893 begin
4894 if g_Game_IsServer and g_Game_IsNet then
4895 begin
4896 if Length(P) < 2 then
4897 begin
4898 g_Console_Add('ban <name>');
4899 Exit;
4900 end;
4901 if P[1] = '' then
4902 begin
4903 g_Console_Add('ban <name>');
4904 Exit;
4905 end;
4907 pl := g_Net_Client_ByName(P[1]);
4908 if (pl <> nil) then
4909 begin
4910 s := g_Net_ClientName_ByID(pl^.ID);
4911 g_Net_BanHost(pl^.Peer^.address.host, False);
4912 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
4913 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
4914 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
4915 g_Net_Slist_ServerPlayerLeaves();
4916 end else
4917 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
4918 end else
4919 g_Console_Add(_lc[I_MSG_SERVERONLY]);
4920 end
4921 else if cmd = 'ban_id' then
4922 begin
4923 if g_Game_IsServer and g_Game_IsNet then
4924 begin
4925 if Length(P) < 2 then
4926 begin
4927 g_Console_Add('ban_id <client ID>');
4928 Exit;
4929 end;
4930 if P[1] = '' then
4931 begin
4932 g_Console_Add('ban_id <client ID>');
4933 Exit;
4934 end;
4936 a := StrToIntDef(P[1], 0);
4937 if (NetClients <> nil) and (a <= High(NetClients)) then
4938 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
4939 begin
4940 s := g_Net_ClientName_ByID(NetClients[a].ID);
4941 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
4942 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
4943 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
4944 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
4945 g_Net_Slist_ServerPlayerLeaves();
4946 end;
4947 end else
4948 g_Console_Add(_lc[I_MSG_SERVERONLY]);
4949 end
4950 else if cmd = 'ban_pid' then
4951 begin
4952 if g_Game_IsServer and g_Game_IsNet then
4953 begin
4954 if Length(P) < 2 then
4955 begin
4956 g_Console_Add('ban_pid <player ID>');
4957 Exit;
4958 end;
4959 if P[1] = '' then
4960 begin
4961 g_Console_Add('ban_pid <player ID>');
4962 Exit;
4963 end;
4965 a := StrToIntDef(P[1], 0);
4966 pl := g_Net_Client_ByPlayer(a);
4967 if (pl <> nil) and pl^.Used and (pl^.Peer <> nil) then
4968 begin
4969 s := g_Net_ClientName_ByID(pl^.ID);
4970 g_Net_BanHost(pl^.Peer^.address.host, False);
4971 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
4972 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
4973 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
4974 g_Net_Slist_ServerPlayerLeaves();
4975 end;
4976 end else
4977 g_Console_Add(_lc[I_MSG_SERVERONLY]);
4978 end
4979 else if cmd = 'permban' then
4980 begin
4981 if g_Game_IsServer and g_Game_IsNet then
4982 begin
4983 if Length(P) < 2 then
4984 begin
4985 g_Console_Add('permban <name>');
4986 Exit;
4987 end;
4988 if P[1] = '' then
4989 begin
4990 g_Console_Add('permban <name>');
4991 Exit;
4992 end;
4994 pl := g_Net_Client_ByName(P[1]);
4995 if (pl <> nil) then
4996 begin
4997 s := g_Net_ClientName_ByID(pl^.ID);
4998 g_Net_BanHost(pl^.Peer^.address.host);
4999 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5000 g_Net_SaveBanList();
5001 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5002 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5003 g_Net_Slist_ServerPlayerLeaves();
5004 end else
5005 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5006 end else
5007 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5008 end
5009 else if cmd = 'permban_id' then
5010 begin
5011 if g_Game_IsServer and g_Game_IsNet then
5012 begin
5013 if Length(P) < 2 then
5014 begin
5015 g_Console_Add('permban_id <client ID>');
5016 Exit;
5017 end;
5018 if P[1] = '' then
5019 begin
5020 g_Console_Add('permban_id <client ID>');
5021 Exit;
5022 end;
5024 a := StrToIntDef(P[1], 0);
5025 if (NetClients <> nil) and (a <= High(NetClients)) then
5026 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5027 begin
5028 s := g_Net_ClientName_ByID(NetClients[a].ID);
5029 g_Net_BanHost(NetClients[a].Peer^.address.host);
5030 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5031 g_Net_SaveBanList();
5032 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5033 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5034 g_Net_Slist_ServerPlayerLeaves();
5035 end;
5036 end else
5037 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5038 end
5039 else if cmd = 'permban_pid' then
5040 begin
5041 if g_Game_IsServer and g_Game_IsNet then
5042 begin
5043 if Length(P) < 2 then
5044 begin
5045 g_Console_Add('permban_pid <player ID>');
5046 Exit;
5047 end;
5048 if P[1] = '' then
5049 begin
5050 g_Console_Add('permban_pid <player ID>');
5051 Exit;
5052 end;
5054 a := StrToIntDef(P[1], 0);
5055 pl := g_Net_Client_ByPlayer(a);
5056 if (pl <> nil) and pl^.Used and (pl^.Peer <> nil) then
5057 begin
5058 s := g_Net_ClientName_ByID(pl^.ID);
5059 g_Net_BanHost(pl^.Peer^.address.host);
5060 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5061 g_Net_SaveBanList();
5062 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5063 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5064 g_Net_Slist_ServerPlayerLeaves();
5065 end;
5066 end else
5067 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5068 end
5069 else if cmd = 'permban_ip' then
5070 begin
5071 if g_Game_IsServer and g_Game_IsNet then
5072 begin
5073 if Length(P) < 2 then
5074 begin
5075 g_Console_Add('permban_ip <IP address>');
5076 Exit;
5077 end;
5078 if P[1] = '' then
5079 begin
5080 g_Console_Add('permban_ip <IP address>');
5081 Exit;
5082 end;
5084 g_Net_BanHost(P[1]);
5085 g_Net_SaveBanList();
5086 g_Console_Add(Format(_lc[I_PLAYER_BAN], [P[1]]));
5087 end else
5088 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5089 end
5090 else if cmd = 'unban' then
5091 begin
5092 if g_Game_IsServer and g_Game_IsNet then
5093 begin
5094 if Length(P) < 2 then
5095 begin
5096 g_Console_Add('unban <IP Address>');
5097 Exit;
5098 end;
5099 if P[1] = '' then
5100 begin
5101 g_Console_Add('unban <IP Address>');
5102 Exit;
5103 end;
5105 if g_Net_UnbanHost(P[1]) then
5106 begin
5107 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5108 g_Net_SaveBanList();
5109 end else
5110 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5111 end else
5112 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5113 end
5114 else if cmd = 'clientlist' then
5115 begin
5116 if g_Game_IsServer and g_Game_IsNet then
5117 begin
5118 b := 0;
5119 if NetClients <> nil then
5120 for a := Low(NetClients) to High(NetClients) do
5121 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5122 begin
5123 plr := g_Player_Get(NetClients[a].Player);
5124 if plr = nil then continue;
5125 Inc(b);
5126 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5127 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5128 end;
5129 if b = 0 then
5130 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5131 end else
5132 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5133 end
5134 else if cmd = 'connect' then
5135 begin
5136 if (NetMode = NET_NONE) then
5137 begin
5138 if Length(P) < 2 then
5139 begin
5140 g_Console_Add('connect <IP> [port] [password]');
5141 Exit;
5142 end;
5143 if P[1] = '' then
5144 begin
5145 g_Console_Add('connect <IP> [port] [password]');
5146 Exit;
5147 end;
5149 if Length(P) > 2 then
5150 prt := StrToIntDef(P[2], 25666)
5151 else
5152 prt := 25666;
5154 if Length(P) > 3 then
5155 pw := P[3]
5156 else
5157 pw := '';
5159 g_Game_StartClient(P[1], prt, pw);
5160 end;
5161 end
5162 else if cmd = 'disconnect' then
5163 begin
5164 if (NetMode = NET_CLIENT) then
5165 g_Net_Disconnect();
5166 end
5167 else if cmd = 'reconnect' then
5168 begin
5169 if (NetMode = NET_SERVER) then
5170 Exit;
5172 if (NetMode = NET_CLIENT) then
5173 begin
5174 g_Net_Disconnect();
5175 gExit := EXIT_SIMPLE;
5176 EndGame;
5177 end;
5179 //TODO: Use last successful password to reconnect, instead of ''
5180 g_Game_StartClient(NetClientIP, NetClientPort, '');
5181 end
5182 else if (cmd = 'addbot') or
5183 (cmd = 'bot_add') then
5184 begin
5185 if Length(P) > 2 then
5186 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2), StrToIntDef(P[2], 100))
5187 else if Length(P) > 1 then
5188 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5189 else
5190 g_Bot_Add(TEAM_NONE, 2);
5191 end
5192 else if cmd = 'bot_addlist' then
5193 begin
5194 if Length(P) > 1 then
5195 begin
5196 if Length(P) = 2 then
5197 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5198 else if Length(P) = 3 then
5199 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1), StrToIntDef(P[2], 100))
5200 else
5201 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5202 end;
5203 end
5204 else if cmd = 'bot_removeall' then
5205 g_Bot_RemoveAll()
5206 else if cmd = 'chat' then
5207 begin
5208 if g_Game_IsNet then
5209 begin
5210 if Length(P) > 1 then
5211 begin
5212 for a := 1 to High(P) do
5213 chstr := chstr + P[a] + ' ';
5215 if Length(chstr) > 200 then SetLength(chstr, 200);
5217 if Length(chstr) < 1 then
5218 begin
5219 g_Console_Add('chat <text>');
5220 Exit;
5221 end;
5223 chstr := b_Text_Format(chstr);
5224 if g_Game_IsClient then
5225 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5226 else
5227 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5228 end
5229 else
5230 g_Console_Add('chat <text>');
5231 end else
5232 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5233 end
5234 else if cmd = 'teamchat' then
5235 begin
5236 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5237 begin
5238 if Length(P) > 1 then
5239 begin
5240 for a := 1 to High(P) do
5241 chstr := chstr + P[a] + ' ';
5243 if Length(chstr) > 200 then SetLength(chstr, 200);
5245 if Length(chstr) < 1 then
5246 begin
5247 g_Console_Add('teamchat <text>');
5248 Exit;
5249 end;
5251 chstr := b_Text_Format(chstr);
5252 if g_Game_IsClient then
5253 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5254 else
5255 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5256 gPlayer1Settings.Team);
5257 end
5258 else
5259 g_Console_Add('teamchat <text>');
5260 end else
5261 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5262 end
5263 else if cmd = 'game' then
5264 begin
5265 if gGameSettings.GameType <> GT_NONE then
5266 begin
5267 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5268 Exit;
5269 end;
5270 if Length(P) = 1 then
5271 begin
5272 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5273 Exit;
5274 end;
5275 // game not started yet, load fist map from some wad
5276 found := false;
5277 s := addWadExtension(P[1]);
5278 found := e_FindResource(AllMapDirs, s);
5279 P[1] := s;
5280 if found then
5281 begin
5282 P[1] := ExpandFileName(P[1]);
5283 // if map not choosed then set first map
5284 if Length(P) < 3 then
5285 begin
5286 SetLength(P, 3);
5287 P[2] := g_Game_GetFirstMap(P[1]);
5288 end;
5290 s := P[1] + ':\' + UpperCase(P[2]);
5292 if g_Map_Exist(s) then
5293 begin
5294 // start game
5295 g_Game_Free();
5296 with gGameSettings do
5297 begin
5298 Options := gsGameFlags;
5299 GameMode := g_Game_TextToMode(gsGameMode);
5300 if gSwitchGameMode <> GM_NONE then
5301 GameMode := gSwitchGameMode;
5302 if GameMode = GM_NONE then GameMode := GM_DM;
5303 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5304 b := 1;
5305 if Length(P) >= 4 then
5306 b := StrToIntDef(P[3], 1);
5307 g_Game_StartCustom(s, GameMode, TimeLimit,
5308 GoalLimit, MaxLives, Options, b);
5309 end;
5310 end
5311 else
5312 if P[2] = '' then
5313 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5314 else
5315 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[2]), P[1]]));
5316 end else
5317 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5318 end
5319 else if cmd = 'host' then
5320 begin
5321 if gGameSettings.GameType <> GT_NONE then
5322 begin
5323 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5324 Exit;
5325 end;
5326 if Length(P) < 4 then
5327 begin
5328 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5329 Exit;
5330 end;
5331 if not StrToIp(P[1], listen) then
5332 Exit;
5333 prt := StrToIntDef(P[2], 25666);
5335 s := addWadExtension(P[3]);
5336 found := e_FindResource(AllMapDirs, s);
5337 P[3] := s;
5338 if found then
5339 begin
5340 // get first map in wad, if not specified
5341 if Length(P) < 5 then
5342 begin
5343 SetLength(P, 5);
5344 P[4] := g_Game_GetFirstMap(P[1]);
5345 end;
5346 s := P[3] + ':\' + UpperCase(P[4]);
5347 if g_Map_Exist(s) then
5348 begin
5349 // start game
5350 g_Game_Free();
5351 with gGameSettings do
5352 begin
5353 Options := gsGameFlags;
5354 GameMode := g_Game_TextToMode(gsGameMode);
5355 if gSwitchGameMode <> GM_NONE then GameMode := gSwitchGameMode;
5356 if GameMode = GM_NONE then GameMode := GM_DM;
5357 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5358 b := 0;
5359 if Length(P) >= 6 then
5360 b := StrToIntDef(P[5], 0);
5361 g_Game_StartServer(s, GameMode, TimeLimit, GoalLimit, MaxLives, Options, b, listen, prt)
5362 end
5363 end
5364 else
5365 begin
5366 if P[4] = '' then
5367 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5368 else
5369 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]))
5370 end
5371 end
5372 else
5373 begin
5374 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]))
5375 end
5376 end
5377 else if cmd = 'map' then
5378 begin
5379 if Length(P) = 1 then
5380 begin
5381 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5382 begin
5383 g_Console_Add(cmd + ' <MAP>');
5384 g_Console_Add(cmd + ' <WAD> [MAP]')
5385 end
5386 else
5387 begin
5388 g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
5389 end
5390 end
5391 else
5392 begin
5393 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5394 begin
5395 if Length(P) < 3 then
5396 begin
5397 // first param is map or wad
5398 s := UpperCase(P[1]);
5399 if g_Map_Exist(gGameSettings.WAD + ':\' + s) then
5400 begin
5401 gExitByTrigger := False;
5402 if gGameOn then
5403 begin
5404 // already in game, finish current map
5405 gNextMap := s;
5406 gExit := EXIT_ENDLEVELCUSTOM;
5407 end
5408 else
5409 begin
5410 // intermission, so change map immediately
5411 g_Game_ChangeMap(s)
5412 end
5413 end
5414 else
5415 begin
5416 s := P[1];
5417 found := e_FindResource(AllMapDirs, s);
5418 P[1] := s;
5419 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, 'WAD ' + P[1]]));
5420 if found then
5421 begin
5422 // no such map, found wad
5423 pw := P[1];
5424 SetLength(P, 3);
5425 P[1] := ExpandFileName(pw);
5426 P[2] := g_Game_GetFirstMap(P[1]);
5427 s := P[1] + ':\' + P[2];
5428 if g_Map_Exist(s) then
5429 begin
5430 gExitByTrigger := False;
5431 if gGameOn then
5432 begin
5433 // already in game, finish current map
5434 gNextMap := s;
5435 gExit := EXIT_ENDLEVELCUSTOM
5436 end
5437 else
5438 begin
5439 // intermission, so change map immediately
5440 g_Game_ChangeMap(s)
5441 end
5442 end
5443 else
5444 begin
5445 if P[2] = '' then
5446 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5447 else
5448 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
5449 end
5450 end
5451 else
5452 begin
5453 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
5454 end
5455 end;
5456 end
5457 else
5458 begin
5459 s := addWadExtension(P[1]);
5460 found := e_FindResource(AllMapDirs, s);
5461 P[1] := s;
5462 if found then
5463 begin
5464 P[2] := UpperCase(P[2]);
5465 s := P[1] + ':\' + P[2];
5466 if g_Map_Exist(s) then
5467 begin
5468 gExitByTrigger := False;
5469 if gGameOn then
5470 begin
5471 gNextMap := s;
5472 gExit := EXIT_ENDLEVELCUSTOM
5473 end
5474 else
5475 begin
5476 g_Game_ChangeMap(s)
5477 end
5478 end
5479 else
5480 begin
5481 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
5482 end
5483 end
5484 else
5485 begin
5486 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
5487 end
5488 end
5489 end
5490 else
5491 begin
5492 g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
5493 end
5494 end
5495 end
5496 else if cmd = 'nextmap' then
5497 begin
5498 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5499 begin
5500 g_Console_Add(_lc[I_MSG_NOT_GAME])
5501 end
5502 else
5503 begin
5504 nm := True;
5505 if Length(P) = 1 then
5506 begin
5507 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5508 begin
5509 g_Console_Add(cmd + ' <MAP>');
5510 g_Console_Add(cmd + ' <WAD> [MAP]');
5511 end
5512 else
5513 begin
5514 nm := False;
5515 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5516 end;
5517 end
5518 else
5519 begin
5520 nm := False;
5521 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5522 begin
5523 if Length(P) < 3 then
5524 begin
5525 // first param is map or wad
5526 s := UpperCase(P[1]);
5527 if g_Map_Exist(gGameSettings.WAD + ':\' + s) then
5528 begin
5529 // map founded
5530 gExitByTrigger := False;
5531 gNextMap := s;
5532 nm := True;
5533 end
5534 else
5535 begin
5536 // no such map, found wad
5537 pw := addWadExtension(P[1]);
5538 found := e_FindResource(MapDirs, pw);
5539 if not found then
5540 found := e_FindResource(WadDirs, pw);
5541 P[1] := pw;
5542 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
5543 if found then
5544 begin
5545 // map not specified, select first map
5546 SetLength(P, 3);
5547 P[2] := g_Game_GetFirstMap(P[1]);
5548 s := P[1] + ':\' + P[2];
5549 if g_Map_Exist(s) then
5550 begin
5551 gExitByTrigger := False;
5552 gNextMap := s;
5553 nm := True
5554 end
5555 else
5556 begin
5557 if P[2] = '' then
5558 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5559 else
5560 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
5561 end
5562 end
5563 else
5564 begin
5565 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
5566 end
5567 end
5568 end
5569 else
5570 begin
5571 // specified two params wad + map
5572 pw := addWadExtension(P[1]);
5573 found := e_FindResource(MapDirs, pw);
5574 if not found then
5575 found := e_FindResource(MapDirs, pw);
5576 P[1] := pw;
5577 if found then
5578 begin
5579 P[2] := UpperCase(P[2]);
5580 s := P[1] + ':\' + P[2];
5581 if g_Map_Exist(s) then
5582 begin
5583 gExitByTrigger := False;
5584 gNextMap := s;
5585 nm := True
5586 end
5587 else
5588 begin
5589 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
5590 end
5591 end
5592 else
5593 begin
5594 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
5595 end
5596 end
5597 end
5598 else
5599 begin
5600 g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
5601 end
5602 end;
5603 if nm then
5604 begin
5605 if gNextMap = '' then
5606 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
5607 else
5608 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]))
5609 end
5610 end
5611 end
5612 else if (cmd = 'endmap') or (cmd = 'goodbye') then
5613 begin
5614 if not gGameOn then
5615 begin
5616 g_Console_Add(_lc[I_MSG_NOT_GAME])
5617 end
5618 else
5619 begin
5620 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5621 begin
5622 gExitByTrigger := False;
5623 // next map not specified, try to find trigger EXIT
5624 if (gNextMap = '') and (gTriggers <> nil) then
5625 begin
5626 for a := 0 to High(gTriggers) do
5627 begin
5628 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5629 begin
5630 gExitByTrigger := True;
5631 //gNextMap := gTriggers[a].Data.MapName;
5632 gNextMap := gTriggers[a].tgcMap;
5633 Break
5634 end
5635 end
5636 end;
5637 if gNextMap = '' then
5638 gNextMap := g_Game_GetNextMap();
5639 if not isWadPath(gNextMap) then
5640 s := gGameSettings.WAD + ':\' + gNextMap
5641 else
5642 s := gNextMap;
5643 if g_Map_Exist(s) then
5644 gExit := EXIT_ENDLEVELCUSTOM
5645 else
5646 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]))
5647 end
5648 else
5649 begin
5650 g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
5651 end
5652 end
5653 end
5654 else if (cmd = 'event') then
5655 begin
5656 if (Length(P) <= 1) then
5657 begin
5658 for a := 0 to High(gEvents) do
5659 if gEvents[a].Command = '' then
5660 g_Console_Add(gEvents[a].Name + ' <none>')
5661 else
5662 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5663 Exit;
5664 end;
5665 if (Length(P) = 2) then
5666 begin
5667 for a := 0 to High(gEvents) do
5668 if gEvents[a].Name = P[1] then
5669 if gEvents[a].Command = '' then
5670 g_Console_Add(gEvents[a].Name + ' <none>')
5671 else
5672 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5673 Exit;
5674 end;
5675 for a := 0 to High(gEvents) do
5676 if gEvents[a].Name = P[1] then
5677 begin
5678 gEvents[a].Command := '';
5679 for b := 2 to High(P) do
5680 if Pos(' ', P[b]) = 0 then
5681 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
5682 else
5683 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
5684 gEvents[a].Command := Trim(gEvents[a].Command);
5685 Exit;
5686 end;
5687 end
5688 else if cmd = 'suicide' then
5689 begin
5690 if gGameOn then
5691 begin
5692 if g_Game_IsClient then
5693 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
5694 else
5695 begin
5696 if gPlayer1 <> nil then
5697 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
5698 if gPlayer2 <> nil then
5699 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
5700 end;
5701 end;
5702 end
5703 else if cmd = 'screenshot' then
5704 begin
5705 g_TakeScreenShot()
5706 end
5707 else if cmd = 'weapon' then
5708 begin
5709 if Length(p) = 2 then
5710 begin
5711 a := WP_FIRST + StrToInt(p[1]) - 1;
5712 if (a >= WP_FIRST) and (a <= WP_LAST) then
5713 gSelectWeapon[0, a] := True
5714 end
5715 end
5716 else if (cmd = 'p1_weapon') or (cmd = 'p2_weapon') then
5717 begin
5718 if Length(p) = 2 then
5719 begin
5720 a := WP_FIRST + StrToInt(p[1]) - 1;
5721 b := ord(cmd[2]) - ord('1');
5722 if (a >= WP_FIRST) and (a <= WP_LAST) then
5723 gSelectWeapon[b, a] := True
5724 end
5725 end
5726 // Команды Своей игры:
5727 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5728 begin
5729 if cmd = 'bot_addred' then
5730 begin
5731 if Length(P) > 1 then
5732 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
5733 else
5734 g_Bot_Add(TEAM_RED, 2);
5735 end
5736 else if cmd = 'bot_addblue' then
5737 begin
5738 if Length(P) > 1 then
5739 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
5740 else
5741 g_Bot_Add(TEAM_BLUE, 2);
5742 end
5743 else if cmd = 'spectate' then
5744 begin
5745 if not gGameOn then
5746 Exit;
5747 g_Game_Spectate();
5748 end
5749 else if cmd = 'say' then
5750 begin
5751 if g_Game_IsServer and g_Game_IsNet then
5752 begin
5753 if Length(P) > 1 then
5754 begin
5755 chstr := '';
5756 for a := 1 to High(P) do
5757 chstr := chstr + P[a] + ' ';
5759 if Length(chstr) > 200 then SetLength(chstr, 200);
5761 if Length(chstr) < 1 then
5762 begin
5763 g_Console_Add('say <text>');
5764 Exit;
5765 end;
5767 chstr := b_Text_Format(chstr);
5768 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
5769 end
5770 else g_Console_Add('say <text>');
5771 end else
5772 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5773 end
5774 else if cmd = 'tell' then
5775 begin
5776 if g_Game_IsServer and g_Game_IsNet then
5777 begin
5778 if (Length(P) > 2) and (P[1] <> '') then
5779 begin
5780 chstr := '';
5781 for a := 2 to High(P) do
5782 chstr := chstr + P[a] + ' ';
5784 if Length(chstr) > 200 then SetLength(chstr, 200);
5786 if Length(chstr) < 1 then
5787 begin
5788 g_Console_Add('tell <playername> <text>');
5789 Exit;
5790 end;
5792 pl := g_Net_Client_ByName(P[1]);
5793 if pl <> nil then
5794 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
5795 else
5796 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5797 end
5798 else g_Console_Add('tell <playername> <text>');
5799 end else
5800 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5801 end
5802 else if cmd = 'centerprint' then
5803 begin
5804 if (Length(P) > 2) and (P[1] <> '') then
5805 begin
5806 chstr := '';
5807 for a := 2 to High(P) do
5808 chstr := chstr + P[a] + ' ';
5810 if Length(chstr) > 200 then SetLength(chstr, 200);
5812 if Length(chstr) < 1 then
5813 begin
5814 g_Console_Add('centerprint <timeout> <text>');
5815 Exit;
5816 end;
5818 a := StrToIntDef(P[1], 100);
5819 chstr := b_Text_Format(chstr);
5820 g_Game_Message(chstr, a);
5821 if g_Game_IsNet and g_Game_IsServer then
5822 MH_SEND_GameEvent(NET_EV_BIGTEXT, a, chstr);
5823 end
5824 else g_Console_Add('centerprint <timeout> <text>');
5825 end
5826 else if (cmd = 'overtime') and not g_Game_IsClient then
5827 begin
5828 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
5829 Exit;
5830 // Дополнительное время:
5831 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
5833 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5834 [gGameSettings.TimeLimit div 3600,
5835 (gGameSettings.TimeLimit div 60) mod 60,
5836 gGameSettings.TimeLimit mod 60]));
5837 if g_Game_IsNet then MH_SEND_GameSettings;
5838 end
5839 else if (cmd = 'rcon_password') and g_Game_IsClient then
5840 begin
5841 if (Length(P) <= 1) then
5842 g_Console_Add('rcon_password <password>')
5843 else
5844 MC_SEND_RCONPassword(P[1]);
5845 end
5846 else if cmd = 'rcon' then
5847 begin
5848 if g_Game_IsClient then
5849 begin
5850 if Length(P) > 1 then
5851 begin
5852 chstr := '';
5853 for a := 1 to High(P) do
5854 chstr := chstr + P[a] + ' ';
5856 if Length(chstr) > 200 then SetLength(chstr, 200);
5858 if Length(chstr) < 1 then
5859 begin
5860 g_Console_Add('rcon <command>');
5861 Exit;
5862 end;
5864 MC_SEND_RCONCommand(chstr);
5865 end
5866 else g_Console_Add('rcon <command>');
5867 end;
5868 end
5869 else if cmd = 'ready' then
5870 begin
5871 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
5872 gLMSRespawnTime := gTime + 100;
5873 end
5874 else if (cmd = 'callvote') and g_Game_IsNet then
5875 begin
5876 if Length(P) > 1 then
5877 begin
5878 chstr := '';
5879 for a := 1 to High(P) do begin
5880 if a > 1 then chstr := chstr + ' ';
5881 chstr := chstr + P[a];
5882 end;
5884 if Length(chstr) > 200 then SetLength(chstr, 200);
5886 if Length(chstr) < 1 then
5887 begin
5888 g_Console_Add('callvote <command>');
5889 Exit;
5890 end;
5892 if g_Game_IsClient then
5893 MC_SEND_Vote(True, chstr)
5894 else
5895 g_Game_StartVote(chstr, gPlayer1Settings.Name);
5896 g_Console_Process('vote', True);
5897 end
5898 else
5899 g_Console_Add('callvote <command>');
5900 end
5901 else if (cmd = 'vote') and g_Game_IsNet then
5902 begin
5903 if g_Game_IsClient then
5904 MC_SEND_Vote(False)
5905 else if gVoteInProgress then
5906 begin
5907 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
5908 a := Floor((NetClientCount+1)/2.0) + 1
5909 else
5910 a := Floor(NetClientCount/2.0) + 1;
5911 if gVoted then
5912 begin
5913 Dec(gVoteCount);
5914 gVoted := False;
5915 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
5916 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
5917 end
5918 else
5919 begin
5920 Inc(gVoteCount);
5921 gVoted := True;
5922 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
5923 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
5924 g_Game_CheckVote;
5925 end;
5926 end;
5927 end
5928 end;
5929 end;
5931 procedure SystemCommands(P: SSArray);
5932 var
5933 cmd: string;
5934 begin
5935 cmd := LowerCase(P[0]);
5936 case cmd of
5937 'exit', 'quit':
5938 begin
5939 g_Game_Free();
5940 g_Game_Quit();
5941 end;
5942 'r_reset':
5943 begin
5944 gRC_Width := Max(1, gRC_Width);
5945 gRC_Height := Max(1, gRC_Height);
5946 gBPP := Max(1, gBPP);
5947 if sys_SetDisplayMode(gRC_Width, gRC_Height, gBPP, gRC_FullScreen, gRC_Maximized) = True then
5948 e_LogWriteln('resolution changed')
5949 else
5950 e_LogWriteln('resolution not changed');
5951 sys_EnableVSync(gVSync);
5952 end;
5953 'r_maxfps':
5954 begin
5955 if Length(p) = 2 then
5956 begin
5957 gMaxFPS := StrToIntDef(p[1], gMaxFPS);
5958 if gMaxFPS > 0 then
5959 gFrameTime := 1000 div gMaxFPS
5960 else
5961 gFrameTime := 0;
5962 end;
5963 e_LogWritefln('r_maxfps %d', [gMaxFPS]);
5964 end;
5965 'g_language':
5966 begin
5967 if Length(p) = 2 then
5968 begin
5969 gAskLanguage := true;
5970 gLanguage := LANGUAGE_ENGLISH;
5971 case LowerCase(p[1]) of
5972 'english':
5973 begin
5974 gAskLanguage := false;
5975 gLanguage := LANGUAGE_ENGLISH;
5976 end;
5977 'russian':
5978 begin
5979 gAskLanguage := false;
5980 gLanguage := LANGUAGE_RUSSIAN;
5981 end;
5982 'ask':
5983 begin
5984 gAskLanguage := true;
5985 gLanguage := LANGUAGE_ENGLISH;
5986 end;
5987 end;
5988 g_Language_Set(gLanguage);
5989 end
5990 else
5991 begin
5992 e_LogWritefln('usage: %s <English|Russian|Ask>', [cmd]);
5993 end
5994 end;
5995 end;
5996 end;
5998 procedure g_TakeScreenShot(Filename: string = '');
5999 var s: TStream; t: TDateTime; dir, date, name: String;
6000 begin
6001 if e_NoGraphics then Exit;
6002 try
6003 dir := e_GetWriteableDir(ScreenshotDirs);
6005 if Filename = '' then
6006 begin
6007 t := Now;
6008 DateTimeToString(date, 'yyyy-mm-dd-hh-nn-ss', t);
6009 Filename := 'screenshot-' + date;
6010 end;
6012 name := e_CatPath(dir, Filename + '.png');
6013 s := createDiskFile(name);
6014 try
6015 e_MakeScreenshot(s, gScreenWidth, gScreenHeight);
6016 s.Free;
6017 g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [name]))
6018 except
6019 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [name]));
6020 s.Free;
6021 DeleteFile(name)
6022 end
6023 except
6024 g_Console_Add('oh shit, i can''t create screenshot!')
6025 end
6026 end;
6028 procedure g_Game_InGameMenu(Show: Boolean);
6029 begin
6030 if (g_ActiveWindow = nil) and Show then
6031 begin
6032 if gGameSettings.GameType = GT_SINGLE then
6033 g_GUI_ShowWindow('GameSingleMenu')
6034 else
6035 begin
6036 if g_Game_IsClient then
6037 g_GUI_ShowWindow('GameClientMenu')
6038 else
6039 if g_Game_IsNet then
6040 g_GUI_ShowWindow('GameServerMenu')
6041 else
6042 g_GUI_ShowWindow('GameCustomMenu');
6043 end;
6044 g_Sound_PlayEx('MENU_OPEN');
6046 // Пауза при меню только в одиночной игре:
6047 if (not g_Game_IsNet) then
6048 g_Game_Pause(True);
6049 end
6050 else
6051 if (g_ActiveWindow <> nil) and (not Show) then
6052 begin
6053 // Пауза при меню только в одиночной игре:
6054 if (not g_Game_IsNet) then
6055 g_Game_Pause(False);
6056 end;
6057 end;
6059 procedure g_Game_Pause (Enable: Boolean);
6060 var
6061 oldPause: Boolean;
6062 begin
6063 if not gGameOn then exit;
6065 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6067 oldPause := gPause;
6068 gPauseMain := Enable;
6070 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6071 end;
6073 procedure g_Game_HolmesPause (Enable: Boolean);
6074 var
6075 oldPause: Boolean;
6076 begin
6077 if not gGameOn then exit;
6078 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
6080 oldPause := gPause;
6081 gPauseHolmes := Enable;
6083 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
6084 end;
6086 procedure g_Game_PauseAllSounds(Enable: Boolean);
6087 var
6088 i: Integer;
6089 begin
6090 // Триггеры:
6091 if gTriggers <> nil then
6092 for i := 0 to High(gTriggers) do
6093 with gTriggers[i] do
6094 if (TriggerType = TRIGGER_SOUND) and
6095 (Sound <> nil) and
6096 Sound.IsPlaying() then
6097 begin
6098 Sound.Pause(Enable);
6099 end;
6101 // Звуки игроков:
6102 if gPlayers <> nil then
6103 for i := 0 to High(gPlayers) do
6104 if gPlayers[i] <> nil then
6105 gPlayers[i].PauseSounds(Enable);
6107 // Музыка:
6108 if gMusic <> nil then
6109 gMusic.Pause(Enable);
6110 end;
6112 procedure g_Game_StopAllSounds(all: Boolean);
6113 var
6114 i: Integer;
6115 begin
6116 if gTriggers <> nil then
6117 for i := 0 to High(gTriggers) do
6118 with gTriggers[i] do
6119 if (TriggerType = TRIGGER_SOUND) and
6120 (Sound <> nil) then
6121 Sound.Stop();
6123 if gMusic <> nil then
6124 gMusic.Stop();
6126 if all then
6127 e_StopChannels();
6128 end;
6130 procedure g_Game_UpdateTriggerSounds;
6131 var i: Integer;
6132 begin
6133 if gTriggers <> nil then
6134 for i := 0 to High(gTriggers) do
6135 with gTriggers[i] do
6136 if (TriggerType = TRIGGER_SOUND) and (Sound <> nil) and tgcLocal and Sound.IsPlaying() then
6137 Sound.SetCoordsRect(X, Y, Width, Height, tgcVolume / 255.0)
6138 end;
6140 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6141 begin
6142 Result := False;
6143 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6144 begin
6145 Result := True;
6146 Exit;
6147 end;
6148 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6149 begin
6150 Result := True;
6151 Exit;
6152 end;
6153 if gSpectMode <> SPECT_PLAYERS then
6154 Exit;
6155 if gSpectPID1 = UID then
6156 begin
6157 Result := True;
6158 Exit;
6159 end;
6160 if gSpectViewTwo and (gSpectPID2 = UID) then
6161 begin
6162 Result := True;
6163 Exit;
6164 end;
6165 end;
6167 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6168 var
6169 Pl: TPlayer;
6170 begin
6171 Result := False;
6172 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6173 begin
6174 Result := True;
6175 Exit;
6176 end;
6177 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6178 begin
6179 Result := True;
6180 Exit;
6181 end;
6182 if gSpectMode <> SPECT_PLAYERS then
6183 Exit;
6184 Pl := g_Player_Get(gSpectPID1);
6185 if (Pl <> nil) and (Pl.Team = Team) then
6186 begin
6187 Result := True;
6188 Exit;
6189 end;
6190 if gSpectViewTwo then
6191 begin
6192 Pl := g_Player_Get(gSpectPID2);
6193 if (Pl <> nil) and (Pl.Team = Team) then
6194 begin
6195 Result := True;
6196 Exit;
6197 end;
6198 end;
6199 end;
6201 procedure g_Game_Message(Msg: string; Time: Word);
6202 begin
6203 MessageLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont);
6204 MessageText := b_Text_Wrap(b_Text_Format(Msg), MessageLineLength);
6205 MessageTime := Time;
6206 end;
6208 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
6209 const
6210 punct: Array[0..13] of String =
6211 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
6212 var
6213 i, j: Integer;
6214 ok: Boolean;
6215 fpText: String;
6217 function IsPunctuation(S: String): Boolean;
6218 var
6219 i: Integer;
6220 begin
6221 Result := False;
6222 if Length(S) <> 1 then
6223 Exit;
6224 for i := Low(punct) to High(punct) do
6225 if S = punct[i] then
6226 begin
6227 Result := True;
6228 break;
6229 end;
6230 end;
6231 function FilterPunctuation(S: String): String;
6232 var
6233 i: Integer;
6234 begin
6235 for i := Low(punct) to High(punct) do
6236 S := StringReplace(S, punct[i], ' ', [rfReplaceAll]);
6237 Result := S;
6238 end;
6239 begin
6240 ok := False;
6242 if gUseChatSounds and Taunt and (gChatSounds <> nil) and (Pos(': ', Text) > 0) then
6243 begin
6244 // remove player name
6245 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
6246 // for FullWord check
6247 Text := toLowerCase1251(' ' + Text + ' ');
6248 fpText := FilterPunctuation(Text);
6250 for i := 0 to Length(gChatSounds) - 1 do
6251 begin
6252 ok := True;
6253 for j := 0 to Length(gChatSounds[i].Tags) - 1 do
6254 begin
6255 if gChatSounds[i].FullWord and (not IsPunctuation(gChatSounds[i].Tags[j])) then
6256 ok := Pos(' ' + gChatSounds[i].Tags[j] + ' ', fpText) > 0
6257 else
6258 ok := Pos(gChatSounds[i].Tags[j], Text) > 0;
6259 if not ok then
6260 break;
6261 end;
6262 if ok then
6263 begin
6264 gChatSounds[i].Sound.Play();
6265 break;
6266 end;
6267 end;
6268 end;
6269 if not ok then
6270 g_Sound_PlayEx('SOUND_GAME_RADIO');
6271 end;
6273 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6274 var
6275 a: Integer;
6276 begin
6277 case gAnnouncer of
6278 ANNOUNCE_NONE:
6279 Exit;
6280 ANNOUNCE_ME,
6281 ANNOUNCE_MEPLUS:
6282 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6283 Exit;
6284 end;
6285 for a := 0 to 3 do
6286 if goodsnd[a].IsPlaying() then
6287 Exit;
6289 goodsnd[Random(4)].Play();
6290 end;
6292 procedure g_Game_Announce_KillCombo(Param: Integer);
6293 var
6294 UID: Word;
6295 c, n: Byte;
6296 Pl: TPlayer;
6297 Name: String;
6298 begin
6299 UID := Param and $FFFF;
6300 c := Param shr 16;
6301 if c < 2 then
6302 Exit;
6304 Pl := g_Player_Get(UID);
6305 if Pl = nil then
6306 Name := '?'
6307 else
6308 Name := Pl.Name;
6310 case c of
6311 2: begin
6312 n := 0;
6313 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6314 end;
6315 3: begin
6316 n := 1;
6317 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6318 end;
6319 4: begin
6320 n := 2;
6321 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6322 end;
6323 else begin
6324 n := 3;
6325 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6326 end;
6327 end;
6329 case gAnnouncer of
6330 ANNOUNCE_NONE:
6331 Exit;
6332 ANNOUNCE_ME:
6333 if not g_Game_IsWatchedPlayer(UID) then
6334 Exit;
6335 ANNOUNCE_MEPLUS:
6336 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6337 Exit;
6338 end;
6340 if killsnd[n].IsPlaying() then
6341 killsnd[n].Stop();
6342 killsnd[n].Play();
6343 end;
6345 procedure g_Game_Announce_BodyKill(SpawnerUID: Word);
6346 var
6347 a: Integer;
6348 begin
6349 case gAnnouncer of
6350 ANNOUNCE_NONE:
6351 Exit;
6352 ANNOUNCE_ME:
6353 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6354 Exit;
6355 end;
6356 for a := 0 to 2 do
6357 if hahasnd[a].IsPlaying() then
6358 Exit;
6360 hahasnd[Random(3)].Play();
6361 end;
6363 procedure g_Game_StartVote(Command, Initiator: string);
6364 var
6365 Need: Integer;
6366 begin
6367 if not gVotesEnabled then Exit;
6368 if gGameSettings.GameType <> GT_SERVER then Exit;
6369 if gVoteInProgress or gVotePassed then
6370 begin
6371 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6372 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6373 Exit;
6374 end;
6375 gVoteInProgress := True;
6376 gVotePassed := False;
6377 gVoteTimer := gTime + gVoteTimeout * 1000;
6378 gVoteCount := 0;
6379 gVoted := False;
6380 gVoteCommand := Command;
6382 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6383 Need := Floor((NetClientCount+1)/2.0)+1
6384 else
6385 Need := Floor(NetClientCount/2.0)+1;
6386 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6387 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6388 end;
6390 procedure g_Game_CheckVote;
6391 var
6392 I, Need: Integer;
6393 begin
6394 if gGameSettings.GameType <> GT_SERVER then Exit;
6395 if not gVoteInProgress then Exit;
6397 if (gTime >= gVoteTimer) then
6398 begin
6399 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6400 Need := Floor((NetClientCount+1)/2.0) + 1
6401 else
6402 Need := Floor(NetClientCount/2.0) + 1;
6403 if gVoteCount >= Need then
6404 begin
6405 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6406 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6407 gVotePassed := True;
6408 gVoteCmdTimer := gTime + 5000;
6409 end
6410 else
6411 begin
6412 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6413 MH_SEND_VoteEvent(NET_VE_FAILED);
6414 end;
6415 if NetClients <> nil then
6416 for I := Low(NetClients) to High(NetClients) do
6417 if NetClients[i].Used then
6418 NetClients[i].Voted := False;
6419 gVoteInProgress := False;
6420 gVoted := False;
6421 gVoteCount := 0;
6422 end
6423 else
6424 begin
6425 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6426 Need := Floor((NetClientCount+1)/2.0) + 1
6427 else
6428 Need := Floor(NetClientCount/2.0) + 1;
6429 if gVoteCount >= Need then
6430 begin
6431 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6432 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6433 gVoteInProgress := False;
6434 gVotePassed := True;
6435 gVoteCmdTimer := gTime + 5000;
6436 gVoted := False;
6437 gVoteCount := 0;
6438 if NetClients <> nil then
6439 for I := Low(NetClients) to High(NetClients) do
6440 if NetClients[i].Used then
6441 NetClients[i].Voted := False;
6442 end;
6443 end;
6444 end;
6446 procedure g_Game_LoadMapList(FileName: string);
6447 var
6448 ListFile: TextFile;
6449 s: string;
6450 begin
6451 MapList := nil;
6452 MapIndex := -1;
6454 if not FileExists(FileName) then Exit;
6456 AssignFile(ListFile, FileName);
6457 Reset(ListFile);
6458 while not EOF(ListFile) do
6459 begin
6460 ReadLn(ListFile, s);
6462 s := Trim(s);
6463 if s = '' then Continue;
6465 SetLength(MapList, Length(MapList)+1);
6466 MapList[High(MapList)] := s;
6467 end;
6468 CloseFile(ListFile);
6469 end;
6471 procedure g_Game_SetDebugMode();
6472 begin
6473 gDebugMode := True;
6474 // Читы (даже в своей игре):
6475 gCheats := True;
6476 end;
6478 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6479 var
6480 i: Word;
6481 begin
6482 if Length(LoadingStat.Msgs) = 0 then
6483 Exit;
6485 with LoadingStat do
6486 begin
6487 if not reWrite then
6488 begin // Переходим на следующую строку или скроллируем:
6489 if NextMsg = Length(Msgs) then
6490 begin // scroll
6491 for i := 0 to High(Msgs)-1 do
6492 Msgs[i] := Msgs[i+1];
6493 end
6494 else
6495 Inc(NextMsg);
6496 end else
6497 if NextMsg = 0 then
6498 Inc(NextMsg);
6500 Msgs[NextMsg-1] := Text;
6501 CurValue := 0;
6502 MaxValue := Max;
6503 ShowCount := 0;
6504 PBarWasHere := false;
6505 end;
6507 g_ActiveWindow := nil;
6509 ProcessLoading(true);
6510 end;
6512 procedure g_Game_StepLoading(Value: Integer = -1);
6513 begin
6514 with LoadingStat do
6515 begin
6516 if Value = -1 then
6517 begin
6518 Inc(CurValue);
6519 Inc(ShowCount);
6520 end
6521 else
6522 CurValue := Value;
6523 if (ShowCount > LOADING_SHOW_STEP) or (Value > -1) then
6524 begin
6525 ShowCount := 0;
6526 ProcessLoading();
6527 end;
6528 end;
6529 end;
6531 procedure g_Game_ClearLoading();
6532 var
6533 len: Word;
6534 begin
6535 with LoadingStat do
6536 begin
6537 CurValue := 0;
6538 MaxValue := 0;
6539 ShowCount := 0;
6540 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6541 if len < 1 then len := 1;
6542 SetLength(Msgs, len);
6543 for len := Low(Msgs) to High(Msgs) do
6544 Msgs[len] := '';
6545 NextMsg := 0;
6546 PBarWasHere := false;
6547 end;
6548 end;
6550 procedure Parse_Params(var pars: TParamStrValues);
6551 var
6552 i: Integer;
6553 s: String;
6554 begin
6555 SetLength(pars, 0);
6556 i := 1;
6557 while i <= ParamCount do
6558 begin
6559 s := ParamStr(i);
6560 if (s[1] = '-') and (Length(s) > 1) then
6561 begin
6562 if (s[2] = '-') and (Length(s) > 2) then
6563 begin // Одиночный параметр
6564 SetLength(pars, Length(pars) + 1);
6565 with pars[High(pars)] do
6566 begin
6567 Name := LowerCase(s);
6568 Value := '+';
6569 end;
6570 end
6571 else
6572 if (i < ParamCount) then
6573 begin // Параметр со значением
6574 Inc(i);
6575 SetLength(pars, Length(pars) + 1);
6576 with pars[High(pars)] do
6577 begin
6578 Name := LowerCase(s);
6579 Value := LowerCase(ParamStr(i));
6580 end;
6581 end;
6582 end;
6584 Inc(i);
6585 end;
6586 end;
6588 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6589 var
6590 i: Integer;
6591 begin
6592 Result := '';
6593 for i := 0 to High(pars) do
6594 if pars[i].Name = aName then
6595 begin
6596 Result := pars[i].Value;
6597 Break;
6598 end;
6599 end;
6601 procedure g_Game_Process_Params();
6602 var
6603 pars: TParamStrValues;
6604 map: String;
6605 GMode, n: Byte;
6606 LimT, LimS: Integer;
6607 Opt: LongWord;
6608 Lives: Integer;
6609 s: String;
6610 Port: Integer;
6611 ip: String;
6612 F: TextFile;
6613 begin
6614 Parse_Params(pars);
6616 // Debug mode:
6617 s := Find_Param_Value(pars, '--debug');
6618 if (s <> '') then
6619 begin
6620 g_Game_SetDebugMode();
6621 s := Find_Param_Value(pars, '--netdump');
6622 if (s <> '') then
6623 NetDump := True;
6624 end;
6626 // Connect when game loads
6627 ip := Find_Param_Value(pars, '-connect');
6629 if ip <> '' then
6630 begin
6631 s := Find_Param_Value(pars, '-port');
6632 if (s = '') or not TryStrToInt(s, Port) then
6633 Port := 25666;
6635 s := Find_Param_Value(pars, '-pw');
6637 g_Game_StartClient(ip, Port, s);
6638 Exit;
6639 end;
6641 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
6642 if (s <> '') then
6643 begin
6644 gDefaultMegawadStart := s;
6645 end;
6647 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
6648 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
6649 begin
6650 gDefaultMegawadStart := DF_Default_Megawad_Start;
6651 end;
6653 // Start map when game loads:
6654 map := LowerCase(Find_Param_Value(pars, '-map'));
6655 if isWadPath(map) then
6656 begin
6657 // Game mode:
6658 s := Find_Param_Value(pars, '-gm');
6659 GMode := g_Game_TextToMode(s);
6660 if GMode = GM_NONE then GMode := GM_DM;
6661 if GMode = GM_SINGLE then GMode := GM_COOP;
6663 // Time limit:
6664 s := Find_Param_Value(pars, '-limt');
6665 if (s = '') or (not TryStrToInt(s, LimT)) then
6666 LimT := 0;
6667 if LimT < 0 then
6668 LimT := 0;
6670 // Goal limit:
6671 s := Find_Param_Value(pars, '-lims');
6672 if (s = '') or (not TryStrToInt(s, LimS)) then
6673 LimS := 0;
6674 if LimS < 0 then
6675 LimS := 0;
6677 // Lives limit:
6678 s := Find_Param_Value(pars, '-lives');
6679 if (s = '') or (not TryStrToInt(s, Lives)) then
6680 Lives := 0;
6681 if Lives < 0 then
6682 Lives := 0;
6684 // Options:
6685 s := Find_Param_Value(pars, '-opt');
6686 if (s = '') then
6687 Opt := gsGameFlags
6688 else
6689 Opt := StrToIntDef(s, 0);
6691 // Close after map:
6692 s := Find_Param_Value(pars, '--close');
6693 if (s <> '') then
6694 gMapOnce := True;
6696 // Override map to test:
6697 s := LowerCase(Find_Param_Value(pars, '-testmap'));
6698 if s <> '' then
6699 begin
6700 if e_IsValidResourceName(s) then
6701 e_FindResource(AllMapDirs, s);
6702 gTestMap := ExpandFileName(s);
6703 end;
6705 // Delete test map after play:
6706 s := Find_Param_Value(pars, '--testdelete');
6707 if (s <> '') then
6708 begin
6709 //gMapToDelete := MapsDir + map;
6710 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
6711 Halt(1);
6712 end;
6714 // Delete temporary WAD after play:
6715 s := Find_Param_Value(pars, '--tempdelete');
6716 if (s <> '') and (gTestMap <> '') then
6717 begin
6718 gMapToDelete := gTestMap;
6719 gTempDelete := True;
6720 end;
6722 // Number of players:
6723 s := Find_Param_Value(pars, '-pl');
6724 if (s = '') then
6725 n := DEFAULT_PLAYERS
6726 else
6727 n := StrToIntDef(s, DEFAULT_PLAYERS);
6729 // Start:
6730 s := Find_Param_Value(pars, '-port');
6731 if (s = '') or not TryStrToInt(s, Port) then
6732 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6733 else
6734 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6735 end;
6737 // Execute script when game loads:
6738 s := Find_Param_Value(pars, '-exec');
6739 if s <> '' then
6740 begin
6741 // if not isWadPath(s) then
6742 // s := GameDir + '/' + s;
6744 {$I-}
6745 AssignFile(F, s);
6746 Reset(F);
6747 if IOResult <> 0 then
6748 begin
6749 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
6750 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6751 CloseFile(F);
6752 Exit;
6753 end;
6754 e_WriteLog('Executing script: ' + s, TMsgType.Notify);
6755 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6757 while not EOF(F) do
6758 begin
6759 ReadLn(F, s);
6760 if IOResult <> 0 then
6761 begin
6762 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
6763 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6764 CloseFile(F);
6765 Exit;
6766 end;
6767 if Pos('#', s) <> 1 then // script comment
6768 g_Console_Process(s, True);
6769 end;
6771 CloseFile(F);
6772 {$I+}
6773 end;
6775 SetLength(pars, 0);
6776 end;
6778 begin
6779 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
6780 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
6781 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
6782 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
6784 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
6785 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
6786 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
6787 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
6789 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
6790 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
6792 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
6793 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
6795 {$IFDEF ENABLE_HOLMES}
6796 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
6797 {$ENDIF}
6799 conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds, 'ignore level bounds', '', false);
6801 conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
6802 conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
6804 conRegVar('r_smallmap_align_h', @r_smallmap_h, 'halign: 0: left; 1: center; 2: right', 'horizontal aligning of small maps');
6805 conRegVar('r_smallmap_align_v', @r_smallmap_v, 'valign: 0: top; 1: center; 2: bottom', 'vertial aligning of small maps');
6807 conRegVar('r_showfps', @gShowFPS, 'draw fps counter', 'draw fps counter');
6808 conRegVar('r_showtime', @gShowTime, 'show game time', 'show game time');
6809 conRegVar('r_showping', @gShowPing, 'show ping', 'show ping');
6810 conRegVar('r_showscore', @gShowGoals, 'show score', 'show score');
6811 conRegVar('r_showkillmsg', @gShowKillMsg, 'show kill log', 'show kill log');
6812 conRegVar('r_showlives', @gShowLives, 'show lives', 'show lives');
6813 conRegVar('r_showspect', @gSpectHUD, 'show spectator hud', 'show spectator hud');
6814 conRegVar('r_showstat', @gShowStat, 'show stats', 'show stats');
6815 conRegVar('r_showpids', @gShowPIDs, 'show PIDs', 'show PIDs');
6816 end.