Remove noreturns from functions that throw
[d2df-sdl.git] / src / game / g_game.pas
blob4d562c90c79bb15d6d5d15a6d3f6d27424bbe4c8
1 (* Copyright (C) 2016 - The Doom2D.org team & involved community members <http://www.doom2d.org>.
2 * This file is part of Doom2D Forever.
4 * This program is free software: you can redistribute it and/or modify it under the terms of
5 * the GNU General Public License as published by the Free Software Foundation, version 3 of
6 * the License ONLY.
8 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 * See the GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License along with this program.
13 * If not, see <http://www.gnu.org/licenses/>.
16 {$INCLUDE ../shared/a_modes.inc}
17 unit g_game;
19 interface
21 uses
22 SysUtils, Classes,
23 MAPDEF, CONFIG,
24 {$IFDEF ENABLE_SOUND}
25 e_sound, g_sound,
26 {$ENDIF}
27 g_basic, g_player, e_graphics, g_res_downloader,
28 g_gui, utils, md5, mempool, xprofiler,
29 g_touch, g_weapons;
31 type
32 TGameOption = (
33 //RESERVED = 0, // FIXME: reuse for something
34 FRIENDLY_FIRE = 1,
35 ALLOW_EXIT,
36 WEAPONS_STAY,
37 MONSTERS,
38 BOTS_VS_PLAYERS,
39 BOTS_VS_MONSTERS,
40 DM_KEYS,
41 TEAM_HIT_TRACE,
42 TEAM_HIT_PROJECTILE,
43 TEAM_ABSORB_ATTACKS,
44 ALLOW_DROP_FLAG,
45 THROW_FLAG,
46 POWERUP_RANDOM,
47 ITEM_ALL_RANDOM,
48 ITEM_LIFE_RANDOM,
49 ITEM_AMMO_RANDOM,
50 ITEM_WEAPON_RANDOM
52 TGameOptions = set of TGameOption;
54 TGameSettings = record
55 GameType: Byte;
56 GameMode: Byte;
57 TimeLimit: Word;
58 ScoreLimit: Word;
59 WarmupTime: Word;
60 SpawnInvul: Word;
61 ItemRespawnTime: Word;
62 ItemRespawnRandom: Word;
63 PowerupRespawnTime: Word;
64 PowerupRespawnRandom: Word;
65 MaxLives: Byte;
66 Options: TGameOptions;
67 WAD: String;
68 end;
70 TGameEvent = record
71 Name: String;
72 Command: String;
73 end;
75 TDelayedEvent = record
76 Pending: Boolean;
77 Time: LongWord;
78 DEType: Byte;
79 DENum: Integer;
80 DEStr: String;
81 end;
83 {$IFDEF ENABLE_SOUND}
84 TChatSound = record
85 Sound: TPlayableSound;
86 Tags: Array of String;
87 FullWord: Boolean;
88 end;
89 {$ENDIF}
91 TPlayerSettings = record
92 Name: String;
93 Model: String;
94 Color: TRGB;
95 Team: Byte;
96 // ones below are sent only to the server
97 WeaponSwitch: Byte;
98 WeaponPreferences: Array[WP_FIRST..WP_LAST+1] of Byte;
99 SwitchToEmpty: Byte;
100 SkipIronFist: Byte;
101 end;
103 TMegaWADInfo = record
104 Name: String;
105 Description: String;
106 Author: String;
107 Pic: String;
108 end;
110 THearPoint = record
111 Active: Boolean;
112 Coords: TDFPoint;
113 end;
115 function g_Game_IsNet(): Boolean;
116 function g_Game_IsServer(): Boolean;
117 function g_Game_IsClient(): Boolean;
118 procedure g_Game_Init();
119 procedure g_Game_Free (freeTextures: Boolean=true);
120 procedure g_Game_LoadData();
121 procedure g_Game_FreeData();
122 procedure g_Game_Update();
123 procedure g_Game_PreUpdate();
124 procedure g_Game_Draw();
125 procedure g_Game_Quit();
126 procedure g_Game_SetupScreenSize();
127 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
128 function g_Game_ModeToText(Mode: Byte): string;
129 function g_Game_TextToMode(Mode: string): Byte;
130 procedure g_Game_ExecuteEvent(Name: String);
131 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
132 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
133 procedure g_Game_RemovePlayer();
134 procedure g_Game_Spectate();
135 procedure g_Game_SpectateCenterView();
136 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
137 procedure g_Game_StartCustom(Map: String; GameMode: Byte; TimeLimit, ScoreLimit: Word; MaxLives: Byte; Options: TGameOptions; nPlayers: Byte);
138 procedure g_Game_StartServer(Map: String; GameMode: Byte; TimeLimit, ScoreLimit: Word; MaxLives: Byte; Options: TGameOptions; nPlayers: Byte; IPAddr: LongWord; Port: Word);
139 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
140 procedure g_Game_Restart();
141 procedure g_Game_RestartLevel();
142 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
143 function g_Game_ClientWAD (NewWAD: String; const WHash: TMD5Digest): AnsiString;
144 function g_Game_StartMap(asMegawad: Boolean; Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
145 procedure g_Game_ChangeMap(const MapPath: String);
146 procedure g_Game_ExitLevel(const Map: AnsiString);
147 function g_Game_GetFirstMap(WAD: String): String;
148 function g_Game_GetNextMap(): String;
149 procedure g_Game_NextLevel();
150 procedure g_Game_Pause(Enable: Boolean);
151 procedure g_Game_HolmesPause(Enable: Boolean);
152 procedure g_Game_InGameMenu(Show: Boolean);
153 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
154 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
155 procedure g_Game_Message(Msg: String; Time: Word);
157 {$IFDEF ENABLE_SOUND}
158 procedure g_Game_PauseAllSounds(Enable: Boolean);
159 procedure g_Game_StopAllSounds(all: Boolean);
160 procedure g_Game_UpdateTriggerSounds();
161 procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True);
162 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
163 procedure g_Game_Announce_KillCombo(Param: Integer);
164 procedure g_Game_Announce_BodyKill(SpawnerUID: Word);
165 {$ENDIF}
167 function g_Game_GetMegaWADInfo(WAD: String; cfg: TConfig = nil): TMegaWADInfo;
168 procedure g_Game_Effect_Bubbles(fX, fY: Integer; count: Word; devX, devY: Byte; Silent: Boolean = False);
169 procedure g_Game_StartVote(Command, Initiator: string);
170 procedure g_Game_CheckVote;
171 procedure g_TakeScreenShot(Filename: string = '');
172 procedure g_FatalError(Text: String);
173 procedure g_SimpleError(Text: String);
174 function g_Game_IsTestMap(): Boolean;
175 procedure g_Game_DeleteTestMap();
176 procedure GameCVars(P: SSArray);
177 procedure PlayerSettingsCVars(P: SSArray);
178 procedure SystemCommands(P: SSArray);
179 procedure GameCommands(P: SSArray);
180 procedure GameCheats(P: SSArray);
181 procedure DebugCommands(P: SSArray);
182 procedure g_Game_Process_Params;
183 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
184 procedure g_Game_StepLoading(Value: Integer = -1);
185 procedure g_Game_ClearLoading();
186 procedure g_Game_SetDebugMode();
187 procedure DrawLoadingStat();
188 procedure DrawMenuBackground(tex: AnsiString);
190 { procedure SetWinPause(Enable: Boolean); }
192 const
193 GAME_TICK = 28;
195 LOADING_SHOW_STEP = 100;
196 LOADING_INTERLINE = 20;
198 GT_NONE = 0;
199 GT_SINGLE = 1;
200 GT_CUSTOM = 2;
201 GT_SERVER = 3;
202 GT_CLIENT = 4;
204 GM_NONE = 0;
205 GM_DM = 1;
206 GM_TDM = 2;
207 GM_CTF = 3;
208 GM_COOP = 4;
209 GM_SINGLE = 5;
211 MESSAGE_DIKEY = WM_USER + 1;
213 EXIT_QUIT = 1;
214 EXIT_SIMPLE = 2;
215 EXIT_RESTART = 3;
216 EXIT_ENDLEVELSINGLE = 4;
217 EXIT_ENDLEVELCUSTOM = 5;
219 STATE_NONE = 0;
220 STATE_MENU = 1;
221 STATE_FOLD = 2;
222 STATE_INTERCUSTOM = 3;
223 STATE_INTERSINGLE = 4;
224 STATE_INTERTEXT = 5;
225 STATE_INTERPIC = 6;
226 STATE_ENDPIC = 7;
227 STATE_SLIST = 8;
229 LMS_RESPAWN_NONE = 0;
230 LMS_RESPAWN_WARMUP = 1;
231 LMS_RESPAWN_FINAL = 2;
233 SPECT_NONE = 0;
234 SPECT_STATS = 1;
235 SPECT_MAPVIEW = 2;
236 SPECT_PLAYERS = 3;
238 DE_GLOBEVENT = 0;
239 DE_BFGHIT = 1;
240 DE_KILLCOMBO = 2;
241 DE_BODYKILL = 3;
243 ANNOUNCE_NONE = 0;
244 ANNOUNCE_ME = 1;
245 ANNOUNCE_MEPLUS = 2;
246 ANNOUNCE_ALL = 3;
248 CONFIG_FILENAME = 'Doom2DF.cfg';
249 TEST_MAP_NAME = '$$$_TEST_$$$';
250 STD_PLAYER_MODEL = 'Doomer';
251 DEFAULT_PLAYERS = {$IFNDEF HEADLESS}1{$ELSE}0{$ENDIF};
252 STATFILE_VERSION = $03;
255 gStdFont: DWORD;
256 gGameSettings: TGameSettings;
257 gPlayer1Settings: TPlayerSettings;
258 gPlayer2Settings: TPlayerSettings;
259 gGameOn: Boolean;
260 gPlayerScreenSize: TDFPoint;
261 gPlayer1ScreenCoord: TDFPoint;
262 gPlayer2ScreenCoord: TDFPoint;
263 gPlayer1: TPlayer;
264 gPlayer2: TPlayer;
265 gPlayerDrawn: TPlayer;
266 gTime: LongWord;
267 gLerpFactor: Single = 1.0;
268 gSwitchGameMode: Byte = GM_DM;
269 gHearPoint1, gHearPoint2: THearPoint;
270 {$IFDEF ENABLE_SOUND}
271 gSoundEffectsDF: Boolean;
272 gSoundTriggerTime: Word;
273 goodsnd: array[0..3] of TPlayableSound;
274 killsnd: array[0..3] of TPlayableSound;
275 hahasnd: array[0..2] of TPlayableSound;
276 sound_get_flag: array[0..1] of TPlayableSound;
277 sound_lost_flag: array[0..1] of TPlayableSound;
278 sound_ret_flag: array[0..1] of TPlayableSound;
279 sound_cap_flag: array[0..1] of TPlayableSound;
280 gUseChatSounds: Boolean = True;
281 gChatSounds: array of TChatSound;
282 gMusic: TMusic;
283 {$ELSE}
284 gMusicName: String = '';
285 gMusicPlay: Boolean = False;
286 gMusicPos: LongWord = 0;
287 gMusicPause: Boolean = False;
288 {$ENDIF}
289 gAnnouncer: Integer = ANNOUNCE_NONE;
290 gBodyKillEvent: Integer = -1;
291 gDefInterTime: ShortInt = -1;
292 gInterEndTime: LongWord;
293 gInterTime: LongWord;
294 gServInterTime: Byte;
295 gGameStartTime: LongWord;
296 gTotalMonsters: Integer;
297 gPauseMain: Boolean;
298 gPauseHolmes: Boolean;
299 gShowTime: Boolean;
300 gShowFPS: Boolean;
301 gShowScore: Boolean = True;
302 gShowStat: Boolean = True;
303 gShowPIDs: Boolean;
304 gShowKillMsg: Boolean = True;
305 gShowLives: Boolean = True;
306 gShowPing: Boolean;
307 gShowMap: Boolean;
308 gExit: Byte;
309 gState: Byte = STATE_NONE;
310 sX, sY: Integer;
311 sWidth, sHeight: Word;
312 gSpectMode: Byte = SPECT_NONE;
313 gSpectHUD: Boolean = True;
314 gSpectKeyPress: Boolean;
315 gSpectX: Integer;
316 gSpectY: Integer;
317 gSpectStep: Byte = 8;
318 gSpectViewTwo: Boolean;
319 gSpectPID1: Integer = -1;
320 gSpectPID2: Integer = -1;
321 gSpectAuto: Boolean;
322 gSpectAutoNext: LongWord;
323 gSpectAutoStepX: Integer;
324 gSpectAutoStepY: Integer;
325 gLoadGameMode: Boolean;
326 gCheats: Boolean;
327 gMapOnce: Boolean;
328 gMapToDelete: String;
329 gTempDelete: Boolean;
330 gLastMap: Boolean;
331 gScreenWidth: Word;
332 gScreenHeight: Word;
333 gResolutionChange: Boolean;
334 gRC_Width, gRC_Height: Integer;
335 gRC_FullScreen, gRC_Maximized: Boolean;
336 gLanguageChange: Boolean;
337 gDebugMode: Boolean;
338 g_debug_Sounds: Boolean;
339 g_debug_Frames: Boolean;
340 g_debug_WinMsgs: Boolean;
341 g_debug_MonsterOff: Boolean;
342 g_debug_BotAIOff: Byte;
343 g_debug_HealthBar: Boolean;
344 g_Debug_Player: Boolean;
346 // NB: Counted during the round, not after it, as players can disconnect and leave mid-game.
347 gCoopMonstersKilled: Word;
348 gCoopSecretsFound: Word;
349 gCoopTotalMonstersKilled: Word;
350 gCoopTotalSecretsFound: Word;
351 gCoopTotalMonsters: Word;
352 gCoopTotalSecrets: Word;
354 gStatsOff: Boolean;
355 gStatsPressed: Boolean;
356 gExitByTrigger: Boolean;
357 gNextMap: String;
358 gLMSRespawn: Byte = LMS_RESPAWN_NONE;
359 gLMSRespawnTime: Cardinal;
360 gLMSSoftSpawn: Boolean;
361 gMissionFailed: Boolean;
362 gVoteInProgress: Boolean;
363 gVotePassed: Boolean;
364 gVoteCommand: String;
365 gVoteTimer: Cardinal;
366 gVoteCmdTimer: Cardinal;
367 gVoteCount: Integer;
368 gVoteTimeout: Cardinal = 30;
369 gVoted: Boolean;
370 gVotesEnabled: Boolean = True;
371 gEvents: array of TGameEvent;
372 gDelayedEvents: array of TDelayedEvent;
373 gWeaponAction: array [0..1, WP_FACT..WP_LACT] of Boolean; // [player, weapon_action]
374 gSelectWeapon: array [0..1, WP_FIRST..WP_LAST] of Boolean; // [player, weapon]
375 gInterReadyCount: Integer;
376 gMaxBots: Integer = 127;
378 g_dbg_ignore_bounds: Boolean;
379 r_smallmap_h: Integer; // 0: left; 1: center; 2: right
380 r_smallmap_v: Integer = 2; // 0: top; 1: center; 2: bottom
382 // move button values:
383 // bits 0-1: l/r state:
384 // 0: neither left, nor right pressed
385 // 1: left pressed
386 // 2: right pressed
387 // bits 4-5: l/r state when strafe was pressed
388 P1MoveButton: Byte;
389 P2MoveButton: Byte;
391 g_profile_frame_update: Boolean;
392 g_profile_frame_draw: Boolean;
393 g_profile_collision: Boolean;
394 g_profile_los: Boolean;
395 g_profile_history_size: Integer = 1000;
397 g_rlayer_back: Boolean = True;
398 g_rlayer_step: Boolean = True;
399 g_rlayer_wall: Boolean = True;
400 g_rlayer_door: Boolean = True;
401 g_rlayer_acid1: Boolean = True;
402 g_rlayer_acid2: Boolean = True;
403 g_rlayer_water: Boolean = True;
404 g_rlayer_fore: Boolean = True;
407 procedure g_ResetDynlights ();
408 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
409 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
411 function conIsCheatsEnabled (): Boolean; inline;
412 function gPause (): Boolean; inline;
414 implementation
416 uses
417 {$INCLUDE ../nogl/noGLuses.inc}
418 {$IFDEF ENABLE_HOLMES}
419 g_holmes,
420 {$ENDIF}
421 e_texture, e_res, g_textures, g_window, g_menu,
422 e_input, e_log, g_console, g_items, g_map, g_panel,
423 g_playermodel, g_gfx, g_options, Math,
424 g_triggers, g_monsters,
425 g_language, g_net, g_main, g_phys,
426 ENet, e_msg, g_netmsg, g_netmaster,
427 sfs, wadreader, g_system, Generics.Collections, Generics.Defaults;
430 hasPBarGfx: Boolean;
431 profileFrameDraw: TProfiler;
433 // ////////////////////////////////////////////////////////////////////////// //
434 function gPause (): Boolean; inline;
435 begin
436 Result := gPauseMain or gPauseHolmes;
437 end;
439 function conIsCheatsEnabled (): Boolean; inline;
440 begin
441 result := false;
442 if g_Game_IsNet then exit;
443 if not gDebugMode then
444 begin
445 //if not gCheats then exit;
446 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit;
447 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then exit;
448 end;
449 result := true;
450 end;
452 // ////////////////////////////////////////////////////////////////////////// //
453 type
454 TDynLight = record
455 x, y, radius: Integer;
456 r, g, b, a: Single;
457 exploCount: Integer;
458 exploRadius: Integer;
459 end;
462 g_dynLights: array of TDynLight = nil;
463 g_dynLightCount: Integer = 0;
464 g_playerLight: Boolean = false;
466 procedure g_ResetDynlights ();
468 lnum, idx: Integer;
469 begin
470 if not gwin_has_stencil then begin g_dynLightCount := 0; exit; end;
471 lnum := 0;
472 for idx := 0 to g_dynLightCount-1 do
473 begin
474 if g_dynLights[idx].exploCount = -666 then
475 begin
476 // skip it
478 else
479 begin
480 // explosion
481 Inc(g_dynLights[idx].exploCount);
482 if (g_dynLights[idx].exploCount < 10) then
483 begin
484 g_dynLights[idx].radius := g_dynLights[idx].exploRadius+g_dynLights[idx].exploCount*8;
485 g_dynLights[idx].a := 0.4+g_dynLights[idx].exploCount/10;
486 if (g_dynLights[idx].a > 0.8) then g_dynLights[idx].a := 0.8;
487 if lnum <> idx then g_dynLights[lnum] := g_dynLights[idx];
488 Inc(lnum);
489 end;
490 end;
491 end;
492 g_dynLightCount := lnum;
493 end;
495 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
496 begin
497 if not gwin_has_stencil then exit;
498 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
499 g_dynLights[g_dynLightCount].x := x;
500 g_dynLights[g_dynLightCount].y := y;
501 g_dynLights[g_dynLightCount].radius := radius;
502 g_dynLights[g_dynLightCount].r := r;
503 g_dynLights[g_dynLightCount].g := g;
504 g_dynLights[g_dynLightCount].b := b;
505 g_dynLights[g_dynLightCount].a := a;
506 g_dynLights[g_dynLightCount].exploCount := -666;
507 Inc(g_dynLightCount);
508 end;
510 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
511 begin
512 if not gwin_has_stencil then exit;
513 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
514 g_dynLights[g_dynLightCount].x := x;
515 g_dynLights[g_dynLightCount].y := y;
516 g_dynLights[g_dynLightCount].radius := 0;
517 g_dynLights[g_dynLightCount].exploRadius := radius;
518 g_dynLights[g_dynLightCount].r := r;
519 g_dynLights[g_dynLightCount].g := g;
520 g_dynLights[g_dynLightCount].b := b;
521 g_dynLights[g_dynLightCount].a := 0;
522 g_dynLights[g_dynLightCount].exploCount := 0;
523 Inc(g_dynLightCount);
524 end;
527 // ////////////////////////////////////////////////////////////////////////// //
528 function calcProfilesHeight (prof: TProfiler): Integer;
529 begin
530 result := 0;
531 if (prof = nil) then exit;
532 if (length(prof.bars) = 0) then exit;
533 result := length(prof.bars)*(16+2);
534 end;
536 // returns width
537 function drawProfiles (x, y: Integer; prof: TProfiler): Integer;
539 wdt, hgt: Integer;
540 yy: Integer;
541 ii: Integer;
542 begin
543 result := 0;
544 if (prof = nil) then exit;
545 // gScreenWidth
546 if (length(prof.bars) = 0) then exit;
547 wdt := 192;
548 hgt := calcProfilesHeight(prof);
549 if (x < 0) then x := gScreenWidth-(wdt-1)+x;
550 if (y < 0) then y := gScreenHeight-(hgt-1)+y;
551 // background
552 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
553 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
554 e_DarkenQuadWH(x, y, wdt, hgt, 150);
555 // title
556 yy := y+2;
557 for ii := 0 to High(prof.bars) do
558 begin
559 e_TextureFontPrintEx(x+2+4*prof.bars[ii].level, yy, Format('%s: %d µs', [prof.bars[ii].name, prof.bars[ii].value]), gStdFont, 255, 255, 0, 1, false);
560 yy += 16+2;
561 end;
562 result := wdt;
563 end;
566 // ////////////////////////////////////////////////////////////////////////// //
567 type
568 TEndCustomGameStat = record
569 PlayerStat: TPlayerStatArray;
570 TeamStat: TTeamStat;
571 GameTime: LongWord;
572 GameMode: Byte;
573 Map, MapName: String;
574 end;
576 TEndSingleGameStat = record
577 PlayerStat: Array [0..1] of record
578 Kills: Integer;
579 Secrets: Integer;
580 end;
581 GameTime: LongWord;
582 TwoPlayers: Boolean;
583 TotalSecrets: Integer;
584 end;
586 TLoadingStat = record
587 CurValue: Integer;
588 MaxValue: Integer;
589 ShowCount: Integer;
590 Msgs: Array of String;
591 NextMsg: Word;
592 PBarWasHere: Boolean; // did we draw a progress bar for this message?
593 end;
595 TParamStrValue = record
596 Name: String;
597 Value: String;
598 end;
600 TParamStrValues = Array of TParamStrValue;
602 const
603 INTER_ACTION_TEXT = 1;
604 INTER_ACTION_PIC = 2;
605 INTER_ACTION_MUSIC = 3;
608 FPS, UPS: Word;
609 FPSCounter, UPSCounter: Word;
610 FPSTime, UPSTime: LongWord;
611 DataLoaded: Boolean;
612 IsDrawStat: Boolean;
613 CustomStat: TEndCustomGameStat;
614 SingleStat: TEndSingleGameStat;
615 LoadingStat: TLoadingStat;
616 EndingGameCounter: Byte;
617 MessageText: String;
618 MessageTime: Word;
619 MessageLineLength: Integer = 80;
620 MapIndex: Integer = -1;
621 InterReadyTime: Integer = -1;
622 StatShotDone: Boolean;
623 StatFilename: String; // used by stat screenshot to save with the same name as the csv
624 StatDate: String;
625 MegaWAD: record
626 info: TMegaWADInfo;
627 endpic: String;
628 endmus: String;
629 res: record
630 text: Array of ShortString;
631 anim: Array of ShortString;
632 pic: Array of ShortString;
633 mus: Array of ShortString;
634 end;
635 triggers: Array of record
636 event: ShortString;
637 actions: Array of record
638 action, p1, p2: Integer;
639 end;
640 end;
641 cur_trigger: Integer;
642 cur_action: Integer;
643 end;
644 //InterPic: String;
645 InterText: record
646 lines: SSArray;
647 img: String;
648 cur_line: Integer;
649 cur_char: Integer;
650 counter: Integer;
651 endtext: Boolean;
652 end;
654 function Compare(a, b: TPlayerStat): Integer;
655 begin
656 if a.Spectator then Result := 1
657 else if b.Spectator then Result := -1
658 else if a.Frags < b.Frags then Result := 1
659 else if a.Frags > b.Frags then Result := -1
660 else if a.Deaths < b.Deaths then Result := -1
661 else if a.Deaths > b.Deaths then Result := 1
662 else if a.Kills < b.Kills then Result := -1
663 else Result := 1;
664 end;
666 // TODO: eliminate
667 procedure SortGameStat(var stat: TPlayerStatArray);
669 I, J: Integer;
670 T: TPlayerStat;
671 begin
672 if stat = nil then Exit;
674 for I := High(stat) downto Low(stat) do
675 for J := Low(stat) to High(stat) - 1 do
676 if Compare(stat[J], stat[J + 1]) = 1 then
677 begin
678 T := stat[J];
679 stat[J] := stat[J + 1];
680 stat[J + 1] := T;
681 end;
682 end;
684 // saves a shitty CSV containing the game stats passed to it
685 procedure SaveGameStat(Stat: TEndCustomGameStat; Path: string);
687 s: TextFile;
688 dir, fname, map, mode, etime, flags, strf: String;
689 flag: TGameOption;
690 I: Integer;
691 begin
693 dir := e_GetWriteableDir(StatsDirs);
694 // stats are placed in stats/yy/mm/dd/*.csv
695 fname := ConcatPaths([dir, Path]);
696 ForceDirectories(fname); // ensure yy/mm/dd exists within the stats dir
697 fname := ConcatPaths([fname, StatFilename + '.csv']);
698 AssignFile(s, fname);
700 SetTextCodePage(s, CP_UTF8);
701 Rewrite(s);
702 // line 1: stats ver, datetime, server name, map name, game mode, time limit, score limit, dmflags, game time, num players
703 if g_Game_IsNet then fname := NetServerName else fname := '';
704 map := g_ExtractWadNameNoPath(gMapInfo.Map) + ':/' + g_ExtractFileName(gMapInfo.Map);
705 mode := g_Game_ModeToText(Stat.GameMode);
706 etime := Format('%d:%.2d:%.2d', [
707 Stat.GameTime div 1000 div 3600,
708 (Stat.GameTime div 1000 div 60) mod 60,
709 Stat.GameTime div 1000 mod 60
711 flags := '';
712 strf := '';
713 for flag in gGameSettings.Options do
714 begin
715 flags += strf;
716 System.WriteStr(strf, flag); // FIXME: rename our utils.WriteStr()
717 flags += strf;
718 strf := ', ';
719 end;
720 WriteLn(s, 'stats_ver,datetime,server,map,mode,timelimit,scorelimit,dmflags,time,num_players');
721 WriteLn(s, Format('%d,%s,%s,%s,%s,%u,%u,"%s",%s,%d', [
722 STATFILE_VERSION,
723 StatDate,
724 dquoteStr(fname),
725 dquoteStr(map),
726 mode,
727 gGameSettings.TimeLimit,
728 gGameSettings.ScoreLimit,
729 flags,
730 etime,
731 Length(Stat.PlayerStat)
732 ]));
733 // line 2: game specific shit
734 // if it's a team game: red score, blue score
735 // if it's a coop game: monsters killed, monsters total, secrets found, secrets total
736 // otherwise nothing
737 if Stat.GameMode in [GM_TDM, GM_CTF] then
738 WriteLn(s,
739 Format('red_score,blue_score' + LineEnding + '%d,%d', [Stat.TeamStat[TEAM_RED].Score, Stat.TeamStat[TEAM_BLUE].Score]))
740 else if Stat.GameMode in [GM_COOP, GM_SINGLE] then
741 WriteLn(s,
742 Format('mon_killed,mon_total,secrets_found,secrets_total' + LineEnding + '%d,%d,%d,%d',[gCoopMonstersKilled, gTotalMonsters, gCoopSecretsFound, gSecretsCount]));
743 // lines 3-...: team, player name, frags, deaths
744 WriteLn(s, 'team,name,frags,deaths');
745 for I := Low(Stat.PlayerStat) to High(Stat.PlayerStat) do
746 with Stat.PlayerStat[I] do
747 WriteLn(s, Format('%d,%s,%d,%d', [Team, dquoteStr(Name), Frags, Deaths]));
748 except
749 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [fname]));
750 end;
751 except
752 g_Console_Add('could not create gamestats file "' + fname + '"');
753 end;
754 CloseFile(s);
755 end;
757 procedure ClearDebugCvars();
758 begin
759 g_debug_Sounds := False;
760 g_debug_Frames := False;
761 g_debug_WinMsgs := False;
762 g_debug_MonsterOff := False;
763 g_debug_BotAIOff := 0;
764 g_debug_HealthBar := False;
765 g_Debug_Player := False;
766 end;
768 function g_Game_ModeToText(Mode: Byte): string;
769 begin
770 Result := '';
771 case Mode of
772 GM_DM: Result := _lc[I_MENU_GAME_TYPE_DM];
773 GM_TDM: Result := _lc[I_MENU_GAME_TYPE_TDM];
774 GM_CTF: Result := _lc[I_MENU_GAME_TYPE_CTF];
775 GM_COOP: Result := _lc[I_MENU_GAME_TYPE_COOP];
776 GM_SINGLE: Result := _lc[I_MENU_GAME_TYPE_SINGLE];
777 end;
778 end;
780 function g_Game_TextToMode(Mode: string): Byte;
781 begin
782 Result := GM_NONE;
783 Mode := UpperCase(Mode);
784 if Mode = _lc[I_MENU_GAME_TYPE_DM] then
785 begin
786 Result := GM_DM;
787 Exit;
788 end;
789 if Mode = _lc[I_MENU_GAME_TYPE_TDM] then
790 begin
791 Result := GM_TDM;
792 Exit;
793 end;
794 if Mode = _lc[I_MENU_GAME_TYPE_CTF] then
795 begin
796 Result := GM_CTF;
797 Exit;
798 end;
799 if Mode = _lc[I_MENU_GAME_TYPE_COOP] then
800 begin
801 Result := GM_COOP;
802 Exit;
803 end;
804 if Mode = _lc[I_MENU_GAME_TYPE_SINGLE] then
805 begin
806 Result := GM_SINGLE;
807 Exit;
808 end;
809 end;
811 function g_Game_IsNet(): Boolean;
812 begin
813 Result := (gGameSettings.GameType in [GT_SERVER, GT_CLIENT]);
814 end;
816 function g_Game_IsServer(): Boolean;
817 begin
818 Result := (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM, GT_SERVER]);
819 end;
821 function g_Game_IsClient(): Boolean;
822 begin
823 Result := (gGameSettings.GameType = GT_CLIENT);
824 end;
826 function g_Game_GetMegaWADInfo(WAD: String; cfg: TConfig): TMegaWADInfo;
828 w: TWADFile;
829 p: Pointer = nil;
830 len: Integer;
831 begin
832 if cfg = nil then
833 begin
834 w := TWADFile.Create();
835 w.ReadFile(WAD);
837 w.GetResource('INTERSCRIPT', p, len);
838 w.Destroy();
840 if p = nil then
841 begin
842 Result.name := ExtractFileName(WAD);
843 Exit;
844 end;
846 cfg := TConfig.CreateMem(p, len);
847 FreeMem(p);
848 end;
850 Result.name := cfg.ReadStr('megawad', 'name', ExtractFileName(WAD));
851 Result.description := cfg.ReadStr('megawad', 'description', '');
852 Result.author := cfg.ReadStr('megawad', 'author', '');
853 Result.pic := cfg.ReadStr('megawad', 'pic', '');
855 if p <> nil then
856 cfg.Destroy();
857 end;
859 procedure g_Game_FreeCurrentWAD();
861 a: Integer;
862 begin
863 for a := 0 to High(MegaWAD.res.pic) do
864 if MegaWAD.res.pic[a] <> '' then
865 g_Texture_Delete(MegaWAD.res.pic[a]);
867 {$IFDEF ENABLE_SOUND}
868 for a := 0 to High(MegaWAD.res.mus) do
869 if MegaWAD.res.mus[a] <> '' then
870 g_Sound_Delete(MegaWAD.res.mus[a]);
871 {$ENDIF}
873 MegaWAD.res.pic := nil;
874 MegaWAD.res.text := nil;
875 MegaWAD.res.anim := nil;
876 MegaWAD.res.mus := nil;
877 MegaWAD.triggers := nil;
879 g_Texture_Delete('TEXTURE_endpic');
881 {$IFDEF ENABLE_SOUND}
882 g_Sound_Delete('MUSIC_endmus');
883 {$ENDIF}
885 ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
886 gGameSettings.WAD := '';
887 end;
889 procedure g_Game_SetCurrentWAD(WAD: string);
891 w: TWADFile;
892 cfg: TConfig;
893 p: Pointer = nil;
894 {b, }len: Integer;
895 s: AnsiString;
896 begin
897 g_Game_FreeCurrentWAD();
898 gGameSettings.WAD := WAD;
899 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then
900 Exit;
902 w := TWADFile.Create();
903 w.ReadFile(WAD);
904 w.GetResource('INTERSCRIPT', p, len);
905 w.Destroy();
907 if p = nil then Exit;
908 cfg := TConfig.CreateMem(p, len);
909 FreeMem(p);
910 MegaWAD.info := g_Game_GetMegaWADInfo(WAD, cfg);
912 MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
913 if MegaWAD.endpic <> '' then
914 begin
915 TEXTUREFILTER := GL_LINEAR;
916 s := e_GetResourcePath(WadDirs, MegaWAD.endpic, WAD);
917 g_Texture_CreateWADEx('TEXTURE_endpic', s);
918 TEXTUREFILTER := GL_NEAREST;
919 end;
920 MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
922 {$IFDEF ENABLE_SOUND}
923 if MegaWAD.endmus <> '' then
924 begin
925 s := e_GetResourcePath(WadDirs, MegaWAD.endmus, WAD);
926 g_Sound_CreateWADEx('MUSIC_endmus', s, True);
927 end;
928 {$ENDIF}
930 cfg.Destroy();
931 end;
933 {procedure start_trigger(t: string);
934 begin
935 end;
937 function next_trigger(): Boolean;
938 begin
939 end;}
941 procedure DisableCheats();
942 begin
943 MAX_RUNVEL := 8;
944 VEL_JUMP := 10;
945 gFly := False;
947 if gPlayer1 <> nil then gPlayer1.GodMode := False;
948 if gPlayer2 <> nil then gPlayer2.GodMode := False;
949 if gPlayer1 <> nil then gPlayer1.NoTarget := False;
950 if gPlayer2 <> nil then gPlayer2.NoTarget := False;
952 {$IF DEFINED(D2F_DEBUG)}
953 if gPlayer1 <> nil then gPlayer1.NoTarget := True;
954 gAimLine := g_dbg_aimline_on;
955 {$ENDIF}
956 end;
958 procedure g_Game_ExecuteEvent(Name: String);
960 a: Integer;
961 begin
962 if Name = '' then
963 Exit;
964 if gEvents = nil then
965 Exit;
966 for a := 0 to High(gEvents) do
967 if gEvents[a].Name = Name then
968 begin
969 if gEvents[a].Command <> '' then
970 g_Console_Process(gEvents[a].Command, True);
971 break;
972 end;
973 end;
975 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
977 a, n: Integer;
978 begin
979 n := -1;
980 if gDelayedEvents <> nil then
981 for a := 0 to High(gDelayedEvents) do
982 if not gDelayedEvents[a].Pending then
983 begin
984 n := a;
985 break;
986 end;
987 if n = -1 then
988 begin
989 SetLength(gDelayedEvents, Length(gDelayedEvents) + 1);
990 n := High(gDelayedEvents);
991 end;
992 gDelayedEvents[n].Pending := True;
993 gDelayedEvents[n].DEType := DEType;
994 gDelayedEvents[n].DENum := Num;
995 gDelayedEvents[n].DEStr := Str;
996 if DEType = DE_GLOBEVENT then
997 gDelayedEvents[n].Time := (sys_GetTicks() {div 1000}) + Time
998 else
999 gDelayedEvents[n].Time := gTime + Time;
1000 Result := n;
1001 end;
1003 procedure EndGame();
1005 a: Integer;
1006 FileName: string;
1007 t: TDateTime;
1008 begin
1009 if g_Game_IsNet and g_Game_IsServer then
1010 MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed));
1012 // Ñòîï èãðà:
1013 gPauseMain := false;
1014 gPauseHolmes := false;
1015 gGameOn := false;
1017 {$IFDEF ENABLE_SOUND}
1018 g_Game_StopAllSounds(False);
1019 {$ENDIF}
1021 MessageTime := 0;
1022 MessageText := '';
1024 EndingGameCounter := 0;
1025 g_ActiveWindow := nil;
1027 gLMSRespawn := LMS_RESPAWN_NONE;
1028 gLMSRespawnTime := 0;
1030 case gExit of
1031 EXIT_SIMPLE: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
1032 begin
1033 g_Game_Free();
1035 if gMapOnce then
1036 begin // Ýòî áûë òåñò
1037 gExit := EXIT_QUIT;
1039 else
1040 begin // Âûõîä â ãëàâíîå ìåíþ
1041 {$IFDEF ENABLE_SOUND}
1042 gMusic.SetByName('MUSIC_MENU');
1043 gMusic.Play();
1044 {$ENDIF}
1045 if gState <> STATE_SLIST then
1046 begin
1047 g_GUI_ShowWindow('MainMenu');
1048 gState := STATE_MENU;
1049 end else
1050 begin
1051 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
1052 slReturnPressed := True;
1053 if g_Net_Slist_Fetch(slCurrent) then
1054 begin
1055 if slCurrent = nil then
1056 slWaitStr := _lc[I_NET_SLIST_NOSERVERS];
1058 else
1059 slWaitStr := _lc[I_NET_SLIST_ERROR];
1060 g_Serverlist_GenerateTable(slCurrent, slTable);
1061 end;
1063 g_Game_ExecuteEvent('ongameend');
1064 end;
1065 end;
1067 EXIT_RESTART: // Íà÷àòü óðîâåíü ñíà÷àëà
1068 begin
1069 if not g_Game_IsClient then g_Game_Restart();
1070 end;
1072 EXIT_ENDLEVELCUSTOM: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
1073 begin
1074 // Ñòàòèñòèêà Ñâîåé èãðû:
1075 FileName := g_ExtractWadName(gMapInfo.Map);
1077 CustomStat.GameTime := gTime;
1078 CustomStat.Map := ExtractFileName(FileName)+':'+g_ExtractFileName(gMapInfo.Map); //ResName;
1079 CustomStat.MapName := gMapInfo.Name;
1080 CustomStat.GameMode := gGameSettings.GameMode;
1081 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1082 CustomStat.TeamStat := gTeamStat;
1084 CustomStat.PlayerStat := nil;
1086 // Ñòàòèñòèêà èãðîêîâ:
1087 if gPlayers <> nil then
1088 begin
1089 for a := 0 to High(gPlayers) do
1090 if gPlayers[a] <> nil then
1091 begin
1092 SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1);
1093 with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do
1094 begin
1095 Num := a;
1096 Name := gPlayers[a].Name;
1097 Frags := gPlayers[a].Frags;
1098 Deaths := gPlayers[a].Death;
1099 Kills := gPlayers[a].Kills;
1100 Team := gPlayers[a].Team;
1101 Color := gPlayers[a].Model.Color;
1102 Spectator := gPlayers[a].FSpectator;
1103 end;
1104 end;
1106 SortGameStat(CustomStat.PlayerStat);
1108 if (gSaveStats or gScreenshotStats) and (Length(CustomStat.PlayerStat) > 1) then
1109 begin
1110 t := Now;
1111 if g_Game_IsNet then StatFilename := NetServerName else StatFilename := 'local';
1112 StatDate := FormatDateTime('yymmdd_hhnnss', t);
1113 StatFilename := StatFilename + '_' + CustomStat.Map + '_' + g_Game_ModeToText(CustomStat.GameMode);
1114 StatFilename := sanitizeFilename(StatFilename) + '_' + StatDate;
1115 if gSaveStats then
1116 SaveGameStat(CustomStat, FormatDateTime('yyyy"/"mm"/"dd', t));
1117 end;
1119 StatShotDone := False;
1120 end;
1122 g_Game_ExecuteEvent('onmapend');
1123 if not g_Game_IsClient then g_Player_ResetReady;
1124 gInterReadyCount := 0;
1126 // Çàòóõàþùèé ýêðàí:
1127 EndingGameCounter := 255;
1128 gState := STATE_FOLD;
1129 gInterTime := 0;
1130 if gDefInterTime < 0 then
1131 gInterEndTime := IfThen((gGameSettings.GameType = GT_SERVER) and (gPlayer1 = nil), 15000, 25000)
1132 else
1133 gInterEndTime := gDefInterTime * 1000;
1134 end;
1136 EXIT_ENDLEVELSINGLE: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
1137 begin
1138 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
1139 SingleStat.GameTime := gTime;
1140 SingleStat.TwoPlayers := gPlayer2 <> nil;
1141 SingleStat.TotalSecrets := gSecretsCount;
1142 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
1143 SingleStat.PlayerStat[0].Kills := gPlayer1.MonsterKills;
1144 SingleStat.PlayerStat[0].Secrets := gPlayer1.Secrets;
1145 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
1146 if SingleStat.TwoPlayers then
1147 begin
1148 SingleStat.PlayerStat[1].Kills := gPlayer2.MonsterKills;
1149 SingleStat.PlayerStat[1].Secrets := gPlayer2.Secrets;
1150 end;
1152 g_Game_ExecuteEvent('onmapend');
1154 // Åñòü åùå êàðòû:
1155 if gNextMap <> '' then
1156 begin
1157 {$IFDEF ENABLE_SOUND}
1158 gMusic.SetByName('MUSIC_INTERMUS');
1159 gMusic.Play();
1160 {$ENDIF}
1161 gState := STATE_INTERSINGLE;
1162 e_UnpressAllKeys();
1164 g_Game_ExecuteEvent('oninter');
1166 else // Áîëüøå íåò êàðò
1167 begin
1168 // Çàòóõàþùèé ýêðàí:
1169 EndingGameCounter := 255;
1170 gState := STATE_FOLD;
1171 end;
1172 end;
1173 end;
1175 // Îêîí÷àíèå îáðàáîòàíî:
1176 if gExit <> EXIT_QUIT then
1177 gExit := 0;
1178 end;
1180 procedure drawTime(X, Y: Integer); inline;
1181 begin
1182 e_TextureFontPrint(x, y,
1183 Format('%d:%.2d:%.2d', [
1184 gTime div 1000 div 3600,
1185 (gTime div 1000 div 60) mod 60,
1186 gTime div 1000 mod 60
1188 gStdFont);
1189 end;
1191 procedure DrawStat();
1193 pc, x, y, w, h: Integer;
1194 w1, w2, w3, w4: Integer;
1195 a, aa: Integer;
1196 cw, ch, r, g, b, rr, gg, bb: Byte;
1197 s1, s2, s3: String;
1198 _y: Integer;
1199 stat: TPlayerStatArray;
1200 wad, map: String;
1201 mapstr: String;
1202 namestr: String;
1203 begin
1204 pc := g_Player_GetCount();
1205 e_TextureFontGetSize(gStdFont, cw, ch);
1207 w := gScreenWidth-(gScreenWidth div 5);
1208 if gGameSettings.GameMode in [GM_TDM, GM_CTF]
1209 then h := 32+ch*(11+pc)
1210 else h := 40+ch*5+(ch+8)*pc;
1212 x := (gScreenWidth div 2)-(w div 2);
1213 y := (gScreenHeight div 2)-(h div 2);
1215 e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32);
1216 e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0);
1218 drawTime(x+w-78, y+8);
1220 wad := g_ExtractWadNameNoPath(gMapInfo.Map);
1221 map := g_ExtractFileName(gMapInfo.Map);
1222 mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name;
1224 case gGameSettings.GameMode of
1225 GM_DM: begin
1226 if gGameSettings.MaxLives = 0
1227 then s1 := _lc[I_GAME_DM]
1228 else s1 := _lc[I_GAME_LMS];
1229 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.ScoreLimit]);
1230 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600,
1231 (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1232 end;
1234 GM_TDM: begin
1235 if gGameSettings.MaxLives = 0
1236 then s1 := _lc[I_GAME_TDM]
1237 else s1 := _lc[I_GAME_TLMS];
1238 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.ScoreLimit]);
1239 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600,
1240 (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1241 end;
1243 GM_CTF: begin
1244 s1 := _lc[I_GAME_CTF];
1245 s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.ScoreLimit]);
1246 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600,
1247 (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1248 end;
1250 GM_COOP: begin
1251 if gGameSettings.MaxLives = 0
1252 then s1 := _lc[I_GAME_COOP]
1253 else s1 := _lc[I_GAME_SURV];
1254 s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters);
1255 s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount);
1256 end;
1257 end;
1259 _y := y+8;
1260 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1);
1261 _y += ch+8;
1262 e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1);
1263 _y += ch+8;
1264 e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1);
1266 e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3, gStdFont, 200, 200, 200, 1);
1268 case NetMode of
1269 NET_SERVER: e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1);
1270 NET_CLIENT: e_TextureFontPrintEx(x+8, y + 8, NetClientIP + ':' + IntToStr(NetClientPort),
1271 gStdFont, 255, 255, 255, 1);
1272 end;
1274 if pc = 0 then Exit;
1275 stat := g_Player_GetStats();
1276 SortGameStat(stat);
1278 w2 := (w-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1279 w3 := (w-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1280 w4 := w3;
1281 w1 := w-16-w2-w3-w4; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1283 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1284 begin
1285 _y += ch+ch;
1287 // first goes up the red team
1288 s1 := _lc[I_GAME_TEAM_RED];
1289 r := 255;
1290 g := 31;
1291 b := 31;
1293 for a := TEAM_RED to TEAM_BLUE do
1294 begin
1295 e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1);
1296 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Score), gStdFont, r, g, b, 1);
1298 _y += ch + (ch div 4);
1299 e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b);
1300 _y += ch div 4;
1302 for aa := 0 to High(stat) do
1303 begin
1304 if stat[aa].Team = a then
1305 with stat[aa] do
1306 begin
1307 if Spectator then
1308 begin
1309 rr := Color.r div 3;
1310 gg := Color.g div 3;
1311 bb := Color.b div 3;
1313 else
1314 begin
1315 // make player's color a bit brighter affinely (not linearly!)
1316 rr := Min(255, Color.r + g);
1317 gg := Min(255, Color.g + g);
1318 bb := Min(255, Color.b + g);
1319 end;
1321 if gShowPIDs
1322 then namestr := Format('[%5d] %s', [UID, Name])
1323 else namestr := Name;
1325 // Èìÿ, ïèíã/ïîòåðè, ôðàãè, ñìåðòè
1326 e_TextureFontPrintEx(x+16, _y, namestr, gStdFont, rr, gg, bb, 1);
1327 e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1);
1328 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
1329 e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
1331 _y += ch;
1332 end;
1333 end;
1335 _y += ch;
1337 // then show up the blue team
1338 if a = TEAM_RED then
1339 begin
1340 s1 := _lc[I_GAME_TEAM_BLUE];
1341 r := 95;
1342 g := 95;
1343 b := 255;
1344 end;
1345 end;
1347 else if gGameSettings.GameMode in [GM_DM, GM_COOP] then
1348 begin
1349 _y += ch+ch;
1350 e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
1351 e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1);
1352 e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
1353 e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
1355 _y += ch+8;
1356 for aa := 0 to High(stat) do
1357 with stat[aa] do
1358 begin
1359 if Spectator then
1360 begin
1361 r := 127;
1362 g := 64;
1364 else
1365 begin
1366 r := 255;
1367 g := 127;
1368 end;
1369 if gShowPIDs
1370 then namestr := Format('[%5d] %s', [UID, Name])
1371 else namestr := Name;
1373 // Öâåò èãðîêà
1374 e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
1375 e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192);
1377 // Èìÿ, ïèíã/ïîòåðè, ôðàãè, ñìåðòè
1378 e_TextureFontPrintEx(x+16+16+8, _y+4, namestr, gStdFont, r, g, 0, 1);
1379 e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1);
1380 e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1);
1381 e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1);
1383 _y += ch+8;
1384 end;
1386 end;
1388 procedure g_Game_Init();
1390 SR: TSearchRec;
1391 knownFiles: array of AnsiString = nil;
1392 found: Boolean;
1393 wext, s: AnsiString;
1394 f: Integer;
1395 begin
1396 gExit := 0;
1397 gMapToDelete := '';
1398 gTempDelete := False;
1400 sfsGCDisable(); // temporary disable removing of temporary volumes
1403 TEXTUREFILTER := GL_LINEAR;
1404 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE');
1405 g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER');
1406 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN');
1407 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU');
1408 TEXTUREFILTER := GL_NEAREST;
1410 LoadStdFont('STDTXT', 'STDFONT', gStdFont);
1411 LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
1412 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
1414 g_Game_ClearLoading();
1415 g_Game_SetLoadingText(Format('DOOM 2D FOREVER %s', [GAME_VERSION]), 0, False);
1416 g_Game_SetLoadingText('', 0, False);
1418 g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
1419 g_Console_Init();
1421 g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
1422 g_PlayerModel_LoadData();
1424 // load models from all possible wad types, in all known directories
1425 // this does a loosy job (linear search, ooph!), but meh
1426 for wext in wadExtensions do
1427 begin
1428 for f := High(ModelDirs) downto Low(ModelDirs) do
1429 begin
1430 if (FindFirst(ModelDirs[f]+DirectorySeparator+'*'+wext, faAnyFile, SR) = 0) then
1431 begin
1432 repeat
1433 found := false;
1434 for s in knownFiles do
1435 begin
1436 if (strEquCI1251(forceFilenameExt(SR.Name, ''), forceFilenameExt(ExtractFileName(s), ''))) then
1437 begin
1438 found := true;
1439 break;
1440 end;
1441 end;
1442 if not found then
1443 begin
1444 SetLength(knownFiles, length(knownFiles)+1);
1445 knownFiles[High(knownFiles)] := ModelDirs[f]+DirectorySeparator+SR.Name;
1446 end;
1447 until (FindNext(SR) <> 0);
1448 end;
1449 FindClose(SR);
1450 end;
1451 end;
1453 if (length(knownFiles) = 0) then raise Exception.Create('no player models found!');
1455 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);
1456 for s in knownFiles do
1457 begin
1458 if not g_PlayerModel_Load(s) then e_LogWritefln('Error loading model "%s"', [s], TMsgType.Warning);
1459 end;
1461 gGameOn := false;
1462 gPauseMain := false;
1463 gPauseHolmes := false;
1464 gTime := 0;
1466 {e_MouseInfo.Accel := 1.0;}
1468 g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
1469 g_Game_LoadData();
1471 {$IFDEF ENABLE_SOUND}
1472 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1473 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
1474 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
1475 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True, True);
1476 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
1477 {$ENDIF}
1479 {$IFNDEF HEADLESS}
1480 g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
1481 g_Menu_Init();
1482 {$ENDIF}
1484 {$IFDEF ENABLE_SOUND}
1485 gMusic := TMusic.Create();
1486 gMusic.SetByName('MUSIC_MENU');
1487 gMusic.Play();
1488 {$ELSE}
1489 gMusicName := '';
1490 gMusicPlay := False;
1491 gMusicPos := 0;
1492 gMusicPause := False;
1493 {$ENDIF}
1495 gGameSettings.WarmupTime := 30;
1497 gState := STATE_MENU;
1499 SetLength(gEvents, 6);
1500 gEvents[0].Name := 'ongamestart';
1501 gEvents[1].Name := 'ongameend';
1502 gEvents[2].Name := 'onmapstart';
1503 gEvents[3].Name := 'onmapend';
1504 gEvents[4].Name := 'oninter';
1505 gEvents[5].Name := 'onwadend';
1506 finally
1507 sfsGCEnable(); // enable releasing unused volumes
1508 end;
1509 end;
1511 procedure g_Game_Free(freeTextures: Boolean=true);
1512 begin
1513 e_WriteLog('g_Game_Free: completion of the gameplay', TMsgType.Notify);
1514 if NetMode = NET_CLIENT then g_Net_Disconnect();
1515 if NetMode = NET_SERVER then g_Net_Host_Die();
1517 g_Map_Free(freeTextures);
1518 g_Player_Free();
1519 g_Player_RemoveAllCorpses();
1521 gGameSettings.GameType := GT_NONE;
1522 if gGameSettings.GameMode = GM_SINGLE then
1523 gGameSettings.GameMode := GM_DM;
1524 gSwitchGameMode := gGameSettings.GameMode;
1526 gChatShow := False;
1527 gExitByTrigger := False;
1528 end;
1530 function IsActivePlayer(p: TPlayer): Boolean;
1531 begin
1532 Result := False;
1533 if p = nil then
1534 Exit;
1535 Result := (not p.FDummy) and (not p.FSpectator);
1536 end;
1538 function GetActivePlayer_ByID(ID: Integer): TPlayer;
1540 a: Integer;
1541 begin
1542 Result := nil;
1543 if ID < 0 then
1544 Exit;
1545 if gPlayers = nil then
1546 Exit;
1547 for a := Low(gPlayers) to High(gPlayers) do
1548 if IsActivePlayer(gPlayers[a]) then
1549 begin
1550 if gPlayers[a].UID <> ID then
1551 continue;
1552 Result := gPlayers[a];
1553 break;
1554 end;
1555 end;
1557 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
1559 a, idx: Integer;
1560 ids: Array of Word;
1561 begin
1562 Result := -1;
1563 if gPlayers = nil then
1564 Exit;
1565 SetLength(ids, 0);
1566 idx := -1;
1567 for a := Low(gPlayers) to High(gPlayers) do
1568 if IsActivePlayer(gPlayers[a]) then
1569 begin
1570 SetLength(ids, Length(ids) + 1);
1571 ids[High(ids)] := gPlayers[a].UID;
1572 if gPlayers[a].UID = Skip then
1573 idx := High(ids);
1574 end;
1575 if Length(ids) = 0 then
1576 Exit;
1577 if idx = -1 then
1578 Result := ids[0]
1579 else
1580 Result := ids[(idx + 1) mod Length(ids)];
1581 end;
1583 function GetActivePlayerID_Prev(Skip: Integer = -1): Integer;
1585 a, idx: Integer;
1586 ids: Array of Word;
1587 begin
1588 Result := -1;
1589 if gPlayers = nil then
1590 Exit;
1591 SetLength(ids, 0);
1592 idx := -1;
1593 for a := Low(gPlayers) to High(gPlayers) do
1594 if IsActivePlayer(gPlayers[a]) then
1595 begin
1596 SetLength(ids, Length(ids) + 1);
1597 ids[High(ids)] := gPlayers[a].UID;
1598 if gPlayers[a].UID = Skip then
1599 idx := High(ids);
1600 end;
1601 if Length(ids) = 0 then
1602 Exit;
1603 if idx = -1 then
1604 Result := ids[Length(ids) - 1]
1605 else
1606 Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
1607 end;
1609 function GetActivePlayerID_Random(Skip: Integer = -1): Integer;
1611 a, idx: Integer;
1612 ids: Array of Word;
1613 begin
1614 Result := -1;
1615 if gPlayers = nil then
1616 Exit;
1617 SetLength(ids, 0);
1618 idx := -1;
1619 for a := Low(gPlayers) to High(gPlayers) do
1620 if IsActivePlayer(gPlayers[a]) then
1621 begin
1622 SetLength(ids, Length(ids) + 1);
1623 ids[High(ids)] := gPlayers[a].UID;
1624 if gPlayers[a].UID = Skip then
1625 idx := High(ids);
1626 end;
1627 if Length(ids) = 0 then
1628 Exit;
1629 if Length(ids) = 1 then
1630 begin
1631 Result := ids[0];
1632 Exit;
1633 end;
1634 Result := ids[Random(Length(ids))];
1635 a := 10;
1636 while (idx <> -1) and (Result = Skip) and (a > 0) do
1637 begin
1638 Result := ids[Random(Length(ids))];
1639 Dec(a);
1640 end;
1641 end;
1643 function GetRandomSpectMode(Current: Byte): Byte;
1644 label
1645 retry;
1646 begin
1647 Result := Current;
1648 retry:
1649 case Random(7) of
1650 0: Result := SPECT_STATS;
1651 1: Result := SPECT_MAPVIEW;
1652 2: Result := SPECT_MAPVIEW;
1653 3: Result := SPECT_PLAYERS;
1654 4: Result := SPECT_PLAYERS;
1655 5: Result := SPECT_PLAYERS;
1656 6: Result := SPECT_PLAYERS;
1657 end;
1658 if (Current in [SPECT_STATS, SPECT_MAPVIEW]) and (Current = Result) then
1659 goto retry;
1660 end;
1662 procedure ProcessPlayerControls (plr: TPlayer; p: Integer; var MoveButton: Byte);
1664 time: Word;
1665 strafeDir: Byte;
1666 i: Integer;
1667 begin
1668 if (plr = nil) then exit;
1669 if (p = 2) then time := 1000 else time := 1;
1670 strafeDir := MoveButton shr 4;
1671 MoveButton := MoveButton and $0F;
1673 if gPlayerAction[p, ACTION_MOVELEFT] and (not gPlayerAction[p, ACTION_MOVERIGHT]) then
1674 MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1675 else if (not gPlayerAction[p, ACTION_MOVELEFT]) and gPlayerAction[p, ACTION_MOVERIGHT] then
1676 MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1677 else if (not gPlayerAction[p, ACTION_MOVELEFT]) and (not gPlayerAction[p, ACTION_MOVERIGHT]) then
1678 MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1680 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1681 if MoveButton = 1 then
1682 plr.PressKey(KEY_LEFT, time)
1683 else if MoveButton = 2 then
1684 plr.PressKey(KEY_RIGHT, time);
1686 // if we have "strafe" key, turn off old strafe mechanics
1687 if gPlayerAction[p, ACTION_STRAFE] then
1688 begin
1689 // new strafe mechanics
1690 if (strafeDir = 0) then
1691 strafeDir := MoveButton; // start strafing
1692 // now set direction according to strafe (reversed)
1693 if (strafeDir = 2) then
1694 plr.SetDirection(TDirection.D_LEFT)
1695 else if (strafeDir = 1) then
1696 plr.SetDirection(TDirection.D_RIGHT)
1698 else
1699 begin
1700 strafeDir := 0; // not strafing anymore
1701 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1702 if (MoveButton = 2) and gPlayerAction[p, ACTION_MOVELEFT] then
1703 plr.SetDirection(TDirection.D_LEFT)
1704 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1705 else if (MoveButton = 1) and gPlayerAction[p, ACTION_MOVERIGHT] then
1706 plr.SetDirection(TDirection.D_RIGHT)
1707 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1708 else if MoveButton <> 0 then
1709 plr.SetDirection(TDirection(MoveButton-1))
1710 end;
1712 // fix movebutton state
1713 MoveButton := MoveButton or (strafeDir shl 4);
1715 // Îñòàëüíûå êëàâèøè:
1716 if gPlayerAction[p, ACTION_JUMP] then plr.PressKey(KEY_JUMP, time);
1717 if gPlayerAction[p, ACTION_LOOKUP] then plr.PressKey(KEY_UP, time);
1718 if gPlayerAction[p, ACTION_LOOKDOWN] then plr.PressKey(KEY_DOWN, time);
1719 if gPlayerAction[p, ACTION_ATTACK] then plr.PressKey(KEY_FIRE);
1720 if gPlayerAction[p, ACTION_ACTIVATE] then plr.PressKey(KEY_OPEN);
1722 for i := WP_FACT to WP_LACT do
1723 begin
1724 if gWeaponAction[p, i] then
1725 begin
1726 plr.ProcessWeaponAction(i);
1727 gWeaponAction[p, i] := False
1729 end;
1731 for i := WP_FIRST to WP_LAST do
1732 begin
1733 if gSelectWeapon[p, i] then
1734 begin
1735 plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
1736 gSelectWeapon[p, i] := False
1738 end;
1740 // HACK: add dynlight here
1741 if gwin_k8_enable_light_experiments then
1742 begin
1743 if e_KeyPressed(IK_F8) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1744 begin
1745 g_playerLight := true;
1746 end;
1747 if e_KeyPressed(IK_F9) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1748 begin
1749 g_playerLight := false;
1750 end;
1751 end;
1753 if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
1754 end;
1756 // HACK: don't have a "key was pressed" function
1757 procedure InterReady();
1758 begin
1759 if InterReadyTime > gTime then Exit;
1760 InterReadyTime := gTime + 3000;
1761 MC_SEND_CheatRequest(NET_CHEAT_READY);
1762 end;
1764 procedure g_Game_PreUpdate();
1765 begin
1766 // these are in separate PreUpdate functions because they can interact during Update()
1767 // and are synced over the net
1768 // we don't care that much about corpses and gibs
1769 g_Player_PreUpdate();
1770 g_Monsters_PreUpdate();
1771 g_Items_PreUpdate();
1772 g_Weapon_PreUpdate();
1773 end;
1775 procedure g_Game_Update();
1777 Msg: g_gui.TMessage;
1778 Time: Int64;
1779 a: Byte;
1780 w: Word;
1781 i, b: Integer;
1783 function sendMonsPos (mon: TMonster): Boolean;
1784 begin
1785 result := false; // don't stop
1786 // this will also reset "need-send" flag
1787 if mon.gncNeedSend then
1788 begin
1789 MH_SEND_MonsterPos(mon.UID);
1791 else if (mon.MonsterType = MONSTER_BARREL) then
1792 begin
1793 if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1795 else if (mon.MonsterState <> MONSTATE_SLEEP) then
1796 begin
1797 if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1798 end;
1799 end;
1801 function sendMonsPosUnexpected (mon: TMonster): Boolean;
1802 begin
1803 result := false; // don't stop
1804 // this will also reset "need-send" flag
1805 if mon.gncNeedSend then MH_SEND_MonsterPos(mon.UID);
1806 end;
1808 function sendItemPos (it: PItem): Boolean;
1809 begin
1810 result := false; // don't stop
1811 if it.needSend then
1812 begin
1813 MH_SEND_ItemPos(it.myId);
1814 it.needSend := False;
1815 end;
1816 end;
1819 reliableUpdate: Boolean;
1820 begin
1821 g_ResetDynlights();
1822 framePool.reset();
1824 // Ïîðà âûêëþ÷àòü èãðó:
1825 if gExit = EXIT_QUIT then
1826 Exit;
1827 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1828 if gExit <> 0 then
1829 begin
1830 EndGame();
1831 if gExit = EXIT_QUIT then
1832 Exit;
1833 end;
1835 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî
1836 // no need to, as we'll do it in event handler
1838 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1839 g_Console_Update();
1841 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1842 begin
1843 gExit := EXIT_SIMPLE;
1844 EndGame();
1845 Exit;
1846 end;
1848 // process master server communications
1849 g_Net_Slist_Pulse();
1851 case gState of
1852 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1853 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1854 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1855 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1856 begin
1857 if g_Game_IsNet and g_Game_IsServer then
1858 begin
1859 gInterTime := gInterTime + GAME_TICK;
1860 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1861 if a <> gServInterTime then
1862 begin
1863 gServInterTime := a;
1864 MH_SEND_TimeSync(gServInterTime);
1865 end;
1866 end;
1868 if (not g_Game_IsClient) and
1872 e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or e_KeyPressed(IK_SELECT) or
1873 e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) or
1874 e_KeyPressed(JOY0_ATTACK) or e_KeyPressed(JOY1_ATTACK) or
1875 e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK)
1877 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1878 and (g_ActiveWindow = nil)
1880 or (g_Game_IsNet and ((gInterTime > gInterEndTime) or ((gInterReadyCount >= NetClientCount) and (NetClientCount > 0))))
1882 then
1883 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1884 {$IFDEF ENABLE_SOUND}
1885 g_Game_StopAllSounds(True);
1886 {$ENDIF}
1888 if gMapOnce then // Ýòî áûë òåñò
1889 gExit := EXIT_SIMPLE
1890 else
1891 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1892 g_Game_ChangeMap(gNextMap)
1893 else // Ñëåäóþùåé êàðòû íåò
1894 begin
1895 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1896 begin
1897 // Âûõîä â ãëàâíîå ìåíþ:
1898 g_Game_Free();
1899 g_GUI_ShowWindow('MainMenu');
1900 {$IFDEF ENABLE_SOUND}
1901 gMusic.SetByName('MUSIC_MENU');
1902 gMusic.Play();
1903 {$ENDIF}
1904 gState := STATE_MENU;
1905 end else
1906 begin
1907 // Ôèíàëüíàÿ êàðòèíêà:
1908 g_Game_ExecuteEvent('onwadend');
1909 g_Game_Free();
1910 {$IFDEF ENABLE_SOUND}
1911 if not gMusic.SetByName('MUSIC_endmus') then
1912 gMusic.SetByName('MUSIC_STDENDMUS');
1913 gMusic.Play();
1914 {$ENDIF}
1915 gState := STATE_ENDPIC;
1916 end;
1917 g_Game_ExecuteEvent('ongameend');
1918 end;
1920 Exit;
1922 else if g_Game_IsClient and
1925 e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or e_KeyPressed(IK_SELECT) or
1926 e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) or
1927 e_KeyPressed(JOY0_ATTACK) or e_KeyPressed(JOY1_ATTACK) or
1928 e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK)
1930 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1931 and (g_ActiveWindow = nil)
1933 then
1934 begin
1935 // ready / unready
1936 InterReady();
1937 end;
1939 if gState = STATE_INTERTEXT then
1940 if InterText.counter > 0 then
1941 InterText.counter := InterText.counter - 1;
1942 end;
1944 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1945 begin
1946 if EndingGameCounter = 0 then
1947 begin
1948 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1949 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1950 begin
1951 gState := STATE_INTERCUSTOM;
1952 InterReadyTime := -1;
1953 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1954 begin
1955 g_Game_ExecuteEvent('onwadend');
1956 {$IFDEF ENABLE_SOUND}
1957 if not gMusic.SetByName('MUSIC_endmus') then
1958 gMusic.SetByName('MUSIC_STDENDMUS');
1959 {$ENDIF}
1961 else
1962 begin
1963 {$IFDEF ENABLE_SOUND}
1964 gMusic.SetByName('MUSIC_ROUNDMUS');
1965 {$ENDIF}
1966 end;
1967 {$IFDEF ENABLE_SOUND}
1968 gMusic.Play();
1969 {$ENDIF}
1970 e_UnpressAllKeys();
1972 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1973 begin
1974 {$IFDEF ENABLE_SOUND}
1975 gMusic.SetByName('MUSIC_INTERMUS');
1976 gMusic.Play();
1977 {$ENDIF}
1978 gState := STATE_INTERSINGLE;
1979 e_UnpressAllKeys();
1980 end;
1981 g_Game_ExecuteEvent('oninter');
1983 else
1984 DecMin(EndingGameCounter, 6, 0);
1985 end;
1987 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1988 begin
1989 if gMapOnce then // Ýòî áûë òåñò
1990 begin
1991 gExit := EXIT_SIMPLE;
1992 Exit;
1993 end;
1994 end;
1996 STATE_SLIST:
1997 g_Serverlist_Control(slCurrent, slTable);
1998 end;
2000 // Ñòàòèñòèêà ïî Tab:
2001 if gGameOn then
2002 IsDrawStat := (not gConsoleShow) and (not gChatShow) and (gGameSettings.GameType <> GT_SINGLE) and g_Console_Action(ACTION_SCORES);
2004 // Èãðà èäåò:
2005 if gGameOn and not gPause and (gState <> STATE_FOLD) then
2006 begin
2007 // Âðåìÿ += 28 ìèëëèñåêóíä:
2008 gTime := gTime + GAME_TICK;
2010 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
2011 if MessageTime = 0 then
2012 MessageText := '';
2013 if MessageTime > 0 then
2014 MessageTime := MessageTime - 1;
2016 if (g_Game_IsServer) then
2017 begin
2018 // Áûë çàäàí ëèìèò âðåìåíè:
2019 if (gGameSettings.TimeLimit > 0) then
2020 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
2021 begin // Îí ïðîøåë => êîíåö óðîâíÿ
2022 g_Game_NextLevel();
2023 Exit;
2024 end;
2026 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
2027 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
2028 g_Game_RestartRound(gLMSSoftSpawn);
2030 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
2031 if gVoteInProgress and (gVoteTimer < gTime) then
2032 g_Game_CheckVote
2033 else if gVotePassed and (gVoteCmdTimer < gTime) then
2034 begin
2035 g_Console_Process(gVoteCommand);
2036 gVoteCommand := '';
2037 gVotePassed := False;
2038 end;
2040 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
2041 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
2042 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
2043 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
2044 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
2046 // Áûë çàäàí ëèìèò ïîáåä:
2047 if (gGameSettings.ScoreLimit > 0) then
2048 begin
2049 b := 0;
2051 if gGameSettings.GameMode = GM_DM then
2052 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
2053 for i := 0 to High(gPlayers) do
2054 if gPlayers[i] <> nil then
2055 if gPlayers[i].Frags > b then
2056 b := gPlayers[i].Frags;
2058 else
2059 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2060 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
2061 b := Max(gTeamStat[TEAM_RED].Score, gTeamStat[TEAM_BLUE].Score);
2062 end;
2064 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
2065 if b >= gGameSettings.ScoreLimit then
2066 begin
2067 g_Game_NextLevel();
2068 Exit;
2069 end;
2070 end;
2072 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
2073 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
2074 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
2075 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
2076 begin
2077 ProcessPlayerControls(gPlayer1, 0, P1MoveButton);
2078 ProcessPlayerControls(gPlayer2, 1, P2MoveButton);
2079 end // if not console
2080 else
2081 begin
2082 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
2083 end;
2084 // process weapon switch queue
2085 end; // if server
2087 // Íàáëþäàòåëü
2088 if (gPlayer1 = nil) and (gPlayer2 = nil) and
2089 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
2090 begin
2091 if not gSpectKeyPress then
2092 begin
2093 if gPlayerAction[0, ACTION_JUMP] and (not gSpectAuto) then
2094 begin
2095 // switch spect mode
2096 case gSpectMode of
2097 SPECT_NONE: ; // not spectator
2098 SPECT_STATS,
2099 SPECT_MAPVIEW: Inc(gSpectMode);
2100 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
2101 end;
2102 gSpectKeyPress := True;
2103 end;
2104 if (gSpectMode = SPECT_MAPVIEW)
2105 and (not gSpectAuto) then
2106 begin
2107 if gPlayerAction[0, ACTION_MOVELEFT] then
2108 gSpectX := Max(gSpectX - gSpectStep, 0);
2109 if gPlayerAction[0, ACTION_MOVERIGHT] then
2110 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
2111 if gPlayerAction[0, ACTION_LOOKUP] then
2112 gSpectY := Max(gSpectY - gSpectStep, 0);
2113 if gPlayerAction[0, ACTION_LOOKDOWN] then
2114 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
2115 if gWeaponAction[0, WP_PREV] then
2116 begin
2117 // decrease step
2118 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
2119 gWeaponAction[0, WP_PREV] := False;
2120 end;
2121 if gWeaponAction[0, WP_NEXT] then
2122 begin
2123 // increase step
2124 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
2125 gWeaponAction[0, WP_NEXT] := False;
2126 end;
2127 end;
2128 if (gSpectMode = SPECT_PLAYERS)
2129 and (not gSpectAuto) then
2130 begin
2131 if gPlayerAction[0, ACTION_LOOKUP] then
2132 begin
2133 // add second view
2134 gSpectViewTwo := True;
2135 gSpectKeyPress := True;
2136 end;
2137 if gPlayerAction[0, ACTION_LOOKDOWN] then
2138 begin
2139 // remove second view
2140 gSpectViewTwo := False;
2141 gSpectKeyPress := True;
2142 end;
2143 if gPlayerAction[0, ACTION_MOVELEFT] then
2144 begin
2145 // prev player (view 1)
2146 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
2147 gSpectKeyPress := True;
2148 end;
2149 if gPlayerAction[0, ACTION_MOVERIGHT] then
2150 begin
2151 // next player (view 1)
2152 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
2153 gSpectKeyPress := True;
2154 end;
2155 if gWeaponAction[0, WP_PREV] then
2156 begin
2157 // prev player (view 2)
2158 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
2159 gWeaponAction[0, WP_PREV] := False;
2160 end;
2161 if gWeaponAction[0, WP_NEXT] then
2162 begin
2163 // next player (view 2)
2164 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
2165 gWeaponAction[0, WP_NEXT] := False;
2166 end;
2167 end;
2168 if gPlayerAction[0, ACTION_ATTACK] then
2169 begin
2170 if (gSpectMode = SPECT_STATS) and (not gSpectAuto) then
2171 begin
2172 gSpectAuto := True;
2173 gSpectAutoNext := 0;
2174 gSpectViewTwo := False;
2175 gSpectKeyPress := True;
2177 else
2178 if gSpectAuto then
2179 begin
2180 gSpectMode := SPECT_STATS;
2181 gSpectAuto := False;
2182 gSpectKeyPress := True;
2183 end;
2184 end;
2186 else
2187 if (not gPlayerAction[0, ACTION_JUMP]) and
2188 (not gPlayerAction[0, ACTION_ATTACK]) and
2189 (not gPlayerAction[0, ACTION_MOVELEFT]) and
2190 (not gPlayerAction[0, ACTION_MOVERIGHT]) and
2191 (not gPlayerAction[0, ACTION_LOOKUP]) and
2192 (not gPlayerAction[0, ACTION_LOOKDOWN]) then
2193 gSpectKeyPress := False;
2195 if gSpectAuto then
2196 begin
2197 if gSpectMode = SPECT_MAPVIEW then
2198 begin
2199 i := Min(Max(gSpectX + gSpectAutoStepX, 0), gMapInfo.Width - gScreenWidth);
2200 if i = gSpectX then
2201 gSpectAutoNext := gTime
2202 else
2203 gSpectX := i;
2204 i := Min(Max(gSpectY + gSpectAutoStepY, 0), gMapInfo.Height - gScreenHeight);
2205 if i = gSpectY then
2206 gSpectAutoNext := gTime
2207 else
2208 gSpectY := i;
2209 end;
2210 if gSpectAutoNext <= gTime then
2211 begin
2212 if gSpectAutoNext > 0 then
2213 begin
2214 gSpectMode := GetRandomSpectMode(gSpectMode);
2215 case gSpectMode of
2216 SPECT_MAPVIEW:
2217 begin
2218 gSpectX := Random(gMapInfo.Width - gScreenWidth);
2219 gSpectY := Random(gMapInfo.Height - gScreenHeight);
2220 gSpectAutoStepX := Random(9) - 4;
2221 gSpectAutoStepY := Random(9) - 4;
2222 if ((gSpectX < 800) and (gSpectAutoStepX < 0)) or
2223 ((gSpectX > gMapInfo.Width - gScreenWidth - 800) and (gSpectAutoStepX > 0)) then
2224 gSpectAutoStepX := gSpectAutoStepX * -1;
2225 if ((gSpectY < 800) and (gSpectAutoStepY < 0)) or
2226 ((gSpectY > gMapInfo.Height - gScreenHeight - 800) and (gSpectAutoStepY > 0)) then
2227 gSpectAutoStepY := gSpectAutoStepY * -1;
2228 end;
2229 SPECT_PLAYERS:
2230 begin
2231 gSpectPID1 := GetActivePlayerID_Random(gSpectPID1);
2232 end;
2233 end;
2234 end;
2235 case gSpectMode of
2236 SPECT_STATS: gSpectAutoNext := gTime + (Random(3) + 5) * 1000;
2237 SPECT_MAPVIEW: gSpectAutoNext := gTime + (Random(4) + 7) * 1000;
2238 SPECT_PLAYERS: gSpectAutoNext := gTime + (Random(7) + 8) * 1000;
2239 end;
2240 end;
2241 end;
2242 end;
2244 // Îáíîâëÿåì âñå îñòàëüíîå:
2245 g_Map_Update();
2246 g_Items_Update();
2247 g_Triggers_Update();
2248 g_Weapon_Update();
2249 g_Monsters_Update();
2250 g_GFX_Update();
2251 g_Player_UpdateAll();
2252 g_Player_UpdatePhysicalObjects();
2254 // server: send newly spawned monsters unconditionally
2255 if (gGameSettings.GameType = GT_SERVER) then
2256 begin
2257 if (Length(gMonstersSpawned) > 0) then
2258 begin
2259 for I := 0 to High(gMonstersSpawned) do MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
2260 SetLength(gMonstersSpawned, 0);
2261 end;
2262 end;
2264 {$IFDEF ENABLE_SOUND}
2265 if (gSoundTriggerTime > 8) then
2266 begin
2267 g_Game_UpdateTriggerSounds();
2268 gSoundTriggerTime := 0;
2270 else
2271 begin
2272 Inc(gSoundTriggerTime);
2273 end;
2274 {$ENDIF}
2276 if (NetMode = NET_SERVER) then
2277 begin
2278 Inc(NetTimeToUpdate);
2279 Inc(NetTimeToReliable);
2281 // send monster updates
2282 if (NetTimeToReliable >= NetRelupdRate) or (NetTimeToUpdate >= NetUpdateRate) then
2283 begin
2284 // send all monsters (periodic sync)
2285 reliableUpdate := (NetTimeToReliable >= NetRelupdRate);
2287 for I := 0 to High(gPlayers) do
2288 begin
2289 if (gPlayers[I] <> nil) then MH_SEND_PlayerPos(reliableUpdate, gPlayers[I].UID);
2290 end;
2292 g_Mons_ForEach(sendMonsPos);
2294 // update flags that aren't stationary
2295 if gGameSettings.GameMode = GM_CTF then
2296 for I := FLAG_RED to FLAG_BLUE do
2297 if gFlags[I].NeedSend then
2298 begin
2299 gFlags[I].NeedSend := False;
2300 MH_SEND_FlagPos(I);
2301 end;
2303 // update items that aren't stationary
2304 g_Items_ForEachAlive(sendItemPos);
2306 if reliableUpdate then
2307 begin
2308 NetTimeToReliable := 0;
2309 NetTimeToUpdate := NetUpdateRate;
2311 else
2312 begin
2313 NetTimeToUpdate := 0;
2314 end;
2316 else
2317 begin
2318 // send only mosters with some unexpected changes
2319 g_Mons_ForEach(sendMonsPosUnexpected);
2320 end;
2322 // send unexpected platform changes
2323 g_Map_NetSendInterestingPanels();
2325 g_Net_Slist_ServerUpdate();
2327 if NetUseMaster then
2328 begin
2329 if (gTime >= NetTimeToMaster) or g_Net_Slist_IsConnectionInProgress then
2330 begin
2331 if (not g_Net_Slist_IsConnectionActive) then g_Net_Slist_Connect(false); // non-blocking connection to the master
2332 g_Net_Slist_Update;
2333 NetTimeToMaster := gTime + NetMasterRate;
2334 end;
2335 end;
2338 else if (NetMode = NET_CLIENT) then
2339 begin
2340 MC_SEND_PlayerPos();
2341 end;
2342 end; // if gameOn ...
2344 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
2345 if g_ActiveWindow <> nil then
2346 begin
2347 w := e_GetFirstKeyPressed();
2349 if (w <> IK_INVALID) then
2350 begin
2351 Msg.Msg := MESSAGE_DIKEY;
2352 Msg.wParam := w;
2353 g_ActiveWindow.OnMessage(Msg);
2354 end;
2356 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
2357 if g_ActiveWindow <> nil then
2358 g_ActiveWindow.Update();
2360 // Íóæíî ñìåíèòü ðàçðåøåíèå:
2361 if gResolutionChange then
2362 begin
2363 e_WriteLog('Changing resolution', TMsgType.Notify);
2364 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
2365 gResolutionChange := False;
2366 g_ActiveWindow := nil;
2367 end;
2369 // Íóæíî ñìåíèòü ÿçûê:
2370 if gLanguageChange then
2371 begin
2372 //e_WriteLog('Read language file', MSG_NOTIFY);
2373 //g_Language_Load(DataDir + gLanguage + '.txt');
2374 g_Language_Set(gLanguage);
2375 {$IFNDEF HEADLESS}
2376 g_Menu_Reset();
2377 {$ENDIF}
2378 gLanguageChange := False;
2379 end;
2380 end;
2382 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
2383 if e_KeyPressed(IK_F10) and
2384 gGameOn and
2385 (not gConsoleShow) and
2386 (g_ActiveWindow = nil) then
2387 begin
2388 KeyPress(IK_F10);
2389 end;
2391 Time := sys_GetTicks() {div 1000};
2393 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
2394 if gDelayedEvents <> nil then
2395 for a := 0 to High(gDelayedEvents) do
2396 if gDelayedEvents[a].Pending and
2398 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
2399 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
2400 ) then
2401 begin
2402 case gDelayedEvents[a].DEType of
2403 DE_GLOBEVENT:
2404 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
2405 DE_BFGHIT:
2406 if gGameOn then
2407 begin
2408 {$IFDEF ENABLE_SOUND}
2409 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
2410 {$ENDIF}
2411 end;
2412 DE_KILLCOMBO:
2413 if gGameOn then
2414 begin
2415 {$IFDEF ENABLE_SOUND}
2416 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
2417 {$ENDIF}
2418 if g_Game_IsNet and g_Game_IsServer then
2419 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
2420 end;
2421 DE_BODYKILL:
2422 if gGameOn then
2423 begin
2424 {$IFDEF ENABLE_SOUND}
2425 g_Game_Announce_BodyKill(gDelayedEvents[a].DENum);
2426 {$ENDIF}
2427 end;
2428 end;
2429 gDelayedEvents[a].Pending := False;
2430 end;
2432 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2433 UPSCounter := UPSCounter + 1;
2434 if Time - UPSTime >= 1000 then
2435 begin
2436 UPS := UPSCounter;
2437 UPSCounter := 0;
2438 UPSTime := Time;
2439 end;
2441 if gGameOn then
2442 begin
2443 g_Weapon_AddDynLights();
2444 g_Items_AddDynLights();
2445 end;
2446 end;
2448 {$IFDEF ENABLE_SOUND}
2449 procedure g_Game_LoadChatSounds(Resource: string);
2451 WAD: TWADFile;
2452 FileName, Snd: string;
2453 p: Pointer;
2454 len, cnt, tags, i, j: Integer;
2455 cfg: TConfig;
2456 begin
2457 FileName := g_ExtractWadName(Resource);
2459 WAD := TWADFile.Create();
2460 WAD.ReadFile(FileName);
2462 if not WAD.GetResource(g_ExtractFilePathName(Resource), p, len) then
2463 begin
2464 gChatSounds := nil;
2465 WAD.Destroy();
2466 Exit;
2467 end;
2468 WAD.Destroy();
2470 cfg := TConfig.CreateMem(p, len);
2471 FreeMem(p);
2472 cnt := cfg.ReadInt('ChatSounds', 'Count', 0);
2474 SetLength(gChatSounds, cnt);
2475 for i := 0 to Length(gChatSounds) - 1 do
2476 begin
2477 gChatSounds[i].Sound := nil;
2478 Snd := Trim(cfg.ReadStr(IntToStr(i), 'Sound', ''));
2479 tags := cfg.ReadInt(IntToStr(i), 'Tags', 0);
2480 if (Snd = '') or (Tags <= 0) then
2481 continue;
2482 g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i), GameWAD+':'+Snd);
2483 gChatSounds[i].Sound := TPlayableSound.Create();
2484 gChatSounds[i].Sound.SetByName('SOUND_CHAT_MACRO' + IntToStr(i));
2485 SetLength(gChatSounds[i].Tags, tags);
2486 for j := 0 to tags - 1 do
2487 gChatSounds[i].Tags[j] := toLowerCase1251(cfg.ReadStr(IntToStr(i), 'Tag' + IntToStr(j), ''));
2488 gChatSounds[i].FullWord := cfg.ReadBool(IntToStr(i), 'FullWord', False);
2489 end;
2491 cfg.Destroy();
2492 end;
2494 procedure g_Game_FreeChatSounds();
2496 i: Integer;
2497 begin
2498 for i := 0 to Length(gChatSounds) - 1 do
2499 begin
2500 gChatSounds[i].Sound.Free();
2501 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i));
2502 end;
2503 SetLength(gChatSounds, 0);
2504 gChatSounds := nil;
2505 end;
2506 {$ENDIF}
2508 procedure g_Game_LoadData();
2510 wl, hl: Integer;
2511 wr, hr: Integer;
2512 wb, hb: Integer;
2513 wm, hm: Integer;
2514 begin
2515 if DataLoaded then Exit;
2517 e_WriteLog('Loading game data...', TMsgType.Notify);
2519 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2520 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2521 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2522 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2523 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2524 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2525 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
2526 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
2527 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
2528 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
2529 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
2530 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
2531 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2532 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2533 g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD+':TEXTURES\PLRIND');
2535 hasPBarGfx := true;
2536 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false;
2537 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false;
2538 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false;
2539 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false;
2541 if hasPBarGfx then
2542 begin
2543 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
2544 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
2545 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
2546 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
2547 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
2548 begin
2549 // yay!
2551 else
2552 begin
2553 hasPBarGfx := false;
2554 end;
2555 end;
2557 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2558 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD+':WEAPONS\PUNCH', 64, 64, 4, False);
2559 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD+':WEAPONS\PUNCH_UP', 64, 64, 4, False);
2560 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD+':WEAPONS\PUNCH_DN', 64, 64, 4, False);
2561 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD+':WEAPONS\PUNCHB', 64, 64, 4, False);
2562 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD+':WEAPONS\PUNCHB_UP', 64, 64, 4, False);
2563 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD+':WEAPONS\PUNCHB_DN', 64, 64, 4, False);
2565 {$IFDEF ENABLE_SOUND}
2566 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2567 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2568 g_Sound_CreateWADEx('SOUND_GAME_SECRET', GameWAD+':SOUNDS\SECRET');
2569 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2570 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2571 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2572 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2573 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2574 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2575 g_Sound_CreateWADEx('SOUND_GAME_BURNING', GameWAD+':SOUNDS\BURNING');
2576 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2577 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2578 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2579 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2580 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2581 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2582 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2583 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2584 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2585 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2586 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2587 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD+':SOUNDS\MUHAHA1');
2588 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD+':SOUNDS\MUHAHA2');
2589 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD+':SOUNDS\MUHAHA3');
2590 g_Sound_CreateWADEx('SOUND_CTF_GET1', GameWAD+':SOUNDS\GETFLAG1');
2591 g_Sound_CreateWADEx('SOUND_CTF_GET2', GameWAD+':SOUNDS\GETFLAG2');
2592 g_Sound_CreateWADEx('SOUND_CTF_LOST1', GameWAD+':SOUNDS\LOSTFLG1');
2593 g_Sound_CreateWADEx('SOUND_CTF_LOST2', GameWAD+':SOUNDS\LOSTFLG2');
2594 g_Sound_CreateWADEx('SOUND_CTF_RETURN1', GameWAD+':SOUNDS\RETFLAG1');
2595 g_Sound_CreateWADEx('SOUND_CTF_RETURN2', GameWAD+':SOUNDS\RETFLAG2');
2596 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE1', GameWAD+':SOUNDS\CAPFLAG1');
2597 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE2', GameWAD+':SOUNDS\CAPFLAG2');
2599 goodsnd[0] := TPlayableSound.Create();
2600 goodsnd[1] := TPlayableSound.Create();
2601 goodsnd[2] := TPlayableSound.Create();
2602 goodsnd[3] := TPlayableSound.Create();
2604 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2605 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2606 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2607 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2609 killsnd[0] := TPlayableSound.Create();
2610 killsnd[1] := TPlayableSound.Create();
2611 killsnd[2] := TPlayableSound.Create();
2612 killsnd[3] := TPlayableSound.Create();
2614 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2615 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2616 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2617 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2619 hahasnd[0] := TPlayableSound.Create();
2620 hahasnd[1] := TPlayableSound.Create();
2621 hahasnd[2] := TPlayableSound.Create();
2623 hahasnd[0].SetByName('SOUND_ANNOUNCER_MUHAHA1');
2624 hahasnd[1].SetByName('SOUND_ANNOUNCER_MUHAHA2');
2625 hahasnd[2].SetByName('SOUND_ANNOUNCER_MUHAHA3');
2627 sound_get_flag[0] := TPlayableSound.Create();
2628 sound_get_flag[1] := TPlayableSound.Create();
2629 sound_lost_flag[0] := TPlayableSound.Create();
2630 sound_lost_flag[1] := TPlayableSound.Create();
2631 sound_ret_flag[0] := TPlayableSound.Create();
2632 sound_ret_flag[1] := TPlayableSound.Create();
2633 sound_cap_flag[0] := TPlayableSound.Create();
2634 sound_cap_flag[1] := TPlayableSound.Create();
2636 sound_get_flag[0].SetByName('SOUND_CTF_GET1');
2637 sound_get_flag[1].SetByName('SOUND_CTF_GET2');
2638 sound_lost_flag[0].SetByName('SOUND_CTF_LOST1');
2639 sound_lost_flag[1].SetByName('SOUND_CTF_LOST2');
2640 sound_ret_flag[0].SetByName('SOUND_CTF_RETURN1');
2641 sound_ret_flag[1].SetByName('SOUND_CTF_RETURN2');
2642 sound_cap_flag[0].SetByName('SOUND_CTF_CAPTURE1');
2643 sound_cap_flag[1].SetByName('SOUND_CTF_CAPTURE2');
2645 g_Game_LoadChatSounds(GameWAD+':CHATSND\SNDCFG');
2646 {$ENDIF}
2648 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2649 g_Items_LoadData();
2651 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2652 g_Weapon_LoadData();
2654 g_Monsters_LoadData();
2656 DataLoaded := True;
2657 end;
2659 procedure g_Game_FreeData();
2660 begin
2661 if not DataLoaded then Exit;
2663 g_Items_FreeData();
2664 g_Weapon_FreeData();
2665 g_Monsters_FreeData();
2667 e_WriteLog('Releasing game data...', TMsgType.Notify);
2669 g_Texture_Delete('NOTEXTURE');
2670 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2671 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2672 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2673 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2674 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2675 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2676 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2677 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2678 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2679 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2680 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2681 g_Frames_DeleteByName('FRAMES_TELEPORT');
2682 g_Frames_DeleteByName('FRAMES_PUNCH');
2683 g_Frames_DeleteByName('FRAMES_PUNCH_UP');
2684 g_Frames_DeleteByName('FRAMES_PUNCH_DN');
2685 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK');
2686 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP');
2687 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
2689 {$IFDEF ENABLE_SOUND}
2690 g_Sound_Delete('SOUND_GAME_TELEPORT');
2691 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2692 g_Sound_Delete('SOUND_GAME_SECRET');
2693 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2694 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2695 g_Sound_Delete('SOUND_GAME_BULK1');
2696 g_Sound_Delete('SOUND_GAME_BULK2');
2697 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2698 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2699 g_Sound_Delete('SOUND_GAME_BURNING');
2700 g_Sound_Delete('SOUND_GAME_SWITCH1');
2701 g_Sound_Delete('SOUND_GAME_SWITCH0');
2703 goodsnd[0].Free();
2704 goodsnd[1].Free();
2705 goodsnd[2].Free();
2706 goodsnd[3].Free();
2708 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2709 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2710 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2711 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2713 killsnd[0].Free();
2714 killsnd[1].Free();
2715 killsnd[2].Free();
2716 killsnd[3].Free();
2718 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2719 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2720 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2721 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2723 hahasnd[0].Free();
2724 hahasnd[1].Free();
2725 hahasnd[2].Free();
2727 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1');
2728 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2');
2729 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3');
2731 sound_get_flag[0].Free();
2732 sound_get_flag[1].Free();
2733 sound_lost_flag[0].Free();
2734 sound_lost_flag[1].Free();
2735 sound_ret_flag[0].Free();
2736 sound_ret_flag[1].Free();
2737 sound_cap_flag[0].Free();
2738 sound_cap_flag[1].Free();
2740 g_Sound_Delete('SOUND_CTF_GET1');
2741 g_Sound_Delete('SOUND_CTF_GET2');
2742 g_Sound_Delete('SOUND_CTF_LOST1');
2743 g_Sound_Delete('SOUND_CTF_LOST2');
2744 g_Sound_Delete('SOUND_CTF_RETURN1');
2745 g_Sound_Delete('SOUND_CTF_RETURN2');
2746 g_Sound_Delete('SOUND_CTF_CAPTURE1');
2747 g_Sound_Delete('SOUND_CTF_CAPTURE2');
2749 g_Game_FreeChatSounds();
2750 {$ENDIF}
2752 DataLoaded := False;
2753 end;
2755 procedure DrawCustomStat();
2757 pc, x, y, w, _y,
2758 w1, w2, w3,
2759 t, p, m: Integer;
2760 ww1, hh1: Word;
2761 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2762 s1, s2, topstr: String;
2763 begin
2764 e_TextureFontGetSize(gStdFont, ww2, hh2);
2766 sys_HandleEvents();
2768 if g_Console_Action(ACTION_SCORES) then
2769 begin
2770 if not gStatsPressed then
2771 begin
2772 gStatsOff := not gStatsOff;
2773 gStatsPressed := True;
2774 end;
2776 else
2777 gStatsPressed := False;
2779 if gStatsOff then
2780 begin
2781 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2782 w := (Length(s1) * ww2) div 2;
2783 x := gScreenWidth div 2 - w;
2784 y := 8;
2785 e_TextureFontPrint(x, y, s1, gStdFont);
2786 Exit;
2787 end;
2789 if gGameSettings.GameMode = GM_COOP then
2790 begin
2791 if gMissionFailed
2792 then topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2793 else topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2795 else
2796 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2798 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2799 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2801 if g_Game_IsNet then
2802 begin
2803 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2804 if not gChatShow then
2805 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2806 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2807 end;
2809 if g_Game_IsClient
2810 then topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2811 else topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2812 if not gChatShow then
2813 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2814 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2816 x := 32;
2817 y := 16+hh1+16;
2819 w := gScreenWidth-x*2;
2821 w2 := (w-16) div 6;
2822 w3 := w2;
2823 w1 := w-16-w2-w3;
2825 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2826 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2828 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2830 case CustomStat.GameMode of
2831 GM_DM:
2832 if gGameSettings.MaxLives = 0
2833 then s1 := _lc[I_GAME_DM]
2834 else s1 := _lc[I_GAME_LMS];
2835 GM_TDM:
2836 if gGameSettings.MaxLives = 0
2837 then s1 := _lc[I_GAME_TDM]
2838 else s1 := _lc[I_GAME_TLMS];
2839 GM_COOP:
2840 if gGameSettings.MaxLives = 0
2841 then s1 := _lc[I_GAME_COOP]
2842 else s1 := _lc[I_GAME_SURV];
2844 GM_CTF: s1 := _lc[I_GAME_CTF];
2845 else s1 := '';
2846 end;
2848 _y := y+16;
2849 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2850 _y += 8;
2852 _y += 16;
2853 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2854 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2856 _y += 16;
2857 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2858 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2859 (CustomStat.GameTime div 1000 div 60) mod 60,
2860 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2862 pc := Length(CustomStat.PlayerStat);
2863 if pc = 0 then Exit;
2865 if CustomStat.GameMode = GM_COOP then
2866 begin
2867 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2868 _y += 32;
2869 s2 := _lc[I_GAME_MONSTERS];
2870 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2871 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2872 _y += 16;
2873 s2 := _lc[I_GAME_SECRETS];
2874 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2875 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2876 if gLastMap then
2877 begin
2878 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2879 _y -= 16;
2880 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2881 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2882 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2883 _y += 16;
2884 s2 := _lc[I_GAME_SECRETS_TOTAL];
2885 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2886 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2887 end;
2888 end;
2890 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2891 begin
2892 _y += 16+16;
2894 with CustomStat do
2895 if TeamStat[TEAM_RED].Score > TeamStat[TEAM_BLUE].Score then
2896 s1 := _lc[I_GAME_WIN_RED]
2897 else if TeamStat[TEAM_BLUE].Score > TeamStat[TEAM_RED].Score then
2898 s1 := _lc[I_GAME_WIN_BLUE]
2899 else
2900 s1 := _lc[I_GAME_WIN_DRAW];
2902 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2903 _y += 40;
2905 // first goes up the red team
2906 s1 := _lc[I_GAME_TEAM_RED];
2907 r := 255;
2908 g := 31;
2909 b := 31;
2911 for t := TEAM_RED to TEAM_BLUE do
2912 begin
2913 e_TextureFontPrintEx(x+8, _y, s1, gStdFont, r, g, b, 1);
2914 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[t].Score), gStdFont, r, g, b, 1);
2916 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2917 _y += 24;
2919 for p := 0 to High(CustomStat.PlayerStat) do
2920 if CustomStat.PlayerStat[p].Team = t then
2921 with CustomStat.PlayerStat[p] do
2922 begin
2923 if Spectator then
2924 begin
2925 rr := Color.r div 3;
2926 gg := Color.g div 3;
2927 bb := Color.b div 3;
2929 else
2930 begin
2931 // make player's color a bit brighter affinely (not linearly!)
2932 rr := Min(255, Color.r + g);
2933 gg := Min(255, Color.g + g);
2934 bb := Min(255, Color.b + g);
2935 end;
2936 if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady)
2937 then e_TextureFontPrintEx(x+16, _y, Name + ' *', gStdFont, rr, gg, bb, 1)
2938 else e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
2939 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2940 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2941 _y += 24;
2942 end;
2944 _y += 16+16;
2946 // then show up the blue team
2947 if t = TEAM_RED then
2948 begin
2949 s1 := _lc[I_GAME_TEAM_BLUE];
2950 r := 95;
2951 g := 95;
2952 b := 255;
2953 end;
2954 end;
2956 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2957 begin
2958 _y += 40;
2959 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2960 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2961 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2963 _y += 24;
2964 for p := 0 to High(CustomStat.PlayerStat) do
2965 with CustomStat.PlayerStat[p] do
2966 begin
2967 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2969 if Spectator
2970 then r := 127
2971 else r := 255;
2973 if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady)
2974 then e_TextureFontPrintEx(x+8+16+8, _y+4, Name + ' *', gStdFont, r, r, r, 1, True)
2975 else e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2976 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2977 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2978 _y += 24;
2979 end;
2980 end;
2982 // HACK: take stats screenshot immediately after the first frame of the stats showing
2983 if gScreenshotStats and (not StatShotDone) and (Length(CustomStat.PlayerStat) > 1) then
2984 begin
2985 g_TakeScreenShot('stats/' + StatFilename);
2986 StatShotDone := True;
2987 end;
2988 end;
2990 procedure DrawSingleStat();
2992 tm, key_x, val_x, y: Integer;
2993 w1, w2, h: Word;
2994 s1, s2: String;
2996 procedure player_stat(n: Integer);
2998 kpm: Real;
3000 begin
3001 // "Kills: # / #":
3002 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
3003 s2 := Format(' %d', [gTotalMonsters]);
3005 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
3006 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
3007 e_CharFont_GetSize(gMenuFont, s1, w1, h);
3008 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
3009 s1 := s1 + '/';
3010 e_CharFont_GetSize(gMenuFont, s1, w1, h);
3011 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
3013 // "Kills-per-minute: ##.#":
3014 s1 := _lc[I_MENU_INTER_KPM];
3015 if tm > 0 then
3016 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
3017 else
3018 kpm := SingleStat.PlayerStat[n].Kills;
3019 s2 := Format(' %.1f', [kpm]);
3021 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
3022 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
3024 // "Secrets found: # / #":
3025 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
3026 s2 := Format(' %d', [SingleStat.TotalSecrets]);
3028 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
3029 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
3030 e_CharFont_GetSize(gMenuFont, s1, w1, h);
3031 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
3032 s1 := s1 + '/';
3033 e_CharFont_GetSize(gMenuFont, s1, w1, h);
3034 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
3035 end;
3037 begin
3038 // "Level Complete":
3039 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
3040 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
3042 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
3043 s1 := _lc[I_MENU_INTER_KPM];
3044 e_CharFont_GetSize(gMenuFont, s1, w1, h);
3045 Inc(w1, 16);
3046 s1 := ' 9999.9';
3047 e_CharFont_GetSize(gMenuFont, s1, w2, h);
3049 key_x := (gScreenWidth-w1-w2) div 2;
3050 val_x := key_x + w1;
3052 // "Time: #:##:##":
3053 tm := SingleStat.GameTime div 1000;
3054 s1 := _lc[I_MENU_INTER_TIME];
3055 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
3057 e_CharFont_Print(gMenuFont, key_x, 80, s1);
3058 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
3060 if SingleStat.TwoPlayers then
3061 begin
3062 // "Player 1":
3063 s1 := _lc[I_MENU_PLAYER_1];
3064 e_CharFont_GetSize(gMenuFont, s1, w1, h);
3065 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
3067 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3068 y := 176;
3069 player_stat(0);
3071 // "Player 2":
3072 s1 := _lc[I_MENU_PLAYER_2];
3073 e_CharFont_GetSize(gMenuFont, s1, w1, h);
3074 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
3076 // Ñòàòèñòèêà âòîðîãî èãðîêà:
3077 y := 336;
3078 player_stat(1);
3080 else
3081 begin
3082 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3083 y := 128;
3084 player_stat(0);
3085 end;
3086 end;
3088 procedure DrawLoadingStat();
3089 procedure drawRect (x, y, w, h: Integer);
3090 begin
3091 if (w < 1) or (h < 1) then exit;
3092 glBegin(GL_QUADS);
3093 glVertex2f(x+0.375, y+0.375);
3094 glVertex2f(x+w+0.375, y+0.375);
3095 glVertex2f(x+w+0.375, y+h+0.375);
3096 glVertex2f(x+0.375, y+h+0.375);
3097 glEnd();
3098 end;
3100 function drawPBar (cur, total: Integer; washere: Boolean): Boolean;
3102 rectW, rectH: Integer;
3103 x0, y0: Integer;
3104 wdt: Integer;
3105 wl, hl: Integer;
3106 wr, hr: Integer;
3107 wb, hb: Integer;
3108 wm, hm: Integer;
3109 idl, idr, idb, idm: LongWord;
3110 f, my: Integer;
3111 begin
3112 result := false;
3113 if (total < 1) then exit;
3114 if (cur < 1) then exit; // don't blink
3115 if (not washere) and (cur >= total) then exit; // don't blink
3116 //if (cur < 0) then cur := 0;
3117 //if (cur > total) then cur := total;
3118 result := true;
3120 if (hasPBarGfx) then
3121 begin
3122 g_Texture_Get('UI_GFX_PBAR_LEFT', idl);
3123 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
3124 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr);
3125 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
3126 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb);
3127 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
3128 g_Texture_Get('UI_GFX_PBAR_MARKER', idm);
3129 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
3131 //rectW := gScreenWidth-360;
3132 rectW := trunc(624.0*gScreenWidth/1024.0);
3133 rectH := hl;
3135 x0 := (gScreenWidth-rectW) div 2;
3136 y0 := gScreenHeight-rectH-64;
3137 if (y0 < 2) then y0 := 2;
3139 glEnable(GL_SCISSOR_TEST);
3141 // left and right
3142 glScissor(x0, gScreenHeight-y0-rectH, rectW, rectH);
3143 e_DrawSize(idl, x0, y0, 0, true, false, wl, hl);
3144 e_DrawSize(idr, x0+rectW-wr, y0, 0, true, false, wr, hr);
3146 // body
3147 glScissor(x0+wl, gScreenHeight-y0-rectH, rectW-wl-wr, rectH);
3148 f := x0+wl;
3149 while (f < x0+rectW) do
3150 begin
3151 e_DrawSize(idb, f, y0, 0, true, false, wb, hb);
3152 f += wb;
3153 end;
3155 // filled part
3156 wdt := (rectW-wl-wr)*cur div total;
3157 if (wdt > rectW-wl-wr) then wdt := rectW-wr-wr;
3158 if (wdt > 0) then
3159 begin
3160 my := y0; // don't be so smart, ketmar: +(rectH-wm) div 2;
3161 glScissor(x0+wl, gScreenHeight-my-rectH, wdt, hm);
3162 f := x0+wl;
3163 while (wdt > 0) do
3164 begin
3165 e_DrawSize(idm, f, y0, 0, true, false, wm, hm);
3166 f += wm;
3167 wdt -= wm;
3168 end;
3169 end;
3171 glScissor(0, 0, gScreenWidth, gScreenHeight);
3173 else
3174 begin
3175 rectW := gScreenWidth-64;
3176 rectH := 16;
3178 x0 := (gScreenWidth-rectW) div 2;
3179 y0 := gScreenHeight-rectH-64;
3180 if (y0 < 2) then y0 := 2;
3182 glDisable(GL_BLEND);
3183 glDisable(GL_TEXTURE_2D);
3185 //glClearColor(0, 0, 0, 0);
3186 //glClear(GL_COLOR_BUFFER_BIT);
3188 glColor4ub(127, 127, 127, 255);
3189 drawRect(x0-2, y0-2, rectW+4, rectH+4);
3191 glColor4ub(0, 0, 0, 255);
3192 drawRect(x0-1, y0-1, rectW+2, rectH+2);
3194 glColor4ub(127, 127, 127, 255);
3195 wdt := rectW*cur div total;
3196 if (wdt > rectW) then wdt := rectW;
3197 drawRect(x0, y0, wdt, rectH);
3198 end;
3199 end;
3202 ww, hh: Word;
3203 xx, yy, i: Integer;
3204 s: String;
3205 begin
3206 if (Length(LoadingStat.Msgs) = 0) then exit;
3208 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
3209 yy := (gScreenHeight div 3);
3210 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
3211 xx := (gScreenWidth div 3);
3213 with LoadingStat do
3214 begin
3215 for i := 0 to NextMsg-1 do
3216 begin
3217 if (i = (NextMsg-1)) and (MaxValue > 0) then
3218 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
3219 else
3220 s := Msgs[i];
3222 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
3223 yy := yy + LOADING_INTERLINE;
3224 PBarWasHere := drawPBar(CurValue, MaxValue, PBarWasHere);
3225 end;
3226 end;
3227 end;
3229 procedure DrawMenuBackground(tex: AnsiString);
3231 w, h: Word;
3232 ID: DWord;
3234 begin
3235 if g_Texture_Get(tex, ID) then
3236 begin
3237 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3238 e_GetTextureSize(ID, @w, @h);
3239 if w = h then
3240 w := round(w * 1.333 * (gScreenHeight / h))
3241 else
3242 w := trunc(w * (gScreenHeight / h));
3243 e_DrawSize(ID, (gScreenWidth - w) div 2, 0, 0, False, False, w, gScreenHeight);
3245 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3246 end;
3248 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
3250 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
3252 function monDraw (mon: TMonster): Boolean;
3253 begin
3254 result := false; // don't stop
3255 with mon do
3256 begin
3257 if alive then
3258 begin
3259 // Ëåâûé âåðõíèé óãîë
3260 aX := Obj.X div ScaleSz + 1;
3261 aY := Obj.Y div ScaleSz + 1;
3262 // Ðàçìåðû
3263 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
3264 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
3265 // Ïðàâûé íèæíèé óãîë
3266 aX2 := aX + aX2 - 1;
3267 aY2 := aY + aY2 - 1;
3268 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
3269 end;
3270 end;
3271 end;
3273 begin
3274 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
3275 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
3276 begin
3277 Scale := 1;
3278 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
3279 ScaleSz := 16 div Scale;
3280 // Ðàçìåðû ìèíè-êàðòû:
3281 aX := max(gMapInfo.Width div ScaleSz, 1);
3282 aY := max(gMapInfo.Height div ScaleSz, 1);
3283 // Ðàìêà êàðòû:
3284 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
3286 if gWalls <> nil then
3287 begin
3288 // Ðèñóåì ñòåíû:
3289 for a := 0 to High(gWalls) do
3290 with gWalls[a] do
3291 if PanelType <> 0 then
3292 begin
3293 // Ëåâûé âåðõíèé óãîë:
3294 aX := X div ScaleSz;
3295 aY := Y div ScaleSz;
3296 // Ðàçìåðû:
3297 aX2 := max(Width div ScaleSz, 1);
3298 aY2 := max(Height div ScaleSz, 1);
3299 // Ïðàâûé íèæíèé óãîë:
3300 aX2 := aX + aX2 - 1;
3301 aY2 := aY + aY2 - 1;
3303 case PanelType of
3304 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
3305 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
3306 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
3307 end;
3308 end;
3309 end;
3310 if gSteps <> nil then
3311 begin
3312 // Ðèñóåì ñòóïåíè:
3313 for a := 0 to High(gSteps) do
3314 with gSteps[a] do
3315 if PanelType <> 0 then
3316 begin
3317 // Ëåâûé âåðõíèé óãîë:
3318 aX := X div ScaleSz;
3319 aY := Y div ScaleSz;
3320 // Ðàçìåðû:
3321 aX2 := max(Width div ScaleSz, 1);
3322 aY2 := max(Height div ScaleSz, 1);
3323 // Ïðàâûé íèæíèé óãîë:
3324 aX2 := aX + aX2 - 1;
3325 aY2 := aY + aY2 - 1;
3327 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
3328 end;
3329 end;
3330 if gLifts <> nil then
3331 begin
3332 // Ðèñóåì ëèôòû:
3333 for a := 0 to High(gLifts) do
3334 with gLifts[a] do
3335 if PanelType <> 0 then
3336 begin
3337 // Ëåâûé âåðõíèé óãîë:
3338 aX := X div ScaleSz;
3339 aY := Y div ScaleSz;
3340 // Ðàçìåðû:
3341 aX2 := max(Width div ScaleSz, 1);
3342 aY2 := max(Height div ScaleSz, 1);
3343 // Ïðàâûé íèæíèé óãîë:
3344 aX2 := aX + aX2 - 1;
3345 aY2 := aY + aY2 - 1;
3347 case LiftType of
3348 LIFTTYPE_UP: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
3349 LIFTTYPE_DOWN: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
3350 LIFTTYPE_LEFT: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
3351 LIFTTYPE_RIGHT: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
3352 end;
3353 end;
3354 end;
3355 if gWater <> nil then
3356 begin
3357 // Ðèñóåì âîäó:
3358 for a := 0 to High(gWater) do
3359 with gWater[a] do
3360 if PanelType <> 0 then
3361 begin
3362 // Ëåâûé âåðõíèé óãîë:
3363 aX := X div ScaleSz;
3364 aY := Y div ScaleSz;
3365 // Ðàçìåðû:
3366 aX2 := max(Width div ScaleSz, 1);
3367 aY2 := max(Height div ScaleSz, 1);
3368 // Ïðàâûé íèæíèé óãîë:
3369 aX2 := aX + aX2 - 1;
3370 aY2 := aY + aY2 - 1;
3372 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
3373 end;
3374 end;
3375 if gAcid1 <> nil then
3376 begin
3377 // Ðèñóåì êèñëîòó 1:
3378 for a := 0 to High(gAcid1) do
3379 with gAcid1[a] do
3380 if PanelType <> 0 then
3381 begin
3382 // Ëåâûé âåðõíèé óãîë:
3383 aX := X div ScaleSz;
3384 aY := Y div ScaleSz;
3385 // Ðàçìåðû:
3386 aX2 := max(Width div ScaleSz, 1);
3387 aY2 := max(Height div ScaleSz, 1);
3388 // Ïðàâûé íèæíèé óãîë:
3389 aX2 := aX + aX2 - 1;
3390 aY2 := aY + aY2 - 1;
3392 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
3393 end;
3394 end;
3395 if gAcid2 <> nil then
3396 begin
3397 // Ðèñóåì êèñëîòó 2:
3398 for a := 0 to High(gAcid2) do
3399 with gAcid2[a] do
3400 if PanelType <> 0 then
3401 begin
3402 // Ëåâûé âåðõíèé óãîë:
3403 aX := X div ScaleSz;
3404 aY := Y div ScaleSz;
3405 // Ðàçìåðû:
3406 aX2 := max(Width div ScaleSz, 1);
3407 aY2 := max(Height div ScaleSz, 1);
3408 // Ïðàâûé íèæíèé óãîë:
3409 aX2 := aX + aX2 - 1;
3410 aY2 := aY + aY2 - 1;
3412 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
3413 end;
3414 end;
3415 if gPlayers <> nil then
3416 begin
3417 // Ðèñóåì èãðîêîâ:
3418 for a := 0 to High(gPlayers) do
3419 if gPlayers[a] <> nil then with gPlayers[a] do
3420 if alive then begin
3421 // Ëåâûé âåðõíèé óãîë:
3422 aX := Obj.X div ScaleSz + 1;
3423 aY := Obj.Y div ScaleSz + 1;
3424 // Ðàçìåðû:
3425 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
3426 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
3427 // Ïðàâûé íèæíèé óãîë:
3428 aX2 := aX + aX2 - 1;
3429 aY2 := aY + aY2 - 1;
3431 if gPlayers[a] = p then
3432 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
3433 else
3434 case Team of
3435 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
3436 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
3437 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
3438 end;
3439 end;
3440 end;
3441 // Ðèñóåì ìîíñòðîâ
3442 g_Mons_ForEach(monDraw);
3443 end;
3444 end;
3447 procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor);
3448 begin
3449 if not hasAmbient then exit;
3450 e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3451 end;
3454 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3455 //FIXME: broken for splitscreen mode
3456 procedure renderDynLightsInternal ();
3458 //hasAmbient: Boolean;
3459 //ambColor: TDFColor;
3460 lln: Integer;
3461 lx, ly, lrad: Integer;
3462 scxywh: array[0..3] of GLint;
3463 wassc: Boolean;
3464 begin
3465 if e_NoGraphics then exit;
3467 //TODO: lights should be in separate grid, i think
3468 // but on the other side: grid may be slower for dynlights, as their lifetime is short
3469 if (not gwin_k8_enable_light_experiments) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit;
3471 // rendering mode
3472 //ambColor := gCurrentMap['light_ambient'].rgba;
3473 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3475 { // this will multiply incoming color to alpha from framebuffer
3476 glEnable(GL_BLEND);
3477 glBlendFunc(GL_DST_ALPHA, GL_ONE);
3481 * light rendering: (INVALID!)
3482 * glStencilFunc(GL_EQUAL, 0, $ff);
3483 * for each light:
3484 * glClear(GL_STENCIL_BUFFER_BIT);
3485 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3486 * draw shadow volume into stencil buffer
3487 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3488 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
3489 * turn off blending
3490 * draw color-less quad with light alpha (WARNING! don't touch color!)
3491 * glEnable(GL_BLEND);
3492 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
3493 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
3495 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
3496 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
3498 // setup OpenGL parameters
3499 glStencilMask($FFFFFFFF);
3500 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
3501 glEnable(GL_STENCIL_TEST);
3502 glEnable(GL_SCISSOR_TEST);
3503 glClear(GL_STENCIL_BUFFER_BIT);
3504 glStencilFunc(GL_EQUAL, 0, $ff);
3506 for lln := 0 to g_dynLightCount-1 do
3507 begin
3508 lx := g_dynLights[lln].x;
3509 ly := g_dynLights[lln].y;
3510 lrad := g_dynLights[lln].radius;
3511 if (lrad < 3) then continue;
3513 if (lx-sX+lrad < 0) then continue;
3514 if (ly-sY+lrad < 0) then continue;
3515 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
3516 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
3518 // set scissor to optimize drawing
3519 if (g_dbg_scale = 1.0) then
3520 begin
3521 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
3523 else
3524 begin
3525 glScissor(0, 0, gScreenWidth, gScreenHeight);
3526 end;
3527 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3528 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
3529 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3530 // draw extruded panels
3531 glDisable(GL_TEXTURE_2D);
3532 glDisable(GL_BLEND);
3533 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
3534 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
3535 // render light texture
3536 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3537 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
3538 // blend it
3539 glEnable(GL_BLEND);
3540 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3541 // color and opacity
3542 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
3543 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
3544 glEnable(GL_TEXTURE_2D);
3545 glBegin(GL_QUADS);
3546 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
3547 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
3548 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
3549 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
3550 glEnd();
3551 glDisable(GL_TEXTURE_2D);
3552 end;
3554 // done
3555 glDisable(GL_STENCIL_TEST);
3556 glDisable(GL_BLEND);
3557 glDisable(GL_SCISSOR_TEST);
3558 //glScissor(0, 0, sWidth, sHeight);
3560 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
3561 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
3562 end;
3565 function fixViewportForScale (): Boolean;
3567 nx0, ny0, nw, nh: Integer;
3568 begin
3569 result := false;
3570 if (g_dbg_scale <> 1.0) then
3571 begin
3572 result := true;
3573 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
3574 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
3575 nw := round(sWidth/g_dbg_scale);
3576 nh := round(sHeight/g_dbg_scale);
3577 sX := nx0;
3578 sY := ny0;
3579 sWidth := nw;
3580 sHeight := nh;
3581 end;
3582 end;
3585 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3586 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3587 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
3588 type
3589 TDrawCB = procedure ();
3592 hasAmbient: Boolean;
3593 ambColor: TDFColor;
3594 doAmbient: Boolean = false;
3596 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
3598 tagmask: Integer;
3599 pan: TPanel;
3600 begin
3601 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
3602 if gdbg_map_use_accel_render then
3603 begin
3604 tagmask := panelTypeToTag(panType);
3605 while (gDrawPanelList.count > 0) do
3606 begin
3607 pan := TPanel(gDrawPanelList.front());
3608 if ((pan.tag and tagmask) = 0) then break;
3609 if doDraw then pan.Draw(doAmbient, ambColor);
3610 gDrawPanelList.popFront();
3611 end;
3613 else
3614 begin
3615 if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor);
3616 end;
3617 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3618 end;
3620 procedure drawOther (profname: AnsiString; cb: TDrawCB);
3621 begin
3622 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
3623 if assigned(cb) then cb();
3624 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3625 end;
3627 begin
3628 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('total');
3630 // our accelerated renderer will collect all panels to gDrawPanelList
3631 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3632 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('collect');
3633 if gdbg_map_use_accel_render then
3634 begin
3635 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
3636 end;
3637 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3639 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('skyback');
3640 g_Map_DrawBack(backXOfs, backYOfs);
3641 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
3643 if setTransMatrix then
3644 begin
3645 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3646 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
3647 glTranslatef(-sX, -sY, 0);
3648 end;
3650 // rendering mode
3651 ambColor := gCurrentMap['light_ambient'].rgba;
3652 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3655 if hasAmbient then
3656 begin
3657 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3658 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3659 glClear(GL_COLOR_BUFFER_BIT);
3660 end;
3662 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3665 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
3666 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
3667 drawOther('items', @g_Items_Draw);
3668 drawOther('weapons', @g_Weapon_Draw);
3669 drawOther('shells', @g_Player_DrawShells);
3670 drawOther('drawall', @g_Player_DrawAll);
3671 drawOther('corpses', @g_Player_DrawCorpses);
3672 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
3673 drawOther('monsters', @g_Monsters_Draw);
3674 drawOther('itemdrop', @g_Items_DrawDrop);
3675 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
3676 drawOther('gfx', @g_GFX_Draw);
3677 drawOther('flags', @g_Map_DrawFlags);
3678 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
3679 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
3680 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
3681 drawOther('dynlights', @renderDynLightsInternal);
3683 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3684 begin
3685 renderAmbientQuad(hasAmbient, ambColor);
3686 end;
3688 doAmbient := true;
3689 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
3692 if g_debug_HealthBar then
3693 begin
3694 g_Monsters_DrawHealth();
3695 g_Player_DrawHealth();
3696 end;
3698 if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering
3699 end;
3702 procedure DrawMapView(x, y, w, h: Integer);
3705 bx, by: Integer;
3706 begin
3707 glPushMatrix();
3709 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
3710 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
3712 sX := x;
3713 sY := y;
3714 sWidth := w;
3715 sHeight := h;
3717 fixViewportForScale();
3718 renderMapInternal(-bx, -by, true);
3720 glPopMatrix();
3721 end;
3724 procedure DrawPlayer(p: TPlayer);
3726 px, py, a, b, c, d, i, fX, fY: Integer;
3727 camObj: TObj;
3728 //R: TRect;
3729 begin
3730 if (p = nil) or (p.FDummy) then
3731 begin
3732 glPushMatrix();
3733 g_Map_DrawBack(0, 0);
3734 glPopMatrix();
3735 Exit;
3736 end;
3738 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3739 if (profileFrameDraw <> nil) then profileFrameDraw.mainBegin(g_profile_frame_draw);
3741 gPlayerDrawn := p;
3743 glPushMatrix();
3745 camObj := p.getCameraObj();
3746 camObj.lerp(gLerpFactor, fX, fY);
3747 px := fX + PLAYER_RECT_CX;
3748 py := fY + PLAYER_RECT_CY+nlerp(p.SlopeOld, camObj.slopeUpLeft, gLerpFactor);
3750 if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then
3751 begin
3752 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3753 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3755 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3756 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3758 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3759 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3760 begin
3761 // hcenter
3762 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3763 end;
3765 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3766 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3767 begin
3768 // vcenter
3769 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3770 end;
3772 else
3773 begin
3774 // scaled, ignore level bounds
3775 a := -px+(gPlayerScreenSize.X div 2);
3776 b := -py+(gPlayerScreenSize.Y div 2);
3777 end;
3779 sX := -a;
3780 sY := -b;
3781 sWidth := gPlayerScreenSize.X;
3782 sHeight := gPlayerScreenSize.Y;
3783 fixViewportForScale();
3785 i := py - (sY + sHeight div 2);
3786 if (p.IncCam > 0) then
3787 begin
3788 // clamp to level bounds
3789 if (sY - p.IncCam < 0) then
3790 p.IncCam := nclamp(sY, 0, 120);
3791 // clamp around player position
3792 if (i > 0) then
3793 p.IncCam := nclamp(p.IncCam, 0, max(0, 120 - i));
3795 else if (p.IncCam < 0) then
3796 begin
3797 // clamp to level bounds
3798 if (sY + sHeight - p.IncCam > gMapInfo.Height) then
3799 p.IncCam := nclamp(sY + sHeight - gMapInfo.Height, -120, 0);
3800 // clamp around player position
3801 if (i < 0) then
3802 p.IncCam := nclamp(p.IncCam, min(0, -120 - i), 0);
3803 end;
3805 sY := sY - nlerp(p.IncCamOld, p.IncCam, gLerpFactor);
3807 if (not g_dbg_ignore_bounds) then
3808 begin
3809 if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth;
3810 if (sY+sHeight > gMapInfo.Height) then sY := gMapInfo.Height-sHeight;
3811 if (sX < 0) then sX := 0;
3812 if (sY < 0) then sY := 0;
3813 end;
3815 if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth));
3816 if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight));
3818 //r_smallmap_h: 0: left; 1: center; 2: right
3819 //r_smallmap_v: 0: top; 1: center; 2: bottom
3820 // horiz small map?
3821 if (gMapInfo.Width = sWidth) then
3822 begin
3823 sX := 0;
3825 else if (gMapInfo.Width < sWidth) then
3826 begin
3827 case r_smallmap_h of
3828 1: sX := -((sWidth-gMapInfo.Width) div 2); // center
3829 2: sX := -(sWidth-gMapInfo.Width); // right
3830 else sX := 0; // left
3831 end;
3832 end;
3833 // vert small map?
3834 if (gMapInfo.Height = sHeight) then
3835 begin
3836 sY := 0;
3838 else if (gMapInfo.Height < sHeight) then
3839 begin
3840 case r_smallmap_v of
3841 1: sY := -((sHeight-gMapInfo.Height) div 2); // center
3842 2: sY := -(sHeight-gMapInfo.Height); // bottom
3843 else sY := 0; // top
3844 end;
3845 end;
3847 p.viewPortX := sX;
3848 p.viewPortY := sY;
3849 p.viewPortW := sWidth;
3850 p.viewPortH := sHeight;
3852 {$IFDEF ENABLE_HOLMES}
3853 if (p = gPlayer1) then
3854 begin
3855 g_Holmes_plrViewPos(sX, sY);
3856 g_Holmes_plrViewSize(sWidth, sHeight);
3857 end;
3858 {$ENDIF}
3860 renderMapInternal(-c, -d, true);
3862 if (gGameSettings.GameMode <> GM_SINGLE) and (gPlayerIndicator > 0) then
3863 case gPlayerIndicator of
3865 p.DrawIndicator(_RGB(255, 255, 255));
3868 for i := 0 to High(gPlayers) do
3869 if gPlayers[i] <> nil then
3870 if gPlayers[i] = p then p.DrawIndicator(_RGB(255, 255, 255))
3871 else if (gPlayers[i].Team = p.Team) and (gPlayers[i].Team <> TEAM_NONE) then
3872 if gPlayerIndicatorStyle = 1 then
3873 gPlayers[i].DrawIndicator(_RGB(192, 192, 192))
3874 else gPlayers[i].DrawIndicator(gPlayers[i].GetColor);
3875 end;
3878 for a := 0 to High(gCollideMap) do
3879 for b := 0 to High(gCollideMap[a]) do
3880 begin
3881 d := 0;
3882 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3883 d := d + 1;
3884 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3885 d := d + 2;
3887 case d of
3888 1: e_DrawPoint(1, b, a, 200, 200, 200);
3889 2: e_DrawPoint(1, b, a, 64, 64, 255);
3890 3: e_DrawPoint(1, b, a, 255, 0, 255);
3891 end;
3892 end;
3895 glPopMatrix();
3897 p.DrawPain();
3898 p.DrawPickup();
3899 p.DrawOverlay();
3900 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3901 if g_Debug_Player then
3902 g_Player_DrawDebug(p);
3903 p.DrawGUI();
3904 end;
3906 procedure drawProfilers ();
3908 px: Integer = -1;
3909 py: Integer = -1;
3910 begin
3911 if g_profile_frame_draw and (profileFrameDraw <> nil) then
3912 px -= drawProfiles(px, py, profileFrameDraw);
3914 if g_profile_collision and (profMapCollision <> nil) then
3915 begin
3916 px -= drawProfiles(px, py, profMapCollision);
3917 py -= calcProfilesHeight(profMonsLOS);
3918 end;
3920 if g_profile_los and (profMonsLOS <> nil) then
3921 begin
3922 px -= drawProfiles(px, py, profMonsLOS);
3923 py -= calcProfilesHeight(profMonsLOS);
3924 end;
3925 end;
3927 procedure g_Game_Draw();
3929 ID: DWORD;
3930 w, h: Word;
3931 ww, hh: Byte;
3932 Time: Int64;
3933 back: string;
3934 plView1, plView2: TPlayer;
3935 Split: Boolean;
3936 begin
3937 if gExit = EXIT_QUIT then Exit;
3939 Time := sys_GetTicks() {div 1000};
3940 FPSCounter := FPSCounter+1;
3941 if Time - FPSTime >= 1000 then
3942 begin
3943 FPS := FPSCounter;
3944 FPSCounter := 0;
3945 FPSTime := Time;
3946 end;
3948 e_SetRendertarget(True);
3949 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3951 if gGameOn or (gState = STATE_FOLD) then
3952 begin
3953 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3954 begin
3955 gSpectMode := SPECT_NONE;
3956 if not gSwapPlayers then
3957 begin
3958 plView1 := gPlayer1;
3959 plView2 := gPlayer2;
3961 else
3962 begin
3963 plView1 := gPlayer2;
3964 plView2 := gPlayer1;
3965 end;
3967 else
3968 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3969 begin
3970 gSpectMode := SPECT_NONE;
3971 if gPlayer2 = nil then
3972 plView1 := gPlayer1
3973 else
3974 plView1 := gPlayer2;
3975 plView2 := nil;
3977 else
3978 begin
3979 plView1 := nil;
3980 plView2 := nil;
3981 end;
3983 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3984 gSpectMode := SPECT_STATS;
3986 if gSpectMode = SPECT_PLAYERS then
3987 if gPlayers <> nil then
3988 begin
3989 plView1 := GetActivePlayer_ByID(gSpectPID1);
3990 if plView1 = nil then
3991 begin
3992 gSpectPID1 := GetActivePlayerID_Next();
3993 plView1 := GetActivePlayer_ByID(gSpectPID1);
3994 end;
3995 if gSpectViewTwo then
3996 begin
3997 plView2 := GetActivePlayer_ByID(gSpectPID2);
3998 if plView2 = nil then
3999 begin
4000 gSpectPID2 := GetActivePlayerID_Next();
4001 plView2 := GetActivePlayer_ByID(gSpectPID2);
4002 end;
4003 end;
4004 end;
4006 if gSpectMode = SPECT_MAPVIEW then
4007 begin
4008 // Ðåæèì ïðîñìîòðà êàðòû
4009 Split := False;
4010 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
4011 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
4012 gHearPoint1.Active := True;
4013 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
4014 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
4015 gHearPoint2.Active := False;
4017 else
4018 begin
4019 Split := (plView1 <> nil) and (plView2 <> nil);
4021 // Òî÷êè ñëóõà èãðîêîâ
4022 if plView1 <> nil then
4023 begin
4024 gHearPoint1.Active := True;
4025 gHearPoint1.Coords.X := plView1.GameX + PLAYER_RECT.Width;
4026 gHearPoint1.Coords.Y := plView1.GameY + PLAYER_RECT.Height DIV 2;
4027 end else
4028 gHearPoint1.Active := False;
4029 if plView2 <> nil then
4030 begin
4031 gHearPoint2.Active := True;
4032 gHearPoint2.Coords.X := plView2.GameX + PLAYER_RECT.Width;
4033 gHearPoint2.Coords.Y := plView2.GameY + PLAYER_RECT.Height DIV 2;
4034 end else
4035 gHearPoint2.Active := False;
4037 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4038 gPlayerScreenSize.X := gScreenWidth-196;
4039 if Split then
4040 begin
4041 gPlayerScreenSize.Y := gScreenHeight div 2;
4042 if gScreenHeight mod 2 = 0 then
4043 Dec(gPlayerScreenSize.Y);
4045 else
4046 gPlayerScreenSize.Y := gScreenHeight;
4048 if Split then
4049 if gScreenHeight mod 2 = 0 then
4050 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
4051 else
4052 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
4054 DrawPlayer(plView1);
4055 gPlayer1ScreenCoord.X := sX;
4056 gPlayer1ScreenCoord.Y := sY;
4058 if Split then
4059 begin
4060 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
4062 DrawPlayer(plView2);
4063 gPlayer2ScreenCoord.X := sX;
4064 gPlayer2ScreenCoord.Y := sY;
4065 end;
4067 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
4069 if Split then
4070 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
4071 end;