DEADSOFTWARE

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