DEADSOFTWARE

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