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
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}
27 g_basic
, g_player
, e_graphics
, g_res_downloader
,
28 g_gui
, utils
, md5
, mempool
, xprofiler
,
33 //RESERVED = 0, // FIXME: reuse for something
52 TGameOptions
= set of TGameOption
;
54 TGameSettings
= record
61 ItemRespawnTime
: Word;
62 ItemRespawnRandom
: Word;
63 PowerupRespawnTime
: Word;
64 PowerupRespawnRandom
: Word;
66 Options
: TGameOptions
;
75 TDelayedEvent
= record
85 Sound
: TPlayableSound
;
86 Tags
: Array of String;
91 TPlayerSettings
= record
96 // ones below are sent only to the server
98 WeaponPreferences
: Array[WP_FIRST
..WP_LAST
+1] of Byte;
103 TMegaWADInfo
= record
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);
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); }
195 LOADING_SHOW_STEP
= 100;
196 LOADING_INTERLINE
= 20;
211 MESSAGE_DIKEY
= WM_USER
+ 1;
216 EXIT_ENDLEVELSINGLE
= 4;
217 EXIT_ENDLEVELCUSTOM
= 5;
222 STATE_INTERCUSTOM
= 3;
223 STATE_INTERSINGLE
= 4;
229 LMS_RESPAWN_NONE
= 0;
230 LMS_RESPAWN_WARMUP
= 1;
231 LMS_RESPAWN_FINAL
= 2;
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;
256 gGameSettings
: TGameSettings
;
257 gPlayer1Settings
: TPlayerSettings
;
258 gPlayer2Settings
: TPlayerSettings
;
260 gPlayerScreenSize
: TDFPoint
;
261 gPlayer1ScreenCoord
: TDFPoint
;
262 gPlayer2ScreenCoord
: TDFPoint
;
265 gPlayerDrawn
: TPlayer
;
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
;
284 gMusicName
: String = '';
285 gMusicPlay
: Boolean = False;
286 gMusicPos
: LongWord
= 0;
287 gMusicPause
: Boolean = False;
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;
298 gPauseHolmes
: Boolean;
301 gShowScore
: Boolean = True;
302 gShowStat
: Boolean = True;
304 gShowKillMsg
: Boolean = True;
305 gShowLives
: Boolean = True;
309 gState
: Byte = STATE_NONE
;
311 sWidth
, sHeight
: Word;
312 gSpectMode
: Byte = SPECT_NONE
;
313 gSpectHUD
: Boolean = True;
314 gSpectKeyPress
: Boolean;
317 gSpectStep
: Byte = 8;
318 gSpectViewTwo
: Boolean;
319 gSpectPID1
: Integer = -1;
320 gSpectPID2
: Integer = -1;
322 gSpectAutoNext
: LongWord
;
323 gSpectAutoStepX
: Integer;
324 gSpectAutoStepY
: Integer;
325 gLoadGameMode
: Boolean;
328 gMapToDelete
: String;
329 gTempDelete
: Boolean;
333 gResolutionChange
: Boolean;
334 gRC_Width
, gRC_Height
: Integer;
335 gRC_FullScreen
, gRC_Maximized
: Boolean;
336 gLanguageChange
: 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;
355 gStatsPressed
: Boolean;
356 gExitByTrigger
: Boolean;
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;
368 gVoteTimeout
: Cardinal = 30;
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
387 // bits 4-5: l/r state when strafe was pressed
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;
417 {$INCLUDE ../nogl/noGLuses.inc}
418 {$IFDEF ENABLE_HOLMES}
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
;
431 profileFrameDraw
: TProfiler
;
433 // ////////////////////////////////////////////////////////////////////////// //
434 function gPause (): Boolean; inline;
436 Result
:= gPauseMain
or gPauseHolmes
;
439 function conIsCheatsEnabled (): Boolean; inline;
442 if g_Game_IsNet
then exit
;
443 if not gDebugMode
then
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
;
452 // ////////////////////////////////////////////////////////////////////////// //
455 x
, y
, radius
: Integer;
458 exploRadius
: Integer;
462 g_dynLights
: array of TDynLight
= nil;
463 g_dynLightCount
: Integer = 0;
464 g_playerLight
: Boolean = false;
466 procedure g_ResetDynlights ();
470 if not gwin_has_stencil
then begin g_dynLightCount
:= 0; exit
; end;
472 for idx
:= 0 to g_dynLightCount
-1 do
474 if g_dynLights
[idx
].exploCount
= -666 then
481 Inc(g_dynLights
[idx
].exploCount
);
482 if (g_dynLights
[idx
].exploCount
< 10) then
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
];
492 g_dynLightCount
:= lnum
;
495 procedure g_AddDynLight (x
, y
, radius
: Integer; r
, g
, b
, a
: Single);
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
);
510 procedure g_DynLightExplosion (x
, y
, radius
: Integer; r
, g
, b
: Single);
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
);
527 // ////////////////////////////////////////////////////////////////////////// //
528 function calcProfilesHeight (prof
: TProfiler
): Integer;
531 if (prof
= nil) then exit
;
532 if (length(prof
.bars
) = 0) then exit
;
533 result
:= length(prof
.bars
)*(16+2);
537 function drawProfiles (x
, y
: Integer; prof
: TProfiler
): Integer;
544 if (prof
= nil) then exit
;
546 if (length(prof
.bars
) = 0) then exit
;
548 hgt
:= calcProfilesHeight(prof
);
549 if (x
< 0) then x
:= gScreenWidth
-(wdt
-1)+x
;
550 if (y
< 0) then y
:= gScreenHeight
-(hgt
-1)+y
;
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);
557 for ii
:= 0 to High(prof
.bars
) do
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);
566 // ////////////////////////////////////////////////////////////////////////// //
568 TEndCustomGameStat
= record
569 PlayerStat
: TPlayerStatArray
;
573 Map
, MapName
: String;
576 TEndSingleGameStat
= record
577 PlayerStat
: Array [0..1] of record
583 TotalSecrets
: Integer;
586 TLoadingStat
= record
590 Msgs
: Array of String;
592 PBarWasHere
: Boolean; // did we draw a progress bar for this message?
595 TParamStrValue
= record
600 TParamStrValues
= Array of TParamStrValue
;
603 INTER_ACTION_TEXT
= 1;
604 INTER_ACTION_PIC
= 2;
605 INTER_ACTION_MUSIC
= 3;
609 FPSCounter
, UPSCounter
: Word;
610 FPSTime
, UPSTime
: LongWord
;
613 CustomStat
: TEndCustomGameStat
;
614 SingleStat
: TEndSingleGameStat
;
615 LoadingStat
: TLoadingStat
;
616 EndingGameCounter
: Byte;
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
630 text: Array of ShortString
;
631 anim
: Array of ShortString
;
632 pic
: Array of ShortString
;
633 mus
: Array of ShortString
;
635 triggers
: Array of record
637 actions
: Array of record
638 action
, p1
, p2
: Integer;
641 cur_trigger
: Integer;
654 function Compare(a
, b
: TPlayerStat
): Integer;
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
667 procedure SortGameStat(var stat
: TPlayerStatArray
);
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
679 stat
[J
] := stat
[J
+ 1];
684 // saves a shitty CSV containing the game stats passed to it
685 procedure SaveGameStat(Stat
: TEndCustomGameStat
; Path
: string);
688 dir
, fname
, map
, mode
, etime
, flags
, strf
: String;
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
);
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
713 for flag
in gGameSettings
.Options
do
716 System
.WriteStr(strf
, flag
); // FIXME: rename our utils.WriteStr()
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', [
727 gGameSettings
.TimeLimit
,
728 gGameSettings
.ScoreLimit
,
731 Length(Stat
.PlayerStat
)
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
737 if Stat
.GameMode
in [GM_TDM
, GM_CTF
] then
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
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
]));
749 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_WRITE
], [fname
]));
752 g_Console_Add('could not create gamestats file "' + fname
+ '"');
757 procedure ClearDebugCvars();
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;
768 function g_Game_ModeToText(Mode
: Byte): string;
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
];
780 function g_Game_TextToMode(Mode
: string): Byte;
783 Mode
:= UpperCase(Mode
);
784 if Mode
= _lc
[I_MENU_GAME_TYPE_DM
] then
789 if Mode
= _lc
[I_MENU_GAME_TYPE_TDM
] then
794 if Mode
= _lc
[I_MENU_GAME_TYPE_CTF
] then
799 if Mode
= _lc
[I_MENU_GAME_TYPE_COOP
] then
804 if Mode
= _lc
[I_MENU_GAME_TYPE_SINGLE
] then
811 function g_Game_IsNet(): Boolean;
813 Result
:= (gGameSettings
.GameType
in [GT_SERVER
, GT_CLIENT
]);
816 function g_Game_IsServer(): Boolean;
818 Result
:= (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
, GT_SERVER
]);
821 function g_Game_IsClient(): Boolean;
823 Result
:= (gGameSettings
.GameType
= GT_CLIENT
);
826 function g_Game_GetMegaWADInfo(WAD
: String; cfg
: TConfig
): TMegaWADInfo
;
834 w
:= TWADFile
.Create();
837 w
.GetResource('INTERSCRIPT', p
, len
);
842 Result
.name
:= ExtractFileName(WAD
);
846 cfg
:= TConfig
.CreateMem(p
, len
);
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', '');
859 procedure g_Game_FreeCurrentWAD();
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
]);
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');
885 ZeroMemory(@MegaWAD
, SizeOf(MegaWAD
));
886 gGameSettings
.WAD
:= '';
889 procedure g_Game_SetCurrentWAD(WAD
: string);
897 g_Game_FreeCurrentWAD();
898 gGameSettings
.WAD
:= WAD
;
899 if not (gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
]) then
902 w
:= TWADFile
.Create();
904 w
.GetResource('INTERSCRIPT', p
, len
);
907 if p
= nil then Exit
;
908 cfg
:= TConfig
.CreateMem(p
, len
);
910 MegaWAD
.info
:= g_Game_GetMegaWADInfo(WAD
, cfg
);
912 MegaWAD
.endpic
:= cfg
.ReadStr('megawad', 'endpic', '');
913 if MegaWAD
.endpic
<> '' then
915 TEXTUREFILTER
:= GL_LINEAR
;
916 s
:= e_GetResourcePath(WadDirs
, MegaWAD
.endpic
, WAD
);
917 g_Texture_CreateWADEx('TEXTURE_endpic', s
);
918 TEXTUREFILTER
:= GL_NEAREST
;
920 MegaWAD
.endmus
:= cfg
.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
922 {$IFDEF ENABLE_SOUND}
923 if MegaWAD
.endmus
<> '' then
925 s
:= e_GetResourcePath(WadDirs
, MegaWAD
.endmus
, WAD
);
926 g_Sound_CreateWADEx('MUSIC_endmus', s
, True);
933 {procedure start_trigger(t: string);
937 function next_trigger(): Boolean;
941 procedure DisableCheats();
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
;
958 procedure g_Game_ExecuteEvent(Name
: String);
964 if gEvents
= nil then
966 for a
:= 0 to High(gEvents
) do
967 if gEvents
[a
].Name
= Name
then
969 if gEvents
[a
].Command
<> '' then
970 g_Console_Process(gEvents
[a
].Command
, True);
975 function g_Game_DelayEvent(DEType
: Byte; Time
: LongWord
; Num
: Integer = 0; Str
: String = ''): Integer;
980 if gDelayedEvents
<> nil then
981 for a
:= 0 to High(gDelayedEvents
) do
982 if not gDelayedEvents
[a
].Pending
then
989 SetLength(gDelayedEvents
, Length(gDelayedEvents
) + 1);
990 n
:= High(gDelayedEvents
);
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
999 gDelayedEvents
[n
].Time
:= gTime
+ Time
;
1003 procedure EndGame();
1009 if g_Game_IsNet
and g_Game_IsServer
then
1010 MH_SEND_GameEvent(NET_EV_MAPEND
, Byte(gMissionFailed
));
1013 gPauseMain
:= false;
1014 gPauseHolmes
:= false;
1017 {$IFDEF ENABLE_SOUND}
1018 g_Game_StopAllSounds(False);
1024 EndingGameCounter
:= 0;
1025 g_ActiveWindow
:= nil;
1027 gLMSRespawn
:= LMS_RESPAWN_NONE
;
1028 gLMSRespawnTime
:= 0;
1031 EXIT_SIMPLE
: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
1036 begin // Ýòî áûë òåñò
1040 begin // Âûõîä â ãëàâíîå ìåíþ
1041 {$IFDEF ENABLE_SOUND}
1042 gMusic
.SetByName('MUSIC_MENU');
1045 if gState
<> STATE_SLIST
then
1047 g_GUI_ShowWindow('MainMenu');
1048 gState
:= STATE_MENU
;
1051 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
1052 slReturnPressed
:= True;
1053 if g_Net_Slist_Fetch(slCurrent
) then
1055 if slCurrent
= nil then
1056 slWaitStr
:= _lc
[I_NET_SLIST_NOSERVERS
];
1059 slWaitStr
:= _lc
[I_NET_SLIST_ERROR
];
1060 g_Serverlist_GenerateTable(slCurrent
, slTable
);
1063 g_Game_ExecuteEvent('ongameend');
1067 EXIT_RESTART
: // Íà÷àòü óðîâåíü ñíà÷àëà
1069 if not g_Game_IsClient
then g_Game_Restart();
1072 EXIT_ENDLEVELCUSTOM
: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
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
1089 for a
:= 0 to High(gPlayers
) do
1090 if gPlayers
[a
] <> nil then
1092 SetLength(CustomStat
.PlayerStat
, Length(CustomStat
.PlayerStat
)+1);
1093 with CustomStat
.PlayerStat
[High(CustomStat
.PlayerStat
)] do
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
;
1106 SortGameStat(CustomStat
.PlayerStat
);
1108 if (gSaveStats
or gScreenshotStats
) and (Length(CustomStat
.PlayerStat
) > 1) then
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
;
1116 SaveGameStat(CustomStat
, FormatDateTime('yyyy"/"mm"/"dd', t
));
1119 StatShotDone
:= False;
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
;
1130 if gDefInterTime
< 0 then
1131 gInterEndTime
:= IfThen((gGameSettings
.GameType
= GT_SERVER
) and (gPlayer1
= nil), 15000, 25000)
1133 gInterEndTime
:= gDefInterTime
* 1000;
1136 EXIT_ENDLEVELSINGLE
: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
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
1148 SingleStat
.PlayerStat
[1].Kills
:= gPlayer2
.MonsterKills
;
1149 SingleStat
.PlayerStat
[1].Secrets
:= gPlayer2
.Secrets
;
1152 g_Game_ExecuteEvent('onmapend');
1155 if gNextMap
<> '' then
1157 {$IFDEF ENABLE_SOUND}
1158 gMusic
.SetByName('MUSIC_INTERMUS');
1161 gState
:= STATE_INTERSINGLE
;
1164 g_Game_ExecuteEvent('oninter');
1166 else // Áîëüøå íåò êàðò
1168 // Çàòóõàþùèé ýêðàí:
1169 EndingGameCounter
:= 255;
1170 gState
:= STATE_FOLD
;
1175 // Îêîí÷àíèå îáðàáîòàíî:
1176 if gExit
<> EXIT_QUIT
then
1180 procedure drawTime(X
, Y
: Integer); inline;
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
1191 procedure DrawStat();
1193 pc
, x
, y
, w
, h
: Integer;
1194 w1
, w2
, w3
, w4
: Integer;
1196 cw
, ch
, r
, g
, b
, rr
, gg
, bb
: Byte;
1199 stat
: TPlayerStatArray
;
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
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]);
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]);
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]);
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
);
1260 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*cw
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
1262 e_TextureFontPrintEx(x
+(w
div 2)-(Length(mapstr
)*cw
div 2), _y
, mapstr
, gStdFont
, 200, 200, 200, 1);
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);
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);
1274 if pc
= 0 then Exit
;
1275 stat
:= g_Player_GetStats();
1278 w2
:= (w
-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1279 w3
:= (w
-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1281 w1
:= w
-16-w2
-w3
-w4
; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1283 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1287 // first goes up the red team
1288 s1
:= _lc
[I_GAME_TEAM_RED
];
1293 for a
:= TEAM_RED
to TEAM_BLUE
do
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
);
1302 for aa
:= 0 to High(stat
) do
1304 if stat
[aa
].Team
= a
then
1309 rr
:= Color
.r
div 3;
1310 gg
:= Color
.g
div 3;
1311 bb
:= Color
.b
div 3;
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
);
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);
1337 // then show up the blue team
1338 if a
= TEAM_RED
then
1340 s1
:= _lc
[I_GAME_TEAM_BLUE
];
1347 else if gGameSettings
.GameMode
in [GM_DM
, GM_COOP
] then
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);
1356 for aa
:= 0 to High(stat
) do
1370 then namestr
:= Format('[%5d] %s', [UID
, Name
])
1371 else namestr
:= Name
;
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);
1388 procedure g_Game_Init();
1391 knownFiles
: array of AnsiString
= nil;
1393 wext
, s
: AnsiString
;
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);
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
1428 for f
:= High(ModelDirs
) downto Low(ModelDirs
) do
1430 if (FindFirst(ModelDirs
[f
]+DirectorySeparator
+'*'+wext
, faAnyFile
, SR
) = 0) then
1434 for s
in knownFiles
do
1436 if (strEquCI1251(forceFilenameExt(SR
.Name
, ''), forceFilenameExt(ExtractFileName(s
), ''))) then
1444 SetLength(knownFiles
, length(knownFiles
)+1);
1445 knownFiles
[High(knownFiles
)] := ModelDirs
[f
]+DirectorySeparator
+SR
.Name
;
1447 until (FindNext(SR
) <> 0);
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
1458 if not g_PlayerModel_Load(s
) then e_LogWritefln('Error loading model "%s"', [s
], TMsgType
.Warning
);
1462 gPauseMain
:= false;
1463 gPauseHolmes
:= false;
1466 {e_MouseInfo.Accel := 1.0;}
1468 g_Game_SetLoadingText(_lc
[I_LOAD_GAME_DATA
], 0, False);
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);
1480 g_Game_SetLoadingText(_lc
[I_LOAD_MENUS
], 0, False);
1484 {$IFDEF ENABLE_SOUND}
1485 gMusic
:= TMusic
.Create();
1486 gMusic
.SetByName('MUSIC_MENU');
1490 gMusicPlay
:= False;
1492 gMusicPause
:= False;
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';
1507 sfsGCEnable(); // enable releasing unused volumes
1511 procedure g_Game_Free(freeTextures
: Boolean=true);
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
);
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
;
1527 gExitByTrigger
:= False;
1530 function IsActivePlayer(p
: TPlayer
): Boolean;
1535 Result
:= (not p
.FDummy
) and (not p
.FSpectator
);
1538 function GetActivePlayer_ByID(ID
: Integer): TPlayer
;
1545 if gPlayers
= nil then
1547 for a
:= Low(gPlayers
) to High(gPlayers
) do
1548 if IsActivePlayer(gPlayers
[a
]) then
1550 if gPlayers
[a
].UID
<> ID
then
1552 Result
:= gPlayers
[a
];
1557 function GetActivePlayerID_Next(Skip
: Integer = -1): Integer;
1563 if gPlayers
= nil then
1567 for a
:= Low(gPlayers
) to High(gPlayers
) do
1568 if IsActivePlayer(gPlayers
[a
]) then
1570 SetLength(ids
, Length(ids
) + 1);
1571 ids
[High(ids
)] := gPlayers
[a
].UID
;
1572 if gPlayers
[a
].UID
= Skip
then
1575 if Length(ids
) = 0 then
1580 Result
:= ids
[(idx
+ 1) mod Length(ids
)];
1583 function GetActivePlayerID_Prev(Skip
: Integer = -1): Integer;
1589 if gPlayers
= nil then
1593 for a
:= Low(gPlayers
) to High(gPlayers
) do
1594 if IsActivePlayer(gPlayers
[a
]) then
1596 SetLength(ids
, Length(ids
) + 1);
1597 ids
[High(ids
)] := gPlayers
[a
].UID
;
1598 if gPlayers
[a
].UID
= Skip
then
1601 if Length(ids
) = 0 then
1604 Result
:= ids
[Length(ids
) - 1]
1606 Result
:= ids
[(Length(ids
) - 1 + idx
) mod Length(ids
)];
1609 function GetActivePlayerID_Random(Skip
: Integer = -1): Integer;
1615 if gPlayers
= nil then
1619 for a
:= Low(gPlayers
) to High(gPlayers
) do
1620 if IsActivePlayer(gPlayers
[a
]) then
1622 SetLength(ids
, Length(ids
) + 1);
1623 ids
[High(ids
)] := gPlayers
[a
].UID
;
1624 if gPlayers
[a
].UID
= Skip
then
1627 if Length(ids
) = 0 then
1629 if Length(ids
) = 1 then
1634 Result
:= ids
[Random(Length(ids
))];
1636 while (idx
<> -1) and (Result
= Skip
) and (a
> 0) do
1638 Result
:= ids
[Random(Length(ids
))];
1643 function GetRandomSpectMode(Current
: Byte): Byte;
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
;
1658 if (Current
in [SPECT_STATS
, SPECT_MAPVIEW
]) and (Current
= Result
) then
1662 procedure ProcessPlayerControls (plr
: TPlayer
; p
: Integer; var MoveButton
: Byte);
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
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
)
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))
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
1724 if gWeaponAction
[p
, i
] then
1726 plr
.ProcessWeaponAction(i
);
1727 gWeaponAction
[p
, i
] := False
1731 for i
:= WP_FIRST
to WP_LAST
do
1733 if gSelectWeapon
[p
, i
] then
1735 plr
.QueueWeaponSwitch(i
); // all choices are passed there, and god will take the best
1736 gSelectWeapon
[p
, i
] := False
1740 // HACK: add dynlight here
1741 if gwin_k8_enable_light_experiments
then
1743 if e_KeyPressed(IK_F8
) and gGameOn
and (not gConsoleShow
) and (g_ActiveWindow
= nil) then
1745 g_playerLight
:= true;
1747 if e_KeyPressed(IK_F9
) and gGameOn
and (not gConsoleShow
) and (g_ActiveWindow
= nil) then
1749 g_playerLight
:= false;
1753 if gwin_has_stencil
and g_playerLight
then g_AddDynLight(plr
.GameX
+32, plr
.GameY
+40, 128, 1, 1, 0, 0.6);
1756 // HACK: don't have a "key was pressed" function
1757 procedure InterReady();
1759 if InterReadyTime
> gTime
then Exit
;
1760 InterReadyTime
:= gTime
+ 3000;
1761 MC_SEND_CheatRequest(NET_CHEAT_READY
);
1764 procedure g_Game_PreUpdate();
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();
1775 procedure g_Game_Update();
1777 Msg
: g_gui
.TMessage
;
1783 function sendMonsPos (mon
: TMonster
): Boolean;
1785 result
:= false; // don't stop
1786 // this will also reset "need-send" flag
1787 if mon
.gncNeedSend
then
1789 MH_SEND_MonsterPos(mon
.UID
);
1791 else if (mon
.MonsterType
= MONSTER_BARREL
) then
1793 if (mon
.GameVelX
<> 0) or (mon
.GameVelY
<> 0) then MH_SEND_MonsterPos(mon
.UID
);
1795 else if (mon
.MonsterState
<> MONSTATE_SLEEP
) then
1797 if (mon
.MonsterState
<> MONSTATE_DEAD
) or (mon
.GameVelX
<> 0) or (mon
.GameVelY
<> 0) then MH_SEND_MonsterPos(mon
.UID
);
1801 function sendMonsPosUnexpected (mon
: TMonster
): Boolean;
1803 result
:= false; // don't stop
1804 // this will also reset "need-send" flag
1805 if mon
.gncNeedSend
then MH_SEND_MonsterPos(mon
.UID
);
1808 function sendItemPos (it
: PItem
): Boolean;
1810 result
:= false; // don't stop
1813 MH_SEND_ItemPos(it
.myId
);
1814 it
.needSend
:= False;
1819 reliableUpdate
: Boolean;
1824 // Ïîðà âûêëþ÷àòü èãðó:
1825 if gExit
= EXIT_QUIT
then
1827 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1831 if gExit
= EXIT_QUIT
then
1835 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî
1836 // no need to, as we'll do it in event handler
1838 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1841 if (NetMode
= NET_NONE
) and (g_Game_IsNet
) and (gGameOn
or (gState
in [STATE_FOLD
, STATE_INTERCUSTOM
])) then
1843 gExit
:= EXIT_SIMPLE
;
1848 // process master server communications
1849 g_Net_Slist_Pulse();
1852 STATE_INTERSINGLE
, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1853 STATE_INTERCUSTOM
, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1854 STATE_INTERTEXT
, // Òåêñò ìåæäó óðîâíÿìè
1855 STATE_INTERPIC
: // Êàðòèíêà ìåæäó óðîâíÿìè
1857 if g_Game_IsNet
and g_Game_IsServer
then
1859 gInterTime
:= gInterTime
+ GAME_TICK
;
1860 a
:= Min((gInterEndTime
- gInterTime
) div 1000 + 1, 255);
1861 if a
<> gServInterTime
then
1863 gServInterTime
:= a
;
1864 MH_SEND_TimeSync(gServInterTime
);
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))))
1883 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1884 {$IFDEF ENABLE_SOUND}
1885 g_Game_StopAllSounds(True);
1888 if gMapOnce
then // Ýòî áûë òåñò
1889 gExit
:= EXIT_SIMPLE
1891 if gNextMap
<> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1892 g_Game_ChangeMap(gNextMap
)
1893 else // Ñëåäóþùåé êàðòû íåò
1895 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
] then
1897 // Âûõîä â ãëàâíîå ìåíþ:
1899 g_GUI_ShowWindow('MainMenu');
1900 {$IFDEF ENABLE_SOUND}
1901 gMusic
.SetByName('MUSIC_MENU');
1904 gState
:= STATE_MENU
;
1907 // Ôèíàëüíàÿ êàðòèíêà:
1908 g_Game_ExecuteEvent('onwadend');
1910 {$IFDEF ENABLE_SOUND}
1911 if not gMusic
.SetByName('MUSIC_endmus') then
1912 gMusic
.SetByName('MUSIC_STDENDMUS');
1915 gState
:= STATE_ENDPIC
;
1917 g_Game_ExecuteEvent('ongameend');
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)
1939 if gState
= STATE_INTERTEXT
then
1940 if InterText
.counter
> 0 then
1941 InterText
.counter
:= InterText
.counter
- 1;
1944 STATE_FOLD
: // Çàòóõàíèå ýêðàíà
1946 if EndingGameCounter
= 0 then
1948 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1949 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
] then
1951 gState
:= STATE_INTERCUSTOM
;
1952 InterReadyTime
:= -1;
1953 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then
1955 g_Game_ExecuteEvent('onwadend');
1956 {$IFDEF ENABLE_SOUND}
1957 if not gMusic
.SetByName('MUSIC_endmus') then
1958 gMusic
.SetByName('MUSIC_STDENDMUS');
1963 {$IFDEF ENABLE_SOUND}
1964 gMusic
.SetByName('MUSIC_ROUNDMUS');
1967 {$IFDEF ENABLE_SOUND}
1972 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1974 {$IFDEF ENABLE_SOUND}
1975 gMusic
.SetByName('MUSIC_INTERMUS');
1978 gState
:= STATE_INTERSINGLE
;
1981 g_Game_ExecuteEvent('oninter');
1984 DecMin(EndingGameCounter
, 6, 0);
1987 STATE_ENDPIC
: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1989 if gMapOnce
then // Ýòî áûë òåñò
1991 gExit
:= EXIT_SIMPLE
;
1997 g_Serverlist_Control(slCurrent
, slTable
);
2000 // Ñòàòèñòèêà ïî Tab:
2002 IsDrawStat
:= (not gConsoleShow
) and (not gChatShow
) and (gGameSettings
.GameType
<> GT_SINGLE
) and g_Console_Action(ACTION_SCORES
);
2005 if gGameOn
and not gPause
and (gState
<> STATE_FOLD
) then
2007 // Âðåìÿ += 28 ìèëëèñåêóíä:
2008 gTime
:= gTime
+ GAME_TICK
;
2010 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
2011 if MessageTime
= 0 then
2013 if MessageTime
> 0 then
2014 MessageTime
:= MessageTime
- 1;
2016 if (g_Game_IsServer
) then
2018 // Áûë çàäàí ëèìèò âðåìåíè:
2019 if (gGameSettings
.TimeLimit
> 0) then
2020 if (gTime
- gGameStartTime
) div 1000 >= gGameSettings
.TimeLimit
then
2021 begin // Îí ïðîøåë => êîíåö óðîâíÿ
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
2033 else if gVotePassed
and (gVoteCmdTimer
< gTime
) then
2035 g_Console_Process(gVoteCommand
);
2037 gVotePassed
:= False;
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
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
;
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
);
2064 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
2065 if b
>= gGameSettings
.ScoreLimit
then
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
2077 ProcessPlayerControls(gPlayer1
, 0, P1MoveButton
);
2078 ProcessPlayerControls(gPlayer2
, 1, P2MoveButton
);
2079 end // if not console
2082 if g_Game_IsNet
and (gPlayer1
<> nil) then gPlayer1
.PressKey(KEY_CHAT
, 10000);
2084 // process weapon switch queue
2088 if (gPlayer1
= nil) and (gPlayer2
= nil) and
2089 (not gConsoleShow
) and (not gChatShow
) and (g_ActiveWindow
= nil) then
2091 if not gSpectKeyPress
then
2093 if gPlayerAction
[0, ACTION_JUMP
] and (not gSpectAuto
) then
2095 // switch spect mode
2097 SPECT_NONE
: ; // not spectator
2099 SPECT_MAPVIEW
: Inc(gSpectMode
);
2100 SPECT_PLAYERS
: gSpectMode
:= SPECT_STATS
; // reset to 1
2102 gSpectKeyPress
:= True;
2104 if (gSpectMode
= SPECT_MAPVIEW
)
2105 and (not gSpectAuto
) then
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
2118 if gSpectStep
> 4 then gSpectStep
:= gSpectStep
shr 1;
2119 gWeaponAction
[0, WP_PREV
] := False;
2121 if gWeaponAction
[0, WP_NEXT
] then
2124 if gSpectStep
< 64 then gSpectStep
:= gSpectStep
shl 1;
2125 gWeaponAction
[0, WP_NEXT
] := False;
2128 if (gSpectMode
= SPECT_PLAYERS
)
2129 and (not gSpectAuto
) then
2131 if gPlayerAction
[0, ACTION_LOOKUP
] then
2134 gSpectViewTwo
:= True;
2135 gSpectKeyPress
:= True;
2137 if gPlayerAction
[0, ACTION_LOOKDOWN
] then
2139 // remove second view
2140 gSpectViewTwo
:= False;
2141 gSpectKeyPress
:= True;
2143 if gPlayerAction
[0, ACTION_MOVELEFT
] then
2145 // prev player (view 1)
2146 gSpectPID1
:= GetActivePlayerID_Prev(gSpectPID1
);
2147 gSpectKeyPress
:= True;
2149 if gPlayerAction
[0, ACTION_MOVERIGHT
] then
2151 // next player (view 1)
2152 gSpectPID1
:= GetActivePlayerID_Next(gSpectPID1
);
2153 gSpectKeyPress
:= True;
2155 if gWeaponAction
[0, WP_PREV
] then
2157 // prev player (view 2)
2158 gSpectPID2
:= GetActivePlayerID_Prev(gSpectPID2
);
2159 gWeaponAction
[0, WP_PREV
] := False;
2161 if gWeaponAction
[0, WP_NEXT
] then
2163 // next player (view 2)
2164 gSpectPID2
:= GetActivePlayerID_Next(gSpectPID2
);
2165 gWeaponAction
[0, WP_NEXT
] := False;
2168 if gPlayerAction
[0, ACTION_ATTACK
] then
2170 if (gSpectMode
= SPECT_STATS
) and (not gSpectAuto
) then
2173 gSpectAutoNext
:= 0;
2174 gSpectViewTwo
:= False;
2175 gSpectKeyPress
:= True;
2180 gSpectMode
:= SPECT_STATS
;
2181 gSpectAuto
:= False;
2182 gSpectKeyPress
:= True;
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;
2197 if gSpectMode
= SPECT_MAPVIEW
then
2199 i
:= Min(Max(gSpectX
+ gSpectAutoStepX
, 0), gMapInfo
.Width
- gScreenWidth
);
2201 gSpectAutoNext
:= gTime
2204 i
:= Min(Max(gSpectY
+ gSpectAutoStepY
, 0), gMapInfo
.Height
- gScreenHeight
);
2206 gSpectAutoNext
:= gTime
2210 if gSpectAutoNext
<= gTime
then
2212 if gSpectAutoNext
> 0 then
2214 gSpectMode
:= GetRandomSpectMode(gSpectMode
);
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;
2231 gSpectPID1
:= GetActivePlayerID_Random(gSpectPID1
);
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;
2244 // Îáíîâëÿåì âñå îñòàëüíîå:
2247 g_Triggers_Update();
2249 g_Monsters_Update();
2251 g_Player_UpdateAll();
2252 g_Player_UpdatePhysicalObjects();
2254 // server: send newly spawned monsters unconditionally
2255 if (gGameSettings
.GameType
= GT_SERVER
) then
2257 if (Length(gMonstersSpawned
) > 0) then
2259 for I
:= 0 to High(gMonstersSpawned
) do MH_SEND_MonsterSpawn(gMonstersSpawned
[I
]);
2260 SetLength(gMonstersSpawned
, 0);
2264 {$IFDEF ENABLE_SOUND}
2265 if (gSoundTriggerTime
> 8) then
2267 g_Game_UpdateTriggerSounds();
2268 gSoundTriggerTime
:= 0;
2272 Inc(gSoundTriggerTime
);
2276 if (NetMode
= NET_SERVER
) then
2278 Inc(NetTimeToUpdate
);
2279 Inc(NetTimeToReliable
);
2281 // send monster updates
2282 if (NetTimeToReliable
>= NetRelupdRate
) or (NetTimeToUpdate
>= NetUpdateRate
) then
2284 // send all monsters (periodic sync)
2285 reliableUpdate
:= (NetTimeToReliable
>= NetRelupdRate
);
2287 for I
:= 0 to High(gPlayers
) do
2289 if (gPlayers
[I
] <> nil) then MH_SEND_PlayerPos(reliableUpdate
, gPlayers
[I
].UID
);
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
2299 gFlags
[I
].NeedSend
:= False;
2303 // update items that aren't stationary
2304 g_Items_ForEachAlive(sendItemPos
);
2306 if reliableUpdate
then
2308 NetTimeToReliable
:= 0;
2309 NetTimeToUpdate
:= NetUpdateRate
;
2313 NetTimeToUpdate
:= 0;
2318 // send only mosters with some unexpected changes
2319 g_Mons_ForEach(sendMonsPosUnexpected
);
2322 // send unexpected platform changes
2323 g_Map_NetSendInterestingPanels();
2325 g_Net_Slist_ServerUpdate();
2327 if NetUseMaster then
2329 if (gTime >= NetTimeToMaster) or g_Net_Slist_IsConnectionInProgress then
2331 if (not g_Net_Slist_IsConnectionActive) then g_Net_Slist_Connect(false); // non-blocking connection to the master
2333 NetTimeToMaster := gTime + NetMasterRate;
2338 else if (NetMode
= NET_CLIENT
) then
2340 MC_SEND_PlayerPos();
2342 end; // if gameOn ...
2344 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
2345 if g_ActiveWindow
<> nil then
2347 w
:= e_GetFirstKeyPressed();
2349 if (w
<> IK_INVALID
) then
2351 Msg
.Msg
:= MESSAGE_DIKEY
;
2353 g_ActiveWindow
.OnMessage(Msg
);
2356 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
2357 if g_ActiveWindow
<> nil then
2358 g_ActiveWindow
.Update();
2360 // Íóæíî ñìåíèòü ðàçðåøåíèå:
2361 if gResolutionChange
then
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;
2369 // Íóæíî ñìåíèòü ÿçûê:
2370 if gLanguageChange
then
2372 //e_WriteLog('Read language file', MSG_NOTIFY);
2373 //g_Language_Load(DataDir + gLanguage + '.txt');
2374 g_Language_Set(gLanguage
);
2378 gLanguageChange
:= False;
2382 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
2383 if e_KeyPressed(IK_F10
) and
2385 (not gConsoleShow
) and
2386 (g_ActiveWindow
= nil) then
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
))
2402 case gDelayedEvents
[a
].DEType
of
2404 g_Game_ExecuteEvent(gDelayedEvents
[a
].DEStr
);
2408 {$IFDEF ENABLE_SOUND}
2409 g_Game_Announce_GoodShot(gDelayedEvents
[a
].DENum
);
2415 {$IFDEF ENABLE_SOUND}
2416 g_Game_Announce_KillCombo(gDelayedEvents
[a
].DENum
);
2418 if g_Game_IsNet
and g_Game_IsServer
then
2419 MH_SEND_GameEvent(NET_EV_KILLCOMBO
, gDelayedEvents
[a
].DENum
);
2424 {$IFDEF ENABLE_SOUND}
2425 g_Game_Announce_BodyKill(gDelayedEvents
[a
].DENum
);
2429 gDelayedEvents
[a
].Pending
:= False;
2432 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2433 UPSCounter
:= UPSCounter
+ 1;
2434 if Time
- UPSTime
>= 1000 then
2443 g_Weapon_AddDynLights();
2444 g_Items_AddDynLights();
2448 {$IFDEF ENABLE_SOUND}
2449 procedure g_Game_LoadChatSounds(Resource
: string);
2452 FileName
, Snd
: string;
2454 len
, cnt
, tags
, i
, j
: Integer;
2457 FileName
:= g_ExtractWadName(Resource
);
2459 WAD
:= TWADFile
.Create();
2460 WAD
.ReadFile(FileName
);
2462 if not WAD
.GetResource(g_ExtractFilePathName(Resource
), p
, len
) then
2470 cfg
:= TConfig
.CreateMem(p
, len
);
2472 cnt
:= cfg
.ReadInt('ChatSounds', 'Count', 0);
2474 SetLength(gChatSounds
, cnt
);
2475 for i
:= 0 to Length(gChatSounds
) - 1 do
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
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);
2494 procedure g_Game_FreeChatSounds();
2498 for i
:= 0 to Length(gChatSounds
) - 1 do
2500 gChatSounds
[i
].Sound
.Free();
2501 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i
));
2503 SetLength(gChatSounds
, 0);
2508 procedure g_Game_LoadData();
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');
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;
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
2553 hasPBarGfx
:= false;
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');
2648 g_Game_SetLoadingText(_lc
[I_LOAD_ITEMS_DATA
], 0, False);
2651 g_Game_SetLoadingText(_lc
[I_LOAD_WEAPONS_DATA
], 0, False);
2652 g_Weapon_LoadData();
2654 g_Monsters_LoadData();
2659 procedure g_Game_FreeData();
2661 if not DataLoaded
then Exit
;
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');
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');
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');
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();
2752 DataLoaded
:= False;
2755 procedure DrawCustomStat();
2761 ww2
, hh2
, r
, g
, b
, rr
, gg
, bb
: Byte;
2762 s1
, s2
, topstr
: String;
2764 e_TextureFontGetSize(gStdFont
, ww2
, hh2
);
2768 if g_Console_Action(ACTION_SCORES
) then
2770 if not gStatsPressed
then
2772 gStatsOff
:= not gStatsOff
;
2773 gStatsPressed
:= True;
2777 gStatsPressed
:= False;
2781 s1
:= _lc
[I_MENU_INTER_NOTICE_TAB
];
2782 w
:= (Length(s1
) * ww2
) div 2;
2783 x
:= gScreenWidth
div 2 - w
;
2785 e_TextureFontPrint(x
, y
, s1
, gStdFont
);
2789 if gGameSettings
.GameMode
= GM_COOP
then
2792 then topstr
:= _lc
[I_MENU_INTER_MISSION_FAIL
]
2793 else topstr
:= _lc
[I_MENU_INTER_LEVEL_COMPLETE
];
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
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);
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);
2819 w
:= gScreenWidth
-x
*2;
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
2832 if gGameSettings
.MaxLives
= 0
2833 then s1
:= _lc
[I_GAME_DM
]
2834 else s1
:= _lc
[I_GAME_LMS
];
2836 if gGameSettings
.MaxLives
= 0
2837 then s1
:= _lc
[I_GAME_TDM
]
2838 else s1
:= _lc
[I_GAME_TLMS
];
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
];
2849 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
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
);
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
2867 m
:= Max(Length(_lc
[I_GAME_MONSTERS
])+1, Length(_lc
[I_GAME_SECRETS
])+1)*ww2
;
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);
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);
2878 m
:= Max(Length(_lc
[I_GAME_MONSTERS_TOTAL
])+1, Length(_lc
[I_GAME_SECRETS_TOTAL
])+1)*ww2
;
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);
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);
2890 if CustomStat
.GameMode
in [GM_TDM
, GM_CTF
] then
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
]
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);
2905 // first goes up the red team
2906 s1
:= _lc
[I_GAME_TEAM_RED
];
2911 for t
:= TEAM_RED
to TEAM_BLUE
do
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
);
2919 for p
:= 0 to High(CustomStat
.PlayerStat
) do
2920 if CustomStat
.PlayerStat
[p
].Team
= t
then
2921 with CustomStat
.PlayerStat
[p
] do
2925 rr
:= Color
.r
div 3;
2926 gg
:= Color
.g
div 3;
2927 bb
:= Color
.b
div 3;
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
);
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);
2946 // then show up the blue team
2947 if t
= TEAM_RED
then
2949 s1
:= _lc
[I_GAME_TEAM_BLUE
];
2956 else if CustomStat
.GameMode
in [GM_DM
, GM_COOP
] then
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);
2964 for p
:= 0 to High(CustomStat
.PlayerStat
) do
2965 with CustomStat
.PlayerStat
[p
] do
2967 e_DrawFillQuad(x
+8, _y
+4, x
+24-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
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);
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
2985 g_TakeScreenShot('stats/' + StatFilename
);
2986 StatShotDone
:= True;
2990 procedure DrawSingleStat();
2992 tm
, key_x
, val_x
, y
: Integer;
2996 procedure player_stat(n
: Integer);
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
, '/');
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
];
3016 kpm
:= (SingleStat
.PlayerStat
[n
].Kills
/ tm
) * 60
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, '/');
3033 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3034 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
+64, s2
, _RGB(255, 0, 0));
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
);
3047 e_CharFont_GetSize(gMenuFont
, s1
, w2
, h
);
3049 key_x
:= (gScreenWidth
-w1
-w2
) div 2;
3050 val_x
:= key_x
+ w1
;
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
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 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
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 // Ñòàòèñòèêà âòîðîãî èãðîêà:
3082 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3088 procedure DrawLoadingStat();
3089 procedure drawRect (x
, y
, w
, h
: Integer);
3091 if (w
< 1) or (h
< 1) then exit
;
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);
3100 function drawPBar (cur
, total
: Integer; washere
: Boolean): Boolean;
3102 rectW
, rectH
: Integer;
3109 idl
, idr
, idb
, idm
: LongWord
;
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;
3120 if (hasPBarGfx
) then
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);
3135 x0
:= (gScreenWidth
-rectW
) div 2;
3136 y0
:= gScreenHeight
-rectH
-64;
3137 if (y0
< 2) then y0
:= 2;
3139 glEnable(GL_SCISSOR_TEST
);
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
);
3147 glScissor(x0
+wl
, gScreenHeight
-y0
-rectH
, rectW
-wl
-wr
, rectH
);
3149 while (f
< x0
+rectW
) do
3151 e_DrawSize(idb
, f
, y0
, 0, true, false, wb
, hb
);
3156 wdt
:= (rectW
-wl
-wr
)*cur
div total
;
3157 if (wdt
> rectW
-wl
-wr
) then wdt
:= rectW
-wr
-wr
;
3160 my
:= y0
; // don't be so smart, ketmar: +(rectH-wm) div 2;
3161 glScissor(x0
+wl
, gScreenHeight
-my
-rectH
, wdt
, hm
);
3165 e_DrawSize(idm
, f
, y0
, 0, true, false, wm
, hm
);
3171 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
3175 rectW
:= gScreenWidth
-64;
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
);
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);
3215 for i
:= 0 to NextMsg
-1 do
3217 if (i
= (NextMsg
-1)) and (MaxValue
> 0) then
3218 s
:= Format('%s: %d/%d', [Msgs
[i
], CurValue
, MaxValue
])
3222 e_CharFont_PrintEx(gMenuSmallFont
, xx
, yy
, s
, _RGB(255, 0, 0));
3223 yy
:= yy
+ LOADING_INTERLINE
;
3224 PBarWasHere
:= drawPBar(CurValue
, MaxValue
, PBarWasHere
);
3229 procedure DrawMenuBackground(tex
: AnsiString
);
3235 if g_Texture_Get(tex
, ID
) then
3237 e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
3238 e_GetTextureSize(ID
, @w
, @h
);
3240 w
:= round(w
* 1.333 * (gScreenHeight
/ h
))
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);
3248 procedure DrawMinimap(p
: TPlayer
; RenderRect
: e_graphics
.TRect
);
3250 a
, aX
, aY
, aX2
, aY2
, Scale
, ScaleSz
: Integer;
3252 function monDraw (mon
: TMonster
): Boolean;
3254 result
:= false; // don't stop
3259 // Ëåâûé âåðõíèé óãîë
3260 aX
:= Obj
.X
div ScaleSz
+ 1;
3261 aY
:= Obj
.Y
div ScaleSz
+ 1;
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);
3274 if (gMapInfo
.Width
> RenderRect
.Right
- RenderRect
.Left
) or
3275 (gMapInfo
.Height
> RenderRect
.Bottom
- RenderRect
.Top
) then
3278 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
3279 ScaleSz
:= 16 div Scale
;
3280 // Ðàçìåðû ìèíè-êàðòû:
3281 aX
:= max(gMapInfo
.Width
div ScaleSz
, 1);
3282 aY
:= max(gMapInfo
.Height
div ScaleSz
, 1);
3284 e_DrawFillQuad(0, 0, aX
-1, aY
-1, 0, 0, 0, 0);
3286 if gWalls
<> nil then
3289 for a
:= 0 to High(gWalls
) do
3291 if PanelType
<> 0 then
3293 // Ëåâûé âåðõíèé óãîë:
3294 aX
:= X
div ScaleSz
;
3295 aY
:= Y
div ScaleSz
;
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;
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);
3310 if gSteps
<> nil then
3313 for a
:= 0 to High(gSteps
) do
3315 if PanelType
<> 0 then
3317 // Ëåâûé âåðõíèé óãîë:
3318 aX
:= X
div ScaleSz
;
3319 aY
:= Y
div ScaleSz
;
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);
3330 if gLifts
<> nil then
3333 for a
:= 0 to High(gLifts
) do
3335 if PanelType
<> 0 then
3337 // Ëåâûé âåðõíèé óãîë:
3338 aX
:= X
div ScaleSz
;
3339 aY
:= Y
div ScaleSz
;
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;
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);
3355 if gWater
<> nil then
3358 for a
:= 0 to High(gWater
) do
3360 if PanelType
<> 0 then
3362 // Ëåâûé âåðõíèé óãîë:
3363 aX
:= X
div ScaleSz
;
3364 aY
:= Y
div ScaleSz
;
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);
3375 if gAcid1
<> nil then
3377 // Ðèñóåì êèñëîòó 1:
3378 for a
:= 0 to High(gAcid1
) do
3380 if PanelType
<> 0 then
3382 // Ëåâûé âåðõíèé óãîë:
3383 aX
:= X
div ScaleSz
;
3384 aY
:= Y
div ScaleSz
;
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);
3395 if gAcid2
<> nil then
3397 // Ðèñóåì êèñëîòó 2:
3398 for a
:= 0 to High(gAcid2
) do
3400 if PanelType
<> 0 then
3402 // Ëåâûé âåðõíèé óãîë:
3403 aX
:= X
div ScaleSz
;
3404 aY
:= Y
div ScaleSz
;
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);
3415 if gPlayers
<> nil then
3418 for a
:= 0 to High(gPlayers
) do
3419 if gPlayers
[a
] <> nil then with gPlayers
[a
] do
3421 // Ëåâûé âåðõíèé óãîë:
3422 aX
:= Obj
.X
div ScaleSz
+ 1;
3423 aY
:= Obj
.Y
div ScaleSz
+ 1;
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)
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);
3442 g_Mons_ForEach(monDraw
);
3447 procedure renderAmbientQuad (hasAmbient
: Boolean; constref ambColor
: TDFColor
);
3449 if not hasAmbient
then exit
;
3450 e_AmbientQuad(sX
, sY
, sWidth
, sHeight
, ambColor
.r
, ambColor
.g
, ambColor
.b
, ambColor
.a
);
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;
3461 lx
, ly
, lrad
: Integer;
3462 scxywh
: array[0..3] of GLint
;
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
;
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
3477 glBlendFunc(GL_DST_ALPHA, GL_ONE);
3481 * light rendering: (INVALID!)
3482 * glStencilFunc(GL_EQUAL, 0, $ff);
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
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
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
3521 glScissor((lx
-sX
)-lrad
+2, gPlayerScreenSize
.Y
-(ly
-sY
)-lrad
-1+2, lrad
*2-4, lrad
*2-4);
3525 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
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
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
);
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
3551 glDisable(GL_TEXTURE_2D
);
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
);
3565 function fixViewportForScale (): Boolean;
3567 nx0
, ny0
, nw
, nh
: Integer;
3570 if (g_dbg_scale
<> 1.0) then
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
);
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);
3589 TDrawCB
= procedure ();
3592 hasAmbient
: Boolean;
3594 doAmbient
: Boolean = false;
3596 procedure drawPanelType (profname
: AnsiString
; panType
: DWord
; doDraw
: Boolean);
3601 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
3602 if gdbg_map_use_accel_render
then
3604 tagmask
:= panelTypeToTag(panType
);
3605 while (gDrawPanelList
.count
> 0) do
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();
3615 if doDraw
then g_Map_DrawPanels(panType
, hasAmbient
, ambColor
);
3617 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3620 procedure drawOther (profname
: AnsiString
; cb
: TDrawCB
);
3622 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
3623 if assigned(cb
) then cb();
3624 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
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
3635 g_Map_CollectDrawPanels(sX
, sY
, sWidth
, sHeight
);
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
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);
3651 ambColor
:= gCurrentMap
['light_ambient'].rgba
;
3652 hasAmbient
:= (not ambColor
.isOpaque
) or (not ambColor
.isBlack
);
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);
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
3685 renderAmbientQuad(hasAmbient
, ambColor
);
3689 drawPanelType('*fore', PANEL_FORE
, g_rlayer_fore
);
3692 if g_debug_HealthBar
then
3694 g_Monsters_DrawHealth();
3695 g_Player_DrawHealth();
3698 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainEnd(); // map rendering
3702 procedure DrawMapView(x
, y
, w
, h
: Integer);
3709 bx
:= Round(x
/(gMapInfo
.Width
- w
)*(gBackSize
.X
- w
));
3710 by
:= Round(y
/(gMapInfo
.Height
- h
)*(gBackSize
.Y
- h
));
3717 fixViewportForScale();
3718 renderMapInternal(-bx
, -by
, true);
3724 procedure DrawPlayer(p
: TPlayer
);
3726 px
, py
, a
, b
, c
, d
, i
, fX
, fY
: Integer;
3730 if (p
= nil) or (p
.FDummy
) then
3733 g_Map_DrawBack(0, 0);
3738 if (profileFrameDraw
= nil) then profileFrameDraw
:= TProfiler
.Create('RENDER', g_profile_history_size
);
3739 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainBegin(g_profile_frame_draw
);
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
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
3762 a
:= (gPlayerScreenSize
.X
-gMapInfo
.Width
) div 2;
3765 if (gMapInfo
.Height
= gPlayerScreenSize
.Y
) then b
:= 0
3766 else if (gMapInfo
.Height
< gPlayerScreenSize
.Y
) then
3769 b
:= (gPlayerScreenSize
.Y
-gMapInfo
.Height
) div 2;
3774 // scaled, ignore level bounds
3775 a
:= -px
+(gPlayerScreenSize
.X
div 2);
3776 b
:= -py
+(gPlayerScreenSize
.Y
div 2);
3781 sWidth
:= gPlayerScreenSize
.X
;
3782 sHeight
:= gPlayerScreenSize
.Y
;
3783 fixViewportForScale();
3785 i
:= py
- (sY
+ sHeight
div 2);
3786 if (p
.IncCam
> 0) then
3788 // clamp to level bounds
3789 if (sY
- p
.IncCam
< 0) then
3790 p
.IncCam
:= nclamp(sY
, 0, 120);
3791 // clamp around player position
3793 p
.IncCam
:= nclamp(p
.IncCam
, 0, max(0, 120 - i
));
3795 else if (p
.IncCam
< 0) then
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
3802 p
.IncCam
:= nclamp(p
.IncCam
, min(0, -120 - i
), 0);
3805 sY
:= sY
- nlerp(p
.IncCamOld
, p
.IncCam
, gLerpFactor
);
3807 if (not g_dbg_ignore_bounds
) then
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;
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
3821 if (gMapInfo
.Width
= sWidth
) then
3825 else if (gMapInfo
.Width
< sWidth
) then
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
3834 if (gMapInfo
.Height
= sHeight
) then
3838 else if (gMapInfo
.Height
< sHeight
) then
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
3849 p
.viewPortW
:= sWidth
;
3850 p
.viewPortH
:= sHeight
;
3852 {$IFDEF ENABLE_HOLMES}
3853 if (p
= gPlayer1
) then
3855 g_Holmes_plrViewPos(sX
, sY
);
3856 g_Holmes_plrViewSize(sWidth
, sHeight
);
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
);
3878 for a := 0 to High(gCollideMap) do
3879 for b := 0 to High(gCollideMap[a]) do
3882 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3884 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
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);
3900 if gShowMap
then DrawMinimap(p
, _TRect(0, 0, 128, 128));
3901 if g_Debug_Player
then
3902 g_Player_DrawDebug(p
);
3906 procedure drawProfilers ();
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
3916 px
-= drawProfiles(px
, py
, profMapCollision
);
3917 py
-= calcProfilesHeight(profMonsLOS
);
3920 if g_profile_los
and (profMonsLOS
<> nil) then
3922 px
-= drawProfiles(px
, py
, profMonsLOS
);
3923 py
-= calcProfilesHeight(profMonsLOS
);
3927 procedure g_Game_Draw();
3934 plView1
, plView2
: TPlayer
;
3937 if gExit
= EXIT_QUIT
then Exit
;
3939 Time
:= sys_GetTicks() {div 1000};
3940 FPSCounter
:= FPSCounter
+1;
3941 if Time
- FPSTime
>= 1000 then
3948 e_SetRendertarget(True);
3949 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
3951 if gGameOn
or (gState
= STATE_FOLD
) then
3953 if (gPlayer1
<> nil) and (gPlayer2
<> nil) then
3955 gSpectMode
:= SPECT_NONE
;
3956 if not gSwapPlayers
then
3958 plView1
:= gPlayer1
;
3959 plView2
:= gPlayer2
;
3963 plView1
:= gPlayer2
;
3964 plView2
:= gPlayer1
;
3968 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
3970 gSpectMode
:= SPECT_NONE
;
3971 if gPlayer2
= nil then
3974 plView1
:= gPlayer2
;
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
3989 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
3990 if plView1
= nil then
3992 gSpectPID1
:= GetActivePlayerID_Next();
3993 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
3995 if gSpectViewTwo
then
3997 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
3998 if plView2
= nil then
4000 gSpectPID2
:= GetActivePlayerID_Next();
4001 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
4006 if gSpectMode
= SPECT_MAPVIEW
then
4008 // Ðåæèì ïðîñìîòðà êàðòû
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;
4019 Split
:= (plView1
<> nil) and (plView2
<> nil);
4021 // Òî÷êè ñëóõà èãðîêîâ
4022 if plView1
<> nil then
4024 gHearPoint1
.Active
:= True;
4025 gHearPoint1
.Coords
.X
:= plView1
.GameX
+ PLAYER_RECT
.Width
;
4026 gHearPoint1
.Coords
.Y
:= plView1
.GameY
+ PLAYER_RECT
.Height
DIV 2;
4028 gHearPoint1
.Active
:= False;
4029 if plView2
<> nil then
4031 gHearPoint2
.Active
:= True;
4032 gHearPoint2
.Coords
.X
:= plView2
.GameX
+ PLAYER_RECT
.Width
;
4033 gHearPoint2
.Coords
.Y
:= plView2
.GameY
+ PLAYER_RECT
.Height
DIV 2;
4035 gHearPoint2
.Active
:= False;
4037 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4038 gPlayerScreenSize
.X
:= gScreenWidth
-196;
4041 gPlayerScreenSize
.Y
:= gScreenHeight
div 2;
4042 if gScreenHeight
mod 2 = 0 then
4043 Dec(gPlayerScreenSize
.Y
);
4046 gPlayerScreenSize
.Y
:= gScreenHeight
;
4049 if gScreenHeight
mod 2 = 0 then
4050 e_SetViewPort(0, gPlayerScreenSize
.Y
+2, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
)
4052 e_SetViewPort(0, gPlayerScreenSize
.Y
+1, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
4054 DrawPlayer(plView1
);
4055 gPlayer1ScreenCoord
.X
:= sX
;
4056 gPlayer1ScreenCoord
.Y
:= sY
;
4060 e_SetViewPort(0, 0, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
4062 DrawPlayer(plView2
);
4063 gPlayer2ScreenCoord
.X
:= sX
;
4064 gPlayer2ScreenCoord
.Y
:= sY
;
4067 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
4070 e_DrawLine(2, 0, gScreenHeight
div 2, gScreenWidth
, gScreenHeight
div 2, 0, 0, 0);