DEADSOFTWARE

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