DEADSOFTWARE

Game: improving weapon autoswitch, added rotation by travi$
[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_basic, g_player, e_graphics, g_res_downloader,
24 g_sound, g_gui, utils, md5, mempool, xprofiler,
25 g_touch, g_weapons;
27 type
28 TGameSettings = record
29 GameType: Byte;
30 GameMode: Byte;
31 TimeLimit: Word;
32 GoalLimit: Word;
33 WarmupTime: Word;
34 SpawnInvul: Word;
35 ItemRespawnTime: Word;
36 MaxLives: Byte;
37 Options: LongWord;
38 WAD: String;
39 end;
41 TGameEvent = record
42 Name: String;
43 Command: String;
44 end;
46 TDelayedEvent = record
47 Pending: Boolean;
48 Time: LongWord;
49 DEType: Byte;
50 DENum: Integer;
51 DEStr: String;
52 end;
54 TChatSound = record
55 Sound: TPlayableSound;
56 Tags: Array of String;
57 FullWord: Boolean;
58 end;
60 TPlayerSettings = record
61 Name: String;
62 Model: String;
63 Color: TRGB;
64 Team: Byte;
65 WeaponSwitch: Byte;
66 WeaponPreferences: Array[WP_FIRST..WP_LAST+1] of Byte;
67 end;
69 TMegaWADInfo = record
70 Name: String;
71 Description: String;
72 Author: String;
73 Pic: String;
74 end;
76 THearPoint = record
77 Active: Boolean;
78 Coords: TDFPoint;
79 end;
81 function g_Game_IsNet(): Boolean;
82 function g_Game_IsServer(): Boolean;
83 function g_Game_IsClient(): Boolean;
84 procedure g_Game_Init();
85 procedure g_Game_Free (freeTextures: Boolean=true);
86 procedure g_Game_LoadData();
87 procedure g_Game_FreeData();
88 procedure g_Game_Update();
89 procedure g_Game_PreUpdate();
90 procedure g_Game_Draw();
91 procedure g_Game_Quit();
92 procedure g_Game_SetupScreenSize();
93 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
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, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte);
104 procedure g_Game_StartServer(Map: String; GameMode: Byte; TimeLimit, GoalLimit: 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();
149 procedure DrawLoadingStat();
150 procedure DrawMenuBackground(tex: AnsiString);
152 { procedure SetWinPause(Enable: Boolean); }
154 const
155 GAME_TICK = 28;
157 LOADING_SHOW_STEP = 100;
158 LOADING_INTERLINE = 20;
160 GT_NONE = 0;
161 GT_SINGLE = 1;
162 GT_CUSTOM = 2;
163 GT_SERVER = 3;
164 GT_CLIENT = 4;
166 GM_NONE = 0;
167 GM_DM = 1;
168 GM_TDM = 2;
169 GM_CTF = 3;
170 GM_COOP = 4;
171 GM_SINGLE = 5;
173 MESSAGE_DIKEY = WM_USER + 1;
175 EXIT_QUIT = 1;
176 EXIT_SIMPLE = 2;
177 EXIT_RESTART = 3;
178 EXIT_ENDLEVELSINGLE = 4;
179 EXIT_ENDLEVELCUSTOM = 5;
181 GAME_OPTION_RESERVED = 1;
182 GAME_OPTION_TEAMDAMAGE = 2;
183 GAME_OPTION_ALLOWEXIT = 4;
184 GAME_OPTION_WEAPONSTAY = 8;
185 GAME_OPTION_MONSTERS = 16;
186 GAME_OPTION_BOTVSPLAYER = 32;
187 GAME_OPTION_BOTVSMONSTER = 64;
188 GAME_OPTION_DMKEYS = 128;
189 GAME_OPTION_TEAMHITTRACE = 256;
190 GAME_OPTION_TEAMHITPROJECTILE = 512;
191 GAME_OPTION_TEAMABSORBDAMAGE = 1024;
192 GAME_OPTION_ALLOWDROPFLAG = 2048;
193 GAME_OPTION_THROWFLAG = 4096;
195 STATE_NONE = 0;
196 STATE_MENU = 1;
197 STATE_FOLD = 2;
198 STATE_INTERCUSTOM = 3;
199 STATE_INTERSINGLE = 4;
200 STATE_INTERTEXT = 5;
201 STATE_INTERPIC = 6;
202 STATE_ENDPIC = 7;
203 STATE_SLIST = 8;
205 LMS_RESPAWN_NONE = 0;
206 LMS_RESPAWN_WARMUP = 1;
207 LMS_RESPAWN_FINAL = 2;
209 SPECT_NONE = 0;
210 SPECT_STATS = 1;
211 SPECT_MAPVIEW = 2;
212 SPECT_PLAYERS = 3;
214 DE_GLOBEVENT = 0;
215 DE_BFGHIT = 1;
216 DE_KILLCOMBO = 2;
217 DE_BODYKILL = 3;
219 ANNOUNCE_NONE = 0;
220 ANNOUNCE_ME = 1;
221 ANNOUNCE_MEPLUS = 2;
222 ANNOUNCE_ALL = 3;
224 CONFIG_FILENAME = 'Doom2DF.cfg';
226 TEST_MAP_NAME = '$$$_TEST_$$$';
228 STD_PLAYER_MODEL = 'Doomer';
230 {$IFDEF HEADLESS}
231 DEFAULT_PLAYERS = 0;
232 {$ELSE}
233 DEFAULT_PLAYERS = 1;
234 {$ENDIF}
236 STATFILE_VERSION = $03;
238 var
239 gStdFont: DWORD;
240 gGameSettings: TGameSettings;
241 gPlayer1Settings: TPlayerSettings;
242 gPlayer2Settings: TPlayerSettings;
243 gGameOn: Boolean;
244 gPlayerScreenSize: TDFPoint;
245 gPlayer1ScreenCoord: TDFPoint;
246 gPlayer2ScreenCoord: TDFPoint;
247 gPlayer1: TPlayer = nil;
248 gPlayer2: TPlayer = nil;
249 gPlayerDrawn: TPlayer = nil;
250 gTime: LongWord;
251 gLerpFactor: Single = 1.0;
252 gSwitchGameMode: Byte = GM_DM;
253 gHearPoint1, gHearPoint2: THearPoint;
254 gSoundEffectsDF: Boolean = False;
255 gSoundTriggerTime: Word = 0;
256 gAnnouncer: Integer = ANNOUNCE_NONE;
257 goodsnd: array[0..3] of TPlayableSound;
258 killsnd: array[0..3] of TPlayableSound;
259 hahasnd: array[0..2] of TPlayableSound;
260 sound_get_flag: array[0..1] of TPlayableSound;
261 sound_lost_flag: array[0..1] of TPlayableSound;
262 sound_ret_flag: array[0..1] of TPlayableSound;
263 sound_cap_flag: array[0..1] of TPlayableSound;
264 gBodyKillEvent: Integer = -1;
265 gDefInterTime: ShortInt = -1;
266 gInterEndTime: LongWord = 0;
267 gInterTime: LongWord = 0;
268 gServInterTime: Byte = 0;
269 gGameStartTime: LongWord = 0;
270 gTotalMonsters: Integer = 0;
271 gPauseMain: Boolean = false;
272 gPauseHolmes: Boolean = false;
273 gShowTime: Boolean = False;
274 gShowFPS: Boolean = False;
275 gShowGoals: Boolean = True;
276 gShowStat: Boolean = True;
277 gShowPIDs: Boolean = False;
278 gShowKillMsg: Boolean = True;
279 gShowLives: Boolean = True;
280 gShowPing: Boolean = False;
281 gShowMap: Boolean = False;
282 gExit: Byte = 0;
283 gState: Byte = STATE_NONE;
284 sX, sY: Integer;
285 sWidth, sHeight: Word;
286 gSpectMode: Byte = SPECT_NONE;
287 gSpectHUD: Boolean = True;
288 gSpectKeyPress: Boolean = False;
289 gSpectX: Integer = 0;
290 gSpectY: Integer = 0;
291 gSpectStep: Byte = 8;
292 gSpectViewTwo: Boolean = False;
293 gSpectPID1: Integer = -1;
294 gSpectPID2: Integer = -1;
295 gSpectAuto: Boolean = False;
296 gSpectAutoNext: LongWord;
297 gSpectAutoStepX: Integer;
298 gSpectAutoStepY: Integer;
299 gMusic: TMusic = nil;
300 gLoadGameMode: Boolean;
301 gCheats: Boolean = False;
302 gMapOnce: Boolean = False;
303 gMapToDelete: String;
304 gTempDelete: Boolean = False;
305 gLastMap: Boolean = False;
306 gScreenWidth: Word;
307 gScreenHeight: Word;
308 gResolutionChange: Boolean = False;
309 gRC_Width, gRC_Height: Integer;
310 gRC_FullScreen, gRC_Maximized: Boolean;
311 gLanguageChange: Boolean = False;
312 gDebugMode: Boolean = False;
313 g_debug_Sounds: Boolean = False;
314 g_debug_Frames: Boolean = False;
315 g_debug_WinMsgs: Boolean = False;
316 g_debug_MonsterOff: Boolean = False;
317 g_debug_BotAIOff: Byte = 0;
318 g_debug_HealthBar: Boolean = False;
319 g_Debug_Player: Boolean = False;
320 gCoopMonstersKilled: Word = 0;
321 gCoopSecretsFound: Word = 0;
322 gCoopTotalMonstersKilled: Word = 0;
323 gCoopTotalSecretsFound: Word = 0;
324 gCoopTotalMonsters: Word = 0;
325 gCoopTotalSecrets: Word = 0;
326 gStatsOff: Boolean = False;
327 gStatsPressed: Boolean = False;
328 gExitByTrigger: Boolean = False;
329 gNextMap: String = '';
330 gLMSRespawn: Byte = LMS_RESPAWN_NONE;
331 gLMSRespawnTime: Cardinal = 0;
332 gLMSSoftSpawn: Boolean = False;
333 gMissionFailed: Boolean = False;
334 gVoteInProgress: Boolean = False;
335 gVotePassed: Boolean = False;
336 gVoteCommand: string = '';
337 gVoteTimer: Cardinal = 0;
338 gVoteCmdTimer: Cardinal = 0;
339 gVoteCount: Integer = 0;
340 gVoteTimeout: Cardinal = 30;
341 gVoted: Boolean = False;
342 gVotesEnabled: Boolean = True;
343 gEvents: Array of TGameEvent;
344 gDelayedEvents: Array of TDelayedEvent;
345 gUseChatSounds: Boolean = True;
346 gChatSounds: Array of TChatSound;
347 gWeaponAction: Array [0..1, WP_FACT..WP_LACT] of Boolean; // [player, weapon_action]
348 gSelectWeapon: Array [0..1, WP_FIRST..WP_LAST] of Boolean; // [player, weapon]
349 gInterReadyCount: Integer = 0;
351 g_dbg_ignore_bounds: Boolean = false;
352 r_smallmap_h: Integer = 0; // 0: left; 1: center; 2: right
353 r_smallmap_v: Integer = 2; // 0: top; 1: center; 2: bottom
355 // move button values:
356 // bits 0-1: l/r state:
357 // 0: neither left, nor right pressed
358 // 1: left pressed
359 // 2: right pressed
360 // bits 4-5: l/r state when strafe was pressed
361 P1MoveButton: Byte = 0;
362 P2MoveButton: Byte = 0;
364 g_profile_frame_update: Boolean = false;
365 g_profile_frame_draw: Boolean = false;
366 g_profile_collision: Boolean = false;
367 g_profile_los: Boolean = false;
368 g_profile_history_size: Integer = 1000;
370 g_rlayer_back: Boolean = true;
371 g_rlayer_step: Boolean = true;
372 g_rlayer_wall: Boolean = true;
373 g_rlayer_door: Boolean = true;
374 g_rlayer_acid1: Boolean = true;
375 g_rlayer_acid2: Boolean = true;
376 g_rlayer_water: Boolean = true;
377 g_rlayer_fore: Boolean = true;
380 procedure g_ResetDynlights ();
381 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
382 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
384 function conIsCheatsEnabled (): Boolean; inline;
385 function gPause (): Boolean; inline;
388 implementation
390 uses
391 {$INCLUDE ../nogl/noGLuses.inc}
392 {$IFDEF ENABLE_HOLMES}
393 g_holmes,
394 {$ENDIF}
395 e_texture, e_res, g_textures, g_window, g_menu,
396 e_input, e_log, g_console, g_items, g_map, g_panel,
397 g_playermodel, g_gfx, g_options, Math,
398 g_triggers, g_monsters, e_sound, CONFIG,
399 g_language, g_net, g_main, g_phys,
400 ENet, e_msg, g_netmsg, g_netmaster,
401 sfs, wadreader, g_system;
404 var
405 hasPBarGfx: Boolean = false;
408 // ////////////////////////////////////////////////////////////////////////// //
409 function gPause (): Boolean; inline; begin result := gPauseMain or gPauseHolmes; end;
412 // ////////////////////////////////////////////////////////////////////////// //
413 function conIsCheatsEnabled (): Boolean; inline;
414 begin
415 result := false;
416 if g_Game_IsNet then exit;
417 if not gDebugMode then
418 begin
419 //if not gCheats then exit;
420 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
421 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then exit;
422 end;
423 result := true;
424 end;
427 // ////////////////////////////////////////////////////////////////////////// //
428 var
429 profileFrameDraw: TProfiler = nil;
432 // ////////////////////////////////////////////////////////////////////////// //
433 type
434 TDynLight = record
435 x, y, radius: Integer;
436 r, g, b, a: Single;
437 exploCount: Integer;
438 exploRadius: Integer;
439 end;
441 var
442 g_dynLights: array of TDynLight = nil;
443 g_dynLightCount: Integer = 0;
444 g_playerLight: Boolean = false;
446 procedure g_ResetDynlights ();
447 var
448 lnum, idx: Integer;
449 begin
450 if not gwin_has_stencil then begin g_dynLightCount := 0; exit; end;
451 lnum := 0;
452 for idx := 0 to g_dynLightCount-1 do
453 begin
454 if g_dynLights[idx].exploCount = -666 then
455 begin
456 // skip it
457 end
458 else
459 begin
460 // explosion
461 Inc(g_dynLights[idx].exploCount);
462 if (g_dynLights[idx].exploCount < 10) then
463 begin
464 g_dynLights[idx].radius := g_dynLights[idx].exploRadius+g_dynLights[idx].exploCount*8;
465 g_dynLights[idx].a := 0.4+g_dynLights[idx].exploCount/10;
466 if (g_dynLights[idx].a > 0.8) then g_dynLights[idx].a := 0.8;
467 if lnum <> idx then g_dynLights[lnum] := g_dynLights[idx];
468 Inc(lnum);
469 end;
470 end;
471 end;
472 g_dynLightCount := lnum;
473 end;
475 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
476 begin
477 if not gwin_has_stencil then exit;
478 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
479 g_dynLights[g_dynLightCount].x := x;
480 g_dynLights[g_dynLightCount].y := y;
481 g_dynLights[g_dynLightCount].radius := radius;
482 g_dynLights[g_dynLightCount].r := r;
483 g_dynLights[g_dynLightCount].g := g;
484 g_dynLights[g_dynLightCount].b := b;
485 g_dynLights[g_dynLightCount].a := a;
486 g_dynLights[g_dynLightCount].exploCount := -666;
487 Inc(g_dynLightCount);
488 end;
490 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
491 begin
492 if not gwin_has_stencil then exit;
493 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
494 g_dynLights[g_dynLightCount].x := x;
495 g_dynLights[g_dynLightCount].y := y;
496 g_dynLights[g_dynLightCount].radius := 0;
497 g_dynLights[g_dynLightCount].exploRadius := radius;
498 g_dynLights[g_dynLightCount].r := r;
499 g_dynLights[g_dynLightCount].g := g;
500 g_dynLights[g_dynLightCount].b := b;
501 g_dynLights[g_dynLightCount].a := 0;
502 g_dynLights[g_dynLightCount].exploCount := 0;
503 Inc(g_dynLightCount);
504 end;
507 // ////////////////////////////////////////////////////////////////////////// //
508 function calcProfilesHeight (prof: TProfiler): Integer;
509 begin
510 result := 0;
511 if (prof = nil) then exit;
512 if (length(prof.bars) = 0) then exit;
513 result := length(prof.bars)*(16+2);
514 end;
516 // returns width
517 function drawProfiles (x, y: Integer; prof: TProfiler): Integer;
518 var
519 wdt, hgt: Integer;
520 yy: Integer;
521 ii: Integer;
522 begin
523 result := 0;
524 if (prof = nil) then exit;
525 // gScreenWidth
526 if (length(prof.bars) = 0) then exit;
527 wdt := 192;
528 hgt := calcProfilesHeight(prof);
529 if (x < 0) then x := gScreenWidth-(wdt-1)+x;
530 if (y < 0) then y := gScreenHeight-(hgt-1)+y;
531 // background
532 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
533 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
534 e_DarkenQuadWH(x, y, wdt, hgt, 150);
535 // title
536 yy := y+2;
537 for ii := 0 to High(prof.bars) do
538 begin
539 e_TextureFontPrintEx(x+2+4*prof.bars[ii].level, yy, Format('%s: %d', [prof.bars[ii].name, prof.bars[ii].value]), gStdFont, 255, 255, 0, 1, false);
540 Inc(yy, 16+2);
541 end;
542 result := wdt;
543 end;
546 // ////////////////////////////////////////////////////////////////////////// //
547 type
548 TEndCustomGameStat = record
549 PlayerStat: TPlayerStatArray;
550 TeamStat: TTeamStat;
551 GameTime: LongWord;
552 GameMode: Byte;
553 Map, MapName: String;
554 end;
556 TEndSingleGameStat = record
557 PlayerStat: Array [0..1] of record
558 Kills: Integer;
559 Secrets: Integer;
560 end;
561 GameTime: LongWord;
562 TwoPlayers: Boolean;
563 TotalSecrets: Integer;
564 end;
566 TLoadingStat = record
567 CurValue: Integer;
568 MaxValue: Integer;
569 ShowCount: Integer;
570 Msgs: Array of String;
571 NextMsg: Word;
572 PBarWasHere: Boolean; // did we draw a progress bar for this message?
573 end;
575 TParamStrValue = record
576 Name: String;
577 Value: String;
578 end;
580 TParamStrValues = Array of TParamStrValue;
582 const
583 INTER_ACTION_TEXT = 1;
584 INTER_ACTION_PIC = 2;
585 INTER_ACTION_MUSIC = 3;
587 var
588 FPS, UPS: Word;
589 FPSCounter, UPSCounter: Word;
590 FPSTime, UPSTime: LongWord;
591 DataLoaded: Boolean = False;
592 IsDrawStat: Boolean = False;
593 CustomStat: TEndCustomGameStat;
594 SingleStat: TEndSingleGameStat;
595 LoadingStat: TLoadingStat;
596 EndingGameCounter: Byte = 0;
597 MessageText: String;
598 MessageTime: Word;
599 MessageLineLength: Integer = 80;
600 MapList: SSArray = nil;
601 MapIndex: Integer = -1;
602 InterReadyTime: Integer = -1;
603 StatShotDone: Boolean = False;
604 StatFilename: string = ''; // used by stat screenshot to save with the same name as the csv
605 StatDate: string = '';
606 MegaWAD: record
607 info: TMegaWADInfo;
608 endpic: String;
609 endmus: String;
610 res: record
611 text: Array of ShortString;
612 anim: Array of ShortString;
613 pic: Array of ShortString;
614 mus: Array of ShortString;
615 end;
616 triggers: Array of record
617 event: ShortString;
618 actions: Array of record
619 action, p1, p2: Integer;
620 end;
621 end;
622 cur_trigger: Integer;
623 cur_action: Integer;
624 end;
625 //InterPic: String;
626 InterText: record
627 lines: SSArray;
628 img: String;
629 cur_line: Integer;
630 cur_char: Integer;
631 counter: Integer;
632 endtext: Boolean;
633 end;
635 function Compare(a, b: TPlayerStat): Integer;
636 begin
637 if a.Spectator then Result := 1
638 else if b.Spectator then Result := -1
639 else if a.Frags < b.Frags then Result := 1
640 else if a.Frags > b.Frags then Result := -1
641 else if a.Deaths < b.Deaths then Result := -1
642 else if a.Deaths > b.Deaths then Result := 1
643 else if a.Kills < b.Kills then Result := -1
644 else Result := 1;
645 end;
647 procedure SortGameStat(var stat: TPlayerStatArray);
648 var
649 I, J: Integer;
650 T: TPlayerStat;
651 begin
652 if stat = nil then Exit;
654 for I := High(stat) downto Low(stat) do
655 for J := Low(stat) to High(stat) - 1 do
656 if Compare(stat[J], stat[J + 1]) = 1 then
657 begin
658 T := stat[J];
659 stat[J] := stat[J + 1];
660 stat[J + 1] := T;
661 end;
662 end;
664 // saves a shitty CSV containing the game stats passed to it
665 procedure SaveGameStat(Stat: TEndCustomGameStat; Path: string);
666 var
667 s: TextFile;
668 dir, fname, map, mode, etime: String;
669 I: Integer;
670 begin
671 try
672 dir := e_GetWriteableDir(StatsDirs);
673 // stats are placed in stats/yy/mm/dd/*.csv
674 fname := e_CatPath(dir, Path);
675 ForceDirectories(fname); // ensure yy/mm/dd exists within the stats dir
676 fname := e_CatPath(fname, StatFilename + '.csv');
677 AssignFile(s, fname);
678 try
679 Rewrite(s);
680 // line 1: stats ver, datetime, server name, map name, game mode, time limit, score limit, dmflags, game time, num players
681 if g_Game_IsNet then fname := NetServerName else fname := '';
682 map := g_ExtractWadNameNoPath(gMapInfo.Map) + ':/' + g_ExtractFileName(gMapInfo.Map);
683 mode := g_Game_ModeToText(Stat.GameMode);
684 etime := Format('%d:%.2d:%.2d', [
685 Stat.GameTime div 1000 div 3600,
686 (Stat.GameTime div 1000 div 60) mod 60,
687 Stat.GameTime div 1000 mod 60
688 ]);
689 WriteLn(s, 'stats_ver,datetime,server,map,mode,timelimit,scorelimit,dmflags,time,num_players');
690 WriteLn(s, Format('%d,%s,%s,%s,%s,%u,%u,%u,%s,%d', [
691 STATFILE_VERSION,
692 StatDate,
693 dquoteStr(fname),
694 dquoteStr(map),
695 mode,
696 gGameSettings.TimeLimit,
697 gGameSettings.GoalLimit,
698 gGameSettings.Options,
699 etime,
700 Length(Stat.PlayerStat)
701 ]));
702 // line 2: game specific shit
703 // if it's a team game: red score, blue score
704 // if it's a coop game: monsters killed, monsters total, secrets found, secrets total
705 // otherwise nothing
706 if Stat.GameMode in [GM_TDM, GM_CTF] then
707 WriteLn(s,
708 Format('red_score,blue_score' + LineEnding + '%d,%d', [Stat.TeamStat[TEAM_RED].Goals, Stat.TeamStat[TEAM_BLUE].Goals]))
709 else if Stat.GameMode in [GM_COOP, GM_SINGLE] then
710 WriteLn(s,
711 Format('mon_killed,mon_total,secrets_found,secrets_total' + LineEnding + '%d,%d,%d,%d',[gCoopMonstersKilled, gTotalMonsters, gCoopSecretsFound, gSecretsCount]));
712 // lines 3-...: team, player name, frags, deaths
713 WriteLn(s, 'team,name,frags,deaths');
714 for I := Low(Stat.PlayerStat) to High(Stat.PlayerStat) do
715 with Stat.PlayerStat[I] do
716 WriteLn(s, Format('%d,%s,%d,%d', [Team, dquoteStr(Name), Frags, Deaths]));
717 except
718 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [fname]));
719 end;
720 except
721 g_Console_Add('could not create gamestats file "' + fname + '"');
722 end;
723 CloseFile(s);
724 end;
726 function g_Game_ModeToText(Mode: Byte): string;
727 begin
728 Result := '';
729 case Mode of
730 GM_DM: Result := _lc[I_MENU_GAME_TYPE_DM];
731 GM_TDM: Result := _lc[I_MENU_GAME_TYPE_TDM];
732 GM_CTF: Result := _lc[I_MENU_GAME_TYPE_CTF];
733 GM_COOP: Result := _lc[I_MENU_GAME_TYPE_COOP];
734 GM_SINGLE: Result := _lc[I_MENU_GAME_TYPE_SINGLE];
735 end;
736 end;
738 function g_Game_TextToMode(Mode: string): Byte;
739 begin
740 Result := GM_NONE;
741 Mode := UpperCase(Mode);
742 if Mode = _lc[I_MENU_GAME_TYPE_DM] then
743 begin
744 Result := GM_DM;
745 Exit;
746 end;
747 if Mode = _lc[I_MENU_GAME_TYPE_TDM] then
748 begin
749 Result := GM_TDM;
750 Exit;
751 end;
752 if Mode = _lc[I_MENU_GAME_TYPE_CTF] then
753 begin
754 Result := GM_CTF;
755 Exit;
756 end;
757 if Mode = _lc[I_MENU_GAME_TYPE_COOP] then
758 begin
759 Result := GM_COOP;
760 Exit;
761 end;
762 if Mode = _lc[I_MENU_GAME_TYPE_SINGLE] then
763 begin
764 Result := GM_SINGLE;
765 Exit;
766 end;
767 end;
769 function g_Game_IsNet(): Boolean;
770 begin
771 Result := (gGameSettings.GameType in [GT_SERVER, GT_CLIENT]);
772 end;
774 function g_Game_IsServer(): Boolean;
775 begin
776 Result := (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM, GT_SERVER]);
777 end;
779 function g_Game_IsClient(): Boolean;
780 begin
781 Result := (gGameSettings.GameType = GT_CLIENT);
782 end;
784 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
785 var
786 w: TWADFile;
787 cfg: TConfig;
788 p: Pointer;
789 len: Integer;
790 begin
791 Result.name := ExtractFileName(WAD);
792 Result.description := '';
793 Result.author := '';
795 w := TWADFile.Create();
796 w.ReadFile(WAD);
798 if not w.GetResource('INTERSCRIPT', p, len) then
799 begin
800 w.Free();
801 Exit;
802 end;
804 cfg := TConfig.CreateMem(p, len);
805 Result.name := cfg.ReadStr('megawad', 'name', ExtractFileName(WAD));
806 Result.description := cfg.ReadStr('megawad', 'description', '');
807 Result.author := cfg.ReadStr('megawad', 'author', '');
808 Result.pic := cfg.ReadStr('megawad', 'pic', '');
809 cfg.Free();
811 FreeMem(p);
812 end;
814 procedure g_Game_FreeWAD();
815 var
816 a: Integer;
817 begin
818 for a := 0 to High(MegaWAD.res.pic) do
819 if MegaWAD.res.pic[a] <> '' then
820 g_Texture_Delete(MegaWAD.res.pic[a]);
822 for a := 0 to High(MegaWAD.res.mus) do
823 if MegaWAD.res.mus[a] <> '' then
824 g_Sound_Delete(MegaWAD.res.mus[a]);
826 MegaWAD.res.pic := nil;
827 MegaWAD.res.text := nil;
828 MegaWAD.res.anim := nil;
829 MegaWAD.res.mus := nil;
830 MegaWAD.triggers := nil;
832 g_Texture_Delete('TEXTURE_endpic');
833 g_Sound_Delete('MUSIC_endmus');
835 ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
836 gGameSettings.WAD := '';
837 end;
839 procedure g_Game_LoadWAD(WAD: string);
840 var
841 w: TWADFile;
842 cfg: TConfig;
843 p: Pointer;
844 {b, }len: Integer;
845 s: AnsiString;
846 begin
847 g_Game_FreeWAD();
848 gGameSettings.WAD := WAD;
849 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then
850 Exit;
852 MegaWAD.info := g_Game_GetMegaWADInfo(WAD);
854 w := TWADFile.Create();
855 w.ReadFile(WAD);
857 if not w.GetResource('INTERSCRIPT', p, len) then
858 begin
859 w.Free();
860 Exit;
861 end;
863 cfg := TConfig.CreateMem(p, len);
865 {b := 1;
866 while True do
867 begin
868 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
869 if s = '' then Break;
870 b := b+1;
872 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
873 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
875 g_Texture_CreateWADEx(s, s);
876 end;
878 b := 1;
879 while True do
880 begin
881 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
882 if s = '' then Break;
883 b := b+1;
885 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
886 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
888 g_Music_CreateWADEx(s, s);
889 end;}
891 MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
892 if MegaWAD.endpic <> '' then
893 begin
894 TEXTUREFILTER := GL_LINEAR;
895 s := e_GetResourcePath(WadDirs, MegaWAD.endpic, WAD);
896 g_Texture_CreateWADEx('TEXTURE_endpic', s);
897 TEXTUREFILTER := GL_NEAREST;
898 end;
899 MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
900 if MegaWAD.endmus <> '' then
901 begin
902 s := e_GetResourcePath(WadDirs, MegaWAD.endmus, WAD);
903 g_Sound_CreateWADEx('MUSIC_endmus', s, True);
904 end;
906 cfg.Free();
907 FreeMem(p);
908 w.Free();
909 end;
911 {procedure start_trigger(t: string);
912 begin
913 end;
915 function next_trigger(): Boolean;
916 begin
917 end;}
919 procedure DisableCheats();
920 begin
921 MAX_RUNVEL := 8;
922 VEL_JUMP := 10;
923 gFly := False;
925 if gPlayer1 <> nil then gPlayer1.GodMode := False;
926 if gPlayer2 <> nil then gPlayer2.GodMode := False;
927 if gPlayer1 <> nil then gPlayer1.NoTarget := False;
928 if gPlayer2 <> nil then gPlayer2.NoTarget := False;
930 {$IF DEFINED(D2F_DEBUG)}
931 if gPlayer1 <> nil then gPlayer1.NoTarget := True;
932 gAimLine := g_dbg_aimline_on;
933 {$ENDIF}
934 end;
936 procedure g_Game_ExecuteEvent(Name: String);
937 var
938 a: Integer;
939 begin
940 if Name = '' then
941 Exit;
942 if gEvents = nil then
943 Exit;
944 for a := 0 to High(gEvents) do
945 if gEvents[a].Name = Name then
946 begin
947 if gEvents[a].Command <> '' then
948 g_Console_Process(gEvents[a].Command, True);
949 break;
950 end;
951 end;
953 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
954 var
955 a, n: Integer;
956 begin
957 n := -1;
958 if gDelayedEvents <> nil then
959 for a := 0 to High(gDelayedEvents) do
960 if not gDelayedEvents[a].Pending then
961 begin
962 n := a;
963 break;
964 end;
965 if n = -1 then
966 begin
967 SetLength(gDelayedEvents, Length(gDelayedEvents) + 1);
968 n := High(gDelayedEvents);
969 end;
970 gDelayedEvents[n].Pending := True;
971 gDelayedEvents[n].DEType := DEType;
972 gDelayedEvents[n].DENum := Num;
973 gDelayedEvents[n].DEStr := Str;
974 if DEType = DE_GLOBEVENT then
975 gDelayedEvents[n].Time := (sys_GetTicks() {div 1000}) + Time
976 else
977 gDelayedEvents[n].Time := gTime + Time;
978 Result := n;
979 end;
981 procedure EndGame();
982 var
983 a: Integer;
984 FileName: string;
985 t: TDateTime;
986 begin
987 if g_Game_IsNet and g_Game_IsServer then
988 MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed));
990 // Ñòîï èãðà:
991 gPauseMain := false;
992 gPauseHolmes := false;
993 gGameOn := false;
995 g_Game_StopAllSounds(False);
997 MessageTime := 0;
998 MessageText := '';
1000 EndingGameCounter := 0;
1001 g_ActiveWindow := nil;
1003 gLMSRespawn := LMS_RESPAWN_NONE;
1004 gLMSRespawnTime := 0;
1006 case gExit of
1007 EXIT_SIMPLE: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
1008 begin
1009 g_Game_Free();
1011 if gMapOnce then
1012 begin // Ýòî áûë òåñò
1013 g_Game_Quit();
1014 end
1015 else
1016 begin // Âûõîä â ãëàâíîå ìåíþ
1017 gMusic.SetByName('MUSIC_MENU');
1018 gMusic.Play();
1019 if gState <> STATE_SLIST then
1020 begin
1021 g_GUI_ShowWindow('MainMenu');
1022 gState := STATE_MENU;
1023 end else
1024 begin
1025 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
1026 slReturnPressed := True;
1027 if g_Net_Slist_Fetch(slCurrent) then
1028 begin
1029 if slCurrent = nil then
1030 slWaitStr := _lc[I_NET_SLIST_NOSERVERS];
1031 end
1032 else
1033 slWaitStr := _lc[I_NET_SLIST_ERROR];
1034 g_Serverlist_GenerateTable(slCurrent, slTable);
1035 end;
1037 g_Game_ExecuteEvent('ongameend');
1038 end;
1039 end;
1041 EXIT_RESTART: // Íà÷àòü óðîâåíü ñíà÷àëà
1042 begin
1043 if not g_Game_IsClient then g_Game_Restart();
1044 end;
1046 EXIT_ENDLEVELCUSTOM: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
1047 begin
1048 // Ñòàòèñòèêà Ñâîåé èãðû:
1049 FileName := g_ExtractWadName(gMapInfo.Map);
1051 CustomStat.GameTime := gTime;
1052 CustomStat.Map := ExtractFileName(FileName)+':'+g_ExtractFileName(gMapInfo.Map); //ResName;
1053 CustomStat.MapName := gMapInfo.Name;
1054 CustomStat.GameMode := gGameSettings.GameMode;
1055 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1056 CustomStat.TeamStat := gTeamStat;
1058 CustomStat.PlayerStat := nil;
1060 // Ñòàòèñòèêà èãðîêîâ:
1061 if gPlayers <> nil then
1062 begin
1063 for a := 0 to High(gPlayers) do
1064 if gPlayers[a] <> nil then
1065 begin
1066 SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1);
1067 with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do
1068 begin
1069 Num := a;
1070 Name := gPlayers[a].Name;
1071 Frags := gPlayers[a].Frags;
1072 Deaths := gPlayers[a].Death;
1073 Kills := gPlayers[a].Kills;
1074 Team := gPlayers[a].Team;
1075 Color := gPlayers[a].Model.Color;
1076 Spectator := gPlayers[a].FSpectator;
1077 end;
1078 end;
1080 SortGameStat(CustomStat.PlayerStat);
1082 if (gSaveStats or gScreenshotStats) and (Length(CustomStat.PlayerStat) > 1) then
1083 begin
1084 t := Now;
1085 if g_Game_IsNet then StatFilename := NetServerName else StatFilename := 'local';
1086 StatDate := FormatDateTime('yymmdd_hhnnss', t);
1087 StatFilename := StatFilename + '_' + CustomStat.Map + '_' + g_Game_ModeToText(CustomStat.GameMode);
1088 StatFilename := sanitizeFilename(StatFilename) + '_' + StatDate;
1089 if gSaveStats then
1090 SaveGameStat(CustomStat, FormatDateTime('yyyy"/"mm"/"dd', t));
1091 end;
1093 StatShotDone := False;
1094 end;
1096 g_Game_ExecuteEvent('onmapend');
1097 if not g_Game_IsClient then g_Player_ResetReady;
1098 gInterReadyCount := 0;
1100 // Çàòóõàþùèé ýêðàí:
1101 EndingGameCounter := 255;
1102 gState := STATE_FOLD;
1103 gInterTime := 0;
1104 if gDefInterTime < 0 then
1105 gInterEndTime := IfThen((gGameSettings.GameType = GT_SERVER) and (gPlayer1 = nil), 15000, 25000)
1106 else
1107 gInterEndTime := gDefInterTime * 1000;
1108 end;
1110 EXIT_ENDLEVELSINGLE: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
1111 begin
1112 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
1113 SingleStat.GameTime := gTime;
1114 SingleStat.TwoPlayers := gPlayer2 <> nil;
1115 SingleStat.TotalSecrets := gSecretsCount;
1116 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
1117 SingleStat.PlayerStat[0].Kills := gPlayer1.MonsterKills;
1118 SingleStat.PlayerStat[0].Secrets := gPlayer1.Secrets;
1119 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
1120 if SingleStat.TwoPlayers then
1121 begin
1122 SingleStat.PlayerStat[1].Kills := gPlayer2.MonsterKills;
1123 SingleStat.PlayerStat[1].Secrets := gPlayer2.Secrets;
1124 end;
1126 g_Game_ExecuteEvent('onmapend');
1128 // Åñòü åùå êàðòû:
1129 if gNextMap <> '' then
1130 begin
1131 gMusic.SetByName('MUSIC_INTERMUS');
1132 gMusic.Play();
1133 gState := STATE_INTERSINGLE;
1134 e_UnpressAllKeys();
1136 g_Game_ExecuteEvent('oninter');
1137 end
1138 else // Áîëüøå íåò êàðò
1139 begin
1140 // Çàòóõàþùèé ýêðàí:
1141 EndingGameCounter := 255;
1142 gState := STATE_FOLD;
1143 end;
1144 end;
1145 end;
1147 // Îêîí÷àíèå îáðàáîòàíî:
1148 if gExit <> EXIT_QUIT then
1149 gExit := 0;
1150 end;
1152 procedure drawTime(X, Y: Integer); inline;
1153 begin
1154 e_TextureFontPrint(x, y,
1155 Format('%d:%.2d:%.2d', [
1156 gTime div 1000 div 3600,
1157 (gTime div 1000 div 60) mod 60,
1158 gTime div 1000 mod 60
1159 ]),
1160 gStdFont);
1161 end;
1163 procedure DrawStat();
1164 var
1165 pc, x, y, w, h: Integer;
1166 w1, w2, w3, w4: Integer;
1167 a, aa: Integer;
1168 cw, ch, r, g, b, rr, gg, bb: Byte;
1169 s1, s2, s3: String;
1170 _y: Integer;
1171 stat: TPlayerStatArray;
1172 wad, map: string;
1173 mapstr: string;
1174 namestr: string;
1175 begin
1176 s1 := '';
1177 s2 := '';
1178 s3 := '';
1179 pc := g_Player_GetCount;
1180 e_TextureFontGetSize(gStdFont, cw, ch);
1182 w := gScreenWidth-(gScreenWidth div 5);
1183 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1184 h := 32+ch*(11+pc)
1185 else
1186 h := 40+ch*5+(ch+8)*pc;
1187 x := (gScreenWidth div 2)-(w div 2);
1188 y := (gScreenHeight div 2)-(h div 2);
1190 e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32);
1191 e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0);
1193 drawTime(x+w-78, y+8);
1195 wad := g_ExtractWadNameNoPath(gMapInfo.Map);
1196 map := g_ExtractFileName(gMapInfo.Map);
1197 mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name;
1199 case gGameSettings.GameMode of
1200 GM_DM:
1201 begin
1202 if gGameSettings.MaxLives = 0 then
1203 s1 := _lc[I_GAME_DM]
1204 else
1205 s1 := _lc[I_GAME_LMS];
1206 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
1207 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1208 end;
1210 GM_TDM:
1211 begin
1212 if gGameSettings.MaxLives = 0 then
1213 s1 := _lc[I_GAME_TDM]
1214 else
1215 s1 := _lc[I_GAME_TLMS];
1216 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
1217 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1218 end;
1220 GM_CTF:
1221 begin
1222 s1 := _lc[I_GAME_CTF];
1223 s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.GoalLimit]);
1224 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1225 end;
1227 GM_COOP:
1228 begin
1229 if gGameSettings.MaxLives = 0 then
1230 s1 := _lc[I_GAME_COOP]
1231 else
1232 s1 := _lc[I_GAME_SURV];
1233 s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters);
1234 s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount);
1235 end;
1237 else
1238 begin
1239 s1 := '';
1240 s2 := '';
1241 end;
1242 end;
1244 _y := y+8;
1245 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1);
1246 _y := _y+ch+8;
1247 e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1);
1248 _y := _y+ch+8;
1249 e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1);
1251 e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3,
1252 gStdFont, 200, 200, 200, 1);
1254 if NetMode = NET_SERVER then
1255 e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1)
1256 else
1257 if NetMode = NET_CLIENT then
1258 e_TextureFontPrintEx(x+8, y + 8,
1259 NetClientIP + ':' + IntToStr(NetClientPort), gStdFont, 255, 255, 255, 1);
1261 if pc = 0 then
1262 Exit;
1263 stat := g_Player_GetStats();
1264 SortGameStat(stat);
1266 w2 := (w-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1267 w3 := (w-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1268 w4 := w3;
1269 w1 := w-16-w2-w3-w4; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1271 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1272 begin
1273 _y := _y+ch+ch;
1275 for a := TEAM_RED to TEAM_BLUE do
1276 begin
1277 if a = TEAM_RED then
1278 begin
1279 s1 := _lc[I_GAME_TEAM_RED];
1280 r := 255;
1281 g := 0;
1282 b := 0;
1283 end
1284 else
1285 begin
1286 s1 := _lc[I_GAME_TEAM_BLUE];
1287 r := 0;
1288 g := 0;
1289 b := 255;
1290 end;
1292 e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1);
1293 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Goals),
1294 gStdFont, r, g, b, 1);
1296 _y := _y+ch+(ch div 4);
1297 e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b);
1298 _y := _y+(ch div 4);
1300 for aa := 0 to High(stat) do
1301 if stat[aa].Team = a then
1302 with stat[aa] do
1303 begin
1304 if Spectator then
1305 begin
1306 rr := r div 2;
1307 gg := g div 2;
1308 bb := b div 2;
1309 end
1310 else
1311 begin
1312 rr := r;
1313 gg := g;
1314 bb := b;
1315 end;
1316 if gShowPIDs then
1317 namestr := Format('[%5d] %s', [UID, Name])
1318 else
1319 namestr := Name;
1320 // Èìÿ
1321 e_TextureFontPrintEx(x+16, _y, namestr, gStdFont, rr, gg, bb, 1);
1322 // Ïèíã/ïîòåðè
1323 e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1);
1324 // Ôðàãè
1325 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
1326 // Ñìåðòè
1327 e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
1328 _y := _y+ch;
1329 end;
1331 _y := _y+ch;
1332 end;
1333 end
1334 else if gGameSettings.GameMode in [GM_DM, GM_COOP] then
1335 begin
1336 _y := _y+ch+ch;
1337 e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
1338 e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1);
1339 e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
1340 e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
1342 _y := _y+ch+8;
1343 for aa := 0 to High(stat) do
1344 with stat[aa] do
1345 begin
1346 if Spectator then
1347 begin
1348 r := 127;
1349 g := 64;
1350 end
1351 else
1352 begin
1353 r := 255;
1354 g := 127;
1355 end;
1356 if gShowPIDs then
1357 namestr := Format('[%5d] %s', [UID, Name])
1358 else
1359 namestr := Name;
1360 // Öâåò èãðîêà
1361 e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
1362 e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192);
1363 // Èìÿ
1364 e_TextureFontPrintEx(x+16+16+8, _y+4, namestr, gStdFont, r, g, 0, 1);
1365 // Ïèíã/ïîòåðè
1366 e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1);
1367 // Ôðàãè
1368 e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1);
1369 // Ñìåðòè
1370 e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1);
1371 _y := _y+ch+8;
1372 end;
1373 end
1374 end;
1376 procedure g_Game_Init();
1377 var
1378 SR: TSearchRec;
1379 knownFiles: array of AnsiString = nil;
1380 found: Boolean;
1381 wext, s: AnsiString;
1382 f: Integer;
1383 begin
1384 gExit := 0;
1385 gMapToDelete := '';
1386 gTempDelete := False;
1388 sfsGCDisable(); // temporary disable removing of temporary volumes
1390 try
1391 TEXTUREFILTER := GL_LINEAR;
1392 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE');
1393 g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER');
1394 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN');
1395 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU');
1396 TEXTUREFILTER := GL_NEAREST;
1398 LoadStdFont('STDTXT', 'STDFONT', gStdFont);
1399 LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
1400 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
1402 g_Game_ClearLoading();
1403 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
1404 g_Game_SetLoadingText('', 0, False);
1406 g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
1407 g_Console_Init();
1409 g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
1410 g_PlayerModel_LoadData();
1412 // load models from all possible wad types, in all known directories
1413 // this does a loosy job (linear search, ooph!), but meh
1414 for wext in wadExtensions do
1415 begin
1416 for f := High(ModelDirs) downto Low(ModelDirs) do
1417 begin
1418 if (FindFirst(ModelDirs[f]+DirectorySeparator+'*'+wext, faAnyFile, SR) = 0) then
1419 begin
1420 repeat
1421 found := false;
1422 for s in knownFiles do
1423 begin
1424 if (strEquCI1251(forceFilenameExt(SR.Name, ''), forceFilenameExt(ExtractFileName(s), ''))) then
1425 begin
1426 found := true;
1427 break;
1428 end;
1429 end;
1430 if not found then
1431 begin
1432 SetLength(knownFiles, length(knownFiles)+1);
1433 knownFiles[High(knownFiles)] := ModelDirs[f]+DirectorySeparator+SR.Name;
1434 end;
1435 until (FindNext(SR) <> 0);
1436 end;
1437 FindClose(SR);
1438 end;
1439 end;
1441 if (length(knownFiles) = 0) then raise Exception.Create('no player models found!');
1443 if (length(knownFiles) = 1) then e_LogWriteln('1 player model found.', TMsgType.Notify) else e_LogWritefln('%d player models found.', [Integer(length(knownFiles))], TMsgType.Notify);
1444 for s in knownFiles do
1445 begin
1446 if not g_PlayerModel_Load(s) then e_LogWritefln('Error loading model "%s"', [s], TMsgType.Warning);
1447 end;
1449 gGameOn := false;
1450 gPauseMain := false;
1451 gPauseHolmes := false;
1452 gTime := 0;
1454 {e_MouseInfo.Accel := 1.0;}
1456 g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
1457 g_Game_LoadData();
1459 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1460 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
1461 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
1462 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True, True);
1463 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
1465 {$IFNDEF HEADLESS}
1466 g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
1467 g_Menu_Init();
1468 {$ENDIF}
1470 gMusic := TMusic.Create();
1471 gMusic.SetByName('MUSIC_MENU');
1472 gMusic.Play();
1474 gGameSettings.WarmupTime := 30;
1476 gState := STATE_MENU;
1478 SetLength(gEvents, 6);
1479 gEvents[0].Name := 'ongamestart';
1480 gEvents[1].Name := 'ongameend';
1481 gEvents[2].Name := 'onmapstart';
1482 gEvents[3].Name := 'onmapend';
1483 gEvents[4].Name := 'oninter';
1484 gEvents[5].Name := 'onwadend';
1485 finally
1486 sfsGCEnable(); // enable releasing unused volumes
1487 end;
1488 end;
1490 procedure g_Game_Free(freeTextures: Boolean=true);
1491 begin
1492 if NetMode = NET_CLIENT then g_Net_Disconnect();
1493 if NetMode = NET_SERVER then g_Net_Host_Die();
1495 g_Map_Free(freeTextures);
1496 g_Player_Free();
1497 g_Player_RemoveAllCorpses();
1499 gGameSettings.GameType := GT_NONE;
1500 if gGameSettings.GameMode = GM_SINGLE then
1501 gGameSettings.GameMode := GM_DM;
1502 gSwitchGameMode := gGameSettings.GameMode;
1504 gChatShow := False;
1505 gExitByTrigger := False;
1506 end;
1508 function IsActivePlayer(p: TPlayer): Boolean;
1509 begin
1510 Result := False;
1511 if p = nil then
1512 Exit;
1513 Result := (not p.FDummy) and (not p.FSpectator);
1514 end;
1516 function GetActivePlayer_ByID(ID: Integer): TPlayer;
1517 var
1518 a: Integer;
1519 begin
1520 Result := nil;
1521 if ID < 0 then
1522 Exit;
1523 if gPlayers = nil then
1524 Exit;
1525 for a := Low(gPlayers) to High(gPlayers) do
1526 if IsActivePlayer(gPlayers[a]) then
1527 begin
1528 if gPlayers[a].UID <> ID then
1529 continue;
1530 Result := gPlayers[a];
1531 break;
1532 end;
1533 end;
1535 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
1536 var
1537 a, idx: Integer;
1538 ids: Array of Word;
1539 begin
1540 Result := -1;
1541 if gPlayers = nil then
1542 Exit;
1543 SetLength(ids, 0);
1544 idx := -1;
1545 for a := Low(gPlayers) to High(gPlayers) do
1546 if IsActivePlayer(gPlayers[a]) then
1547 begin
1548 SetLength(ids, Length(ids) + 1);
1549 ids[High(ids)] := gPlayers[a].UID;
1550 if gPlayers[a].UID = Skip then
1551 idx := High(ids);
1552 end;
1553 if Length(ids) = 0 then
1554 Exit;
1555 if idx = -1 then
1556 Result := ids[0]
1557 else
1558 Result := ids[(idx + 1) mod Length(ids)];
1559 end;
1561 function GetActivePlayerID_Prev(Skip: Integer = -1): Integer;
1562 var
1563 a, idx: Integer;
1564 ids: Array of Word;
1565 begin
1566 Result := -1;
1567 if gPlayers = nil then
1568 Exit;
1569 SetLength(ids, 0);
1570 idx := -1;
1571 for a := Low(gPlayers) to High(gPlayers) do
1572 if IsActivePlayer(gPlayers[a]) then
1573 begin
1574 SetLength(ids, Length(ids) + 1);
1575 ids[High(ids)] := gPlayers[a].UID;
1576 if gPlayers[a].UID = Skip then
1577 idx := High(ids);
1578 end;
1579 if Length(ids) = 0 then
1580 Exit;
1581 if idx = -1 then
1582 Result := ids[Length(ids) - 1]
1583 else
1584 Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
1585 end;
1587 function GetActivePlayerID_Random(Skip: Integer = -1): Integer;
1588 var
1589 a, idx: Integer;
1590 ids: Array of Word;
1591 begin
1592 Result := -1;
1593 if gPlayers = nil then
1594 Exit;
1595 SetLength(ids, 0);
1596 idx := -1;
1597 for a := Low(gPlayers) to High(gPlayers) do
1598 if IsActivePlayer(gPlayers[a]) then
1599 begin
1600 SetLength(ids, Length(ids) + 1);
1601 ids[High(ids)] := gPlayers[a].UID;
1602 if gPlayers[a].UID = Skip then
1603 idx := High(ids);
1604 end;
1605 if Length(ids) = 0 then
1606 Exit;
1607 if Length(ids) = 1 then
1608 begin
1609 Result := ids[0];
1610 Exit;
1611 end;
1612 Result := ids[Random(Length(ids))];
1613 a := 10;
1614 while (idx <> -1) and (Result = Skip) and (a > 0) do
1615 begin
1616 Result := ids[Random(Length(ids))];
1617 Dec(a);
1618 end;
1619 end;
1621 function GetRandomSpectMode(Current: Byte): Byte;
1622 label
1623 retry;
1624 begin
1625 Result := Current;
1626 retry:
1627 case Random(7) of
1628 0: Result := SPECT_STATS;
1629 1: Result := SPECT_MAPVIEW;
1630 2: Result := SPECT_MAPVIEW;
1631 3: Result := SPECT_PLAYERS;
1632 4: Result := SPECT_PLAYERS;
1633 5: Result := SPECT_PLAYERS;
1634 6: Result := SPECT_PLAYERS;
1635 end;
1636 if (Current in [SPECT_STATS, SPECT_MAPVIEW]) and (Current = Result) then
1637 goto retry;
1638 end;
1640 procedure ProcessPlayerControls (plr: TPlayer; p: Integer; var MoveButton: Byte);
1641 var
1642 time: Word;
1643 strafeDir: Byte;
1644 i: Integer;
1645 begin
1646 if (plr = nil) then exit;
1647 if (p = 2) then time := 1000 else time := 1;
1648 strafeDir := MoveButton shr 4;
1649 MoveButton := MoveButton and $0F;
1651 if gPlayerAction[p, ACTION_MOVELEFT] and (not gPlayerAction[p, ACTION_MOVERIGHT]) then
1652 MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1653 else if (not gPlayerAction[p, ACTION_MOVELEFT]) and gPlayerAction[p, ACTION_MOVERIGHT] then
1654 MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1655 else if (not gPlayerAction[p, ACTION_MOVELEFT]) and (not gPlayerAction[p, ACTION_MOVERIGHT]) then
1656 MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1658 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1659 if MoveButton = 1 then
1660 plr.PressKey(KEY_LEFT, time)
1661 else if MoveButton = 2 then
1662 plr.PressKey(KEY_RIGHT, time);
1664 // if we have "strafe" key, turn off old strafe mechanics
1665 if gPlayerAction[p, ACTION_STRAFE] then
1666 begin
1667 // new strafe mechanics
1668 if (strafeDir = 0) then
1669 strafeDir := MoveButton; // start strafing
1670 // now set direction according to strafe (reversed)
1671 if (strafeDir = 2) then
1672 plr.SetDirection(TDirection.D_LEFT)
1673 else if (strafeDir = 1) then
1674 plr.SetDirection(TDirection.D_RIGHT)
1675 end
1676 else
1677 begin
1678 strafeDir := 0; // not strafing anymore
1679 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1680 if (MoveButton = 2) and gPlayerAction[p, ACTION_MOVELEFT] then
1681 plr.SetDirection(TDirection.D_LEFT)
1682 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1683 else if (MoveButton = 1) and gPlayerAction[p, ACTION_MOVERIGHT] then
1684 plr.SetDirection(TDirection.D_RIGHT)
1685 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1686 else if MoveButton <> 0 then
1687 plr.SetDirection(TDirection(MoveButton-1))
1688 end;
1690 // fix movebutton state
1691 MoveButton := MoveButton or (strafeDir shl 4);
1693 // Îñòàëüíûå êëàâèøè:
1694 if gPlayerAction[p, ACTION_JUMP] then plr.PressKey(KEY_JUMP, time);
1695 if gPlayerAction[p, ACTION_LOOKUP] then plr.PressKey(KEY_UP, time);
1696 if gPlayerAction[p, ACTION_LOOKDOWN] then plr.PressKey(KEY_DOWN, time);
1697 if gPlayerAction[p, ACTION_ATTACK] then plr.PressKey(KEY_FIRE);
1698 if gPlayerAction[p, ACTION_ACTIVATE] then plr.PressKey(KEY_OPEN);
1700 for i := WP_FACT to WP_LACT do
1701 begin
1702 if gWeaponAction[p, i] then
1703 begin
1704 plr.ProcessWeaponAction(i);
1705 gWeaponAction[p, i] := False
1706 end
1707 end;
1709 for i := WP_FIRST to WP_LAST do
1710 begin
1711 if gSelectWeapon[p, i] then
1712 begin
1713 plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
1714 gSelectWeapon[p, i] := False
1715 end
1716 end;
1718 // HACK: add dynlight here
1719 if gwin_k8_enable_light_experiments then
1720 begin
1721 if e_KeyPressed(IK_F8) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1722 begin
1723 g_playerLight := true;
1724 end;
1725 if e_KeyPressed(IK_F9) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1726 begin
1727 g_playerLight := false;
1728 end;
1729 end;
1731 if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
1732 end;
1734 // HACK: don't have a "key was pressed" function
1735 procedure InterReady();
1736 begin
1737 if InterReadyTime > gTime then Exit;
1738 InterReadyTime := gTime + 3000;
1739 MC_SEND_CheatRequest(NET_CHEAT_READY);
1740 end;
1742 procedure g_Game_PreUpdate();
1743 begin
1744 // these are in separate PreUpdate functions because they can interact during Update()
1745 // and are synced over the net
1746 // we don't care that much about corpses and gibs
1747 g_Player_PreUpdate();
1748 g_Monsters_PreUpdate();
1749 g_Items_PreUpdate();
1750 g_Weapon_PreUpdate();
1751 end;
1753 procedure g_Game_Update();
1754 var
1755 Msg: g_gui.TMessage;
1756 Time: Int64;
1757 a: Byte;
1758 w: Word;
1759 i, b: Integer;
1761 function sendMonsPos (mon: TMonster): Boolean;
1762 begin
1763 result := false; // don't stop
1764 // this will also reset "need-send" flag
1765 if mon.gncNeedSend then
1766 begin
1767 MH_SEND_MonsterPos(mon.UID);
1768 end
1769 else if (mon.MonsterType = MONSTER_BARREL) then
1770 begin
1771 if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1772 end
1773 else if (mon.MonsterState <> MONSTATE_SLEEP) then
1774 begin
1775 if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1776 end;
1777 end;
1779 function sendMonsPosUnexpected (mon: TMonster): Boolean;
1780 begin
1781 result := false; // don't stop
1782 // this will also reset "need-send" flag
1783 if mon.gncNeedSend then MH_SEND_MonsterPos(mon.UID);
1784 end;
1786 function sendItemPos (it: PItem): Boolean;
1787 begin
1788 result := false; // don't stop
1789 if it.needSend then
1790 begin
1791 MH_SEND_ItemPos(it.myId);
1792 it.needSend := False;
1793 end;
1794 end;
1796 var
1797 reliableUpdate: Boolean;
1798 begin
1799 g_ResetDynlights();
1800 framePool.reset();
1802 // Ïîðà âûêëþ÷àòü èãðó:
1803 if gExit = EXIT_QUIT then
1804 Exit;
1805 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1806 if gExit <> 0 then
1807 begin
1808 EndGame();
1809 if gExit = EXIT_QUIT then
1810 Exit;
1811 end;
1813 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî
1814 // no need to, as we'll do it in event handler
1816 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1817 g_Console_Update();
1819 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1820 begin
1821 gExit := EXIT_SIMPLE;
1822 EndGame();
1823 Exit;
1824 end;
1826 // process master server communications
1827 g_Net_Slist_Pulse();
1829 case gState of
1830 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1831 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1832 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1833 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1834 begin
1835 if g_Game_IsNet and g_Game_IsServer then
1836 begin
1837 gInterTime := gInterTime + GAME_TICK;
1838 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1839 if a <> gServInterTime then
1840 begin
1841 gServInterTime := a;
1842 MH_SEND_TimeSync(gServInterTime);
1843 end;
1844 end;
1846 if (not g_Game_IsClient) and
1850 e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or
1851 e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) or
1852 e_KeyPressed(JOY0_ATTACK) or e_KeyPressed(JOY1_ATTACK) or
1853 e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK)
1855 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1856 and (g_ActiveWindow = nil)
1858 or (g_Game_IsNet and ((gInterTime > gInterEndTime) or ((gInterReadyCount >= NetClientCount) and (NetClientCount > 0))))
1860 then
1861 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1862 g_Game_StopAllSounds(True);
1864 if gMapOnce then // Ýòî áûë òåñò
1865 gExit := EXIT_SIMPLE
1866 else
1867 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1868 g_Game_ChangeMap(gNextMap)
1869 else // Ñëåäóþùåé êàðòû íåò
1870 begin
1871 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1872 begin
1873 // Âûõîä â ãëàâíîå ìåíþ:
1874 g_Game_Free;
1875 g_GUI_ShowWindow('MainMenu');
1876 gMusic.SetByName('MUSIC_MENU');
1877 gMusic.Play();
1878 gState := STATE_MENU;
1879 end else
1880 begin
1881 // Ôèíàëüíàÿ êàðòèíêà:
1882 g_Game_ExecuteEvent('onwadend');
1883 g_Game_Free();
1884 if not gMusic.SetByName('MUSIC_endmus') then
1885 gMusic.SetByName('MUSIC_STDENDMUS');
1886 gMusic.Play();
1887 gState := STATE_ENDPIC;
1888 end;
1889 g_Game_ExecuteEvent('ongameend');
1890 end;
1892 Exit;
1893 end
1894 else if g_Game_IsClient and
1897 e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or
1898 e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) or
1899 e_KeyPressed(JOY0_ATTACK) or e_KeyPressed(JOY1_ATTACK) or
1900 e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK)
1902 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1903 and (g_ActiveWindow = nil)
1905 then
1906 begin
1907 // ready / unready
1908 InterReady();
1909 end;
1911 if gState = STATE_INTERTEXT then
1912 if InterText.counter > 0 then
1913 InterText.counter := InterText.counter - 1;
1914 end;
1916 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1917 begin
1918 if EndingGameCounter = 0 then
1919 begin
1920 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1921 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1922 begin
1923 gState := STATE_INTERCUSTOM;
1924 InterReadyTime := -1;
1925 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1926 begin
1927 g_Game_ExecuteEvent('onwadend');
1928 if not gMusic.SetByName('MUSIC_endmus') then
1929 gMusic.SetByName('MUSIC_STDENDMUS');
1930 end
1931 else
1932 gMusic.SetByName('MUSIC_ROUNDMUS');
1933 gMusic.Play();
1934 e_UnpressAllKeys();
1935 end
1936 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1937 begin
1938 gMusic.SetByName('MUSIC_INTERMUS');
1939 gMusic.Play();
1940 gState := STATE_INTERSINGLE;
1941 e_UnpressAllKeys();
1942 end;
1943 g_Game_ExecuteEvent('oninter');
1944 end
1945 else
1946 DecMin(EndingGameCounter, 6, 0);
1947 end;
1949 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1950 begin
1951 if gMapOnce then // Ýòî áûë òåñò
1952 begin
1953 gExit := EXIT_SIMPLE;
1954 Exit;
1955 end;
1956 end;
1958 STATE_SLIST:
1959 g_Serverlist_Control(slCurrent, slTable);
1960 end;
1962 // Ñòàòèñòèêà ïî Tab:
1963 if gGameOn then
1964 IsDrawStat := (not gConsoleShow) and (not gChatShow) and (gGameSettings.GameType <> GT_SINGLE) and g_Console_Action(ACTION_SCORES);
1966 // Èãðà èäåò:
1967 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1968 begin
1969 // Âðåìÿ += 28 ìèëëèñåêóíä:
1970 gTime := gTime + GAME_TICK;
1972 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1973 if MessageTime = 0 then
1974 MessageText := '';
1975 if MessageTime > 0 then
1976 MessageTime := MessageTime - 1;
1978 if (g_Game_IsServer) then
1979 begin
1980 // Áûë çàäàí ëèìèò âðåìåíè:
1981 if (gGameSettings.TimeLimit > 0) then
1982 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1983 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1984 g_Game_NextLevel();
1985 Exit;
1986 end;
1988 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1989 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1990 g_Game_RestartRound(gLMSSoftSpawn);
1992 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1993 if gVoteInProgress and (gVoteTimer < gTime) then
1994 g_Game_CheckVote
1995 else if gVotePassed and (gVoteCmdTimer < gTime) then
1996 begin
1997 g_Console_Process(gVoteCommand);
1998 gVoteCommand := '';
1999 gVotePassed := False;
2000 end;
2002 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
2003 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
2004 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
2005 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
2006 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
2008 // Áûë çàäàí ëèìèò ïîáåä:
2009 if (gGameSettings.GoalLimit > 0) then
2010 begin
2011 b := 0;
2013 if gGameSettings.GameMode = GM_DM then
2014 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
2015 for i := 0 to High(gPlayers) do
2016 if gPlayers[i] <> nil then
2017 if gPlayers[i].Frags > b then
2018 b := gPlayers[i].Frags;
2019 end
2020 else
2021 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2022 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
2023 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
2024 end;
2026 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
2027 if b >= gGameSettings.GoalLimit then
2028 begin
2029 g_Game_NextLevel();
2030 Exit;
2031 end;
2032 end;
2034 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
2035 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
2036 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
2037 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
2038 begin
2039 ProcessPlayerControls(gPlayer1, 0, P1MoveButton);
2040 ProcessPlayerControls(gPlayer2, 1, P2MoveButton);
2041 end // if not console
2042 else
2043 begin
2044 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
2045 end;
2046 // process weapon switch queue
2047 end; // if server
2049 // Íàáëþäàòåëü
2050 if (gPlayer1 = nil) and (gPlayer2 = nil) and
2051 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
2052 begin
2053 if not gSpectKeyPress then
2054 begin
2055 if gPlayerAction[0, ACTION_JUMP] and (not gSpectAuto) then
2056 begin
2057 // switch spect mode
2058 case gSpectMode of
2059 SPECT_NONE: ; // not spectator
2060 SPECT_STATS,
2061 SPECT_MAPVIEW: Inc(gSpectMode);
2062 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
2063 end;
2064 gSpectKeyPress := True;
2065 end;
2066 if (gSpectMode = SPECT_MAPVIEW)
2067 and (not gSpectAuto) then
2068 begin
2069 if gPlayerAction[0, ACTION_MOVELEFT] then
2070 gSpectX := Max(gSpectX - gSpectStep, 0);
2071 if gPlayerAction[0, ACTION_MOVERIGHT] then
2072 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
2073 if gPlayerAction[0, ACTION_LOOKUP] then
2074 gSpectY := Max(gSpectY - gSpectStep, 0);
2075 if gPlayerAction[0, ACTION_LOOKDOWN] then
2076 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
2077 if gWeaponAction[0, WP_PREV] then
2078 begin
2079 // decrease step
2080 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
2081 gWeaponAction[0, WP_PREV] := False;
2082 end;
2083 if gWeaponAction[0, WP_NEXT] then
2084 begin
2085 // increase step
2086 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
2087 gWeaponAction[0, WP_NEXT] := False;
2088 end;
2089 end;
2090 if (gSpectMode = SPECT_PLAYERS)
2091 and (not gSpectAuto) then
2092 begin
2093 if gPlayerAction[0, ACTION_LOOKUP] then
2094 begin
2095 // add second view
2096 gSpectViewTwo := True;
2097 gSpectKeyPress := True;
2098 end;
2099 if gPlayerAction[0, ACTION_LOOKDOWN] then
2100 begin
2101 // remove second view
2102 gSpectViewTwo := False;
2103 gSpectKeyPress := True;
2104 end;
2105 if gPlayerAction[0, ACTION_MOVELEFT] then
2106 begin
2107 // prev player (view 1)
2108 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
2109 gSpectKeyPress := True;
2110 end;
2111 if gPlayerAction[0, ACTION_MOVERIGHT] then
2112 begin
2113 // next player (view 1)
2114 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
2115 gSpectKeyPress := True;
2116 end;
2117 if gWeaponAction[0, WP_PREV] then
2118 begin
2119 // prev player (view 2)
2120 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
2121 gWeaponAction[0, WP_PREV] := False;
2122 end;
2123 if gWeaponAction[0, WP_NEXT] then
2124 begin
2125 // next player (view 2)
2126 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
2127 gWeaponAction[0, WP_NEXT] := False;
2128 end;
2129 end;
2130 if gPlayerAction[0, ACTION_ATTACK] then
2131 begin
2132 if (gSpectMode = SPECT_STATS) and (not gSpectAuto) then
2133 begin
2134 gSpectAuto := True;
2135 gSpectAutoNext := 0;
2136 gSpectViewTwo := False;
2137 gSpectKeyPress := True;
2138 end
2139 else
2140 if gSpectAuto then
2141 begin
2142 gSpectMode := SPECT_STATS;
2143 gSpectAuto := False;
2144 gSpectKeyPress := True;
2145 end;
2146 end;
2147 end
2148 else
2149 if (not gPlayerAction[0, ACTION_JUMP]) and
2150 (not gPlayerAction[0, ACTION_ATTACK]) and
2151 (not gPlayerAction[0, ACTION_MOVELEFT]) and
2152 (not gPlayerAction[0, ACTION_MOVERIGHT]) and
2153 (not gPlayerAction[0, ACTION_LOOKUP]) and
2154 (not gPlayerAction[0, ACTION_LOOKDOWN]) then
2155 gSpectKeyPress := False;
2157 if gSpectAuto then
2158 begin
2159 if gSpectMode = SPECT_MAPVIEW then
2160 begin
2161 i := Min(Max(gSpectX + gSpectAutoStepX, 0), gMapInfo.Width - gScreenWidth);
2162 if i = gSpectX then
2163 gSpectAutoNext := gTime
2164 else
2165 gSpectX := i;
2166 i := Min(Max(gSpectY + gSpectAutoStepY, 0), gMapInfo.Height - gScreenHeight);
2167 if i = gSpectY then
2168 gSpectAutoNext := gTime
2169 else
2170 gSpectY := i;
2171 end;
2172 if gSpectAutoNext <= gTime then
2173 begin
2174 if gSpectAutoNext > 0 then
2175 begin
2176 gSpectMode := GetRandomSpectMode(gSpectMode);
2177 case gSpectMode of
2178 SPECT_MAPVIEW:
2179 begin
2180 gSpectX := Random(gMapInfo.Width - gScreenWidth);
2181 gSpectY := Random(gMapInfo.Height - gScreenHeight);
2182 gSpectAutoStepX := Random(9) - 4;
2183 gSpectAutoStepY := Random(9) - 4;
2184 if ((gSpectX < 800) and (gSpectAutoStepX < 0)) or
2185 ((gSpectX > gMapInfo.Width - gScreenWidth - 800) and (gSpectAutoStepX > 0)) then
2186 gSpectAutoStepX := gSpectAutoStepX * -1;
2187 if ((gSpectY < 800) and (gSpectAutoStepY < 0)) or
2188 ((gSpectY > gMapInfo.Height - gScreenHeight - 800) and (gSpectAutoStepY > 0)) then
2189 gSpectAutoStepY := gSpectAutoStepY * -1;
2190 end;
2191 SPECT_PLAYERS:
2192 begin
2193 gSpectPID1 := GetActivePlayerID_Random(gSpectPID1);
2194 end;
2195 end;
2196 end;
2197 case gSpectMode of
2198 SPECT_STATS: gSpectAutoNext := gTime + (Random(3) + 5) * 1000;
2199 SPECT_MAPVIEW: gSpectAutoNext := gTime + (Random(4) + 7) * 1000;
2200 SPECT_PLAYERS: gSpectAutoNext := gTime + (Random(7) + 8) * 1000;
2201 end;
2202 end;
2203 end;
2204 end;
2206 // Îáíîâëÿåì âñå îñòàëüíîå:
2207 g_Map_Update();
2208 g_Items_Update();
2209 g_Triggers_Update();
2210 g_Weapon_Update();
2211 g_Monsters_Update();
2212 g_GFX_Update();
2213 g_Player_UpdateAll();
2214 g_Player_UpdatePhysicalObjects();
2216 // server: send newly spawned monsters unconditionally
2217 if (gGameSettings.GameType = GT_SERVER) then
2218 begin
2219 if (Length(gMonstersSpawned) > 0) then
2220 begin
2221 for I := 0 to High(gMonstersSpawned) do MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
2222 SetLength(gMonstersSpawned, 0);
2223 end;
2224 end;
2226 if (gSoundTriggerTime > 8) then
2227 begin
2228 g_Game_UpdateTriggerSounds();
2229 gSoundTriggerTime := 0;
2230 end
2231 else
2232 begin
2233 Inc(gSoundTriggerTime);
2234 end;
2236 if (NetMode = NET_SERVER) then
2237 begin
2238 Inc(NetTimeToUpdate);
2239 Inc(NetTimeToReliable);
2241 // send monster updates
2242 if (NetTimeToReliable >= NetRelupdRate) or (NetTimeToUpdate >= NetUpdateRate) then
2243 begin
2244 // send all monsters (periodic sync)
2245 reliableUpdate := (NetTimeToReliable >= NetRelupdRate);
2247 for I := 0 to High(gPlayers) do
2248 begin
2249 if (gPlayers[I] <> nil) then MH_SEND_PlayerPos(reliableUpdate, gPlayers[I].UID);
2250 end;
2252 g_Mons_ForEach(sendMonsPos);
2254 // update flags that aren't stationary
2255 if gGameSettings.GameMode = GM_CTF then
2256 for I := FLAG_RED to FLAG_BLUE do
2257 if gFlags[I].NeedSend then
2258 begin
2259 gFlags[I].NeedSend := False;
2260 MH_SEND_FlagPos(I);
2261 end;
2263 // update items that aren't stationary
2264 g_Items_ForEachAlive(sendItemPos);
2266 if reliableUpdate then
2267 begin
2268 NetTimeToReliable := 0;
2269 NetTimeToUpdate := NetUpdateRate;
2270 end
2271 else
2272 begin
2273 NetTimeToUpdate := 0;
2274 end;
2275 end
2276 else
2277 begin
2278 // send only mosters with some unexpected changes
2279 g_Mons_ForEach(sendMonsPosUnexpected);
2280 end;
2282 // send unexpected platform changes
2283 g_Map_NetSendInterestingPanels();
2285 g_Net_Slist_ServerUpdate();
2287 if NetUseMaster then
2288 begin
2289 if (gTime >= NetTimeToMaster) or g_Net_Slist_IsConnectionInProgress then
2290 begin
2291 if (not g_Net_Slist_IsConnectionActive) then g_Net_Slist_Connect(false); // non-blocking connection to the master
2292 g_Net_Slist_Update;
2293 NetTimeToMaster := gTime + NetMasterRate;
2294 end;
2295 end;
2297 end
2298 else if (NetMode = NET_CLIENT) then
2299 begin
2300 MC_SEND_PlayerPos();
2301 end;
2302 end; // if gameOn ...
2304 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
2305 if g_ActiveWindow <> nil then
2306 begin
2307 w := e_GetFirstKeyPressed();
2309 if (w <> IK_INVALID) then
2310 begin
2311 Msg.Msg := MESSAGE_DIKEY;
2312 Msg.wParam := w;
2313 g_ActiveWindow.OnMessage(Msg);
2314 end;
2316 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
2317 if g_ActiveWindow <> nil then
2318 g_ActiveWindow.Update();
2320 // Íóæíî ñìåíèòü ðàçðåøåíèå:
2321 if gResolutionChange then
2322 begin
2323 e_WriteLog('Changing resolution', TMsgType.Notify);
2324 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
2325 gResolutionChange := False;
2326 g_ActiveWindow := nil;
2327 end;
2329 // Íóæíî ñìåíèòü ÿçûê:
2330 if gLanguageChange then
2331 begin
2332 //e_WriteLog('Read language file', MSG_NOTIFY);
2333 //g_Language_Load(DataDir + gLanguage + '.txt');
2334 g_Language_Set(gLanguage);
2335 {$IFNDEF HEADLESS}
2336 g_Menu_Reset();
2337 {$ENDIF}
2338 gLanguageChange := False;
2339 end;
2340 end;
2342 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
2343 if e_KeyPressed(IK_F10) and
2344 gGameOn and
2345 (not gConsoleShow) and
2346 (g_ActiveWindow = nil) then
2347 begin
2348 KeyPress(IK_F10);
2349 end;
2351 Time := sys_GetTicks() {div 1000};
2353 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
2354 if gDelayedEvents <> nil then
2355 for a := 0 to High(gDelayedEvents) do
2356 if gDelayedEvents[a].Pending and
2358 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
2359 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
2360 ) then
2361 begin
2362 case gDelayedEvents[a].DEType of
2363 DE_GLOBEVENT:
2364 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
2365 DE_BFGHIT:
2366 if gGameOn then
2367 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
2368 DE_KILLCOMBO:
2369 if gGameOn then
2370 begin
2371 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
2372 if g_Game_IsNet and g_Game_IsServer then
2373 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
2374 end;
2375 DE_BODYKILL:
2376 if gGameOn then
2377 g_Game_Announce_BodyKill(gDelayedEvents[a].DENum);
2378 end;
2379 gDelayedEvents[a].Pending := False;
2380 end;
2382 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2383 UPSCounter := UPSCounter + 1;
2384 if Time - UPSTime >= 1000 then
2385 begin
2386 UPS := UPSCounter;
2387 UPSCounter := 0;
2388 UPSTime := Time;
2389 end;
2391 if gGameOn then
2392 begin
2393 g_Weapon_AddDynLights();
2394 g_Items_AddDynLights();
2395 end;
2396 end;
2398 procedure g_Game_LoadChatSounds(Resource: string);
2399 var
2400 WAD: TWADFile;
2401 FileName, Snd: string;
2402 p: Pointer;
2403 len, cnt, tags, i, j: Integer;
2404 cfg: TConfig;
2405 begin
2406 FileName := g_ExtractWadName(Resource);
2408 WAD := TWADFile.Create();
2409 WAD.ReadFile(FileName);
2411 if not WAD.GetResource(g_ExtractFilePathName(Resource), p, len) then
2412 begin
2413 gChatSounds := nil;
2414 WAD.Free();
2415 Exit;
2416 end;
2418 cfg := TConfig.CreateMem(p, len);
2419 cnt := cfg.ReadInt('ChatSounds', 'Count', 0);
2421 SetLength(gChatSounds, cnt);
2422 for i := 0 to Length(gChatSounds) - 1 do
2423 begin
2424 gChatSounds[i].Sound := nil;
2425 Snd := Trim(cfg.ReadStr(IntToStr(i), 'Sound', ''));
2426 tags := cfg.ReadInt(IntToStr(i), 'Tags', 0);
2427 if (Snd = '') or (Tags <= 0) then
2428 continue;
2429 g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i), GameWAD+':'+Snd);
2430 gChatSounds[i].Sound := TPlayableSound.Create();
2431 gChatSounds[i].Sound.SetByName('SOUND_CHAT_MACRO' + IntToStr(i));
2432 SetLength(gChatSounds[i].Tags, tags);
2433 for j := 0 to tags - 1 do
2434 gChatSounds[i].Tags[j] := toLowerCase1251(cfg.ReadStr(IntToStr(i), 'Tag' + IntToStr(j), ''));
2435 gChatSounds[i].FullWord := cfg.ReadBool(IntToStr(i), 'FullWord', False);
2436 end;
2438 cfg.Free();
2439 WAD.Free();
2440 end;
2442 procedure g_Game_FreeChatSounds();
2443 var
2444 i: Integer;
2445 begin
2446 for i := 0 to Length(gChatSounds) - 1 do
2447 begin
2448 gChatSounds[i].Sound.Free();
2449 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i));
2450 end;
2451 SetLength(gChatSounds, 0);
2452 gChatSounds := nil;
2453 end;
2455 procedure g_Game_LoadData();
2456 var
2457 wl, hl: Integer;
2458 wr, hr: Integer;
2459 wb, hb: Integer;
2460 wm, hm: Integer;
2461 begin
2462 if DataLoaded then Exit;
2464 e_WriteLog('Loading game data...', TMsgType.Notify);
2466 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2467 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2468 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2469 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2470 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2471 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2472 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
2473 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
2474 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
2475 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
2476 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
2477 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
2478 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2479 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2480 g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD+':TEXTURES\PLRIND');
2482 hasPBarGfx := true;
2483 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false;
2484 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false;
2485 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false;
2486 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false;
2488 if hasPBarGfx then
2489 begin
2490 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2491 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2492 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2493 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2494 if (wl > 0) and (hl > 0) and (wr > 0) and (hr = hl) and (wb > 0) and (hb = hl) and (wm > 0) and (hm > 0) and (hm <= hl) then
2495 begin
2496 // yay!
2497 end
2498 else
2499 begin
2500 hasPBarGfx := false;
2501 end;
2502 end;
2504 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2505 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD+':WEAPONS\PUNCH', 64, 64, 4, False);
2506 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD+':WEAPONS\PUNCH_UP', 64, 64, 4, False);
2507 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD+':WEAPONS\PUNCH_DN', 64, 64, 4, False);
2508 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD+':WEAPONS\PUNCHB', 64, 64, 4, False);
2509 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD+':WEAPONS\PUNCHB_UP', 64, 64, 4, False);
2510 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD+':WEAPONS\PUNCHB_DN', 64, 64, 4, False);
2511 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2512 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2513 g_Sound_CreateWADEx('SOUND_GAME_SECRET', GameWAD+':SOUNDS\SECRET');
2514 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2515 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2516 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2517 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2518 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2519 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2520 g_Sound_CreateWADEx('SOUND_GAME_BURNING', GameWAD+':SOUNDS\BURNING');
2521 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2522 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2523 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2524 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2525 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2526 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2527 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2528 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2529 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2530 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2531 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2532 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD+':SOUNDS\MUHAHA1');
2533 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD+':SOUNDS\MUHAHA2');
2534 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD+':SOUNDS\MUHAHA3');
2535 g_Sound_CreateWADEx('SOUND_CTF_GET1', GameWAD+':SOUNDS\GETFLAG1');
2536 g_Sound_CreateWADEx('SOUND_CTF_GET2', GameWAD+':SOUNDS\GETFLAG2');
2537 g_Sound_CreateWADEx('SOUND_CTF_LOST1', GameWAD+':SOUNDS\LOSTFLG1');
2538 g_Sound_CreateWADEx('SOUND_CTF_LOST2', GameWAD+':SOUNDS\LOSTFLG2');
2539 g_Sound_CreateWADEx('SOUND_CTF_RETURN1', GameWAD+':SOUNDS\RETFLAG1');
2540 g_Sound_CreateWADEx('SOUND_CTF_RETURN2', GameWAD+':SOUNDS\RETFLAG2');
2541 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE1', GameWAD+':SOUNDS\CAPFLAG1');
2542 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE2', GameWAD+':SOUNDS\CAPFLAG2');
2544 goodsnd[0] := TPlayableSound.Create();
2545 goodsnd[1] := TPlayableSound.Create();
2546 goodsnd[2] := TPlayableSound.Create();
2547 goodsnd[3] := TPlayableSound.Create();
2549 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2550 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2551 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2552 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2554 killsnd[0] := TPlayableSound.Create();
2555 killsnd[1] := TPlayableSound.Create();
2556 killsnd[2] := TPlayableSound.Create();
2557 killsnd[3] := TPlayableSound.Create();
2559 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2560 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2561 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2562 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2564 hahasnd[0] := TPlayableSound.Create();
2565 hahasnd[1] := TPlayableSound.Create();
2566 hahasnd[2] := TPlayableSound.Create();
2568 hahasnd[0].SetByName('SOUND_ANNOUNCER_MUHAHA1');
2569 hahasnd[1].SetByName('SOUND_ANNOUNCER_MUHAHA2');
2570 hahasnd[2].SetByName('SOUND_ANNOUNCER_MUHAHA3');
2572 sound_get_flag[0] := TPlayableSound.Create();
2573 sound_get_flag[1] := TPlayableSound.Create();
2574 sound_lost_flag[0] := TPlayableSound.Create();
2575 sound_lost_flag[1] := TPlayableSound.Create();
2576 sound_ret_flag[0] := TPlayableSound.Create();
2577 sound_ret_flag[1] := TPlayableSound.Create();
2578 sound_cap_flag[0] := TPlayableSound.Create();
2579 sound_cap_flag[1] := TPlayableSound.Create();
2581 sound_get_flag[0].SetByName('SOUND_CTF_GET1');
2582 sound_get_flag[1].SetByName('SOUND_CTF_GET2');
2583 sound_lost_flag[0].SetByName('SOUND_CTF_LOST1');
2584 sound_lost_flag[1].SetByName('SOUND_CTF_LOST2');
2585 sound_ret_flag[0].SetByName('SOUND_CTF_RETURN1');
2586 sound_ret_flag[1].SetByName('SOUND_CTF_RETURN2');
2587 sound_cap_flag[0].SetByName('SOUND_CTF_CAPTURE1');
2588 sound_cap_flag[1].SetByName('SOUND_CTF_CAPTURE2');
2590 g_Game_LoadChatSounds(GameWAD+':CHATSND\SNDCFG');
2592 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2593 g_Items_LoadData();
2595 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2596 g_Weapon_LoadData();
2598 g_Monsters_LoadData();
2600 DataLoaded := True;
2601 end;
2603 procedure g_Game_FreeData();
2604 begin
2605 if not DataLoaded then Exit;
2607 g_Items_FreeData();
2608 g_Weapon_FreeData();
2609 g_Monsters_FreeData();
2611 e_WriteLog('Releasing game data...', TMsgType.Notify);
2613 g_Texture_Delete('NOTEXTURE');
2614 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2615 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2616 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2617 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2618 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2619 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2620 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2621 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2622 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2623 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2624 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2625 g_Frames_DeleteByName('FRAMES_TELEPORT');
2626 g_Frames_DeleteByName('FRAMES_PUNCH');
2627 g_Frames_DeleteByName('FRAMES_PUNCH_UP');
2628 g_Frames_DeleteByName('FRAMES_PUNCH_DN');
2629 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK');
2630 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP');
2631 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
2632 g_Sound_Delete('SOUND_GAME_TELEPORT');
2633 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2634 g_Sound_Delete('SOUND_GAME_SECRET');
2635 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2636 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2637 g_Sound_Delete('SOUND_GAME_BULK1');
2638 g_Sound_Delete('SOUND_GAME_BULK2');
2639 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2640 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2641 g_Sound_Delete('SOUND_GAME_BURNING');
2642 g_Sound_Delete('SOUND_GAME_SWITCH1');
2643 g_Sound_Delete('SOUND_GAME_SWITCH0');
2645 goodsnd[0].Free();
2646 goodsnd[1].Free();
2647 goodsnd[2].Free();
2648 goodsnd[3].Free();
2650 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2651 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2652 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2653 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2655 killsnd[0].Free();
2656 killsnd[1].Free();
2657 killsnd[2].Free();
2658 killsnd[3].Free();
2660 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2661 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2662 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2663 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2665 hahasnd[0].Free();
2666 hahasnd[1].Free();
2667 hahasnd[2].Free();
2669 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1');
2670 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2');
2671 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3');
2673 sound_get_flag[0].Free();
2674 sound_get_flag[1].Free();
2675 sound_lost_flag[0].Free();
2676 sound_lost_flag[1].Free();
2677 sound_ret_flag[0].Free();
2678 sound_ret_flag[1].Free();
2679 sound_cap_flag[0].Free();
2680 sound_cap_flag[1].Free();
2682 g_Sound_Delete('SOUND_CTF_GET1');
2683 g_Sound_Delete('SOUND_CTF_GET2');
2684 g_Sound_Delete('SOUND_CTF_LOST1');
2685 g_Sound_Delete('SOUND_CTF_LOST2');
2686 g_Sound_Delete('SOUND_CTF_RETURN1');
2687 g_Sound_Delete('SOUND_CTF_RETURN2');
2688 g_Sound_Delete('SOUND_CTF_CAPTURE1');
2689 g_Sound_Delete('SOUND_CTF_CAPTURE2');
2691 g_Game_FreeChatSounds();
2693 DataLoaded := False;
2694 end;
2696 procedure DrawCustomStat();
2697 var
2698 pc, x, y, w, _y,
2699 w1, w2, w3,
2700 t, p, m: Integer;
2701 ww1, hh1: Word;
2702 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2703 s1, s2, topstr: String;
2704 begin
2705 e_TextureFontGetSize(gStdFont, ww2, hh2);
2707 sys_HandleInput;
2709 if g_Console_Action(ACTION_SCORES) then
2710 begin
2711 if not gStatsPressed then
2712 begin
2713 gStatsOff := not gStatsOff;
2714 gStatsPressed := True;
2715 end;
2716 end
2717 else
2718 gStatsPressed := False;
2720 if gStatsOff then
2721 begin
2722 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2723 w := (Length(s1) * ww2) div 2;
2724 x := gScreenWidth div 2 - w;
2725 y := 8;
2726 e_TextureFontPrint(x, y, s1, gStdFont);
2727 Exit;
2728 end;
2730 if (gGameSettings.GameMode = GM_COOP) then
2731 begin
2732 if gMissionFailed then
2733 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2734 else
2735 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2736 end
2737 else
2738 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2740 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2741 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2743 if g_Game_IsNet then
2744 begin
2745 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2746 if not gChatShow then
2747 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2748 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2749 end;
2751 if g_Game_IsClient then
2752 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2753 else
2754 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2755 if not gChatShow then
2756 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2757 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2759 x := 32;
2760 y := 16+hh1+16;
2762 w := gScreenWidth-x*2;
2764 w2 := (w-16) div 6;
2765 w3 := w2;
2766 w1 := w-16-w2-w3;
2768 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2769 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2771 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2773 case CustomStat.GameMode of
2774 GM_DM:
2775 begin
2776 if gGameSettings.MaxLives = 0 then
2777 s1 := _lc[I_GAME_DM]
2778 else
2779 s1 := _lc[I_GAME_LMS];
2780 end;
2781 GM_TDM:
2782 begin
2783 if gGameSettings.MaxLives = 0 then
2784 s1 := _lc[I_GAME_TDM]
2785 else
2786 s1 := _lc[I_GAME_TLMS];
2787 end;
2788 GM_CTF: s1 := _lc[I_GAME_CTF];
2789 GM_COOP:
2790 begin
2791 if gGameSettings.MaxLives = 0 then
2792 s1 := _lc[I_GAME_COOP]
2793 else
2794 s1 := _lc[I_GAME_SURV];
2795 end;
2796 else s1 := '';
2797 end;
2799 _y := y+16;
2800 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2801 _y := _y+8;
2803 _y := _y+16;
2804 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2805 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2807 _y := _y+16;
2808 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2809 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2810 (CustomStat.GameTime div 1000 div 60) mod 60,
2811 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2813 pc := Length(CustomStat.PlayerStat);
2814 if pc = 0 then Exit;
2816 if CustomStat.GameMode = GM_COOP then
2817 begin
2818 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2819 _y := _y+32;
2820 s2 := _lc[I_GAME_MONSTERS];
2821 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2822 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2823 _y := _y+16;
2824 s2 := _lc[I_GAME_SECRETS];
2825 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2826 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2827 if gLastMap then
2828 begin
2829 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2830 _y := _y-16;
2831 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2832 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2833 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2834 _y := _y+16;
2835 s2 := _lc[I_GAME_SECRETS_TOTAL];
2836 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2837 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2838 end;
2839 end;
2841 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2842 begin
2843 _y := _y+16+16;
2845 with CustomStat do
2846 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2847 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2848 else s1 := _lc[I_GAME_WIN_DRAW];
2850 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2851 _y := _y+40;
2853 for t := TEAM_RED to TEAM_BLUE do
2854 begin
2855 if t = TEAM_RED then
2856 begin
2857 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2858 gStdFont, 255, 0, 0, 1);
2859 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2860 gStdFont, 255, 0, 0, 1);
2861 r := 255;
2862 g := 0;
2863 b := 0;
2864 end
2865 else
2866 begin
2867 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2868 gStdFont, 0, 0, 255, 1);
2869 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2870 gStdFont, 0, 0, 255, 1);
2871 r := 0;
2872 g := 0;
2873 b := 255;
2874 end;
2876 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2877 _y := _y+24;
2879 for p := 0 to High(CustomStat.PlayerStat) do
2880 if CustomStat.PlayerStat[p].Team = t then
2881 with CustomStat.PlayerStat[p] do
2882 begin
2883 if Spectator then
2884 begin
2885 rr := r div 2;
2886 gg := g div 2;
2887 bb := b div 2;
2888 end
2889 else
2890 begin
2891 rr := r;
2892 gg := g;
2893 bb := b;
2894 end;
2895 if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then
2896 e_TextureFontPrintEx(x+16, _y, Name + ' *', gStdFont, rr, gg, bb, 1)
2897 else
2898 e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
2899 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2900 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2901 _y := _y+24;
2902 end;
2904 _y := _y+16+16;
2905 end;
2906 end
2907 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2908 begin
2909 _y := _y+40;
2910 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2911 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2912 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2914 _y := _y+24;
2915 for p := 0 to High(CustomStat.PlayerStat) do
2916 with CustomStat.PlayerStat[p] do
2917 begin
2918 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2920 if Spectator then
2921 r := 127
2922 else
2923 r := 255;
2925 if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then
2926 e_TextureFontPrintEx(x+8+16+8, _y+4, Name + ' *', gStdFont, r, r, r, 1, True)
2927 else
2928 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2929 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2930 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2931 _y := _y+24;
2932 end;
2933 end;
2935 // HACK: take stats screenshot immediately after the first frame of the stats showing
2936 if gScreenshotStats and (not StatShotDone) and (Length(CustomStat.PlayerStat) > 1) then
2937 begin
2938 g_TakeScreenShot('stats/' + StatFilename);
2939 StatShotDone := True;
2940 end;
2941 end;
2943 procedure DrawSingleStat();
2944 var
2945 tm, key_x, val_x, y: Integer;
2946 w1, w2, h: Word;
2947 s1, s2: String;
2949 procedure player_stat(n: Integer);
2950 var
2951 kpm: Real;
2953 begin
2954 // "Kills: # / #":
2955 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2956 s2 := Format(' %d', [gTotalMonsters]);
2958 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2959 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2960 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2961 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2962 s1 := s1 + '/';
2963 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2964 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2966 // "Kills-per-minute: ##.#":
2967 s1 := _lc[I_MENU_INTER_KPM];
2968 if tm > 0 then
2969 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2970 else
2971 kpm := SingleStat.PlayerStat[n].Kills;
2972 s2 := Format(' %.1f', [kpm]);
2974 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2975 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2977 // "Secrets found: # / #":
2978 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2979 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2981 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2982 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2983 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2984 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2985 s1 := s1 + '/';
2986 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2987 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2988 end;
2990 begin
2991 // "Level Complete":
2992 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2993 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2995 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2996 s1 := _lc[I_MENU_INTER_KPM];
2997 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2998 Inc(w1, 16);
2999 s1 := ' 9999.9';
3000 e_CharFont_GetSize(gMenuFont, s1, w2, h);
3002 key_x := (gScreenWidth-w1-w2) div 2;
3003 val_x := key_x + w1;
3005 // "Time: #:##:##":
3006 tm := SingleStat.GameTime div 1000;
3007 s1 := _lc[I_MENU_INTER_TIME];
3008 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
3010 e_CharFont_Print(gMenuFont, key_x, 80, s1);
3011 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
3013 if SingleStat.TwoPlayers then
3014 begin
3015 // "Player 1":
3016 s1 := _lc[I_MENU_PLAYER_1];
3017 e_CharFont_GetSize(gMenuFont, s1, w1, h);
3018 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
3020 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3021 y := 176;
3022 player_stat(0);
3024 // "Player 2":
3025 s1 := _lc[I_MENU_PLAYER_2];
3026 e_CharFont_GetSize(gMenuFont, s1, w1, h);
3027 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
3029 // Ñòàòèñòèêà âòîðîãî èãðîêà:
3030 y := 336;
3031 player_stat(1);
3032 end
3033 else
3034 begin
3035 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3036 y := 128;
3037 player_stat(0);
3038 end;
3039 end;
3041 procedure DrawLoadingStat();
3042 procedure drawRect (x, y, w, h: Integer);
3043 begin
3044 if (w < 1) or (h < 1) then exit;
3045 glBegin(GL_QUADS);
3046 glVertex2f(x+0.375, y+0.375);
3047 glVertex2f(x+w+0.375, y+0.375);
3048 glVertex2f(x+w+0.375, y+h+0.375);
3049 glVertex2f(x+0.375, y+h+0.375);
3050 glEnd();
3051 end;
3053 function drawPBar (cur, total: Integer; washere: Boolean): Boolean;
3054 var
3055 rectW, rectH: Integer;
3056 x0, y0: Integer;
3057 wdt: Integer;
3058 wl, hl: Integer;
3059 wr, hr: Integer;
3060 wb, hb: Integer;
3061 wm, hm: Integer;
3062 idl, idr, idb, idm: LongWord;
3063 f, my: Integer;
3064 begin
3065 result := false;
3066 if (total < 1) then exit;
3067 if (cur < 1) then exit; // don't blink
3068 if (not washere) and (cur >= total) then exit; // don't blink
3069 //if (cur < 0) then cur := 0;
3070 //if (cur > total) then cur := total;
3071 result := true;
3073 if (hasPBarGfx) then
3074 begin
3075 g_Texture_Get('UI_GFX_PBAR_LEFT', idl);
3076 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
3077 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr);
3078 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
3079 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb);
3080 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
3081 g_Texture_Get('UI_GFX_PBAR_MARKER', idm);
3082 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
3084 //rectW := gScreenWidth-360;
3085 rectW := trunc(624.0*gScreenWidth/1024.0);
3086 rectH := hl;
3088 x0 := (gScreenWidth-rectW) div 2;
3089 y0 := gScreenHeight-rectH-64;
3090 if (y0 < 2) then y0 := 2;
3092 glEnable(GL_SCISSOR_TEST);
3094 // left and right
3095 glScissor(x0, gScreenHeight-y0-rectH, rectW, rectH);
3096 e_DrawSize(idl, x0, y0, 0, true, false, wl, hl);
3097 e_DrawSize(idr, x0+rectW-wr, y0, 0, true, false, wr, hr);
3099 // body
3100 glScissor(x0+wl, gScreenHeight-y0-rectH, rectW-wl-wr, rectH);
3101 f := x0+wl;
3102 while (f < x0+rectW) do
3103 begin
3104 e_DrawSize(idb, f, y0, 0, true, false, wb, hb);
3105 f += wb;
3106 end;
3108 // filled part
3109 wdt := (rectW-wl-wr)*cur div total;
3110 if (wdt > rectW-wl-wr) then wdt := rectW-wr-wr;
3111 if (wdt > 0) then
3112 begin
3113 my := y0; // don't be so smart, ketmar: +(rectH-wm) div 2;
3114 glScissor(x0+wl, gScreenHeight-my-rectH, wdt, hm);
3115 f := x0+wl;
3116 while (wdt > 0) do
3117 begin
3118 e_DrawSize(idm, f, y0, 0, true, false, wm, hm);
3119 f += wm;
3120 wdt -= wm;
3121 end;
3122 end;
3124 glScissor(0, 0, gScreenWidth, gScreenHeight);
3125 end
3126 else
3127 begin
3128 rectW := gScreenWidth-64;
3129 rectH := 16;
3131 x0 := (gScreenWidth-rectW) div 2;
3132 y0 := gScreenHeight-rectH-64;
3133 if (y0 < 2) then y0 := 2;
3135 glDisable(GL_BLEND);
3136 glDisable(GL_TEXTURE_2D);
3138 //glClearColor(0, 0, 0, 0);
3139 //glClear(GL_COLOR_BUFFER_BIT);
3141 glColor4ub(127, 127, 127, 255);
3142 drawRect(x0-2, y0-2, rectW+4, rectH+4);
3144 glColor4ub(0, 0, 0, 255);
3145 drawRect(x0-1, y0-1, rectW+2, rectH+2);
3147 glColor4ub(127, 127, 127, 255);
3148 wdt := rectW*cur div total;
3149 if (wdt > rectW) then wdt := rectW;
3150 drawRect(x0, y0, wdt, rectH);
3151 end;
3152 end;
3154 var
3155 ww, hh: Word;
3156 xx, yy, i: Integer;
3157 s: String;
3158 begin
3159 if (Length(LoadingStat.Msgs) = 0) then exit;
3161 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
3162 yy := (gScreenHeight div 3);
3163 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
3164 xx := (gScreenWidth div 3);
3166 with LoadingStat do
3167 begin
3168 for i := 0 to NextMsg-1 do
3169 begin
3170 if (i = (NextMsg-1)) and (MaxValue > 0) then
3171 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
3172 else
3173 s := Msgs[i];
3175 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
3176 yy := yy + LOADING_INTERLINE;
3177 PBarWasHere := drawPBar(CurValue, MaxValue, PBarWasHere);
3178 end;
3179 end;
3180 end;
3182 procedure DrawMenuBackground(tex: AnsiString);
3183 var
3184 w, h: Word;
3185 ID: DWord;
3187 begin
3188 if g_Texture_Get(tex, ID) then
3189 begin
3190 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3191 e_GetTextureSize(ID, @w, @h);
3192 if w = h then
3193 w := round(w * 1.333 * (gScreenHeight / h))
3194 else
3195 w := trunc(w * (gScreenHeight / h));
3196 e_DrawSize(ID, (gScreenWidth - w) div 2, 0, 0, False, False, w, gScreenHeight);
3197 end
3198 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3199 end;
3201 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
3202 var
3203 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
3205 function monDraw (mon: TMonster): Boolean;
3206 begin
3207 result := false; // don't stop
3208 with mon do
3209 begin
3210 if alive then
3211 begin
3212 // Ëåâûé âåðõíèé óãîë
3213 aX := Obj.X div ScaleSz + 1;
3214 aY := Obj.Y div ScaleSz + 1;
3215 // Ðàçìåðû
3216 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
3217 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
3218 // Ïðàâûé íèæíèé óãîë
3219 aX2 := aX + aX2 - 1;
3220 aY2 := aY + aY2 - 1;
3221 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
3222 end;
3223 end;
3224 end;
3226 begin
3227 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
3228 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
3229 begin
3230 Scale := 1;
3231 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
3232 ScaleSz := 16 div Scale;
3233 // Ðàçìåðû ìèíè-êàðòû:
3234 aX := max(gMapInfo.Width div ScaleSz, 1);
3235 aY := max(gMapInfo.Height div ScaleSz, 1);
3236 // Ðàìêà êàðòû:
3237 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
3239 if gWalls <> nil then
3240 begin
3241 // Ðèñóåì ñòåíû:
3242 for a := 0 to High(gWalls) do
3243 with gWalls[a] do
3244 if PanelType <> 0 then
3245 begin
3246 // Ëåâûé âåðõíèé óãîë:
3247 aX := X div ScaleSz;
3248 aY := Y div ScaleSz;
3249 // Ðàçìåðû:
3250 aX2 := max(Width div ScaleSz, 1);
3251 aY2 := max(Height div ScaleSz, 1);
3252 // Ïðàâûé íèæíèé óãîë:
3253 aX2 := aX + aX2 - 1;
3254 aY2 := aY + aY2 - 1;
3256 case PanelType of
3257 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
3258 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
3259 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
3260 end;
3261 end;
3262 end;
3263 if gSteps <> nil then
3264 begin
3265 // Ðèñóåì ñòóïåíè:
3266 for a := 0 to High(gSteps) do
3267 with gSteps[a] do
3268 if PanelType <> 0 then
3269 begin
3270 // Ëåâûé âåðõíèé óãîë:
3271 aX := X div ScaleSz;
3272 aY := Y div ScaleSz;
3273 // Ðàçìåðû:
3274 aX2 := max(Width div ScaleSz, 1);
3275 aY2 := max(Height div ScaleSz, 1);
3276 // Ïðàâûé íèæíèé óãîë:
3277 aX2 := aX + aX2 - 1;
3278 aY2 := aY + aY2 - 1;
3280 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
3281 end;
3282 end;
3283 if gLifts <> nil then
3284 begin
3285 // Ðèñóåì ëèôòû:
3286 for a := 0 to High(gLifts) do
3287 with gLifts[a] do
3288 if PanelType <> 0 then
3289 begin
3290 // Ëåâûé âåðõíèé óãîë:
3291 aX := X div ScaleSz;
3292 aY := Y div ScaleSz;
3293 // Ðàçìåðû:
3294 aX2 := max(Width div ScaleSz, 1);
3295 aY2 := max(Height div ScaleSz, 1);
3296 // Ïðàâûé íèæíèé óãîë:
3297 aX2 := aX + aX2 - 1;
3298 aY2 := aY + aY2 - 1;
3300 case LiftType of
3301 LIFTTYPE_UP: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
3302 LIFTTYPE_DOWN: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
3303 LIFTTYPE_LEFT: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
3304 LIFTTYPE_RIGHT: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
3305 end;
3306 end;
3307 end;
3308 if gWater <> nil then
3309 begin
3310 // Ðèñóåì âîäó:
3311 for a := 0 to High(gWater) do
3312 with gWater[a] do
3313 if PanelType <> 0 then
3314 begin
3315 // Ëåâûé âåðõíèé óãîë:
3316 aX := X div ScaleSz;
3317 aY := Y div ScaleSz;
3318 // Ðàçìåðû:
3319 aX2 := max(Width div ScaleSz, 1);
3320 aY2 := max(Height div ScaleSz, 1);
3321 // Ïðàâûé íèæíèé óãîë:
3322 aX2 := aX + aX2 - 1;
3323 aY2 := aY + aY2 - 1;
3325 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
3326 end;
3327 end;
3328 if gAcid1 <> nil then
3329 begin
3330 // Ðèñóåì êèñëîòó 1:
3331 for a := 0 to High(gAcid1) do
3332 with gAcid1[a] do
3333 if PanelType <> 0 then
3334 begin
3335 // Ëåâûé âåðõíèé óãîë:
3336 aX := X div ScaleSz;
3337 aY := Y div ScaleSz;
3338 // Ðàçìåðû:
3339 aX2 := max(Width div ScaleSz, 1);
3340 aY2 := max(Height div ScaleSz, 1);
3341 // Ïðàâûé íèæíèé óãîë:
3342 aX2 := aX + aX2 - 1;
3343 aY2 := aY + aY2 - 1;
3345 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
3346 end;
3347 end;
3348 if gAcid2 <> nil then
3349 begin
3350 // Ðèñóåì êèñëîòó 2:
3351 for a := 0 to High(gAcid2) do
3352 with gAcid2[a] do
3353 if PanelType <> 0 then
3354 begin
3355 // Ëåâûé âåðõíèé óãîë:
3356 aX := X div ScaleSz;
3357 aY := Y div ScaleSz;
3358 // Ðàçìåðû:
3359 aX2 := max(Width div ScaleSz, 1);
3360 aY2 := max(Height div ScaleSz, 1);
3361 // Ïðàâûé íèæíèé óãîë:
3362 aX2 := aX + aX2 - 1;
3363 aY2 := aY + aY2 - 1;
3365 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
3366 end;
3367 end;
3368 if gPlayers <> nil then
3369 begin
3370 // Ðèñóåì èãðîêîâ:
3371 for a := 0 to High(gPlayers) do
3372 if gPlayers[a] <> nil then with gPlayers[a] do
3373 if alive then begin
3374 // Ëåâûé âåðõíèé óãîë:
3375 aX := Obj.X div ScaleSz + 1;
3376 aY := Obj.Y div ScaleSz + 1;
3377 // Ðàçìåðû:
3378 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
3379 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
3380 // Ïðàâûé íèæíèé óãîë:
3381 aX2 := aX + aX2 - 1;
3382 aY2 := aY + aY2 - 1;
3384 if gPlayers[a] = p then
3385 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
3386 else
3387 case Team of
3388 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
3389 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
3390 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
3391 end;
3392 end;
3393 end;
3394 // Ðèñóåì ìîíñòðîâ
3395 g_Mons_ForEach(monDraw);
3396 end;
3397 end;
3400 procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor);
3401 begin
3402 if not hasAmbient then exit;
3403 e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3404 end;
3407 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3408 //FIXME: broken for splitscreen mode
3409 procedure renderDynLightsInternal ();
3410 var
3411 //hasAmbient: Boolean;
3412 //ambColor: TDFColor;
3413 lln: Integer;
3414 lx, ly, lrad: Integer;
3415 scxywh: array[0..3] of GLint;
3416 wassc: Boolean;
3417 begin
3418 if e_NoGraphics then exit;
3420 //TODO: lights should be in separate grid, i think
3421 // but on the other side: grid may be slower for dynlights, as their lifetime is short
3422 if (not gwin_k8_enable_light_experiments) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit;
3424 // rendering mode
3425 //ambColor := gCurrentMap['light_ambient'].rgba;
3426 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3428 { // this will multiply incoming color to alpha from framebuffer
3429 glEnable(GL_BLEND);
3430 glBlendFunc(GL_DST_ALPHA, GL_ONE);
3433 (*
3434 * light rendering: (INVALID!)
3435 * glStencilFunc(GL_EQUAL, 0, $ff);
3436 * for each light:
3437 * glClear(GL_STENCIL_BUFFER_BIT);
3438 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3439 * draw shadow volume into stencil buffer
3440 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3441 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
3442 * turn off blending
3443 * draw color-less quad with light alpha (WARNING! don't touch color!)
3444 * glEnable(GL_BLEND);
3445 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
3446 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
3447 *)
3448 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
3449 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
3451 // setup OpenGL parameters
3452 glStencilMask($FFFFFFFF);
3453 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
3454 glEnable(GL_STENCIL_TEST);
3455 glEnable(GL_SCISSOR_TEST);
3456 glClear(GL_STENCIL_BUFFER_BIT);
3457 glStencilFunc(GL_EQUAL, 0, $ff);
3459 for lln := 0 to g_dynLightCount-1 do
3460 begin
3461 lx := g_dynLights[lln].x;
3462 ly := g_dynLights[lln].y;
3463 lrad := g_dynLights[lln].radius;
3464 if (lrad < 3) then continue;
3466 if (lx-sX+lrad < 0) then continue;
3467 if (ly-sY+lrad < 0) then continue;
3468 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
3469 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
3471 // set scissor to optimize drawing
3472 if (g_dbg_scale = 1.0) then
3473 begin
3474 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
3475 end
3476 else
3477 begin
3478 glScissor(0, 0, gScreenWidth, gScreenHeight);
3479 end;
3480 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3481 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
3482 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3483 // draw extruded panels
3484 glDisable(GL_TEXTURE_2D);
3485 glDisable(GL_BLEND);
3486 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
3487 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
3488 // render light texture
3489 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3490 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
3491 // blend it
3492 glEnable(GL_BLEND);
3493 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3494 glEnable(GL_TEXTURE_2D);
3495 // color and opacity
3496 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
3497 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
3498 glBegin(GL_QUADS);
3499 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
3500 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
3501 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
3502 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
3503 glEnd();
3504 end;
3506 // done
3507 glDisable(GL_STENCIL_TEST);
3508 glDisable(GL_BLEND);
3509 glDisable(GL_SCISSOR_TEST);
3510 //glScissor(0, 0, sWidth, sHeight);
3512 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
3513 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
3514 end;
3517 function fixViewportForScale (): Boolean;
3518 var
3519 nx0, ny0, nw, nh: Integer;
3520 begin
3521 result := false;
3522 if (g_dbg_scale <> 1.0) then
3523 begin
3524 result := true;
3525 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
3526 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
3527 nw := round(sWidth/g_dbg_scale);
3528 nh := round(sHeight/g_dbg_scale);
3529 sX := nx0;
3530 sY := ny0;
3531 sWidth := nw;
3532 sHeight := nh;
3533 end;
3534 end;
3537 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3538 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3539 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
3540 type
3541 TDrawCB = procedure ();
3543 var
3544 hasAmbient: Boolean;
3545 ambColor: TDFColor;
3546 doAmbient: Boolean = false;
3548 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
3549 var
3550 tagmask: Integer;
3551 pan: TPanel;
3552 begin
3553 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
3554 if gdbg_map_use_accel_render then
3555 begin
3556 tagmask := panelTypeToTag(panType);
3557 while (gDrawPanelList.count > 0) do
3558 begin
3559 pan := TPanel(gDrawPanelList.front());
3560 if ((pan.tag and tagmask) = 0) then break;
3561 if doDraw then pan.Draw(doAmbient, ambColor);
3562 gDrawPanelList.popFront();
3563 end;
3564 end
3565 else
3566 begin
3567 if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor);
3568 end;
3569 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3570 end;
3572 procedure drawOther (profname: AnsiString; cb: TDrawCB);
3573 begin
3574 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
3575 if assigned(cb) then cb();
3576 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3577 end;
3579 begin
3580 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('total');
3582 // our accelerated renderer will collect all panels to gDrawPanelList
3583 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3584 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('collect');
3585 if gdbg_map_use_accel_render then
3586 begin
3587 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
3588 end;
3589 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3591 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('skyback');
3592 g_Map_DrawBack(backXOfs, backYOfs);
3593 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3595 if setTransMatrix then
3596 begin
3597 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3598 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
3599 glTranslatef(-sX, -sY, 0);
3600 end;
3602 // rendering mode
3603 ambColor := gCurrentMap['light_ambient'].rgba;
3604 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3607 if hasAmbient then
3608 begin
3609 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3610 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3611 glClear(GL_COLOR_BUFFER_BIT);
3612 end;
3614 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3617 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
3618 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
3619 drawOther('items', @g_Items_Draw);
3620 drawOther('weapons', @g_Weapon_Draw);
3621 drawOther('shells', @g_Player_DrawShells);
3622 drawOther('drawall', @g_Player_DrawAll);
3623 drawOther('corpses', @g_Player_DrawCorpses);
3624 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
3625 drawOther('monsters', @g_Monsters_Draw);
3626 drawOther('itemdrop', @g_Items_DrawDrop);
3627 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
3628 drawOther('gfx', @g_GFX_Draw);
3629 drawOther('flags', @g_Map_DrawFlags);
3630 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
3631 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
3632 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
3633 drawOther('dynlights', @renderDynLightsInternal);
3635 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3636 begin
3637 renderAmbientQuad(hasAmbient, ambColor);
3638 end;
3640 doAmbient := true;
3641 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
3644 if g_debug_HealthBar then
3645 begin
3646 g_Monsters_DrawHealth();
3647 g_Player_DrawHealth();
3648 end;
3650 if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering
3651 end;
3654 procedure DrawMapView(x, y, w, h: Integer);
3656 var
3657 bx, by: Integer;
3658 begin
3659 glPushMatrix();
3661 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
3662 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
3664 sX := x;
3665 sY := y;
3666 sWidth := w;
3667 sHeight := h;
3669 fixViewportForScale();
3670 renderMapInternal(-bx, -by, true);
3672 glPopMatrix();
3673 end;
3676 procedure DrawPlayer(p: TPlayer);
3677 var
3678 px, py, a, b, c, d, i, fX, fY: Integer;
3679 camObj: TObj;
3680 //R: TRect;
3681 begin
3682 if (p = nil) or (p.FDummy) then
3683 begin
3684 glPushMatrix();
3685 g_Map_DrawBack(0, 0);
3686 glPopMatrix();
3687 Exit;
3688 end;
3690 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3691 if (profileFrameDraw <> nil) then profileFrameDraw.mainBegin(g_profile_frame_draw);
3693 gPlayerDrawn := p;
3695 glPushMatrix();
3697 camObj := p.getCameraObj();
3698 camObj.lerp(gLerpFactor, fX, fY);
3699 px := fX + PLAYER_RECT_CX;
3700 py := fY + PLAYER_RECT_CY+nlerp(p.SlopeOld, camObj.slopeUpLeft, gLerpFactor);
3702 if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then
3703 begin
3704 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3705 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3707 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3708 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3710 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3711 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3712 begin
3713 // hcenter
3714 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3715 end;
3717 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3718 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3719 begin
3720 // vcenter
3721 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3722 end;
3723 end
3724 else
3725 begin
3726 // scaled, ignore level bounds
3727 a := -px+(gPlayerScreenSize.X div 2);
3728 b := -py+(gPlayerScreenSize.Y div 2);
3729 end;
3731 sX := -a;
3732 sY := -b;
3733 sWidth := gPlayerScreenSize.X;
3734 sHeight := gPlayerScreenSize.Y;
3735 fixViewportForScale();
3737 i := py - (sY + sHeight div 2);
3738 if (p.IncCam > 0) then
3739 begin
3740 // clamp to level bounds
3741 if (sY - p.IncCam < 0) then
3742 p.IncCam := nclamp(sY, 0, 120);
3743 // clamp around player position
3744 if (i > 0) then
3745 p.IncCam := nclamp(p.IncCam, 0, max(0, 120 - i));
3746 end
3747 else if (p.IncCam < 0) then
3748 begin
3749 // clamp to level bounds
3750 if (sY + sHeight - p.IncCam > gMapInfo.Height) then
3751 p.IncCam := nclamp(sY + sHeight - gMapInfo.Height, -120, 0);
3752 // clamp around player position
3753 if (i < 0) then
3754 p.IncCam := nclamp(p.IncCam, min(0, -120 - i), 0);
3755 end;
3757 sY := sY - nlerp(p.IncCamOld, p.IncCam, gLerpFactor);
3759 if (not g_dbg_ignore_bounds) then
3760 begin
3761 if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth;
3762 if (sY+sHeight > gMapInfo.Height) then sY := gMapInfo.Height-sHeight;
3763 if (sX < 0) then sX := 0;
3764 if (sY < 0) then sY := 0;
3765 end;
3767 if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth));
3768 if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight));
3770 //r_smallmap_h: 0: left; 1: center; 2: right
3771 //r_smallmap_v: 0: top; 1: center; 2: bottom
3772 // horiz small map?
3773 if (gMapInfo.Width = sWidth) then
3774 begin
3775 sX := 0;
3776 end
3777 else if (gMapInfo.Width < sWidth) then
3778 begin
3779 case r_smallmap_h of
3780 1: sX := -((sWidth-gMapInfo.Width) div 2); // center
3781 2: sX := -(sWidth-gMapInfo.Width); // right
3782 else sX := 0; // left
3783 end;
3784 end;
3785 // vert small map?
3786 if (gMapInfo.Height = sHeight) then
3787 begin
3788 sY := 0;
3789 end
3790 else if (gMapInfo.Height < sHeight) then
3791 begin
3792 case r_smallmap_v of
3793 1: sY := -((sHeight-gMapInfo.Height) div 2); // center
3794 2: sY := -(sHeight-gMapInfo.Height); // bottom
3795 else sY := 0; // top
3796 end;
3797 end;
3799 p.viewPortX := sX;
3800 p.viewPortY := sY;
3801 p.viewPortW := sWidth;
3802 p.viewPortH := sHeight;
3804 {$IFDEF ENABLE_HOLMES}
3805 if (p = gPlayer1) then
3806 begin
3807 g_Holmes_plrViewPos(sX, sY);
3808 g_Holmes_plrViewSize(sWidth, sHeight);
3809 end;
3810 {$ENDIF}
3812 renderMapInternal(-c, -d, true);
3814 if (gGameSettings.GameMode <> GM_SINGLE) and (gPlayerIndicator > 0) then
3815 case gPlayerIndicator of
3816 1:
3817 p.DrawIndicator(_RGB(255, 255, 255));
3819 2:
3820 for i := 0 to High(gPlayers) do
3821 if gPlayers[i] <> nil then
3822 if gPlayers[i] = p then p.DrawIndicator(_RGB(255, 255, 255))
3823 else if (gPlayers[i].Team = p.Team) and (gPlayers[i].Team <> TEAM_NONE) then
3824 if gPlayerIndicatorStyle = 1 then
3825 gPlayers[i].DrawIndicator(_RGB(192, 192, 192))
3826 else gPlayers[i].DrawIndicator(gPlayers[i].GetColor);
3827 end;
3830 for a := 0 to High(gCollideMap) do
3831 for b := 0 to High(gCollideMap[a]) do
3832 begin
3833 d := 0;
3834 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3835 d := d + 1;
3836 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3837 d := d + 2;
3839 case d of
3840 1: e_DrawPoint(1, b, a, 200, 200, 200);
3841 2: e_DrawPoint(1, b, a, 64, 64, 255);
3842 3: e_DrawPoint(1, b, a, 255, 0, 255);
3843 end;
3844 end;
3847 glPopMatrix();
3849 p.DrawPain();
3850 p.DrawPickup();
3851 p.DrawRulez();
3852 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3853 if g_Debug_Player then
3854 g_Player_DrawDebug(p);
3855 p.DrawGUI();
3856 end;
3858 procedure drawProfilers ();
3859 var
3860 px: Integer = -1;
3861 py: Integer = -1;
3862 begin
3863 if g_profile_frame_draw and (profileFrameDraw <> nil) then px := px-drawProfiles(px, py, profileFrameDraw);
3864 if g_profile_collision and (profMapCollision <> nil) then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3865 if g_profile_los and (profMonsLOS <> nil) then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3866 end;
3868 procedure g_Game_Draw();
3869 var
3870 ID: DWORD;
3871 w, h: Word;
3872 ww, hh: Byte;
3873 Time: Int64;
3874 back: string;
3875 plView1, plView2: TPlayer;
3876 Split: Boolean;
3877 begin
3878 if gExit = EXIT_QUIT then Exit;
3880 Time := sys_GetTicks() {div 1000};
3881 FPSCounter := FPSCounter+1;
3882 if Time - FPSTime >= 1000 then
3883 begin
3884 FPS := FPSCounter;
3885 FPSCounter := 0;
3886 FPSTime := Time;
3887 end;
3889 e_SetRendertarget(True);
3890 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3892 if gGameOn or (gState = STATE_FOLD) then
3893 begin
3894 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3895 begin
3896 gSpectMode := SPECT_NONE;
3897 if not gRevertPlayers then
3898 begin
3899 plView1 := gPlayer1;
3900 plView2 := gPlayer2;
3901 end
3902 else
3903 begin
3904 plView1 := gPlayer2;
3905 plView2 := gPlayer1;
3906 end;
3907 end
3908 else
3909 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3910 begin
3911 gSpectMode := SPECT_NONE;
3912 if gPlayer2 = nil then
3913 plView1 := gPlayer1
3914 else
3915 plView1 := gPlayer2;
3916 plView2 := nil;
3917 end
3918 else
3919 begin
3920 plView1 := nil;
3921 plView2 := nil;
3922 end;
3924 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3925 gSpectMode := SPECT_STATS;
3927 if gSpectMode = SPECT_PLAYERS then
3928 if gPlayers <> nil then
3929 begin
3930 plView1 := GetActivePlayer_ByID(gSpectPID1);
3931 if plView1 = nil then
3932 begin
3933 gSpectPID1 := GetActivePlayerID_Next();
3934 plView1 := GetActivePlayer_ByID(gSpectPID1);
3935 end;
3936 if gSpectViewTwo then
3937 begin
3938 plView2 := GetActivePlayer_ByID(gSpectPID2);
3939 if plView2 = nil then
3940 begin
3941 gSpectPID2 := GetActivePlayerID_Next();
3942 plView2 := GetActivePlayer_ByID(gSpectPID2);
3943 end;
3944 end;
3945 end;
3947 if gSpectMode = SPECT_MAPVIEW then
3948 begin
3949 // Ðåæèì ïðîñìîòðà êàðòû
3950 Split := False;
3951 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3952 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3953 gHearPoint1.Active := True;
3954 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3955 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3956 gHearPoint2.Active := False;
3957 end
3958 else
3959 begin
3960 Split := (plView1 <> nil) and (plView2 <> nil);
3962 // Òî÷êè ñëóõà èãðîêîâ
3963 if plView1 <> nil then
3964 begin
3965 gHearPoint1.Active := True;
3966 gHearPoint1.Coords.X := plView1.GameX + PLAYER_RECT.Width;
3967 gHearPoint1.Coords.Y := plView1.GameY + PLAYER_RECT.Height DIV 2;
3968 end else
3969 gHearPoint1.Active := False;
3970 if plView2 <> nil then
3971 begin
3972 gHearPoint2.Active := True;
3973 gHearPoint2.Coords.X := plView2.GameX + PLAYER_RECT.Width;
3974 gHearPoint2.Coords.Y := plView2.GameY + PLAYER_RECT.Height DIV 2;
3975 end else
3976 gHearPoint2.Active := False;
3978 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3979 gPlayerScreenSize.X := gScreenWidth-196;
3980 if Split then
3981 begin
3982 gPlayerScreenSize.Y := gScreenHeight div 2;
3983 if gScreenHeight mod 2 = 0 then
3984 Dec(gPlayerScreenSize.Y);
3985 end
3986 else
3987 gPlayerScreenSize.Y := gScreenHeight;
3989 if Split then
3990 if gScreenHeight mod 2 = 0 then
3991 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3992 else
3993 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3995 DrawPlayer(plView1);
3996 gPlayer1ScreenCoord.X := sX;
3997 gPlayer1ScreenCoord.Y := sY;
3999 if Split then
4000 begin
4001 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
4003 DrawPlayer(plView2);
4004 gPlayer2ScreenCoord.X := sX;
4005 gPlayer2ScreenCoord.Y := sY;
4006 end;
4008 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
4010 if Split then
4011 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
4012 end;
4014 {$IFDEF ENABLE_HOLMES}
4015 // draw inspector
4016 if (g_holmes_enabled) then g_Holmes_Draw();
4017 {$ENDIF}
4019 if MessageText <> '' then
4020 begin
4021 w := 0;
4022 h := 0;
4023 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
4024 if Split then
4025 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
4026 (gScreenHeight div 2)-(h div 2), MessageText)
4027 else
4028 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
4029 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
4030 end;
4032 if IsDrawStat or (gSpectMode = SPECT_STATS) then
4033 DrawStat();
4035 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) and (not gSpectAuto) then
4036 begin
4037 // Draw spectator GUI
4038 ww := 0;
4039 hh := 0;
4040 e_TextureFontGetSize(gStdFont, ww, hh);
4041 case gSpectMode of
4042 SPECT_STATS:
4043 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
4044 SPECT_MAPVIEW:
4045 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
4046 SPECT_PLAYERS:
4047 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
4048 end;
4049 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
4050 if gSpectMode = SPECT_STATS then
4051 begin
4052 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2)*2, 'Autoview', gStdFont, 255, 255, 255, 1);
4053 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2), '< fire >', gStdFont, 255, 255, 255, 1);
4054 end;
4055 if gSpectMode = SPECT_MAPVIEW then
4056 begin
4057 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
4058 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
4059 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
4060 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
4061 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
4062 end;
4063 if gSpectMode = SPECT_PLAYERS then
4064 begin
4065 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
4066 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
4067 if gSpectViewTwo then
4068 begin
4069 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
4070 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
4071 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
4072 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
4073 end
4074 else
4075 begin
4076 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
4077 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
4078 end;
4079 end;
4080 end;
4081 end;
4083 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
4084 begin
4085 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4086 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4088 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
4089 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
4090 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
4091 end;
4093 if not gGameOn then
4094 begin
4095 if (gState = STATE_MENU) then
4096 begin
4097 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then DrawMenuBackground('MENU_BACKGROUND');
4098 // F3 at menu will show game loading dialog
4099 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
4100 if (g_ActiveWindow <> nil) then
4101 begin
4102 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4103 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4104 end
4105 else
4106 begin
4107 // F3 at titlepic will show game loading dialog
4108 if e_KeyPressed(IK_F3) then
4109 begin
4110 g_Menu_Show_LoadMenu(true);
4111 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4112 end;
4113 end;
4114 end;
4116 if gState = STATE_FOLD then
4117 begin
4118 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
4119 end;
4121 if gState = STATE_INTERCUSTOM then
4122 begin
4123 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
4124 begin
4125 back := 'TEXTURE_endpic';
4126 if not g_Texture_Get(back, ID) then
4127 back := _lc[I_TEXTURE_ENDPIC];
4128 end
4129 else
4130 back := 'INTER';
4132 DrawMenuBackground(back);
4134 DrawCustomStat();
4136 if g_ActiveWindow <> nil then
4137 begin
4138 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4139 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4140 end;
4141 end;
4143 if gState = STATE_INTERSINGLE then
4144 begin
4145 if EndingGameCounter > 0 then
4146 begin
4147 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
4148 end
4149 else
4150 begin
4151 back := 'INTER';
4153 DrawMenuBackground(back);
4155 DrawSingleStat();
4157 if g_ActiveWindow <> nil then
4158 begin
4159 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4160 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4161 end;
4162 end;
4163 end;
4165 if gState = STATE_ENDPIC then
4166 begin
4167 ID := DWORD(-1);
4168 if g_Texture_Get('TEXTURE_endpic', ID) then DrawMenuBackground('TEXTURE_endpic')
4169 else DrawMenuBackground(_lc[I_TEXTURE_ENDPIC]);
4171 if g_ActiveWindow <> nil then
4172 begin
4173 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4174 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4175 end;
4176 end;
4178 if gState = STATE_SLIST then
4179 begin
4180 // if g_Texture_Get('MENU_BACKGROUND', ID) then
4181 // begin
4182 // e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
4183 // //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4184 // end;
4185 DrawMenuBackground('MENU_BACKGROUND');
4186 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4187 g_Serverlist_Draw(slCurrent, slTable);
4188 end;
4189 end;
4191 if g_ActiveWindow <> nil then
4192 begin
4193 if gGameOn then
4194 begin
4195 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4196 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
4197 end;
4198 g_ActiveWindow.Draw();
4199 end;
4201 {$IFNDEF HEADLESS}
4202 g_Console_Draw();
4203 {$ENDIF}
4205 if g_debug_Sounds and gGameOn then
4206 begin
4207 for w := 0 to High(e_SoundsArray) do
4208 for h := 0 to e_SoundsArray[w].nRefs do
4209 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
4210 end;
4212 if gShowFPS then
4213 begin
4214 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
4215 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
4216 end;
4218 if gGameOn and gShowTime then
4219 drawTime(gScreenWidth-72, gScreenHeight-16);
4221 if gGameOn then drawProfilers();
4223 // TODO: draw this after the FBO and remap mouse click coordinates
4225 {$IFDEF ENABLE_HOLMES}
4226 g_Holmes_DrawUI();
4227 {$ENDIF}
4229 // blit framebuffer to screen
4231 e_SetRendertarget(False);
4232 e_SetViewPort(0, 0, gWinSizeX, gWinSizeY);
4233 e_BlitFramebuffer(gWinSizeX, gWinSizeY);
4235 // draw the overlay stuff on top of it
4237 g_Touch_Draw;
4238 end;
4240 procedure g_Game_Quit();
4241 begin
4242 g_Game_StopAllSounds(True);
4243 gMusic.Free();
4244 g_Game_FreeData();
4245 g_PlayerModel_FreeData();
4246 g_Texture_DeleteAll();
4247 g_Frames_DeleteAll();
4248 {$IFNDEF HEADLESS}
4249 //g_Menu_Free(); //k8: this segfaults after resolution change; who cares?
4250 {$ENDIF}
4252 if NetInitDone then g_Net_Free;
4254 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
4255 if gMapToDelete <> '' then
4256 g_Game_DeleteTestMap();
4258 gExit := EXIT_QUIT;
4259 sys_RequestQuit;
4260 end;
4262 procedure g_FatalError(Text: String);
4263 begin
4264 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
4265 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning);
4267 gExit := EXIT_SIMPLE;
4268 if gGameOn then EndGame;
4269 end;
4271 procedure g_SimpleError(Text: String);
4272 begin
4273 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
4274 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning);
4275 end;
4277 procedure g_Game_SetupScreenSize();
4278 const
4279 RES_FACTOR = 4.0 / 3.0;
4280 var
4281 s: Single;
4282 rf: Single;
4283 bw, bh: Word;
4284 begin
4285 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4286 gPlayerScreenSize.X := gScreenWidth-196;
4287 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
4288 gPlayerScreenSize.Y := gScreenHeight div 2
4289 else
4290 gPlayerScreenSize.Y := gScreenHeight;
4292 // Ðàçìåð çàäíåãî ïëàíà:
4293 if BackID <> DWORD(-1) then
4294 begin
4295 s := SKY_STRETCH;
4296 if (gScreenWidth*s > gMapInfo.Width) or
4297 (gScreenHeight*s > gMapInfo.Height) then
4298 begin
4299 gBackSize.X := gScreenWidth;
4300 gBackSize.Y := gScreenHeight;
4301 end
4302 else
4303 begin
4304 e_GetTextureSize(BackID, @bw, @bh);
4305 rf := Single(bw) / Single(bh);
4306 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
4307 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
4308 s := Max(gScreenWidth / bw, gScreenHeight / bh);
4309 if (s < 1.0) then s := 1.0;
4310 gBackSize.X := Round(bw*s);
4311 gBackSize.Y := Round(bh*s);
4312 end;
4313 end;
4314 end;
4316 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
4317 begin
4318 sys_SetDisplayMode(newWidth, newHeight, gBPP, nowFull, nowMax);
4319 end;
4321 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
4322 begin
4323 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
4324 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
4325 Exit;
4327 if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then
4328 Exit;
4330 if gPlayer1 = nil then
4331 begin
4332 if g_Game_IsClient then
4333 begin
4334 if NetPlrUID1 > -1 then
4335 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
4336 Exit;
4337 end;
4339 if not (Team in [TEAM_RED, TEAM_BLUE]) then
4340 Team := gPlayer1Settings.Team;
4342 // Ñîçäàíèå ïåðâîãî èãðîêà:
4343 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4344 gPlayer1Settings.Color,
4345 Team, False));
4346 if gPlayer1 = nil then
4347 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
4348 else
4349 begin
4350 gPlayer1.Name := gPlayer1Settings.Name;
4351 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
4352 if g_Game_IsServer and g_Game_IsNet then
4353 MH_SEND_PlayerCreate(gPlayer1.UID);
4354 gPlayer1.Respawn(False, True);
4355 g_Net_Slist_ServerPlayerComes();
4356 end;
4358 Exit;
4359 end;
4360 if gPlayer2 = nil then
4361 begin
4362 if g_Game_IsClient then
4363 begin
4364 if NetPlrUID2 > -1 then
4365 gPlayer2 := g_Player_Get(NetPlrUID2);
4366 Exit;
4367 end;
4369 if not (Team in [TEAM_RED, TEAM_BLUE]) then
4370 Team := gPlayer2Settings.Team;
4372 // Ñîçäàíèå âòîðîãî èãðîêà:
4373 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4374 gPlayer2Settings.Color,
4375 Team, False));
4376 if gPlayer2 = nil then
4377 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
4378 else
4379 begin
4380 gPlayer2.Name := gPlayer2Settings.Name;
4381 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
4382 if g_Game_IsServer and g_Game_IsNet then
4383 MH_SEND_PlayerCreate(gPlayer2.UID);
4384 gPlayer2.Respawn(False, True);
4385 g_Net_Slist_ServerPlayerComes();
4386 end;
4388 Exit;
4389 end;
4390 end;
4392 procedure g_Game_RemovePlayer();
4393 var
4394 Pl: TPlayer;
4395 begin
4396 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
4397 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
4398 Exit;
4399 Pl := gPlayer2;
4400 if Pl <> nil then
4401 begin
4402 if g_Game_IsServer then
4403 begin
4404 Pl.Lives := 0;
4405 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4406 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
4407 g_Player_Remove(Pl.UID);
4408 g_Net_Slist_ServerPlayerLeaves();
4409 end
4410 else
4411 begin
4412 gSpectLatchPID2 := Pl.UID;
4413 gPlayer2 := nil;
4414 end;
4415 Exit;
4416 end;
4417 Pl := gPlayer1;
4418 if Pl <> nil then
4419 begin
4420 if g_Game_IsServer then
4421 begin
4422 Pl.Lives := 0;
4423 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4424 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
4425 g_Player_Remove(Pl.UID);
4426 g_Net_Slist_ServerPlayerLeaves();
4427 end else
4428 begin
4429 gSpectLatchPID1 := Pl.UID;
4430 gPlayer1 := nil;
4431 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
4432 end;
4433 Exit;
4434 end;
4435 g_Net_Slist_ServerPlayerLeaves();
4436 end;
4438 procedure g_Game_Spectate();
4439 begin
4440 g_Game_RemovePlayer();
4441 if gPlayer1 <> nil then
4442 g_Game_RemovePlayer();
4443 end;
4445 procedure g_Game_SpectateCenterView();
4446 begin
4447 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
4448 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
4449 end;
4451 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
4452 var
4453 i, nPl: Integer;
4454 tmps: AnsiString;
4455 begin
4456 g_Game_Free();
4458 e_WriteLog('Starting singleplayer game...', TMsgType.Notify);
4460 g_Game_ClearLoading();
4462 // Íàñòðîéêè èãðû:
4463 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
4464 gAimLine := False;
4465 gShowMap := False;
4466 gGameSettings.GameType := GT_SINGLE;
4467 gGameSettings.MaxLives := 0;
4468 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
4469 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
4470 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
4471 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_TEAMHITPROJECTILE;
4472 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_TEAMHITTRACE;
4473 gSwitchGameMode := GM_SINGLE;
4475 gLMSRespawn := LMS_RESPAWN_NONE;
4476 gLMSRespawnTime := 0;
4477 gSpectLatchPID1 := 0;
4478 gSpectLatchPID2 := 0;
4480 g_Game_ExecuteEvent('ongamestart');
4482 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4483 g_Game_SetupScreenSize();
4485 // Ñîçäàíèå ïåðâîãî èãðîêà:
4486 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4487 gPlayer1Settings.Color,
4488 gPlayer1Settings.Team, False));
4489 if gPlayer1 = nil then
4490 begin
4491 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4492 Exit;
4493 end;
4495 gPlayer1.Name := gPlayer1Settings.Name;
4496 nPl := 1;
4498 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
4499 if TwoPlayers then
4500 begin
4501 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4502 gPlayer2Settings.Color,
4503 gPlayer2Settings.Team, False));
4504 if gPlayer2 = nil then
4505 begin
4506 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4507 Exit;
4508 end;
4510 gPlayer2.Name := gPlayer2Settings.Name;
4511 Inc(nPl);
4512 end;
4514 // Çàãðóçêà è çàïóñê êàðòû:
4515 if not g_Game_StartMap(false{asMegawad}, MAP, True) then
4516 begin
4517 if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP;
4518 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps]));
4519 Exit;
4520 end;
4522 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4523 g_Player_Init();
4525 // Ñîçäàåì áîòîâ:
4526 for i := nPl+1 to nPlayers do
4527 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4528 end;
4530 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
4531 TimeLimit, GoalLimit: Word;
4532 MaxLives: Byte;
4533 Options: LongWord; nPlayers: Byte);
4534 var
4535 i, nPl: Integer;
4536 begin
4537 g_Game_Free();
4539 e_WriteLog('Starting custom game...', TMsgType.Notify);
4541 g_Game_ClearLoading();
4543 // Íàñòðîéêè èãðû:
4544 gGameSettings.GameType := GT_CUSTOM;
4545 gGameSettings.GameMode := GameMode;
4546 gSwitchGameMode := GameMode;
4547 gGameSettings.TimeLimit := TimeLimit;
4548 gGameSettings.GoalLimit := GoalLimit;
4549 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4550 gGameSettings.Options := Options;
4552 gCoopTotalMonstersKilled := 0;
4553 gCoopTotalSecretsFound := 0;
4554 gCoopTotalMonsters := 0;
4555 gCoopTotalSecrets := 0;
4556 gAimLine := False;
4557 gShowMap := False;
4559 gLMSRespawn := LMS_RESPAWN_NONE;
4560 gLMSRespawnTime := 0;
4561 gSpectLatchPID1 := 0;
4562 gSpectLatchPID2 := 0;
4564 g_Game_ExecuteEvent('ongamestart');
4566 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4567 g_Game_SetupScreenSize();
4569 // Ðåæèì íàáëþäàòåëÿ:
4570 if nPlayers = 0 then
4571 begin
4572 gPlayer1 := nil;
4573 gPlayer2 := nil;
4574 end;
4576 nPl := 0;
4577 if nPlayers >= 1 then
4578 begin
4579 // Ñîçäàíèå ïåðâîãî èãðîêà:
4580 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4581 gPlayer1Settings.Color,
4582 gPlayer1Settings.Team, False));
4583 if gPlayer1 = nil then
4584 begin
4585 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4586 Exit;
4587 end;
4589 gPlayer1.Name := gPlayer1Settings.Name;
4590 Inc(nPl);
4591 end;
4593 if nPlayers >= 2 then
4594 begin
4595 // Ñîçäàíèå âòîðîãî èãðîêà:
4596 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4597 gPlayer2Settings.Color,
4598 gPlayer2Settings.Team, False));
4599 if gPlayer2 = nil then
4600 begin
4601 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4602 Exit;
4603 end;
4605 gPlayer2.Name := gPlayer2Settings.Name;
4606 Inc(nPl);
4607 end;
4609 // Çàãðóçêà è çàïóñê êàðòû:
4610 if not g_Game_StartMap(true{asMegawad}, Map, True) then
4611 begin
4612 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4613 Exit;
4614 end;
4616 // Íåò òî÷åê ïîÿâëåíèÿ:
4617 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4618 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4619 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4620 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4621 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4622 begin
4623 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4624 Exit;
4625 end;
4627 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4628 g_Player_Init();
4630 // Ñîçäàåì áîòîâ:
4631 for i := nPl+1 to nPlayers do
4632 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
4633 end;
4635 procedure g_Game_StartServer(Map: String; GameMode: Byte;
4636 TimeLimit, GoalLimit: Word; MaxLives: Byte;
4637 Options: LongWord; nPlayers: Byte;
4638 IPAddr: LongWord; Port: Word);
4639 begin
4640 g_Game_Free();
4641 g_Net_Slist_ServerClosed();
4643 e_WriteLog('Starting net game (server)...', TMsgType.Notify);
4645 g_Game_ClearLoading();
4647 // Íàñòðîéêè èãðû:
4648 gGameSettings.GameType := GT_SERVER;
4649 gGameSettings.GameMode := GameMode;
4650 gSwitchGameMode := GameMode;
4651 gGameSettings.TimeLimit := TimeLimit;
4652 gGameSettings.GoalLimit := GoalLimit;
4653 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
4654 gGameSettings.Options := Options;
4656 gCoopTotalMonstersKilled := 0;
4657 gCoopTotalSecretsFound := 0;
4658 gCoopTotalMonsters := 0;
4659 gCoopTotalSecrets := 0;
4660 gAimLine := False;
4661 gShowMap := False;
4663 gLMSRespawn := LMS_RESPAWN_NONE;
4664 gLMSRespawnTime := 0;
4665 gSpectLatchPID1 := 0;
4666 gSpectLatchPID2 := 0;
4668 g_Game_ExecuteEvent('ongamestart');
4670 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
4671 g_Game_SetupScreenSize();
4673 // Ðåæèì íàáëþäàòåëÿ:
4674 if nPlayers = 0 then
4675 begin
4676 gPlayer1 := nil;
4677 gPlayer2 := nil;
4678 end;
4680 if nPlayers >= 1 then
4681 begin
4682 // Ñîçäàíèå ïåðâîãî èãðîêà:
4683 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4684 gPlayer1Settings.Color,
4685 gPlayer1Settings.Team, False));
4686 if gPlayer1 = nil then
4687 begin
4688 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4689 Exit;
4690 end;
4692 gPlayer1.Name := gPlayer1Settings.Name;
4693 end;
4695 if nPlayers >= 2 then
4696 begin
4697 // Ñîçäàíèå âòîðîãî èãðîêà:
4698 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
4699 gPlayer2Settings.Color,
4700 gPlayer2Settings.Team, False));
4701 if gPlayer2 = nil then
4702 begin
4703 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
4704 Exit;
4705 end;
4707 gPlayer2.Name := gPlayer2Settings.Name;
4708 end;
4710 g_Game_SetLoadingText(_lc[I_LOAD_HOST], 0, False);
4711 if NetForwardPorts then
4712 g_Game_SetLoadingText(_lc[I_LOAD_PORTS], 0, False);
4714 // Ñòàðòóåì ñåðâåð
4715 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
4716 begin
4717 g_FatalError(_lc[I_NET_MSG] + Format(_lc[I_NET_ERR_HOST], [Port]));
4718 Exit;
4719 end;
4721 g_Net_Slist_Set(NetMasterList);
4723 g_Net_Slist_ServerStarted();
4725 // Çàãðóçêà è çàïóñê êàðòû:
4726 if not g_Game_StartMap(false{asMegawad}, Map, True) then
4727 begin
4728 g_Net_Slist_ServerClosed();
4729 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
4730 Exit;
4731 end;
4733 // Íåò òî÷åê ïîÿâëåíèÿ:
4734 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
4735 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
4736 g_Map_GetPointCount(RESPAWNPOINT_DM) +
4737 g_Map_GetPointCount(RESPAWNPOINT_RED)+
4738 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
4739 begin
4740 g_Net_Slist_ServerClosed();
4741 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
4742 Exit;
4743 end;
4745 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4746 g_Player_Init();
4748 g_Net_Slist_ServerMapStarted();
4749 NetState := NET_STATE_GAME;
4750 end;
4752 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
4753 var
4754 Map: String;
4755 WadName: string;
4756 Ptr: Pointer;
4757 T: Cardinal;
4758 MID: Byte;
4759 State: Byte;
4760 OuterLoop: Boolean;
4761 newResPath: string;
4762 InMsg: TMsg;
4763 begin
4764 g_Game_Free();
4766 State := 0;
4767 e_WriteLog('Starting net game (client)...', TMsgType.Notify);
4768 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', TMsgType.Notify);
4770 g_Game_ClearLoading();
4772 // Íàñòðîéêè èãðû:
4773 gGameSettings.GameType := GT_CLIENT;
4775 gCoopTotalMonstersKilled := 0;
4776 gCoopTotalSecretsFound := 0;
4777 gCoopTotalMonsters := 0;
4778 gCoopTotalSecrets := 0;
4779 gAimLine := False;
4780 gShowMap := False;
4782 g_Game_ExecuteEvent('ongamestart');
4784 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4785 g_Game_SetupScreenSize();
4787 NetState := NET_STATE_AUTH;
4789 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4791 // create (or update) map/resource databases
4792 g_Res_CreateDatabases(true);
4794 gLMSRespawn := LMS_RESPAWN_NONE;
4795 gLMSRespawnTime := 0;
4796 gSpectLatchPID1 := 0;
4797 gSpectLatchPID2 := 0;
4799 // Ñòàðòóåì êëèåíò
4800 if not g_Net_Connect(Addr, Port) then
4801 begin
4802 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4803 NetState := NET_STATE_NONE;
4804 Exit;
4805 end;
4807 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4808 MC_SEND_Info(PW);
4809 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4811 OuterLoop := True;
4812 while OuterLoop do
4813 begin
4814 // fuck! https://www.mail-archive.com/enet-discuss@cubik.org/msg00852.html
4815 // tl;dr: on shitdows, we can get -1 sometimes, and it is *NOT* a failure.
4816 // thank you, enet. let's ignore failures altogether then.
4817 while (enet_host_service(NetHost, @NetEvent, 50) > 0) do
4818 begin
4819 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4820 begin
4821 if (NetEvent.channelID = NET_CHAN_DOWNLOAD_EX) then
4822 begin
4823 // ignore all download packets, they're processed by separate code
4824 enet_packet_destroy(NetEvent.packet);
4825 continue;
4826 end;
4827 Ptr := NetEvent.packet^.data;
4828 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4829 begin
4830 enet_packet_destroy(NetEvent.packet);
4831 continue;
4832 end;
4834 InMsg.ReadLongWord(); // skip size
4835 MID := InMsg.ReadByte();
4837 if (MID = NET_MSG_INFO) and (State = 0) then
4838 begin
4839 NetMyID := InMsg.ReadByte();
4840 NetPlrUID1 := InMsg.ReadWord();
4842 WadName := InMsg.ReadString();
4843 Map := InMsg.ReadString();
4845 gWADHash := InMsg.ReadMD5();
4847 gGameSettings.GameMode := InMsg.ReadByte();
4848 gSwitchGameMode := gGameSettings.GameMode;
4849 gGameSettings.GoalLimit := InMsg.ReadWord();
4850 gGameSettings.TimeLimit := InMsg.ReadWord();
4851 gGameSettings.MaxLives := InMsg.ReadByte();
4852 gGameSettings.Options := InMsg.ReadLongWord();
4853 T := InMsg.ReadLongWord();
4855 //newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4856 //if newResPath = '' then
4857 begin
4858 //g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4859 newResPath := g_Res_DownloadMapWAD(ExtractFileName(WadName), gWADHash);
4860 if newResPath = '' then
4861 begin
4862 g_FatalError(_lc[I_NET_ERR_HASH]);
4863 enet_packet_destroy(NetEvent.packet);
4864 NetState := NET_STATE_NONE;
4865 Exit;
4866 end;
4867 e_LogWritefln('using downloaded map wad [%s] for [%s]`', [newResPath, WadName], TMsgType.Notify);
4868 end;
4869 //newResPath := ExtractRelativePath(MapsDir, newResPath);
4872 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4873 gPlayer1Settings.Color,
4874 gPlayer1Settings.Team, False));
4876 if gPlayer1 = nil then
4877 begin
4878 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4880 enet_packet_destroy(NetEvent.packet);
4881 NetState := NET_STATE_NONE;
4882 Exit;
4883 end;
4885 gPlayer1.Name := gPlayer1Settings.Name;
4886 gPlayer1.UID := NetPlrUID1;
4887 gPlayer1.Reset(True);
4889 if not g_Game_StartMap(false{asMegawad}, newResPath + ':\' + Map, True) then
4890 begin
4891 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4893 enet_packet_destroy(NetEvent.packet);
4894 NetState := NET_STATE_NONE;
4895 Exit;
4896 end;
4898 gTime := T;
4900 State := 1;
4901 OuterLoop := False;
4902 enet_packet_destroy(NetEvent.packet);
4903 break;
4904 end
4905 else
4906 enet_packet_destroy(NetEvent.packet);
4907 end
4908 else
4909 begin
4910 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4911 begin
4912 State := 0;
4913 if (NetEvent.data <= NET_DISC_MAX) then
4914 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4915 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4916 OuterLoop := False;
4917 Break;
4918 end;
4919 end;
4920 end;
4922 ProcessLoading(true);
4924 if g_Net_UserRequestExit() then
4925 begin
4926 State := 0;
4927 break;
4928 end;
4929 end;
4931 if State <> 1 then
4932 begin
4933 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4934 NetState := NET_STATE_NONE;
4935 Exit;
4936 end;
4938 g_Player_Init();
4939 NetState := NET_STATE_GAME;
4940 MC_SEND_FullStateRequest;
4941 e_WriteLog('NET: Connection successful.', TMsgType.Notify);
4942 end;
4944 var
4945 lastAsMegaWad: Boolean = false;
4947 procedure g_Game_ChangeMap(const MapPath: String);
4948 var
4949 Force: Boolean;
4950 begin
4951 g_Game_ClearLoading();
4953 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4954 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4955 if gExitByTrigger then
4956 begin
4957 Force := False;
4958 gExitByTrigger := False;
4959 end;
4960 if not g_Game_StartMap(lastAsMegaWad, MapPath, Force) then
4961 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4962 end;
4964 procedure g_Game_Restart();
4965 var
4966 Map: string;
4967 begin
4968 if g_Game_IsClient then
4969 Exit;
4970 map := g_ExtractFileName(gMapInfo.Map);
4971 e_LogWritefln('g_Game_Restart: map = "%s" gCurrentMapFileName = "%s"', [map, gCurrentMapFileName]);
4973 MessageTime := 0;
4974 gGameOn := False;
4975 g_Game_ClearLoading();
4976 g_Game_StartMap(lastAsMegaWad, Map, True, gCurrentMapFileName);
4977 end;
4979 function g_Game_StartMap (asMegawad: Boolean; Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4980 var
4981 NewWAD, ResName: String;
4982 I: Integer;
4983 nws: AnsiString;
4984 begin
4985 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4986 g_Player_RemoveAllCorpses();
4988 if (not g_Game_IsClient) and
4989 (gSwitchGameMode <> gGameSettings.GameMode) and
4990 (gGameSettings.GameMode <> GM_SINGLE) then
4991 begin
4992 if gSwitchGameMode = GM_CTF then
4993 gGameSettings.MaxLives := 0;
4994 gGameSettings.GameMode := gSwitchGameMode;
4995 Force := True;
4996 end else
4997 gSwitchGameMode := gGameSettings.GameMode;
4999 g_Player_ResetTeams();
5001 lastAsMegaWad := asMegawad;
5002 if isWadPath(Map) then
5003 begin
5004 NewWAD := g_ExtractWadName(Map);
5005 ResName := g_ExtractFileName(Map);
5006 if g_Game_IsServer then
5007 begin
5008 nws := findDiskWad(NewWAD);
5009 //writeln('000: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']');
5010 if (asMegawad) then
5011 begin
5012 if (length(nws) = 0) then nws := e_FindWad(MegawadDirs, NewWAD);
5013 if (length(nws) = 0) then nws := e_FindWad(MapDirs, NewWAD);
5014 end
5015 else
5016 begin
5017 if (length(nws) = 0) then nws := e_FindWad(MapDirs, NewWAD);
5018 if (length(nws) = 0) then nws := e_FindWad(MegawadDirs, NewWAD);
5019 end;
5020 //if (length(nws) = 0) then nws := e_FindWad(MapDownloadDirs, NewWAD);
5021 //writeln('001: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']');
5022 //nws := NewWAD;
5023 if (length(nws) = 0) then
5024 begin
5025 ResName := ''; // failed
5026 end
5027 else
5028 begin
5029 NewWAD := nws;
5030 if (g_Game_IsNet) then gWADHash := MD5File(nws);
5031 //writeln('********: nws=', nws, ' : Map=', Map, ' : nw=', NewWAD, ' : resname=', ResName);
5032 g_Game_LoadWAD(NewWAD);
5033 end;
5034 end
5035 else
5036 begin
5037 // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART
5038 NewWAD := g_Game_ClientWAD(NewWAD, gWADHash);
5039 end;
5040 end
5041 else
5042 begin
5043 NewWAD := gGameSettings.WAD;
5044 ResName := Map;
5045 end;
5047 gTime := 0;
5049 //writeln('********: gsw=', gGameSettings.WAD, '; rn=', ResName);
5050 result := false;
5051 if (ResName <> '') and (NewWAD <> '') then
5052 begin
5053 //result := g_Map_Load(gGameSettings.WAD + ':\' + ResName);
5054 result := g_Map_Load(NewWAD+':\'+ResName);
5055 end;
5056 if Result then
5057 begin
5058 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
5060 gState := STATE_NONE;
5061 g_ActiveWindow := nil;
5062 gGameOn := True;
5064 DisableCheats();
5065 ResetTimer();
5067 if gGameSettings.GameMode = GM_CTF then
5068 begin
5069 g_Map_ResetFlag(FLAG_RED);
5070 g_Map_ResetFlag(FLAG_BLUE);
5071 // CTF, à ôëàãîâ íåò:
5072 if not g_Map_HaveFlagPoints() then
5073 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
5074 end;
5075 end
5076 else
5077 begin
5078 gState := STATE_MENU;
5079 gGameOn := False;
5080 end;
5082 gExit := 0;
5083 gPauseMain := false;
5084 gPauseHolmes := false;
5085 NetTimeToUpdate := 1;
5086 NetTimeToReliable := 0;
5087 NetTimeToMaster := NetMasterRate;
5088 gSpectLatchPID1 := 0;
5089 gSpectLatchPID2 := 0;
5090 gMissionFailed := False;
5091 gNextMap := '';
5093 gCoopMonstersKilled := 0;
5094 gCoopSecretsFound := 0;
5096 gVoteInProgress := False;
5097 gVotePassed := False;
5098 gVoteCount := 0;
5099 gVoted := False;
5101 gStatsOff := False;
5103 if not gGameOn then Exit;
5105 g_Game_SpectateCenterView();
5107 if g_Game_IsServer then
5108 begin
5109 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
5110 begin
5111 gLMSRespawn := LMS_RESPAWN_WARMUP;
5112 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
5113 gLMSSoftSpawn := True;
5114 if g_Game_IsNet then
5115 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime);
5116 end
5117 else
5118 begin
5119 gLMSRespawn := LMS_RESPAWN_NONE;
5120 gLMSRespawnTime := 0;
5121 end;
5122 end;
5124 if NetMode = NET_SERVER then
5125 begin
5126 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
5128 // Ìàñòåðñåðâåð
5129 g_Net_Slist_ServerMapStarted();
5131 if NetClients <> nil then
5132 for I := 0 to High(NetClients) do
5133 if NetClients[I].Used then
5134 begin
5135 NetClients[I].Voted := False;
5136 if NetClients[I].RequestedFullUpdate then
5137 begin
5138 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
5139 NetClients[I].RequestedFullUpdate := False;
5140 end;
5141 end;
5143 g_Net_UnbanNonPermHosts();
5144 end;
5146 if gLastMap then
5147 begin
5148 gCoopTotalMonstersKilled := 0;
5149 gCoopTotalSecretsFound := 0;
5150 gCoopTotalMonsters := 0;
5151 gCoopTotalSecrets := 0;
5152 gLastMap := False;
5153 end;
5155 g_Game_ExecuteEvent('onmapstart');
5156 end;
5158 procedure SetFirstLevel;
5159 begin
5160 gNextMap := '';
5162 MapList := g_Map_GetMapsList(gGameSettings.WAD);
5163 if MapList = nil then
5164 Exit;
5166 SortSArray(MapList);
5167 gNextMap := MapList[Low(MapList)];
5169 MapList := nil;
5170 end;
5172 procedure g_Game_ExitLevel(const Map: AnsiString);
5173 begin
5174 gNextMap := Map;
5176 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
5177 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
5178 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
5179 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
5181 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
5182 if gGameSettings.GameType = GT_SINGLE then
5183 gExit := EXIT_ENDLEVELSINGLE
5184 else // Âûøëè â âûõîä â Ñâîåé èãðå
5185 begin
5186 gExit := EXIT_ENDLEVELCUSTOM;
5187 if gGameSettings.GameMode = GM_COOP then
5188 g_Player_RememberAll;
5190 if not g_Map_Exist(gGameSettings.WAD + ':\' + gNextMap) then
5191 begin
5192 gLastMap := True;
5193 if gGameSettings.GameMode = GM_COOP then
5194 gStatsOff := True;
5196 gStatsPressed := True;
5197 gNextMap := 'MAP01';
5199 if not g_Map_Exist(gGameSettings.WAD + ':\' + gNextMap) then
5200 g_Game_NextLevel;
5202 if g_Game_IsNet then
5203 begin
5204 MH_SEND_GameStats();
5205 MH_SEND_CoopStats();
5206 end;
5207 end;
5208 end;
5209 end;
5211 procedure g_Game_RestartLevel();
5212 var
5213 Map: string;
5214 begin
5215 if gGameSettings.GameMode = GM_SINGLE then
5216 begin
5217 g_Game_Restart();
5218 Exit;
5219 end;
5220 gExit := EXIT_ENDLEVELCUSTOM;
5221 Map := g_ExtractFileName(gMapInfo.Map);
5222 gNextMap := Map;
5223 end;
5225 function g_Game_ClientWAD (NewWAD: String; const WHash: TMD5Digest): AnsiString;
5226 var
5227 gWAD{, xwad}: String;
5228 begin
5229 result := NewWAD;
5230 if not g_Game_IsClient then Exit;
5231 //e_LogWritefln('*** g_Game_ClientWAD: `%s`', [NewWAD]);
5233 gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash);
5234 if gWAD = '' then
5235 begin
5236 result := '';
5237 g_Game_Free();
5238 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
5239 Exit;
5240 end;
5242 e_LogWritefln('using downloaded client map wad [%s] for [%s]', [gWAD, NewWAD], TMsgType.Notify);
5243 NewWAD := gWAD;
5245 g_Game_LoadWAD(NewWAD);
5246 result := NewWAD;
5249 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then Exit;
5250 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
5251 if gWAD = '' then
5252 begin
5253 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
5254 gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash);
5255 if gWAD = '' then
5256 begin
5257 g_Game_Free();
5258 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
5259 Exit;
5260 end;
5261 end;
5262 NewWAD := ExtractRelativePath(MapsDir, gWAD);
5263 g_Game_LoadWAD(NewWAD);
5265 end;
5267 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
5268 var
5269 i, n, nb, nr: Integer;
5270 begin
5271 if not g_Game_IsServer then Exit;
5272 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
5273 gLMSRespawn := LMS_RESPAWN_NONE;
5274 gLMSRespawnTime := 0;
5275 MessageTime := 0;
5277 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
5278 begin
5279 gMissionFailed := True;
5280 g_Game_RestartLevel;
5281 Exit;
5282 end;
5284 n := 0; nb := 0; nr := 0;
5285 for i := Low(gPlayers) to High(gPlayers) do
5286 if (gPlayers[i] <> nil) and
5287 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
5288 (gPlayers[i] is TBot)) then
5289 begin
5290 Inc(n);
5291 if gPlayers[i].Team = TEAM_RED then Inc(nr)
5292 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
5293 end;
5295 if (n < 1) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
5296 begin
5297 // wait a second until the fuckers finally decide to join
5298 gLMSRespawn := LMS_RESPAWN_WARMUP;
5299 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
5300 gLMSSoftSpawn := NoMapRestart;
5301 if g_Game_IsNet then
5302 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime);
5303 Exit;
5304 end;
5306 g_Player_RemoveAllCorpses;
5307 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
5308 if g_Game_IsNet then
5309 MH_SEND_GameEvent(NET_EV_LMS_START);
5311 for i := Low(gPlayers) to High(gPlayers) do
5312 begin
5313 if gPlayers[i] = nil then continue;
5314 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
5315 // don't touch normal spectators
5316 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
5317 begin
5318 gPlayers[i].FNoRespawn := True;
5319 gPlayers[i].Lives := 0;
5320 if g_Game_IsNet then
5321 MH_SEND_PlayerStats(gPlayers[I].UID);
5322 continue;
5323 end;
5324 gPlayers[i].FNoRespawn := False;
5325 gPlayers[i].Lives := gGameSettings.MaxLives;
5326 gPlayers[i].Respawn(False, True);
5327 if gGameSettings.GameMode = GM_COOP then
5328 begin
5329 gPlayers[i].Frags := 0;
5330 gPlayers[i].RecallState;
5331 end;
5332 if (gPlayer1 = nil) and (gSpectLatchPID1 > 0) then
5333 gPlayer1 := g_Player_Get(gSpectLatchPID1);
5334 if (gPlayer2 = nil) and (gSpectLatchPID2 > 0) then
5335 gPlayer2 := g_Player_Get(gSpectLatchPID2);
5336 end;
5338 g_Items_RestartRound();
5340 gLMSSoftSpawn := False;
5341 end;
5343 function g_Game_GetFirstMap(WAD: String): String;
5344 begin
5345 Result := '';
5347 MapList := g_Map_GetMapsList(WAD);
5348 if MapList = nil then
5349 Exit;
5351 SortSArray(MapList);
5352 Result := MapList[Low(MapList)];
5354 if not g_Map_Exist(WAD + ':\' + Result) then
5355 Result := '';
5357 MapList := nil;
5358 end;
5360 function g_Game_GetNextMap(): String;
5361 var
5362 I: Integer;
5363 Map: string;
5364 begin
5365 Result := '';
5367 MapList := g_Map_GetMapsList(gGameSettings.WAD);
5368 if MapList = nil then
5369 Exit;
5371 Map := g_ExtractFileName(gMapInfo.Map);
5373 SortSArray(MapList);
5374 MapIndex := -255;
5375 for I := Low(MapList) to High(MapList) do
5376 if Map = MapList[I] then
5377 begin
5378 MapIndex := I;
5379 Break;
5380 end;
5382 if MapIndex <> -255 then
5383 begin
5384 if MapIndex = High(MapList) then
5385 Result := MapList[Low(MapList)]
5386 else
5387 Result := MapList[MapIndex + 1];
5389 if not g_Map_Exist(gGameSettings.WAD + ':\' + Result) then Result := Map;
5390 end;
5392 MapList := nil;
5393 end;
5395 procedure g_Game_NextLevel();
5396 begin
5397 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
5398 gExit := EXIT_ENDLEVELCUSTOM
5399 else
5400 begin
5401 gExit := EXIT_ENDLEVELSINGLE;
5402 Exit;
5403 end;
5405 if gNextMap <> '' then Exit;
5406 gNextMap := g_Game_GetNextMap();
5407 end;
5409 function g_Game_IsTestMap(): Boolean;
5410 begin
5411 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
5412 end;
5414 procedure g_Game_DeleteTestMap();
5415 var
5416 a: Integer;
5417 //MapName: AnsiString;
5418 WadName: string;
5420 WAD: TWADFile;
5421 MapList: SSArray;
5422 time: Integer;
5424 begin
5425 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
5426 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
5427 if (a = 0) then exit;
5429 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
5430 WadName := Copy(gMapToDelete, 1, a+3);
5431 Delete(gMapToDelete, 1, a+5);
5432 gMapToDelete := UpperCase(gMapToDelete);
5433 //MapName := '';
5434 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
5437 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
5438 if MapName <> TEST_MAP_NAME then
5439 Exit;
5441 if not gTempDelete then
5442 begin
5443 time := g_GetFileTime(WadName);
5444 WAD := TWADFile.Create();
5446 // ×èòàåì Wad-ôàéë:
5447 if not WAD.ReadFile(WadName) then
5448 begin // Íåò òàêîãî WAD-ôàéëà
5449 WAD.Free();
5450 Exit;
5451 end;
5453 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
5454 WAD.CreateImage();
5455 MapList := WAD.GetResourcesList('');
5457 if MapList <> nil then
5458 for a := 0 to High(MapList) do
5459 if MapList[a] = MapName then
5460 begin
5461 // Óäàëÿåì è ñîõðàíÿåì:
5462 WAD.RemoveResource('', MapName);
5463 WAD.SaveTo(WadName);
5464 Break;
5465 end;
5467 WAD.Free();
5468 g_SetFileTime(WadName, time);
5469 end else
5471 if gTempDelete then DeleteFile(WadName);
5472 end;
5474 procedure GameCVars(P: SSArray);
5475 var
5476 a, b: Integer;
5477 stat: TPlayerStatArray;
5478 cmd: string;
5480 procedure ParseGameFlag(Flag: LongWord; OffMsg, OnMsg: TStrings_Locale; OnMapChange: Boolean = False);
5481 var
5482 x: Boolean;
5483 begin
5484 if Length(P) > 1 then
5485 begin
5486 x := P[1] = '1';
5488 if x then
5489 gsGameFlags := gsGameFlags or Flag
5490 else
5491 gsGameFlags := gsGameFlags and (not Flag);
5493 if g_Game_IsServer then
5494 begin
5495 if x then
5496 gGameSettings.Options := gGameSettings.Options or Flag
5497 else
5498 gGameSettings.Options := gGameSettings.Options and (not Flag);
5499 if g_Game_IsNet then MH_SEND_GameSettings;
5500 end;
5501 end;
5503 if LongBool(gsGameFlags and Flag) then
5504 g_Console_Add(_lc[OnMsg])
5505 else
5506 g_Console_Add(_lc[OffMsg]);
5508 if OnMapChange and g_Game_IsServer then
5509 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5510 end;
5512 begin
5513 stat := nil;
5514 cmd := LowerCase(P[0]);
5516 if cmd = 'g_gamemode' then
5517 begin
5518 if (Length(P) > 1) then
5519 begin
5520 a := g_Game_TextToMode(P[1]);
5521 if a = GM_SINGLE then a := GM_COOP;
5522 gsGameMode := g_Game_ModeToText(a);
5523 if g_Game_IsServer then
5524 begin
5525 gSwitchGameMode := a;
5526 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
5527 (gState = STATE_INTERSINGLE) then
5528 gSwitchGameMode := GM_SINGLE;
5529 if not gGameOn then
5530 gGameSettings.GameMode := gSwitchGameMode;
5531 end;
5532 end;
5534 if gSwitchGameMode = gGameSettings.GameMode then
5535 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
5536 [g_Game_ModeToText(gGameSettings.GameMode)]))
5537 else
5538 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
5539 [g_Game_ModeToText(gGameSettings.GameMode),
5540 g_Game_ModeToText(gSwitchGameMode)]));
5541 end
5542 else if cmd = 'g_friendlyfire' then
5543 begin
5544 ParseGameFlag(GAME_OPTION_TEAMDAMAGE, I_MSG_FRIENDLY_FIRE_OFF, I_MSG_FRIENDLY_FIRE_ON);
5545 end
5546 else if cmd = 'g_friendly_absorb_damage' then
5547 begin
5548 ParseGameFlag(GAME_OPTION_TEAMABSORBDAMAGE, I_MSG_FRIENDLY_ABSORB_DAMAGE_OFF, I_MSG_FRIENDLY_ABSORB_DAMAGE_ON);
5549 end
5550 else if cmd = 'g_friendly_hit_trace' then
5551 begin
5552 ParseGameFlag(GAME_OPTION_TEAMHITTRACE, I_MSG_FRIENDLY_HIT_TRACE_OFF, I_MSG_FRIENDLY_HIT_TRACE_ON);
5553 end
5554 else if cmd = 'g_friendly_hit_projectile' then
5555 begin
5556 ParseGameFlag(GAME_OPTION_TEAMHITPROJECTILE, I_MSG_FRIENDLY_PROJECT_TRACE_OFF, I_MSG_FRIENDLY_PROJECT_TRACE_ON);
5557 end
5558 else if cmd = 'g_weaponstay' then
5559 begin
5560 ParseGameFlag(GAME_OPTION_WEAPONSTAY, I_MSG_WEAPONSTAY_OFF, I_MSG_WEAPONSTAY_ON);
5561 end
5562 else if cmd = 'g_allow_exit' then
5563 begin
5564 ParseGameFlag(GAME_OPTION_ALLOWEXIT, I_MSG_ALLOWEXIT_OFF, I_MSG_ALLOWEXIT_ON, True);
5565 end
5566 else if cmd = 'g_allow_monsters' then
5567 begin
5568 ParseGameFlag(GAME_OPTION_MONSTERS, I_MSG_ALLOWMON_OFF, I_MSG_ALLOWMON_ON, True);
5569 end
5570 else if cmd = 'g_allow_dropflag' then
5571 begin
5572 ParseGameFlag(GAME_OPTION_ALLOWDROPFLAG, I_MSG_ALLOWDROPFLAG_OFF, I_MSG_ALLOWDROPFLAG_ON);
5573 end
5574 else if cmd = 'g_throw_flag' then
5575 begin
5576 ParseGameFlag(GAME_OPTION_THROWFLAG, I_MSG_THROWFLAG_OFF, I_MSG_THROWFLAG_ON);
5577 end
5578 else if cmd = 'g_bot_vsplayers' then
5579 begin
5580 ParseGameFlag(GAME_OPTION_BOTVSPLAYER, I_MSG_BOTSVSPLAYERS_OFF, I_MSG_BOTSVSPLAYERS_ON);
5581 end
5582 else if cmd = 'g_bot_vsmonsters' then
5583 begin
5584 ParseGameFlag(GAME_OPTION_BOTVSMONSTER, I_MSG_BOTSVSMONSTERS_OFF, I_MSG_BOTSVSMONSTERS_ON);
5585 end
5586 else if cmd = 'g_dm_keys' then
5587 begin
5588 ParseGameFlag(GAME_OPTION_DMKEYS, I_MSG_DMKEYS_OFF, I_MSG_DMKEYS_ON, True);
5589 end
5590 else if cmd = 'g_gameflags' then
5591 begin
5592 if Length(P) > 1 then
5593 begin
5594 gsGameFlags := StrToDWordDef(P[1], gsGameFlags);
5595 if g_Game_IsServer then
5596 begin
5597 gGameSettings.Options := gsGameFlags;
5598 if g_Game_IsNet then MH_SEND_GameSettings;
5599 end;
5600 end;
5602 g_Console_Add(Format('%s %u', [cmd, gsGameFlags]));
5603 end
5604 else if cmd = 'g_warmup_time' then
5605 begin
5606 if Length(P) > 1 then
5607 begin
5608 gsWarmupTime := nclamp(StrToIntDef(P[1], gsWarmupTime), 0, $FFFF);
5609 if g_Game_IsServer then
5610 begin
5611 gGameSettings.WarmupTime := gsWarmupTime;
5612 // extend warmup if it's already going
5613 if gLMSRespawn = LMS_RESPAWN_WARMUP then
5614 begin
5615 gLMSRespawnTime := gTime + gsWarmupTime * 1000;
5616 if g_Game_IsNet then MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime);
5617 end;
5618 if g_Game_IsNet then MH_SEND_GameSettings;
5619 end;
5620 end;
5622 g_Console_Add(Format(_lc[I_MSG_WARMUP], [Integer(gsWarmupTime)]));
5623 if g_Game_IsServer then g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5624 end
5625 else if cmd = 'g_spawn_invul' then
5626 begin
5627 if Length(P) > 1 then
5628 begin
5629 gsSpawnInvul := nclamp(StrToIntDef(P[1], gsSpawnInvul), 0, $FFFF);
5630 if g_Game_IsServer then
5631 begin
5632 gGameSettings.SpawnInvul := gsSpawnInvul;
5633 if g_Game_IsNet then MH_SEND_GameSettings;
5634 end;
5635 end;
5637 g_Console_Add(Format('%s %d', [cmd, Integer(gsSpawnInvul)]));
5638 end
5639 else if cmd = 'g_item_respawn_time' then
5640 begin
5641 if Length(P) > 1 then
5642 begin
5643 gsItemRespawnTime := nclamp(StrToIntDef(P[1], gsItemRespawnTime), 0, $FFFF);
5644 if g_Game_IsServer then
5645 begin
5646 gGameSettings.ItemRespawnTime := gsItemRespawnTime;
5647 if g_Game_IsNet then MH_SEND_GameSettings;
5648 end;
5649 end;
5651 g_Console_Add(Format('%s %d', [cmd, Integer(gsItemRespawnTime)]));
5652 if g_Game_IsServer then g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5653 end
5654 else if cmd = 'sv_intertime' then
5655 begin
5656 if (Length(P) > 1) then
5657 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
5659 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
5660 end
5661 else if cmd = 'g_max_particles' then
5662 begin
5663 if Length(p) = 2 then
5664 begin
5665 a := Max(0, StrToInt(p[1]));
5666 g_GFX_SetMax(a)
5667 end
5668 else if Length(p) = 1 then
5669 begin
5670 e_LogWritefln('%s', [g_GFX_GetMax()])
5671 end
5672 else
5673 begin
5674 e_LogWritefln('usage: %s <n>', [cmd])
5675 end
5676 end
5677 else if cmd = 'g_max_shells' then
5678 begin
5679 if Length(p) = 2 then
5680 begin
5681 a := Max(0, StrToInt(p[1]));
5682 g_Shells_SetMax(a)
5683 end
5684 else if Length(p) = 1 then
5685 begin
5686 e_LogWritefln('%s', [g_Shells_GetMax()])
5687 end
5688 else
5689 begin
5690 e_LogWritefln('usage: %s <n>', [cmd])
5691 end
5692 end
5693 else if cmd = 'g_max_gibs' then
5694 begin
5695 if Length(p) = 2 then
5696 begin
5697 a := Max(0, StrToInt(p[1]));
5698 g_Gibs_SetMax(a)
5699 end
5700 else if Length(p) = 1 then
5701 begin
5702 e_LogWritefln('%s', [g_Gibs_GetMax()])
5703 end
5704 else
5705 begin
5706 e_LogWritefln('usage: %s <n>', [cmd])
5707 end
5708 end
5709 else if cmd = 'g_max_corpses' then
5710 begin
5711 if Length(p) = 2 then
5712 begin
5713 a := Max(0, StrToInt(p[1]));
5714 g_Corpses_SetMax(a)
5715 end
5716 else if Length(p) = 1 then
5717 begin
5718 e_LogWritefln('%s', [g_Corpses_GetMax()])
5719 end
5720 else
5721 begin
5722 e_LogWritefln('usage: %s <n>', [cmd])
5723 end
5724 end
5725 else if cmd = 'g_scorelimit' then
5726 begin
5727 if Length(P) > 1 then
5728 begin
5729 gsGoalLimit := nclamp(StrToIntDef(P[1], gsGoalLimit), 0, $FFFF);
5731 if g_Game_IsServer then
5732 begin
5733 b := 0;
5734 if gGameSettings.GameMode = GM_DM then
5735 begin // DM
5736 stat := g_Player_GetStats();
5737 if stat <> nil then
5738 for a := 0 to High(stat) do
5739 if stat[a].Frags > b then
5740 b := stat[a].Frags;
5741 end
5742 else // TDM/CTF
5743 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5745 // if someone has a higher score, set it to that instead
5746 gsGoalLimit := max(gsGoalLimit, b);
5747 gGameSettings.GoalLimit := gsGoalLimit;
5749 if g_Game_IsNet then MH_SEND_GameSettings;
5750 end;
5751 end;
5753 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [Integer(gsGoalLimit)]));
5754 end
5755 else if cmd = 'g_timelimit' then
5756 begin
5757 if Length(P) > 1 then
5758 begin
5759 gsTimeLimit := nclamp(StrToIntDef(P[1], gsTimeLimit), 0, $FFFF);
5760 if g_Game_IsServer then
5761 begin
5762 gGameSettings.TimeLimit := gsTimeLimit;
5763 if g_Game_IsNet then MH_SEND_GameSettings;
5764 end;
5765 end;
5766 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5767 [gsTimeLimit div 3600,
5768 (gsTimeLimit div 60) mod 60,
5769 gsTimeLimit mod 60]));
5770 end
5771 else if cmd = 'g_maxlives' then
5772 begin
5773 if Length(P) > 1 then
5774 begin
5775 gsMaxLives := nclamp(StrToIntDef(P[1], gsMaxLives), 0, $FFFF);
5776 if g_Game_IsServer then
5777 begin
5778 gGameSettings.MaxLives := gsMaxLives;
5779 if g_Game_IsNet then MH_SEND_GameSettings;
5780 end;
5781 end;
5783 g_Console_Add(Format(_lc[I_MSG_LIVES], [Integer(gsMaxLives)]));
5784 end;
5785 end;
5787 procedure PlayerSettingsCVars(P: SSArray);
5788 var
5789 cmd: string;
5790 team: Byte;
5792 function ParseTeam(s: string): Byte;
5793 begin
5794 result := 0;
5795 case s of
5796 'red', '1': result := TEAM_RED;
5797 'blue', '2': result := TEAM_BLUE;
5798 else result := TEAM_NONE;
5799 end;
5800 end;
5801 begin
5802 cmd := LowerCase(P[0]);
5803 case cmd of
5804 'p1_name':
5805 begin
5806 if (Length(P) > 1) then
5807 begin
5808 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
5809 if g_Game_IsClient then
5810 MC_SEND_PlayerSettings
5811 else if gGameOn and (gPlayer1 <> nil) then
5812 begin
5813 gPlayer1.Name := b_Text_Unformat(P[1]);
5814 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5815 end;
5816 end;
5817 end;
5818 'p2_name':
5819 begin
5820 if (Length(P) > 1) then
5821 begin
5822 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
5823 if g_Game_IsClient then
5824 MC_SEND_PlayerSettings
5825 else if gGameOn and (gPlayer2 <> nil) then
5826 begin
5827 gPlayer2.Name := b_Text_Unformat(P[1]);
5828 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5829 end;
5830 end;
5831 end;
5832 'p1_color':
5833 begin
5834 if Length(P) > 3 then
5835 begin
5836 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5837 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5838 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5839 if g_Game_IsClient then
5840 MC_SEND_PlayerSettings
5841 else if gGameOn and (gPlayer1 <> nil) then
5842 begin
5843 gPlayer1.SetColor(gPlayer1Settings.Color);
5844 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5845 end;
5846 end;
5847 end;
5848 'p2_color':
5849 begin
5850 if Length(P) > 3 then
5851 begin
5852 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5853 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5854 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5855 if g_Game_IsClient then
5856 MC_SEND_PlayerSettings
5857 else if gGameOn and (gPlayer2 <> nil) then
5858 begin
5859 gPlayer2.SetColor(gPlayer2Settings.Color);
5860 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5861 end;
5862 end;
5863 end;
5864 'p1_model':
5865 begin
5866 if (Length(P) > 1) then
5867 begin
5868 gPlayer1Settings.Model := P[1];
5869 if g_Game_IsClient then
5870 MC_SEND_PlayerSettings
5871 else if gGameOn and (gPlayer1 <> nil) then
5872 begin
5873 gPlayer1.FActualModelName := gPlayer1Settings.Model;
5874 gPlayer1.SetModel(gPlayer1Settings.Model);
5875 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
5876 end;
5877 end;
5878 end;
5879 'p2_model':
5880 begin
5881 if (Length(P) > 1) then
5882 begin
5883 gPlayer2Settings.Model := P[1];
5884 if g_Game_IsClient then
5885 MC_SEND_PlayerSettings
5886 else if gGameOn and (gPlayer2 <> nil) then
5887 begin
5888 gPlayer2.FActualModelName := gPlayer2Settings.Model;
5889 gPlayer2.SetModel(gPlayer2Settings.Model);
5890 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5891 end;
5892 end;
5893 end;
5894 'p1_team':
5895 begin
5896 // TODO: switch teams if in game or store this separately
5897 if (Length(P) > 1) then
5898 begin
5899 team := ParseTeam(P[1]);
5900 if team = TEAM_NONE then
5901 g_Console_Add('expected ''red'', ''blue'', 1 or 2')
5902 else if not gGameOn and not g_Game_IsNet then
5903 gPlayer1Settings.Team := team
5904 else
5905 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5906 end;
5907 end;
5908 'p2_team':
5909 begin
5910 // TODO: switch teams if in game or store this separately
5911 if (Length(P) > 1) then
5912 begin
5913 team := ParseTeam(P[1]);
5914 if team = TEAM_NONE then
5915 g_Console_Add('expected ''red'', ''blue'', 1 or 2')
5916 else if not gGameOn and not g_Game_IsNet then
5917 gPlayer2Settings.Team := team
5918 else
5919 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
5920 end;
5921 end;
5922 'p1_autoswitch':
5923 begin
5924 if (Length(P) = 2) then
5925 gPlayer1Settings.WeaponSwitch := EnsureRange(StrTointDef(P[1], 0), 0, 2);
5926 end;
5927 'p2_autoswitch':
5928 begin
5929 if (Length(P) = 2) then
5930 gPlayer2Settings.WeaponSwitch := EnsureRange(StrTointDef(P[1], 0), 0, 2);
5931 end;
5932 'p1_priority_kastet':
5933 begin
5934 if (Length(P) = 2) then
5935 gPlayer1Settings.WeaponPreferences[WEAPON_KASTET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5936 end;
5937 'p2_priority_kastet':
5938 begin
5939 if (Length(P) = 2) then
5940 gPlayer2Settings.WeaponPreferences[WEAPON_KASTET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5941 end;
5942 'p1_priority_saw':
5943 begin
5944 if (Length(P) = 2) then
5945 gPlayer1Settings.WeaponPreferences[WEAPON_SAW] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5946 end;
5947 'p2_priority_saw':
5948 begin
5949 if (Length(P) = 2) then
5950 gPlayer2Settings.WeaponPreferences[WEAPON_SAW] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5951 end;
5952 'p1_priority_pistol':
5953 begin
5954 if (Length(P) = 2) then
5955 gPlayer1Settings.WeaponPreferences[WEAPON_KASTET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5956 end;
5957 'p2_priority_pistol':
5958 begin
5959 if (Length(P) = 2) then
5960 gPlayer2Settings.WeaponPreferences[WEAPON_KASTET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5961 end;
5962 'p1_priority_shotgun1':
5963 begin
5964 if (Length(P) = 2) then
5965 gPlayer1Settings.WeaponPreferences[WEAPON_SHOTGUN1] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5966 end;
5967 'p2_priority_shotgun1':
5968 begin
5969 if (Length(P) = 2) then
5970 gPlayer2Settings.WeaponPreferences[WEAPON_SHOTGUN1] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5971 end;
5972 'p1_priority_shotgun2':
5973 begin
5974 if (Length(P) = 2) then
5975 gPlayer1Settings.WeaponPreferences[WEAPON_SHOTGUN2] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5976 end;
5977 'p2_priority_shotgun2':
5978 begin
5979 if (Length(P) = 2) then
5980 gPlayer2Settings.WeaponPreferences[WEAPON_SHOTGUN2] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5981 end;
5982 'p1_priority_chaingun':
5983 begin
5984 if (Length(P) = 2) then
5985 gPlayer1Settings.WeaponPreferences[WEAPON_CHAINGUN] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5986 end;
5987 'p2_priority_chaingun':
5988 begin
5989 if (Length(P) = 2) then
5990 gPlayer2Settings.WeaponPreferences[WEAPON_CHAINGUN] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5991 end;
5992 'p1_priority_rocketlauncher':
5993 begin
5994 if (Length(P) = 2) then
5995 gPlayer1Settings.WeaponPreferences[WEAPON_ROCKETLAUNCHER] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
5996 end;
5997 'p2_priority_rocketlauncher':
5998 begin
5999 if (Length(P) = 2) then
6000 gPlayer2Settings.WeaponPreferences[WEAPON_ROCKETLAUNCHER] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6001 end;
6002 'p1_priority_plasma':
6003 begin
6004 if (Length(P) = 2) then
6005 gPlayer1Settings.WeaponPreferences[WEAPON_PLASMA] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6006 end;
6007 'p2_priority_plasma':
6008 begin
6009 if (Length(P) = 2) then
6010 gPlayer2Settings.WeaponPreferences[WEAPON_PLASMA] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6011 end;
6012 'p1_priority_bfg':
6013 begin
6014 if (Length(P) = 2) then
6015 gPlayer1Settings.WeaponPreferences[WEAPON_BFG] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6016 end;
6017 'p2_priority_bfg':
6018 begin
6019 if (Length(P) = 2) then
6020 gPlayer2Settings.WeaponPreferences[WEAPON_BFG] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6021 end;
6022 'p1_priority_super':
6023 begin
6024 if (Length(P) = 2) then
6025 gPlayer1Settings.WeaponPreferences[WEAPON_SUPERPULEMET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6026 end;
6027 'p2_priority_super':
6028 begin
6029 if (Length(P) = 2) then
6030 gPlayer2Settings.WeaponPreferences[WEAPON_SUPERPULEMET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6031 end;
6032 'p1_priority_flamethrower':
6033 begin
6034 if (Length(P) = 2) then
6035 gPlayer1Settings.WeaponPreferences[WEAPON_FLAMETHROWER] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6036 end;
6037 'p2_priority_flamethrower':
6038 begin
6039 if (Length(P) = 2) then
6040 gPlayer2Settings.WeaponPreferences[WEAPON_FLAMETHROWER] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6041 end;
6042 'p1_priority_berserk':
6043 begin
6044 if (Length(P) = 2) then
6045 gPlayer1Settings.WeaponPreferences[WP_LAST+1] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6046 end;
6047 'p2_priority_berserk':
6048 begin
6049 if (Length(P) = 2) then
6050 gPlayer2Settings.WeaponPreferences[WP_LAST+1] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1);
6051 end;
6052 end;
6053 end;
6055 procedure PrintHeapStats();
6056 var
6057 hs: TFPCHeapStatus;
6058 begin
6059 hs := GetFPCHeapStatus();
6060 e_LogWriteLn ('v===== heap status =====v');
6061 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
6062 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
6063 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
6064 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
6065 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
6066 e_LogWriteLn ('^=======================^');
6067 end;
6069 procedure DebugCommands(P: SSArray);
6070 var
6071 a, b: Integer;
6072 cmd: string;
6073 //pt: TDFPoint;
6074 mon: TMonster;
6075 begin
6076 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
6077 if {gDebugMode}conIsCheatsEnabled then
6078 begin
6079 cmd := LowerCase(P[0]);
6080 if cmd = 'd_window' then
6081 begin
6082 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
6083 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
6084 end
6085 else if cmd = 'd_sounds' then
6086 begin
6087 if (Length(P) > 1) and
6088 ((P[1] = '1') or (P[1] = '0')) then
6089 g_Debug_Sounds := (P[1][1] = '1');
6091 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
6092 end
6093 else if cmd = 'd_frames' then
6094 begin
6095 if (Length(P) > 1) and
6096 ((P[1] = '1') or (P[1] = '0')) then
6097 g_Debug_Frames := (P[1][1] = '1');
6099 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
6100 end
6101 else if cmd = 'd_winmsg' then
6102 begin
6103 if (Length(P) > 1) and
6104 ((P[1] = '1') or (P[1] = '0')) then
6105 g_Debug_WinMsgs := (P[1][1] = '1');
6107 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
6108 end
6109 else if (cmd = 'd_monoff') and not g_Game_IsNet then
6110 begin
6111 if (Length(P) > 1) and
6112 ((P[1] = '1') or (P[1] = '0')) then
6113 g_Debug_MonsterOff := (P[1][1] = '1');
6115 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
6116 end
6117 else if (cmd = 'd_botoff') and not g_Game_IsNet then
6118 begin
6119 if Length(P) > 1 then
6120 case P[1][1] of
6121 '0': g_debug_BotAIOff := 0;
6122 '1': g_debug_BotAIOff := 1;
6123 '2': g_debug_BotAIOff := 2;
6124 '3': g_debug_BotAIOff := 3;
6125 end;
6127 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
6128 end
6129 else if cmd = 'd_monster' then
6130 begin
6131 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
6132 if Length(P) < 2 then
6133 begin
6134 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
6135 g_Console_Add('ID | Name');
6136 for b := MONSTER_DEMON to MONSTER_MAN do
6137 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
6138 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
6139 end else
6140 begin
6141 a := StrToIntDef(P[1], 0);
6142 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
6143 a := g_Mons_TypeIdByName(P[1]);
6145 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
6146 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
6147 else
6148 begin
6149 with gPlayer1.Obj do
6150 begin
6151 mon := g_Monsters_Create(a,
6152 X + Rect.X + (Rect.Width div 2),
6153 Y + Rect.Y + Rect.Height,
6154 gPlayer1.Direction, True);
6155 end;
6156 if (Length(P) > 2) and (mon <> nil) then
6157 begin
6158 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
6159 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
6160 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
6161 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
6162 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
6163 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
6164 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
6165 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
6166 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
6167 end;
6168 end;
6169 end;
6170 end
6171 else if (cmd = 'd_health') then
6172 begin
6173 if (Length(P) > 1) and
6174 ((P[1] = '1') or (P[1] = '0')) then
6175 g_debug_HealthBar := (P[1][1] = '1');
6177 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
6178 end
6179 else if (cmd = 'd_player') then
6180 begin
6181 if (Length(P) > 1) and
6182 ((P[1] = '1') or (P[1] = '0')) then
6183 g_debug_Player := (P[1][1] = '1');
6185 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
6186 end
6187 else if (cmd = 'd_mem') then
6188 begin
6189 PrintHeapStats();
6190 end;
6191 end
6192 else
6193 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
6194 end;
6197 procedure GameCheats(P: SSArray);
6198 var
6199 cmd: string;
6200 f, a: Integer;
6201 plr: TPlayer;
6202 begin
6203 if (not gGameOn) or (not conIsCheatsEnabled) then
6204 begin
6205 g_Console_Add('not available');
6206 exit;
6207 end;
6208 plr := gPlayer1;
6209 if plr = nil then
6210 begin
6211 g_Console_Add('where is the player?!');
6212 exit;
6213 end;
6214 cmd := LowerCase(P[0]);
6215 // god
6216 if cmd = 'god' then
6217 begin
6218 plr.GodMode := not plr.GodMode;
6219 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
6220 exit;
6221 end;
6222 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
6223 if cmd = 'give' then
6224 begin
6225 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
6226 for f := 1 to High(P) do
6227 begin
6228 cmd := LowerCase(P[f]);
6229 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
6230 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
6231 if cmd = 'exit' then
6232 begin
6233 if gTriggers <> nil then
6234 begin
6235 for a := 0 to High(gTriggers) do
6236 begin
6237 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6238 begin
6239 g_Console_Add('player left the map');
6240 gExitByTrigger := True;
6241 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
6242 g_Game_ExitLevel(gTriggers[a].tgcMap);
6243 break;
6244 end;
6245 end;
6246 end;
6247 continue;
6248 end;
6250 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
6251 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
6252 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
6253 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
6254 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
6256 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
6257 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
6259 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
6260 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;
6262 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
6263 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
6265 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
6266 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
6268 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
6269 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
6271 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
6272 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
6273 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
6275 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
6276 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
6277 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
6278 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;
6279 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
6280 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
6282 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;
6283 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;
6284 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;
6285 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;
6286 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;
6287 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;
6289 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
6290 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;
6292 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;
6293 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;
6295 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
6297 if cmd = 'ammo' then
6298 begin
6299 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
6300 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
6301 plr.GiveItem(ITEM_AMMO_CELL_BIG);
6302 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
6303 plr.GiveItem(ITEM_AMMO_FUELCAN);
6304 g_Console_Add('player got some ammo');
6305 continue;
6306 end;
6308 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
6309 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
6311 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
6312 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
6314 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
6315 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
6317 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
6318 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
6320 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
6322 if cmd = 'weapons' then
6323 begin
6324 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
6325 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
6326 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
6327 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
6328 plr.GiveItem(ITEM_WEAPON_PLASMA);
6329 plr.GiveItem(ITEM_WEAPON_BFG);
6330 g_Console_Add('player got weapons');
6331 continue;
6332 end;
6334 if cmd = 'keys' then
6335 begin
6336 plr.GiveItem(ITEM_KEY_RED);
6337 plr.GiveItem(ITEM_KEY_GREEN);
6338 plr.GiveItem(ITEM_KEY_BLUE);
6339 g_Console_Add('player got all keys');
6340 continue;
6341 end;
6343 g_Console_Add('i don''t know how to give '''+cmd+'''!');
6344 end;
6345 exit;
6346 end;
6347 // open
6348 if cmd = 'open' then
6349 begin
6350 g_Console_Add('player activated sesame');
6351 g_Triggers_OpenAll();
6352 exit;
6353 end;
6354 // fly
6355 if cmd = 'fly' then
6356 begin
6357 gFly := not gFly;
6358 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
6359 exit;
6360 end;
6361 // noclip
6362 if cmd = 'noclip' then
6363 begin
6364 plr.SwitchNoClip;
6365 g_Console_Add('wall hardeness adjusted');
6366 exit;
6367 end;
6368 // notarget
6369 if cmd = 'notarget' then
6370 begin
6371 plr.NoTarget := not plr.NoTarget;
6372 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
6373 exit;
6374 end;
6375 // noreload
6376 if cmd = 'noreload' then
6377 begin
6378 plr.NoReload := not plr.NoReload;
6379 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
6380 exit;
6381 end;
6382 // speedy
6383 if cmd = 'speedy' then
6384 begin
6385 MAX_RUNVEL := 32-MAX_RUNVEL;
6386 g_Console_Add('speed adjusted');
6387 exit;
6388 end;
6389 // jumpy
6390 if cmd = 'jumpy' then
6391 begin
6392 VEL_JUMP := 30-VEL_JUMP;
6393 g_Console_Add('jump height adjusted');
6394 exit;
6395 end;
6396 // automap
6397 if cmd = 'automap' then
6398 begin
6399 gShowMap := not gShowMap;
6400 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
6401 exit;
6402 end;
6403 // aimline
6404 if cmd = 'aimline' then
6405 begin
6406 gAimLine := not gAimLine;
6407 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
6408 exit;
6409 end;
6410 end;
6412 procedure GameCommands(P: SSArray);
6413 var
6414 a, b: Integer;
6415 s, pw: String;
6416 chstr: string;
6417 cmd: string;
6418 pl: pTNetClient = nil;
6419 plr: TPlayer;
6420 prt: Word;
6421 nm: Boolean;
6422 listen: LongWord;
6423 found: Boolean;
6424 begin
6425 // Îáùèå êîìàíäû:
6426 cmd := LowerCase(P[0]);
6427 chstr := '';
6428 if cmd = 'pause' then
6429 begin
6430 if (g_ActiveWindow = nil) then
6431 g_Game_Pause(not gPauseMain);
6432 end
6433 else if cmd = 'endgame' then
6434 gExit := EXIT_SIMPLE
6435 else if cmd = 'restart' then
6436 begin
6437 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
6438 begin
6439 if g_Game_IsClient then
6440 begin
6441 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6442 Exit;
6443 end;
6444 g_Game_Restart();
6445 end else
6446 g_Console_Add(_lc[I_MSG_NOT_GAME]);
6447 end
6448 else if cmd = 'kick' then
6449 begin
6450 if g_Game_IsServer then
6451 begin
6452 if Length(P) < 2 then
6453 begin
6454 g_Console_Add('kick <name>');
6455 Exit;
6456 end;
6457 if P[1] = '' then
6458 begin
6459 g_Console_Add('kick <name>');
6460 Exit;
6461 end;
6463 if g_Game_IsNet then
6464 pl := g_Net_Client_ByName(P[1]);
6465 if (pl <> nil) then
6466 begin
6467 s := g_Net_ClientName_ByID(pl^.ID);
6468 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
6469 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
6470 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
6471 g_Net_Slist_ServerPlayerLeaves();
6472 end else if gPlayers <> nil then
6473 for a := Low(gPlayers) to High(gPlayers) do
6474 if gPlayers[a] <> nil then
6475 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
6476 begin
6477 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
6478 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
6479 continue;
6480 gPlayers[a].Lives := 0;
6481 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
6482 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
6483 g_Player_Remove(gPlayers[a].UID);
6484 g_Net_Slist_ServerPlayerLeaves();
6485 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
6486 g_Bot_MixNames();
6487 end;
6488 end else
6489 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6490 end
6491 else if cmd = 'kick_id' then
6492 begin
6493 if g_Game_IsServer and g_Game_IsNet then
6494 begin
6495 if Length(P) < 2 then
6496 begin
6497 g_Console_Add('kick_id <client ID>');
6498 Exit;
6499 end;
6500 if P[1] = '' then
6501 begin
6502 g_Console_Add('kick_id <client ID>');
6503 Exit;
6504 end;
6506 a := StrToIntDef(P[1], 0);
6507 if (NetClients <> nil) and (a <= High(NetClients)) then
6508 begin
6509 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6510 begin
6511 s := g_Net_ClientName_ByID(NetClients[a].ID);
6512 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
6513 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
6514 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
6515 g_Net_Slist_ServerPlayerLeaves();
6516 end;
6517 end;
6518 end else
6519 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6520 end
6521 else if cmd = 'kick_pid' then
6522 begin
6523 if g_Game_IsServer and g_Game_IsNet then
6524 begin
6525 if Length(P) < 2 then
6526 begin
6527 g_Console_Add('kick_pid <player ID>');
6528 Exit;
6529 end;
6530 if P[1] = '' then
6531 begin
6532 g_Console_Add('kick_pid <player ID>');
6533 Exit;
6534 end;
6536 a := StrToIntDef(P[1], 0);
6537 pl := g_Net_Client_ByPlayer(a);
6538 if (pl <> nil) and pl^.Used and (pl^.Peer <> nil) then
6539 begin
6540 s := g_Net_ClientName_ByID(pl^.ID);
6541 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
6542 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
6543 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
6544 g_Net_Slist_ServerPlayerLeaves();
6545 end;
6546 end else
6547 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6548 end
6549 else if cmd = 'ban' then
6550 begin
6551 if g_Game_IsServer and g_Game_IsNet then
6552 begin
6553 if Length(P) < 2 then
6554 begin
6555 g_Console_Add('ban <name>');
6556 Exit;
6557 end;
6558 if P[1] = '' then
6559 begin
6560 g_Console_Add('ban <name>');
6561 Exit;
6562 end;
6564 pl := g_Net_Client_ByName(P[1]);
6565 if (pl <> nil) then
6566 begin
6567 s := g_Net_ClientName_ByID(pl^.ID);
6568 g_Net_BanHost(pl^.Peer^.address.host, False);
6569 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
6570 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6571 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6572 g_Net_Slist_ServerPlayerLeaves();
6573 end else
6574 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6575 end else
6576 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6577 end
6578 else if cmd = 'ban_id' then
6579 begin
6580 if g_Game_IsServer and g_Game_IsNet then
6581 begin
6582 if Length(P) < 2 then
6583 begin
6584 g_Console_Add('ban_id <client ID>');
6585 Exit;
6586 end;
6587 if P[1] = '' then
6588 begin
6589 g_Console_Add('ban_id <client ID>');
6590 Exit;
6591 end;
6593 a := StrToIntDef(P[1], 0);
6594 if (NetClients <> nil) and (a <= High(NetClients)) then
6595 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6596 begin
6597 s := g_Net_ClientName_ByID(NetClients[a].ID);
6598 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
6599 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
6600 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6601 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6602 g_Net_Slist_ServerPlayerLeaves();
6603 end;
6604 end else
6605 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6606 end
6607 else if cmd = 'ban_pid' then
6608 begin
6609 if g_Game_IsServer and g_Game_IsNet then
6610 begin
6611 if Length(P) < 2 then
6612 begin
6613 g_Console_Add('ban_pid <player ID>');
6614 Exit;
6615 end;
6616 if P[1] = '' then
6617 begin
6618 g_Console_Add('ban_pid <player ID>');
6619 Exit;
6620 end;
6622 a := StrToIntDef(P[1], 0);
6623 pl := g_Net_Client_ByPlayer(a);
6624 if (pl <> nil) and pl^.Used and (pl^.Peer <> nil) then
6625 begin
6626 s := g_Net_ClientName_ByID(pl^.ID);
6627 g_Net_BanHost(pl^.Peer^.address.host, False);
6628 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
6629 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6630 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6631 g_Net_Slist_ServerPlayerLeaves();
6632 end;
6633 end else
6634 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6635 end
6636 else if cmd = 'permban' then
6637 begin
6638 if g_Game_IsServer and g_Game_IsNet then
6639 begin
6640 if Length(P) < 2 then
6641 begin
6642 g_Console_Add('permban <name>');
6643 Exit;
6644 end;
6645 if P[1] = '' then
6646 begin
6647 g_Console_Add('permban <name>');
6648 Exit;
6649 end;
6651 pl := g_Net_Client_ByName(P[1]);
6652 if (pl <> nil) then
6653 begin
6654 s := g_Net_ClientName_ByID(pl^.ID);
6655 g_Net_BanHost(pl^.Peer^.address.host);
6656 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
6657 g_Net_SaveBanList();
6658 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6659 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6660 g_Net_Slist_ServerPlayerLeaves();
6661 end else
6662 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6663 end else
6664 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6665 end
6666 else if cmd = 'permban_id' then
6667 begin
6668 if g_Game_IsServer and g_Game_IsNet then
6669 begin
6670 if Length(P) < 2 then
6671 begin
6672 g_Console_Add('permban_id <client ID>');
6673 Exit;
6674 end;
6675 if P[1] = '' then
6676 begin
6677 g_Console_Add('permban_id <client ID>');
6678 Exit;
6679 end;
6681 a := StrToIntDef(P[1], 0);
6682 if (NetClients <> nil) and (a <= High(NetClients)) then
6683 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6684 begin
6685 s := g_Net_ClientName_ByID(NetClients[a].ID);
6686 g_Net_BanHost(NetClients[a].Peer^.address.host);
6687 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
6688 g_Net_SaveBanList();
6689 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6690 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6691 g_Net_Slist_ServerPlayerLeaves();
6692 end;
6693 end else
6694 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6695 end
6696 else if cmd = 'permban_pid' then
6697 begin
6698 if g_Game_IsServer and g_Game_IsNet then
6699 begin
6700 if Length(P) < 2 then
6701 begin
6702 g_Console_Add('permban_pid <player ID>');
6703 Exit;
6704 end;
6705 if P[1] = '' then
6706 begin
6707 g_Console_Add('permban_pid <player ID>');
6708 Exit;
6709 end;
6711 a := StrToIntDef(P[1], 0);
6712 pl := g_Net_Client_ByPlayer(a);
6713 if (pl <> nil) and pl^.Used and (pl^.Peer <> nil) then
6714 begin
6715 s := g_Net_ClientName_ByID(pl^.ID);
6716 g_Net_BanHost(pl^.Peer^.address.host);
6717 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
6718 g_Net_SaveBanList();
6719 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
6720 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
6721 g_Net_Slist_ServerPlayerLeaves();
6722 end;
6723 end else
6724 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6725 end
6726 else if cmd = 'permban_ip' then
6727 begin
6728 if g_Game_IsServer and g_Game_IsNet then
6729 begin
6730 if Length(P) < 2 then
6731 begin
6732 g_Console_Add('permban_ip <IP address>');
6733 Exit;
6734 end;
6735 if P[1] = '' then
6736 begin
6737 g_Console_Add('permban_ip <IP address>');
6738 Exit;
6739 end;
6741 g_Net_BanHost(P[1]);
6742 g_Net_SaveBanList();
6743 g_Console_Add(Format(_lc[I_PLAYER_BAN], [P[1]]));
6744 end else
6745 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6746 end
6747 else if cmd = 'unban' then
6748 begin
6749 if g_Game_IsServer and g_Game_IsNet then
6750 begin
6751 if Length(P) < 2 then
6752 begin
6753 g_Console_Add('unban <IP Address>');
6754 Exit;
6755 end;
6756 if P[1] = '' then
6757 begin
6758 g_Console_Add('unban <IP Address>');
6759 Exit;
6760 end;
6762 if g_Net_UnbanHost(P[1]) then
6763 begin
6764 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
6765 g_Net_SaveBanList();
6766 end else
6767 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
6768 end else
6769 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6770 end
6771 else if cmd = 'clientlist' then
6772 begin
6773 if g_Game_IsServer and g_Game_IsNet then
6774 begin
6775 b := 0;
6776 if NetClients <> nil then
6777 for a := Low(NetClients) to High(NetClients) do
6778 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
6779 begin
6780 plr := g_Player_Get(NetClients[a].Player);
6781 if plr = nil then continue;
6782 Inc(b);
6783 g_Console_Add(Format('#%2d: %-15s | %s', [a,
6784 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
6785 end;
6786 if b = 0 then
6787 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
6788 end else
6789 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6790 end
6791 else if cmd = 'connect' then
6792 begin
6793 if (NetMode = NET_NONE) then
6794 begin
6795 if Length(P) < 2 then
6796 begin
6797 g_Console_Add('connect <IP> [port] [password]');
6798 Exit;
6799 end;
6800 if P[1] = '' then
6801 begin
6802 g_Console_Add('connect <IP> [port] [password]');
6803 Exit;
6804 end;
6806 if Length(P) > 2 then
6807 prt := StrToIntDef(P[2], 25666)
6808 else
6809 prt := 25666;
6811 if Length(P) > 3 then
6812 pw := P[3]
6813 else
6814 pw := '';
6816 g_Game_StartClient(P[1], prt, pw);
6817 end;
6818 end
6819 else if cmd = 'disconnect' then
6820 begin
6821 if (NetMode = NET_CLIENT) then
6822 g_Net_Disconnect();
6823 end
6824 else if cmd = 'reconnect' then
6825 begin
6826 if (NetMode = NET_SERVER) then
6827 Exit;
6829 if (NetMode = NET_CLIENT) then
6830 begin
6831 g_Net_Disconnect();
6832 gExit := EXIT_SIMPLE;
6833 EndGame;
6834 end;
6836 //TODO: Use last successful password to reconnect, instead of ''
6837 g_Game_StartClient(NetClientIP, NetClientPort, '');
6838 end
6839 else if (cmd = 'addbot') or
6840 (cmd = 'bot_add') then
6841 begin
6842 if Length(P) > 2 then
6843 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2), StrToIntDef(P[2], 100))
6844 else if Length(P) > 1 then
6845 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
6846 else
6847 g_Bot_Add(TEAM_NONE, 2);
6848 end
6849 else if cmd = 'bot_addlist' then
6850 begin
6851 if Length(P) > 1 then
6852 begin
6853 if Length(P) = 2 then
6854 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
6855 else if Length(P) = 3 then
6856 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1), StrToIntDef(P[2], 100))
6857 else
6858 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
6859 end;
6860 end
6861 else if cmd = 'bot_removeall' then
6862 g_Bot_RemoveAll()
6863 else if cmd = 'chat' then
6864 begin
6865 if g_Game_IsNet then
6866 begin
6867 if Length(P) > 1 then
6868 begin
6869 for a := 1 to High(P) do
6870 chstr := chstr + P[a] + ' ';
6872 if Length(chstr) > 200 then SetLength(chstr, 200);
6874 if Length(chstr) < 1 then
6875 begin
6876 g_Console_Add('chat <text>');
6877 Exit;
6878 end;
6880 chstr := b_Text_Format(chstr);
6881 if g_Game_IsClient then
6882 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
6883 else
6884 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
6885 end
6886 else
6887 g_Console_Add('chat <text>');
6888 end else
6889 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6890 end
6891 else if cmd = 'teamchat' then
6892 begin
6893 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
6894 begin
6895 if Length(P) > 1 then
6896 begin
6897 for a := 1 to High(P) do
6898 chstr := chstr + P[a] + ' ';
6900 if Length(chstr) > 200 then SetLength(chstr, 200);
6902 if Length(chstr) < 1 then
6903 begin
6904 g_Console_Add('teamchat <text>');
6905 Exit;
6906 end;
6908 chstr := b_Text_Format(chstr);
6909 if g_Game_IsClient then
6910 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
6911 else
6912 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
6913 gPlayer1Settings.Team);
6914 end
6915 else
6916 g_Console_Add('teamchat <text>');
6917 end else
6918 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6919 end
6920 else if cmd = 'game' then
6921 begin
6922 if gGameSettings.GameType <> GT_NONE then
6923 begin
6924 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6925 Exit;
6926 end;
6927 if Length(P) = 1 then
6928 begin
6929 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
6930 Exit;
6931 end;
6932 // game not started yet, load fist map from some wad
6933 found := false;
6934 s := addWadExtension(P[1]);
6935 found := e_FindResource(AllMapDirs, s);
6936 P[1] := s;
6937 if found then
6938 begin
6939 P[1] := ExpandFileName(P[1]);
6940 // if map not choosed then set first map
6941 if Length(P) < 3 then
6942 begin
6943 SetLength(P, 3);
6944 P[2] := g_Game_GetFirstMap(P[1]);
6945 end;
6947 s := P[1] + ':\' + UpperCase(P[2]);
6949 if g_Map_Exist(s) then
6950 begin
6951 // start game
6952 g_Game_Free();
6953 with gGameSettings do
6954 begin
6955 Options := gsGameFlags;
6956 GameMode := g_Game_TextToMode(gsGameMode);
6957 if gSwitchGameMode <> GM_NONE then
6958 GameMode := gSwitchGameMode;
6959 if GameMode = GM_NONE then GameMode := GM_DM;
6960 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6961 b := 1;
6962 if Length(P) >= 4 then
6963 b := StrToIntDef(P[3], 1);
6964 g_Game_StartCustom(s, GameMode, TimeLimit,
6965 GoalLimit, MaxLives, Options, b);
6966 end;
6967 end
6968 else
6969 if P[2] = '' then
6970 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6971 else
6972 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[2]), P[1]]));
6973 end else
6974 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6975 end
6976 else if cmd = 'host' then
6977 begin
6978 if gGameSettings.GameType <> GT_NONE then
6979 begin
6980 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6981 Exit;
6982 end;
6983 if Length(P) < 4 then
6984 begin
6985 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
6986 Exit;
6987 end;
6988 if not StrToIp(P[1], listen) then
6989 Exit;
6990 prt := StrToIntDef(P[2], 25666);
6992 s := addWadExtension(P[3]);
6993 found := e_FindResource(AllMapDirs, s);
6994 P[3] := s;
6995 if found then
6996 begin
6997 // get first map in wad, if not specified
6998 if Length(P) < 5 then
6999 begin
7000 SetLength(P, 5);
7001 P[4] := g_Game_GetFirstMap(P[1]);
7002 end;
7003 s := P[3] + ':\' + UpperCase(P[4]);
7004 if g_Map_Exist(s) then
7005 begin
7006 // start game
7007 g_Game_Free();
7008 with gGameSettings do
7009 begin
7010 Options := gsGameFlags;
7011 GameMode := g_Game_TextToMode(gsGameMode);
7012 if gSwitchGameMode <> GM_NONE then GameMode := gSwitchGameMode;
7013 if GameMode = GM_NONE then GameMode := GM_DM;
7014 if GameMode = GM_SINGLE then GameMode := GM_COOP;
7015 b := 0;
7016 if Length(P) >= 6 then
7017 b := StrToIntDef(P[5], 0);
7018 g_Game_StartServer(s, GameMode, TimeLimit, GoalLimit, MaxLives, Options, b, listen, prt)
7019 end
7020 end
7021 else
7022 begin
7023 if P[4] = '' then
7024 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
7025 else
7026 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]))
7027 end
7028 end
7029 else
7030 begin
7031 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]))
7032 end
7033 end
7034 else if cmd = 'map' then
7035 begin
7036 if Length(P) = 1 then
7037 begin
7038 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
7039 begin
7040 g_Console_Add(cmd + ' <MAP>');
7041 g_Console_Add(cmd + ' <WAD> [MAP]')
7042 end
7043 else
7044 begin
7045 g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
7046 end
7047 end
7048 else
7049 begin
7050 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
7051 begin
7052 if Length(P) < 3 then
7053 begin
7054 // first param is map or wad
7055 s := UpperCase(P[1]);
7056 if g_Map_Exist(gGameSettings.WAD + ':\' + s) then
7057 begin
7058 gExitByTrigger := False;
7059 if gGameOn then
7060 begin
7061 // already in game, finish current map
7062 gNextMap := s;
7063 gExit := EXIT_ENDLEVELCUSTOM;
7064 end
7065 else
7066 begin
7067 // intermission, so change map immediately
7068 g_Game_ChangeMap(s)
7069 end
7070 end
7071 else
7072 begin
7073 s := P[1];
7074 found := e_FindResource(AllMapDirs, s);
7075 P[1] := s;
7076 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, 'WAD ' + P[1]]));
7077 if found then
7078 begin
7079 // no such map, found wad
7080 pw := P[1];
7081 SetLength(P, 3);
7082 P[1] := ExpandFileName(pw);
7083 P[2] := g_Game_GetFirstMap(P[1]);
7084 s := P[1] + ':\' + P[2];
7085 if g_Map_Exist(s) then
7086 begin
7087 gExitByTrigger := False;
7088 if gGameOn then
7089 begin
7090 // already in game, finish current map
7091 gNextMap := s;
7092 gExit := EXIT_ENDLEVELCUSTOM
7093 end
7094 else
7095 begin
7096 // intermission, so change map immediately
7097 g_Game_ChangeMap(s)
7098 end
7099 end
7100 else
7101 begin
7102 if P[2] = '' then
7103 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
7104 else
7105 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
7106 end
7107 end
7108 else
7109 begin
7110 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
7111 end
7112 end;
7113 end
7114 else
7115 begin
7116 s := addWadExtension(P[1]);
7117 found := e_FindResource(AllMapDirs, s);
7118 P[1] := s;
7119 if found then
7120 begin
7121 P[2] := UpperCase(P[2]);
7122 s := P[1] + ':\' + P[2];
7123 if g_Map_Exist(s) then
7124 begin
7125 gExitByTrigger := False;
7126 if gGameOn then
7127 begin
7128 gNextMap := s;
7129 gExit := EXIT_ENDLEVELCUSTOM
7130 end
7131 else
7132 begin
7133 g_Game_ChangeMap(s)
7134 end
7135 end
7136 else
7137 begin
7138 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
7139 end
7140 end
7141 else
7142 begin
7143 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
7144 end
7145 end
7146 end
7147 else
7148 begin
7149 g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
7150 end
7151 end
7152 end
7153 else if cmd = 'nextmap' then
7154 begin
7155 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
7156 begin
7157 g_Console_Add(_lc[I_MSG_NOT_GAME])
7158 end
7159 else
7160 begin
7161 nm := True;
7162 if Length(P) = 1 then
7163 begin
7164 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
7165 begin
7166 g_Console_Add(cmd + ' <MAP>');
7167 g_Console_Add(cmd + ' <WAD> [MAP]');
7168 end
7169 else
7170 begin
7171 nm := False;
7172 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
7173 end;
7174 end
7175 else
7176 begin
7177 nm := False;
7178 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
7179 begin
7180 if Length(P) < 3 then
7181 begin
7182 // first param is map or wad
7183 s := UpperCase(P[1]);
7184 if g_Map_Exist(gGameSettings.WAD + ':\' + s) then
7185 begin
7186 // map founded
7187 gExitByTrigger := False;
7188 gNextMap := s;
7189 nm := True;
7190 end
7191 else
7192 begin
7193 // no such map, found wad
7194 pw := addWadExtension(P[1]);
7195 found := e_FindResource(MapDirs, pw);
7196 if not found then
7197 found := e_FindResource(WadDirs, pw);
7198 P[1] := pw;
7199 g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
7200 if found then
7201 begin
7202 // map not specified, select first map
7203 SetLength(P, 3);
7204 P[2] := g_Game_GetFirstMap(P[1]);
7205 s := P[1] + ':\' + P[2];
7206 if g_Map_Exist(s) then
7207 begin
7208 gExitByTrigger := False;
7209 gNextMap := s;
7210 nm := True
7211 end
7212 else
7213 begin
7214 if P[2] = '' then
7215 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
7216 else
7217 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
7218 end
7219 end
7220 else
7221 begin
7222 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
7223 end
7224 end
7225 end
7226 else
7227 begin
7228 // specified two params wad + map
7229 pw := addWadExtension(P[1]);
7230 found := e_FindResource(MapDirs, pw);
7231 if not found then
7232 found := e_FindResource(MapDirs, pw);
7233 P[1] := pw;
7234 if found then
7235 begin
7236 P[2] := UpperCase(P[2]);
7237 s := P[1] + ':\' + P[2];
7238 if g_Map_Exist(s) then
7239 begin
7240 gExitByTrigger := False;
7241 gNextMap := s;
7242 nm := True
7243 end
7244 else
7245 begin
7246 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
7247 end
7248 end
7249 else
7250 begin
7251 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
7252 end
7253 end
7254 end
7255 else
7256 begin
7257 g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
7258 end
7259 end;
7260 if nm then
7261 begin
7262 if gNextMap = '' then
7263 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
7264 else
7265 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]))
7266 end
7267 end
7268 end
7269 else if (cmd = 'endmap') or (cmd = 'goodbye') then
7270 begin
7271 if not gGameOn then
7272 begin
7273 g_Console_Add(_lc[I_MSG_NOT_GAME])
7274 end
7275 else
7276 begin
7277 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
7278 begin
7279 gExitByTrigger := False;
7280 // next map not specified, try to find trigger EXIT
7281 if (gNextMap = '') and (gTriggers <> nil) then
7282 begin
7283 for a := 0 to High(gTriggers) do
7284 begin
7285 if gTriggers[a].TriggerType = TRIGGER_EXIT then
7286 begin
7287 gExitByTrigger := True;
7288 //gNextMap := gTriggers[a].Data.MapName;
7289 gNextMap := gTriggers[a].tgcMap;
7290 Break
7291 end
7292 end
7293 end;
7294 if gNextMap = '' then
7295 gNextMap := g_Game_GetNextMap();
7296 if not isWadPath(gNextMap) then
7297 s := gGameSettings.WAD + ':\' + gNextMap
7298 else
7299 s := gNextMap;
7300 if g_Map_Exist(s) then
7301 gExit := EXIT_ENDLEVELCUSTOM
7302 else
7303 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]))
7304 end
7305 else
7306 begin
7307 g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
7308 end
7309 end
7310 end
7311 else if (cmd = 'event') then
7312 begin
7313 if (Length(P) <= 1) then
7314 begin
7315 for a := 0 to High(gEvents) do
7316 if gEvents[a].Command = '' then
7317 g_Console_Add(gEvents[a].Name + ' <none>')
7318 else
7319 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
7320 Exit;
7321 end;
7322 if (Length(P) = 2) then
7323 begin
7324 for a := 0 to High(gEvents) do
7325 if gEvents[a].Name = P[1] then
7326 if gEvents[a].Command = '' then
7327 g_Console_Add(gEvents[a].Name + ' <none>')
7328 else
7329 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
7330 Exit;
7331 end;
7332 for a := 0 to High(gEvents) do
7333 if gEvents[a].Name = P[1] then
7334 begin
7335 gEvents[a].Command := '';
7336 for b := 2 to High(P) do
7337 if Pos(' ', P[b]) = 0 then
7338 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
7339 else
7340 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
7341 gEvents[a].Command := Trim(gEvents[a].Command);
7342 Exit;
7343 end;
7344 end
7345 else if cmd = 'suicide' then
7346 begin
7347 if gGameOn then
7348 begin
7349 if g_Game_IsClient then
7350 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
7351 else
7352 begin
7353 if gPlayer1 <> nil then
7354 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
7355 if gPlayer2 <> nil then
7356 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
7357 end;
7358 end;
7359 end
7360 else if cmd = 'screenshot' then
7361 begin
7362 g_TakeScreenShot()
7363 end
7364 else if (cmd = 'weapnext') or (cmd = 'weapprev') then
7365 begin
7366 a := 1 - (ord(cmd[5]) - ord('n'));
7367 if a = -1 then
7368 gWeaponAction[0, WP_PREV] := True;
7369 if a = 1 then
7370 gWeaponAction[0, WP_NEXT] := True;
7371 end
7372 else if cmd = 'weapon' then
7373 begin
7374 if Length(p) = 2 then
7375 begin
7376 a := WP_FIRST + StrToInt(p[1]) - 1;
7377 if (a >= WP_FIRST) and (a <= WP_LAST) then
7378 gSelectWeapon[0, a] := True
7379 end
7380 end
7381 else if (cmd = 'p1_weapnext') or (cmd = 'p1_weapprev')
7382 or (cmd = 'p2_weapnext') or (cmd = 'p2_weapprev') then
7383 begin
7384 a := 1 - (ord(cmd[8]) - ord('n'));
7385 b := ord(cmd[2]) - ord('1');
7386 if a = -1 then
7387 gWeaponAction[b, WP_PREV] := True;
7388 if a = 1 then
7389 gWeaponAction[b, WP_NEXT] := True;
7390 end
7391 else if (cmd = 'p1_weapon') or (cmd = 'p2_weapon') then
7392 begin
7393 if Length(p) = 2 then
7394 begin
7395 a := WP_FIRST + StrToInt(p[1]) - 1;
7396 b := ord(cmd[2]) - ord('1');
7397 if (a >= WP_FIRST) and (a <= WP_LAST) then
7398 gSelectWeapon[b, a] := True
7399 end
7400 end
7401 else if (cmd = 'dropflag') then
7402 begin
7403 if g_Game_IsServer then
7404 begin
7405 if gPlayer2 <> nil then gPlayer2.TryDropFlag();
7406 if gPlayer1 <> nil then gPlayer1.TryDropFlag();
7407 end
7408 else
7409 MC_SEND_CheatRequest(NET_CHEAT_DROPFLAG);
7410 end
7411 else if (cmd = 'p1_dropflag') or (cmd = 'p2_dropflag') then
7412 begin
7413 b := ord(cmd[2]) - ord('1');
7414 if g_Game_IsServer then
7415 begin
7416 if (b = 1) and (gPlayer2 <> nil) then gPlayer2.TryDropFlag()
7417 else if (b = 0) and (gPlayer1 <> nil) then gPlayer1.TryDropFlag();
7418 end
7419 else
7420 MC_SEND_CheatRequest(NET_CHEAT_DROPFLAG);
7421 end
7422 // Êîìàíäû Ñâîåé èãðû:
7423 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
7424 begin
7425 if cmd = 'bot_addred' then
7426 begin
7427 if Length(P) > 1 then
7428 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
7429 else
7430 g_Bot_Add(TEAM_RED, 2);
7431 end
7432 else if cmd = 'bot_addblue' then
7433 begin
7434 if Length(P) > 1 then
7435 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
7436 else
7437 g_Bot_Add(TEAM_BLUE, 2);
7438 end
7439 else if cmd = 'spectate' then
7440 begin
7441 if not gGameOn then
7442 Exit;
7443 g_Game_Spectate();
7444 end
7445 else if cmd = 'say' then
7446 begin
7447 if g_Game_IsServer and g_Game_IsNet then
7448 begin
7449 if Length(P) > 1 then
7450 begin
7451 chstr := '';
7452 for a := 1 to High(P) do
7453 chstr := chstr + P[a] + ' ';
7455 if Length(chstr) > 200 then SetLength(chstr, 200);
7457 if Length(chstr) < 1 then
7458 begin
7459 g_Console_Add('say <text>');
7460 Exit;
7461 end;
7463 chstr := b_Text_Format(chstr);
7464 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
7465 end
7466 else g_Console_Add('say <text>');
7467 end else
7468 g_Console_Add(_lc[I_MSG_SERVERONLY]);
7469 end
7470 else if cmd = 'tell' then
7471 begin
7472 if g_Game_IsServer and g_Game_IsNet then
7473 begin
7474 if (Length(P) > 2) and (P[1] <> '') then
7475 begin
7476 chstr := '';
7477 for a := 2 to High(P) do
7478 chstr := chstr + P[a] + ' ';
7480 if Length(chstr) > 200 then SetLength(chstr, 200);
7482 if Length(chstr) < 1 then
7483 begin
7484 g_Console_Add('tell <playername> <text>');
7485 Exit;
7486 end;
7488 pl := g_Net_Client_ByName(P[1]);
7489 if pl <> nil then
7490 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
7491 else
7492 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
7493 end
7494 else g_Console_Add('tell <playername> <text>');
7495 end else
7496 g_Console_Add(_lc[I_MSG_SERVERONLY]);
7497 end
7498 else if cmd = 'centerprint' then
7499 begin
7500 if (Length(P) > 2) and (P[1] <> '') then
7501 begin
7502 chstr := '';
7503 for a := 2 to High(P) do
7504 chstr := chstr + P[a] + ' ';
7506 if Length(chstr) > 200 then SetLength(chstr, 200);
7508 if Length(chstr) < 1 then
7509 begin
7510 g_Console_Add('centerprint <timeout> <text>');
7511 Exit;
7512 end;
7514 a := StrToIntDef(P[1], 100);
7515 chstr := b_Text_Format(chstr);
7516 g_Game_Message(chstr, a);
7517 if g_Game_IsNet and g_Game_IsServer then
7518 MH_SEND_GameEvent(NET_EV_BIGTEXT, a, chstr);
7519 end
7520 else g_Console_Add('centerprint <timeout> <text>');
7521 end
7522 else if (cmd = 'overtime') and not g_Game_IsClient then
7523 begin
7524 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
7525 Exit;
7526 // Äîïîëíèòåëüíîå âðåìÿ:
7527 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
7529 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
7530 [gGameSettings.TimeLimit div 3600,
7531 (gGameSettings.TimeLimit div 60) mod 60,
7532 gGameSettings.TimeLimit mod 60]));
7533 if g_Game_IsNet then MH_SEND_GameSettings;
7534 end
7535 else if (cmd = 'rcon_password') and g_Game_IsClient then
7536 begin
7537 if (Length(P) <= 1) then
7538 g_Console_Add('rcon_password <password>')
7539 else
7540 MC_SEND_RCONPassword(P[1]);
7541 end
7542 else if cmd = 'rcon' then
7543 begin
7544 if g_Game_IsClient then
7545 begin
7546 if Length(P) > 1 then
7547 begin
7548 chstr := '';
7549 for a := 1 to High(P) do
7550 chstr := chstr + P[a] + ' ';
7552 if Length(chstr) > 200 then SetLength(chstr, 200);
7554 if Length(chstr) < 1 then
7555 begin
7556 g_Console_Add('rcon <command>');
7557 Exit;
7558 end;
7560 MC_SEND_RCONCommand(chstr);
7561 end
7562 else g_Console_Add('rcon <command>');
7563 end;
7564 end
7565 else if cmd = 'ready' then
7566 begin
7567 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
7568 gLMSRespawnTime := gTime + 100;
7569 end
7570 else if (cmd = 'callvote') and g_Game_IsNet then
7571 begin
7572 if Length(P) > 1 then
7573 begin
7574 chstr := '';
7575 for a := 1 to High(P) do begin
7576 if a > 1 then chstr := chstr + ' ';
7577 chstr := chstr + P[a];
7578 end;
7580 if Length(chstr) > 200 then SetLength(chstr, 200);
7582 if Length(chstr) < 1 then
7583 begin
7584 g_Console_Add('callvote <command>');
7585 Exit;
7586 end;
7588 if g_Game_IsClient then
7589 MC_SEND_Vote(True, chstr)
7590 else
7591 g_Game_StartVote(chstr, gPlayer1Settings.Name);
7592 g_Console_Process('vote', True);
7593 end
7594 else
7595 g_Console_Add('callvote <command>');
7596 end
7597 else if (cmd = 'vote') and g_Game_IsNet then
7598 begin
7599 if g_Game_IsClient then
7600 MC_SEND_Vote(False)
7601 else if gVoteInProgress then
7602 begin
7603 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
7604 a := Floor((NetClientCount+1)/2.0) + 1
7605 else
7606 a := Floor(NetClientCount/2.0) + 1;
7607 if gVoted then
7608 begin
7609 Dec(gVoteCount);
7610 gVoted := False;
7611 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
7612 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
7613 end
7614 else
7615 begin
7616 Inc(gVoteCount);
7617 gVoted := True;
7618 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
7619 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
7620 g_Game_CheckVote;
7621 end;
7622 end;
7623 end
7624 end;
7625 end;
7627 procedure SystemCommands(P: SSArray);
7628 var
7629 cmd: string;
7630 begin
7631 cmd := LowerCase(P[0]);
7632 case cmd of
7633 'exit', 'quit':
7634 begin
7635 g_Game_Free();
7636 g_Game_Quit();
7637 end;
7638 'r_reset':
7639 begin
7640 gRC_Width := Max(1, gRC_Width);
7641 gRC_Height := Max(1, gRC_Height);
7642 gBPP := Max(1, gBPP);
7643 if sys_SetDisplayMode(gRC_Width, gRC_Height, gBPP, gRC_FullScreen, gRC_Maximized) = True then
7644 e_LogWriteln('resolution changed')
7645 else
7646 e_LogWriteln('resolution not changed');
7647 sys_EnableVSync(gVSync);
7648 end;
7649 'r_maxfps':
7650 begin
7651 if Length(p) = 2 then
7652 begin
7653 gMaxFPS := StrToIntDef(p[1], gMaxFPS);
7654 if gMaxFPS > 0 then
7655 gFrameTime := 1000 div gMaxFPS
7656 else
7657 gFrameTime := 0;
7658 end;
7659 e_LogWritefln('r_maxfps %d', [gMaxFPS]);
7660 end;
7661 'g_language':
7662 begin
7663 if Length(p) = 2 then
7664 begin
7665 gAskLanguage := true;
7666 gLanguage := LANGUAGE_ENGLISH;
7667 case LowerCase(p[1]) of
7668 'english':
7669 begin
7670 gAskLanguage := false;
7671 gLanguage := LANGUAGE_ENGLISH;
7672 end;
7673 'russian':
7674 begin
7675 gAskLanguage := false;
7676 gLanguage := LANGUAGE_RUSSIAN;
7677 end;
7678 'ask':
7679 begin
7680 gAskLanguage := true;
7681 gLanguage := LANGUAGE_ENGLISH;
7682 end;
7683 end;
7684 g_Language_Set(gLanguage);
7685 end
7686 else
7687 begin
7688 e_LogWritefln('usage: %s <English|Russian|Ask>', [cmd]);
7689 end
7690 end;
7691 end;
7692 end;
7694 procedure g_TakeScreenShot(Filename: string = '');
7695 var s: TStream; t: TDateTime; dir, date, name: String;
7696 begin
7697 if e_NoGraphics then Exit;
7698 try
7699 dir := e_GetWriteableDir(ScreenshotDirs);
7701 if Filename = '' then
7702 begin
7703 t := Now;
7704 DateTimeToString(date, 'yyyy-mm-dd-hh-nn-ss', t);
7705 Filename := 'screenshot-' + date;
7706 end;
7708 name := e_CatPath(dir, Filename + '.png');
7709 s := createDiskFile(name);
7710 try
7711 e_MakeScreenshot(s, gScreenWidth, gScreenHeight);
7712 s.Free;
7713 g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [name]))
7714 except
7715 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [name]));
7716 s.Free;
7717 DeleteFile(name)
7718 end
7719 except
7720 g_Console_Add('oh shit, i can''t create screenshot!')
7721 end
7722 end;
7724 procedure g_Game_InGameMenu(Show: Boolean);
7725 begin
7726 if (g_ActiveWindow = nil) and Show then
7727 begin
7728 if gGameSettings.GameType = GT_SINGLE then
7729 g_GUI_ShowWindow('GameSingleMenu')
7730 else
7731 begin
7732 if g_Game_IsClient then
7733 g_GUI_ShowWindow('GameClientMenu')
7734 else
7735 if g_Game_IsNet then
7736 g_GUI_ShowWindow('GameServerMenu')
7737 else
7738 g_GUI_ShowWindow('GameCustomMenu');
7739 end;
7740 g_Sound_PlayEx('MENU_OPEN');
7742 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7743 if (not g_Game_IsNet) then
7744 g_Game_Pause(True);
7745 end
7746 else
7747 if (g_ActiveWindow <> nil) and (not Show) then
7748 begin
7749 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7750 if (not g_Game_IsNet) then
7751 g_Game_Pause(False);
7752 end;
7753 end;
7755 procedure g_Game_Pause (Enable: Boolean);
7756 var
7757 oldPause: Boolean;
7758 begin
7759 if not gGameOn then exit;
7761 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
7763 oldPause := gPause;
7764 gPauseMain := Enable;
7766 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
7767 end;
7769 procedure g_Game_HolmesPause (Enable: Boolean);
7770 var
7771 oldPause: Boolean;
7772 begin
7773 if not gGameOn then exit;
7774 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
7776 oldPause := gPause;
7777 gPauseHolmes := Enable;
7779 if (gPause <> oldPause) then g_Game_PauseAllSounds(gPause);
7780 end;
7782 procedure g_Game_PauseAllSounds(Enable: Boolean);
7783 var
7784 i: Integer;
7785 begin
7786 // Òðèããåðû:
7787 if gTriggers <> nil then
7788 for i := 0 to High(gTriggers) do
7789 with gTriggers[i] do
7790 if (TriggerType = TRIGGER_SOUND) and
7791 (Sound <> nil) and
7792 Sound.IsPlaying() then
7793 begin
7794 Sound.Pause(Enable);
7795 end;
7797 // Çâóêè èãðîêîâ:
7798 if gPlayers <> nil then
7799 for i := 0 to High(gPlayers) do
7800 if gPlayers[i] <> nil then
7801 gPlayers[i].PauseSounds(Enable);
7803 // Ìóçûêà:
7804 if gMusic <> nil then
7805 gMusic.Pause(Enable);
7806 end;
7808 procedure g_Game_StopAllSounds(all: Boolean);
7809 var
7810 i: Integer;
7811 begin
7812 if gTriggers <> nil then
7813 for i := 0 to High(gTriggers) do
7814 with gTriggers[i] do
7815 if (TriggerType = TRIGGER_SOUND) and
7816 (Sound <> nil) then
7817 Sound.Stop();
7819 if gMusic <> nil then
7820 gMusic.Stop();
7822 if all then
7823 e_StopChannels();
7824 end;
7826 procedure g_Game_UpdateTriggerSounds;
7827 var i: Integer;
7828 begin
7829 if gTriggers <> nil then
7830 for i := 0 to High(gTriggers) do
7831 with gTriggers[i] do
7832 if (TriggerType = TRIGGER_SOUND) and (Sound <> nil) and tgcLocal and Sound.IsPlaying() then
7833 Sound.SetCoordsRect(X, Y, Width, Height, tgcVolume / 255.0)
7834 end;
7836 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
7837 begin
7838 Result := False;
7839 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
7840 begin
7841 Result := True;
7842 Exit;
7843 end;
7844 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
7845 begin
7846 Result := True;
7847 Exit;
7848 end;
7849 if gSpectMode <> SPECT_PLAYERS then
7850 Exit;
7851 if gSpectPID1 = UID then
7852 begin
7853 Result := True;
7854 Exit;
7855 end;
7856 if gSpectViewTwo and (gSpectPID2 = UID) then
7857 begin
7858 Result := True;
7859 Exit;
7860 end;
7861 end;
7863 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
7864 var
7865 Pl: TPlayer;
7866 begin
7867 Result := False;
7868 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
7869 begin
7870 Result := True;
7871 Exit;
7872 end;
7873 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
7874 begin
7875 Result := True;
7876 Exit;
7877 end;
7878 if gSpectMode <> SPECT_PLAYERS then
7879 Exit;
7880 Pl := g_Player_Get(gSpectPID1);
7881 if (Pl <> nil) and (Pl.Team = Team) then
7882 begin
7883 Result := True;
7884 Exit;
7885 end;
7886 if gSpectViewTwo then
7887 begin
7888 Pl := g_Player_Get(gSpectPID2);
7889 if (Pl <> nil) and (Pl.Team = Team) then
7890 begin
7891 Result := True;
7892 Exit;
7893 end;
7894 end;
7895 end;
7897 procedure g_Game_Message(Msg: string; Time: Word);
7898 begin
7899 MessageLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont);
7900 MessageText := b_Text_Wrap(b_Text_Format(Msg), MessageLineLength);
7901 MessageTime := Time;
7902 end;
7904 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
7905 const
7906 punct: Array[0..13] of String =
7907 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
7908 var
7909 i, j: Integer;
7910 ok: Boolean;
7911 fpText: String;
7913 function IsPunctuation(S: String): Boolean;
7914 var
7915 i: Integer;
7916 begin
7917 Result := False;
7918 if Length(S) <> 1 then
7919 Exit;
7920 for i := Low(punct) to High(punct) do
7921 if S = punct[i] then
7922 begin
7923 Result := True;
7924 break;
7925 end;
7926 end;
7927 function FilterPunctuation(S: String): String;
7928 var
7929 i: Integer;
7930 begin
7931 for i := Low(punct) to High(punct) do
7932 S := StringReplace(S, punct[i], ' ', [rfReplaceAll]);
7933 Result := S;
7934 end;
7935 begin
7936 ok := False;
7938 if gUseChatSounds and Taunt and (gChatSounds <> nil) and (Pos(': ', Text) > 0) then
7939 begin
7940 // remove player name
7941 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
7942 // for FullWord check
7943 Text := toLowerCase1251(' ' + Text + ' ');
7944 fpText := FilterPunctuation(Text);
7946 for i := 0 to Length(gChatSounds) - 1 do
7947 begin
7948 ok := True;
7949 for j := 0 to Length(gChatSounds[i].Tags) - 1 do
7950 begin
7951 if gChatSounds[i].FullWord and (not IsPunctuation(gChatSounds[i].Tags[j])) then
7952 ok := Pos(' ' + gChatSounds[i].Tags[j] + ' ', fpText) > 0
7953 else
7954 ok := Pos(gChatSounds[i].Tags[j], Text) > 0;
7955 if not ok then
7956 break;
7957 end;
7958 if ok then
7959 begin
7960 gChatSounds[i].Sound.Play();
7961 break;
7962 end;
7963 end;
7964 end;
7965 if not ok then
7966 g_Sound_PlayEx('SOUND_GAME_RADIO');
7967 end;
7969 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
7970 var
7971 a: Integer;
7972 begin
7973 case gAnnouncer of
7974 ANNOUNCE_NONE:
7975 Exit;
7976 ANNOUNCE_ME,
7977 ANNOUNCE_MEPLUS:
7978 if not g_Game_IsWatchedPlayer(SpawnerUID) then
7979 Exit;
7980 end;
7981 for a := 0 to 3 do
7982 if goodsnd[a].IsPlaying() then
7983 Exit;
7985 goodsnd[Random(4)].Play();
7986 end;
7988 procedure g_Game_Announce_KillCombo(Param: Integer);
7989 var
7990 UID: Word;
7991 c, n: Byte;
7992 Pl: TPlayer;
7993 Name: String;
7994 begin
7995 UID := Param and $FFFF;
7996 c := Param shr 16;
7997 if c < 2 then
7998 Exit;
8000 Pl := g_Player_Get(UID);
8001 if Pl = nil then
8002 Name := '?'
8003 else
8004 Name := Pl.Name;
8006 case c of
8007 2: begin
8008 n := 0;
8009 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
8010 end;
8011 3: begin
8012 n := 1;
8013 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
8014 end;
8015 4: begin
8016 n := 2;
8017 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
8018 end;
8019 else begin
8020 n := 3;
8021 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
8022 end;
8023 end;
8025 case gAnnouncer of
8026 ANNOUNCE_NONE:
8027 Exit;
8028 ANNOUNCE_ME:
8029 if not g_Game_IsWatchedPlayer(UID) then
8030 Exit;
8031 ANNOUNCE_MEPLUS:
8032 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
8033 Exit;
8034 end;
8036 if killsnd[n].IsPlaying() then
8037 killsnd[n].Stop();
8038 killsnd[n].Play();
8039 end;
8041 procedure g_Game_Announce_BodyKill(SpawnerUID: Word);
8042 var
8043 a: Integer;
8044 begin
8045 case gAnnouncer of
8046 ANNOUNCE_NONE:
8047 Exit;
8048 ANNOUNCE_ME:
8049 if not g_Game_IsWatchedPlayer(SpawnerUID) then
8050 Exit;
8051 end;
8052 for a := 0 to 2 do
8053 if hahasnd[a].IsPlaying() then
8054 Exit;
8056 hahasnd[Random(3)].Play();
8057 end;
8059 procedure g_Game_StartVote(Command, Initiator: string);
8060 var
8061 Need: Integer;
8062 begin
8063 if not gVotesEnabled then Exit;
8064 if gGameSettings.GameType <> GT_SERVER then Exit;
8065 if gVoteInProgress or gVotePassed then
8066 begin
8067 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
8068 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
8069 Exit;
8070 end;
8071 gVoteInProgress := True;
8072 gVotePassed := False;
8073 gVoteTimer := gTime + gVoteTimeout * 1000;
8074 gVoteCount := 0;
8075 gVoted := False;
8076 gVoteCommand := Command;
8078 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
8079 Need := Floor((NetClientCount+1)/2.0)+1
8080 else
8081 Need := Floor(NetClientCount/2.0)+1;
8082 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
8083 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
8084 end;
8086 procedure g_Game_CheckVote;
8087 var
8088 I, Need: Integer;
8089 begin
8090 if gGameSettings.GameType <> GT_SERVER then Exit;
8091 if not gVoteInProgress then Exit;
8093 if (gTime >= gVoteTimer) then
8094 begin
8095 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
8096 Need := Floor((NetClientCount+1)/2.0) + 1
8097 else
8098 Need := Floor(NetClientCount/2.0) + 1;
8099 if gVoteCount >= Need then
8100 begin
8101 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
8102 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
8103 gVotePassed := True;
8104 gVoteCmdTimer := gTime + 5000;
8105 end
8106 else
8107 begin
8108 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
8109 MH_SEND_VoteEvent(NET_VE_FAILED);
8110 end;
8111 if NetClients <> nil then
8112 for I := Low(NetClients) to High(NetClients) do
8113 if NetClients[i].Used then
8114 NetClients[i].Voted := False;
8115 gVoteInProgress := False;
8116 gVoted := False;
8117 gVoteCount := 0;
8118 end
8119 else
8120 begin
8121 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
8122 Need := Floor((NetClientCount+1)/2.0) + 1
8123 else
8124 Need := Floor(NetClientCount/2.0) + 1;
8125 if gVoteCount >= Need then
8126 begin
8127 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
8128 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
8129 gVoteInProgress := False;
8130 gVotePassed := True;
8131 gVoteCmdTimer := gTime + 5000;
8132 gVoted := False;
8133 gVoteCount := 0;
8134 if NetClients <> nil then
8135 for I := Low(NetClients) to High(NetClients) do
8136 if NetClients[i].Used then
8137 NetClients[i].Voted := False;
8138 end;
8139 end;
8140 end;
8142 procedure g_Game_LoadMapList(FileName: string);
8143 var
8144 ListFile: TextFile;
8145 s: string;
8146 begin
8147 MapList := nil;
8148 MapIndex := -1;
8150 if not FileExists(FileName) then Exit;
8152 AssignFile(ListFile, FileName);
8153 Reset(ListFile);
8154 while not EOF(ListFile) do
8155 begin
8156 ReadLn(ListFile, s);
8158 s := Trim(s);
8159 if s = '' then Continue;
8161 SetLength(MapList, Length(MapList)+1);
8162 MapList[High(MapList)] := s;
8163 end;
8164 CloseFile(ListFile);
8165 end;
8167 procedure g_Game_SetDebugMode();
8168 begin
8169 gDebugMode := True;
8170 // ×èòû (äàæå â ñâîåé èãðå):
8171 gCheats := True;
8172 end;
8174 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
8175 var
8176 i: Word;
8177 begin
8178 if Length(LoadingStat.Msgs) = 0 then
8179 Exit;
8181 with LoadingStat do
8182 begin
8183 if not reWrite then
8184 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
8185 if NextMsg = Length(Msgs) then
8186 begin // scroll
8187 for i := 0 to High(Msgs)-1 do
8188 Msgs[i] := Msgs[i+1];
8189 end
8190 else
8191 Inc(NextMsg);
8192 end else
8193 if NextMsg = 0 then
8194 Inc(NextMsg);
8196 Msgs[NextMsg-1] := Text;
8197 CurValue := 0;
8198 MaxValue := Max;
8199 ShowCount := 0;
8200 PBarWasHere := false;
8201 end;
8203 g_ActiveWindow := nil;
8205 ProcessLoading(true);
8206 end;
8208 procedure g_Game_StepLoading(Value: Integer = -1);
8209 begin
8210 with LoadingStat do
8211 begin
8212 if Value = -1 then
8213 begin
8214 Inc(CurValue);
8215 Inc(ShowCount);
8216 end
8217 else
8218 CurValue := Value;
8219 if (ShowCount > LOADING_SHOW_STEP) or (Value > -1) then
8220 begin
8221 ShowCount := 0;
8222 ProcessLoading();
8223 end;
8224 end;
8225 end;
8227 procedure g_Game_ClearLoading();
8228 var
8229 len: Word;
8230 begin
8231 with LoadingStat do
8232 begin
8233 CurValue := 0;
8234 MaxValue := 0;
8235 ShowCount := 0;
8236 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
8237 if len < 1 then len := 1;
8238 SetLength(Msgs, len);
8239 for len := Low(Msgs) to High(Msgs) do
8240 Msgs[len] := '';
8241 NextMsg := 0;
8242 PBarWasHere := false;
8243 end;
8244 end;
8246 procedure Parse_Params(var pars: TParamStrValues);
8247 var
8248 i: Integer;
8249 s: String;
8250 begin
8251 SetLength(pars, 0);
8252 i := 1;
8253 while i <= ParamCount do
8254 begin
8255 s := ParamStr(i);
8256 if (Length(s) > 1) and (s[1] = '-') then
8257 begin
8258 if (Length(s) > 2) and (s[2] = '-') then
8259 begin // Îäèíî÷íûé ïàðàìåòð
8260 SetLength(pars, Length(pars) + 1);
8261 with pars[High(pars)] do
8262 begin
8263 Name := LowerCase(s);
8264 Value := '+';
8265 end;
8266 end
8267 else
8268 if (i < ParamCount) then
8269 begin // Ïàðàìåòð ñî çíà÷åíèåì
8270 Inc(i);
8271 SetLength(pars, Length(pars) + 1);
8272 with pars[High(pars)] do
8273 begin
8274 Name := LowerCase(s);
8275 Value := LowerCase(ParamStr(i));
8276 end;
8277 end;
8278 end;
8280 Inc(i);
8281 end;
8282 end;
8284 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
8285 var
8286 i: Integer;
8287 begin
8288 Result := '';
8289 for i := 0 to High(pars) do
8290 if pars[i].Name = aName then
8291 begin
8292 Result := pars[i].Value;
8293 Break;
8294 end;
8295 end;
8297 procedure g_Game_Process_Params();
8298 var
8299 pars: TParamStrValues;
8300 map: String;
8301 GMode, n: Byte;
8302 LimT, LimS: Integer;
8303 Opt: LongWord;
8304 Lives: Integer;
8305 s: String;
8306 Port: Integer;
8307 ip: String;
8308 F: TextFile;
8309 begin
8310 Parse_Params(pars);
8312 // Debug mode:
8313 s := Find_Param_Value(pars, '--debug');
8314 if (s <> '') then
8315 begin
8316 g_Game_SetDebugMode();
8317 s := Find_Param_Value(pars, '--netdump');
8318 if (s <> '') then
8319 NetDump := True;
8320 end;
8322 // Connect when game loads
8323 ip := Find_Param_Value(pars, '-connect');
8325 if ip <> '' then
8326 begin
8327 s := Find_Param_Value(pars, '-port');
8328 if (s = '') or not TryStrToInt(s, Port) then
8329 Port := 25666;
8331 s := Find_Param_Value(pars, '-pw');
8333 g_Game_StartClient(ip, Port, s);
8334 Exit;
8335 end;
8337 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
8338 if (s <> '') then
8339 begin
8340 gDefaultMegawadStart := s;
8341 end;
8343 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
8344 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
8345 begin
8346 gDefaultMegawadStart := DF_Default_Megawad_Start;
8347 end;
8349 // Start map when game loads:
8350 map := LowerCase(Find_Param_Value(pars, '-map'));
8351 if isWadPath(map) then
8352 begin
8353 // Game mode:
8354 s := Find_Param_Value(pars, '-gm');
8355 GMode := g_Game_TextToMode(s);
8356 if GMode = GM_NONE then GMode := GM_DM;
8357 if GMode = GM_SINGLE then GMode := GM_COOP;
8359 // Time limit:
8360 s := Find_Param_Value(pars, '-limt');
8361 if (s = '') or (not TryStrToInt(s, LimT)) then
8362 LimT := 0;
8363 if LimT < 0 then
8364 LimT := 0;
8366 // Goal limit:
8367 s := Find_Param_Value(pars, '-lims');
8368 if (s = '') or (not TryStrToInt(s, LimS)) then
8369 LimS := 0;
8370 if LimS < 0 then
8371 LimS := 0;
8373 // Lives limit:
8374 s := Find_Param_Value(pars, '-lives');
8375 if (s = '') or (not TryStrToInt(s, Lives)) then
8376 Lives := 0;
8377 if Lives < 0 then
8378 Lives := 0;
8380 // Options:
8381 s := Find_Param_Value(pars, '-opt');
8382 if (s = '') then
8383 Opt := gsGameFlags
8384 else
8385 Opt := StrToIntDef(s, 0);
8387 // Close after map:
8388 s := Find_Param_Value(pars, '--close');
8389 if (s <> '') then
8390 gMapOnce := True;
8392 // Override map to test:
8393 s := LowerCase(Find_Param_Value(pars, '-testmap'));
8394 if s <> '' then
8395 begin
8396 if e_IsValidResourceName(s) then
8397 e_FindResource(AllMapDirs, s);
8398 gTestMap := ExpandFileName(s);
8399 end;
8401 // Delete test map after play:
8402 s := Find_Param_Value(pars, '--testdelete');
8403 if (s <> '') then
8404 begin
8405 //gMapToDelete := MapsDir + map;
8406 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
8407 Halt(1);
8408 end;
8410 // Delete temporary WAD after play:
8411 s := Find_Param_Value(pars, '--tempdelete');
8412 if (s <> '') and (gTestMap <> '') then
8413 begin
8414 gMapToDelete := gTestMap;
8415 gTempDelete := True;
8416 end;
8418 // Number of players:
8419 s := Find_Param_Value(pars, '-pl');
8420 if (s = '') then
8421 n := DEFAULT_PLAYERS
8422 else
8423 n := StrToIntDef(s, DEFAULT_PLAYERS);
8425 // Start:
8426 s := Find_Param_Value(pars, '-port');
8427 if (s = '') or not TryStrToInt(s, Port) then
8428 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
8429 else
8430 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
8431 end;
8433 // Execute script when game loads:
8434 s := Find_Param_Value(pars, '-exec');
8435 if s <> '' then
8436 begin
8437 // if not isWadPath(s) then
8438 // s := GameDir + '/' + s;
8440 {$I-}
8441 AssignFile(F, s);
8442 Reset(F);
8443 if IOResult <> 0 then
8444 begin
8445 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
8446 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
8447 CloseFile(F);
8448 Exit;
8449 end;
8450 e_WriteLog('Executing script: ' + s, TMsgType.Notify);
8451 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
8453 while not EOF(F) do
8454 begin
8455 ReadLn(F, s);
8456 if IOResult <> 0 then
8457 begin
8458 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), TMsgType.Warning);
8459 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
8460 CloseFile(F);
8461 Exit;
8462 end;
8463 if Pos('#', s) <> 1 then // script comment
8464 g_Console_Process(s, True);
8465 end;
8467 CloseFile(F);
8468 {$I+}
8469 end;
8471 SetLength(pars, 0);
8472 end;
8474 begin
8475 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
8476 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
8477 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
8478 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
8480 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
8481 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
8482 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
8483 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
8485 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
8486 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
8488 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
8489 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
8491 {$IFDEF ENABLE_HOLMES}
8492 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
8493 {$ENDIF}
8495 conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds, 'ignore level bounds', '', false);
8497 conRegVar('r_scale', @g_dbg_scale, 0.01, 100.0, 'render scale', '', false);
8498 conRegVar('r_resolution_scale', @r_pixel_scale, 0.01, 100.0, 'upscale factor', '', false);
8500 conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting');
8501 conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo');
8503 conRegVar('r_smallmap_align_h', @r_smallmap_h, 'halign: 0: left; 1: center; 2: right', 'horizontal aligning of small maps');
8504 conRegVar('r_smallmap_align_v', @r_smallmap_v, 'valign: 0: top; 1: center; 2: bottom', 'vertial aligning of small maps');
8506 conRegVar('r_showfps', @gShowFPS, 'draw fps counter', 'draw fps counter');
8507 conRegVar('r_showtime', @gShowTime, 'show game time', 'show game time');
8508 conRegVar('r_showping', @gShowPing, 'show ping', 'show ping');
8509 conRegVar('r_showscore', @gShowGoals, 'show score', 'show score');
8510 conRegVar('r_showkillmsg', @gShowKillMsg, 'show kill log', 'show kill log');
8511 conRegVar('r_showlives', @gShowLives, 'show lives', 'show lives');
8512 conRegVar('r_showspect', @gSpectHUD, 'show spectator hud', 'show spectator hud');
8513 conRegVar('r_showstat', @gShowStat, 'show stats', 'show stats');
8514 conRegVar('r_showpids', @gShowPIDs, 'show PIDs', 'show PIDs');
8515 end.