DEADSOFTWARE

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