1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../shared/a_modes.inc}
23 g_basic
, g_player
, e_graphics
, g_res_downloader
,
24 g_sound
, g_gui
, utils
, md5
, mempool
, xprofiler
,
29 //RESERVED = 0, // FIXME: reuse for something
48 TGameOptions
= set of TGameOption
;
50 TGameSettings
= record
57 ItemRespawnTime
: Word;
58 ItemRespawnRandom
: Word;
59 PowerupRespawnTime
: Word;
60 PowerupRespawnRandom
: Word;
62 Options
: TGameOptions
;
71 TDelayedEvent
= record
80 Sound
: TPlayableSound
;
81 Tags
: Array of String;
85 TPlayerSettings
= record
90 // ones below are sent only to the server
92 WeaponPreferences
: Array[WP_FIRST
..WP_LAST
+1] of Byte;
109 function g_Game_IsNet(): Boolean;
110 function g_Game_IsServer(): Boolean;
111 function g_Game_IsClient(): Boolean;
112 procedure g_Game_Init();
113 procedure g_Game_Free (freeTextures
: Boolean=true);
114 procedure g_Game_LoadData();
115 procedure g_Game_FreeData();
116 procedure g_Game_Update();
117 procedure g_Game_PreUpdate();
118 procedure g_Game_Draw();
119 procedure g_Game_Quit();
120 procedure g_Game_SetupScreenSize();
121 procedure g_Game_ChangeResolution(newWidth
, newHeight
: Word; nowFull
, nowMax
: Boolean);
122 function g_Game_ModeToText(Mode
: Byte): string;
123 function g_Game_TextToMode(Mode
: string): Byte;
124 procedure g_Game_ExecuteEvent(Name
: String);
125 function g_Game_DelayEvent(DEType
: Byte; Time
: LongWord
; Num
: Integer = 0; Str
: String = ''): Integer;
126 procedure g_Game_AddPlayer(Team
: Byte = TEAM_NONE
);
127 procedure g_Game_RemovePlayer();
128 procedure g_Game_Spectate();
129 procedure g_Game_SpectateCenterView();
130 procedure g_Game_StartSingle(Map
: String; TwoPlayers
: Boolean; nPlayers
: Byte);
131 procedure g_Game_StartCustom(Map
: String; GameMode
: Byte; TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte; Options
: TGameOptions
; nPlayers
: Byte);
132 procedure g_Game_StartServer(Map
: String; GameMode
: Byte; TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte; Options
: TGameOptions
; nPlayers
: Byte; IPAddr
: LongWord
; Port
: Word);
133 procedure g_Game_StartClient(Addr
: String; Port
: Word; PW
: String);
134 procedure g_Game_Restart();
135 procedure g_Game_RestartLevel();
136 procedure g_Game_RestartRound(NoMapRestart
: Boolean = False);
137 function g_Game_ClientWAD (NewWAD
: String; const WHash
: TMD5Digest
): AnsiString
;
138 function g_Game_StartMap(asMegawad
: Boolean; Map
: String; Force
: Boolean = False; const oldMapPath
: AnsiString
=''): Boolean;
139 procedure g_Game_ChangeMap(const MapPath
: String);
140 procedure g_Game_ExitLevel(const Map
: AnsiString
);
141 function g_Game_GetFirstMap(WAD
: String): String;
142 function g_Game_GetNextMap(): String;
143 procedure g_Game_NextLevel();
144 procedure g_Game_Pause(Enable
: Boolean);
145 procedure g_Game_HolmesPause(Enable
: Boolean);
146 procedure g_Game_InGameMenu(Show
: Boolean);
147 function g_Game_IsWatchedPlayer(UID
: Word): Boolean;
148 function g_Game_IsWatchedTeam(Team
: Byte): Boolean;
149 procedure g_Game_Message(Msg
: String; Time
: Word);
150 procedure g_Game_LoadMapList(FileName
: String);
151 procedure g_Game_PauseAllSounds(Enable
: Boolean);
152 procedure g_Game_StopAllSounds(all
: Boolean);
153 procedure g_Game_UpdateTriggerSounds();
154 function g_Game_GetMegaWADInfo(WAD
: String): TMegaWADInfo
;
155 procedure g_Game_ChatSound(Text: String; Taunt
: Boolean = True);
156 procedure g_Game_Announce_GoodShot(SpawnerUID
: Word);
157 procedure g_Game_Announce_KillCombo(Param
: Integer);
158 procedure g_Game_Announce_BodyKill(SpawnerUID
: Word);
159 procedure g_Game_Effect_Bubbles(fX
, fY
: Integer; count
: Word; devX
, devY
: Byte; Silent
: Boolean = False);
160 procedure g_Game_StartVote(Command
, Initiator
: string);
161 procedure g_Game_CheckVote
;
162 procedure g_TakeScreenShot(Filename
: string = '');
163 procedure g_FatalError(Text: String);
164 procedure g_SimpleError(Text: String);
165 function g_Game_IsTestMap(): Boolean;
166 procedure g_Game_DeleteTestMap();
167 procedure GameCVars(P
: SSArray
);
168 procedure PlayerSettingsCVars(P
: SSArray
);
169 procedure SystemCommands(P
: SSArray
);
170 procedure GameCommands(P
: SSArray
);
171 procedure GameCheats(P
: SSArray
);
172 procedure DebugCommands(P
: SSArray
);
173 procedure g_Game_Process_Params
;
174 procedure g_Game_SetLoadingText(Text: String; Max
: Integer; reWrite
: Boolean);
175 procedure g_Game_StepLoading(Value
: Integer = -1);
176 procedure g_Game_ClearLoading();
177 procedure g_Game_SetDebugMode();
178 procedure DrawLoadingStat();
179 procedure DrawMenuBackground(tex
: AnsiString
);
181 { procedure SetWinPause(Enable: Boolean); }
186 LOADING_SHOW_STEP
= 100;
187 LOADING_INTERLINE
= 20;
202 MESSAGE_DIKEY
= WM_USER
+ 1;
207 EXIT_ENDLEVELSINGLE
= 4;
208 EXIT_ENDLEVELCUSTOM
= 5;
213 STATE_INTERCUSTOM
= 3;
214 STATE_INTERSINGLE
= 4;
220 LMS_RESPAWN_NONE
= 0;
221 LMS_RESPAWN_WARMUP
= 1;
222 LMS_RESPAWN_FINAL
= 2;
239 CONFIG_FILENAME
= 'Doom2DF.cfg';
241 TEST_MAP_NAME
= '$$$_TEST_$$$';
243 STD_PLAYER_MODEL
= 'Doomer';
251 STATFILE_VERSION
= $03;
255 gGameSettings
: TGameSettings
;
256 gPlayer1Settings
: TPlayerSettings
;
257 gPlayer2Settings
: TPlayerSettings
;
259 gPlayerScreenSize
: TDFPoint
;
260 gPlayer1ScreenCoord
: TDFPoint
;
261 gPlayer2ScreenCoord
: TDFPoint
;
262 gPlayer1
: TPlayer
= nil;
263 gPlayer2
: TPlayer
= nil;
264 gPlayerDrawn
: TPlayer
= nil;
266 gLerpFactor
: Single = 1.0;
267 gSwitchGameMode
: Byte = GM_DM
;
268 gHearPoint1
, gHearPoint2
: THearPoint
;
269 gSoundEffectsDF
: Boolean = False;
270 gSoundTriggerTime
: Word = 0;
271 gAnnouncer
: Integer = ANNOUNCE_NONE
;
272 goodsnd
: array[0..3] of TPlayableSound
;
273 killsnd
: array[0..3] of TPlayableSound
;
274 hahasnd
: array[0..2] of TPlayableSound
;
275 sound_get_flag
: array[0..1] of TPlayableSound
;
276 sound_lost_flag
: array[0..1] of TPlayableSound
;
277 sound_ret_flag
: array[0..1] of TPlayableSound
;
278 sound_cap_flag
: array[0..1] of TPlayableSound
;
279 gBodyKillEvent
: Integer = -1;
280 gDefInterTime
: ShortInt
= -1;
281 gInterEndTime
: LongWord
= 0;
282 gInterTime
: LongWord
= 0;
283 gServInterTime
: Byte = 0;
284 gGameStartTime
: LongWord
= 0;
285 gTotalMonsters
: Integer = 0;
286 gPauseMain
: Boolean = false;
287 gPauseHolmes
: Boolean = false;
288 gShowTime
: Boolean = False;
289 gShowFPS
: Boolean = False;
290 gShowScore
: Boolean = True;
291 gShowStat
: Boolean = True;
292 gShowPIDs
: Boolean = False;
293 gShowKillMsg
: Boolean = True;
294 gShowLives
: Boolean = True;
295 gShowPing
: Boolean = False;
296 gShowMap
: Boolean = False;
298 gState
: Byte = STATE_NONE
;
300 sWidth
, sHeight
: Word;
301 gSpectMode
: Byte = SPECT_NONE
;
302 gSpectHUD
: Boolean = True;
303 gSpectKeyPress
: Boolean = False;
304 gSpectX
: Integer = 0;
305 gSpectY
: Integer = 0;
306 gSpectStep
: Byte = 8;
307 gSpectViewTwo
: Boolean = False;
308 gSpectPID1
: Integer = -1;
309 gSpectPID2
: Integer = -1;
310 gSpectAuto
: Boolean = False;
311 gSpectAutoNext
: LongWord
;
312 gSpectAutoStepX
: Integer;
313 gSpectAutoStepY
: Integer;
314 gMusic
: TMusic
= nil;
315 gLoadGameMode
: Boolean;
316 gCheats
: Boolean = False;
317 gMapOnce
: Boolean = False;
318 gMapToDelete
: String;
319 gTempDelete
: Boolean = False;
320 gLastMap
: Boolean = False;
323 gResolutionChange
: Boolean = False;
324 gRC_Width
, gRC_Height
: Integer;
325 gRC_FullScreen
, gRC_Maximized
: Boolean;
326 gLanguageChange
: Boolean = False;
327 gDebugMode
: Boolean = False;
328 g_debug_Sounds
: Boolean = False;
329 g_debug_Frames
: Boolean = False;
330 g_debug_WinMsgs
: Boolean = False;
331 g_debug_MonsterOff
: Boolean = False;
332 g_debug_BotAIOff
: Byte = 0;
333 g_debug_HealthBar
: Boolean = False;
334 g_Debug_Player
: Boolean = False;
335 gCoopMonstersKilled
: Word = 0;
336 gCoopSecretsFound
: Word = 0;
337 gCoopTotalMonstersKilled
: Word = 0;
338 gCoopTotalSecretsFound
: Word = 0;
339 gCoopTotalMonsters
: Word = 0;
340 gCoopTotalSecrets
: Word = 0;
341 gStatsOff
: Boolean = False;
342 gStatsPressed
: Boolean = False;
343 gExitByTrigger
: Boolean = False;
344 gNextMap
: String = '';
345 gLMSRespawn
: Byte = LMS_RESPAWN_NONE
;
346 gLMSRespawnTime
: Cardinal = 0;
347 gLMSSoftSpawn
: Boolean = False;
348 gMissionFailed
: Boolean = False;
349 gVoteInProgress
: Boolean = False;
350 gVotePassed
: Boolean = False;
351 gVoteCommand
: string = '';
352 gVoteTimer
: Cardinal = 0;
353 gVoteCmdTimer
: Cardinal = 0;
354 gVoteCount
: Integer = 0;
355 gVoteTimeout
: Cardinal = 30;
356 gVoted
: Boolean = False;
357 gVotesEnabled
: Boolean = True;
358 gEvents
: Array of TGameEvent
;
359 gDelayedEvents
: Array of TDelayedEvent
;
360 gUseChatSounds
: Boolean = True;
361 gChatSounds
: Array of TChatSound
;
362 gWeaponAction
: Array [0..1, WP_FACT
..WP_LACT
] of Boolean; // [player, weapon_action]
363 gSelectWeapon
: Array [0..1, WP_FIRST
..WP_LAST
] of Boolean; // [player, weapon]
364 gInterReadyCount
: Integer = 0;
365 gMaxBots
: Integer = 127;
367 g_dbg_ignore_bounds
: Boolean = false;
368 r_smallmap_h
: Integer = 0; // 0: left; 1: center; 2: right
369 r_smallmap_v
: Integer = 2; // 0: top; 1: center; 2: bottom
371 // move button values:
372 // bits 0-1: l/r state:
373 // 0: neither left, nor right pressed
376 // bits 4-5: l/r state when strafe was pressed
377 P1MoveButton
: Byte = 0;
378 P2MoveButton
: Byte = 0;
380 g_profile_frame_update
: Boolean = false;
381 g_profile_frame_draw
: Boolean = false;
382 g_profile_collision
: Boolean = false;
383 g_profile_los
: Boolean = false;
384 g_profile_history_size
: Integer = 1000;
386 g_rlayer_back
: Boolean = true;
387 g_rlayer_step
: Boolean = true;
388 g_rlayer_wall
: Boolean = true;
389 g_rlayer_door
: Boolean = true;
390 g_rlayer_acid1
: Boolean = true;
391 g_rlayer_acid2
: Boolean = true;
392 g_rlayer_water
: Boolean = true;
393 g_rlayer_fore
: Boolean = true;
396 procedure g_ResetDynlights ();
397 procedure g_AddDynLight (x
, y
, radius
: Integer; r
, g
, b
, a
: Single);
398 procedure g_DynLightExplosion (x
, y
, radius
: Integer; r
, g
, b
: Single);
400 function conIsCheatsEnabled (): Boolean; inline;
401 function gPause (): Boolean; inline;
407 {$INCLUDE ../nogl/noGLuses.inc}
408 {$IFDEF ENABLE_HOLMES}
411 e_texture
, e_res
, g_textures
, g_window
, g_menu
,
412 e_input
, e_log
, g_console
, g_items
, g_map
, g_panel
,
413 g_playermodel
, g_gfx
, g_options
, Math
,
414 g_triggers
, g_monsters
, e_sound
, CONFIG
,
415 g_language
, g_net
, g_main
, g_phys
,
416 ENet
, e_msg
, g_netmsg
, g_netmaster
,
417 sfs
, wadreader
, g_system
;
421 hasPBarGfx
: Boolean = false;
424 // ////////////////////////////////////////////////////////////////////////// //
425 function gPause (): Boolean; inline; begin result
:= gPauseMain
or gPauseHolmes
; end;
428 // ////////////////////////////////////////////////////////////////////////// //
429 function conIsCheatsEnabled (): Boolean; inline;
432 if g_Game_IsNet
then exit
;
433 if not gDebugMode
then
435 //if not gCheats then exit;
436 if not (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
]) then exit
;
437 if not (gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
]) then exit
;
443 // ////////////////////////////////////////////////////////////////////////// //
445 profileFrameDraw
: TProfiler
= nil;
448 // ////////////////////////////////////////////////////////////////////////// //
451 x
, y
, radius
: Integer;
454 exploRadius
: Integer;
458 g_dynLights
: array of TDynLight
= nil;
459 g_dynLightCount
: Integer = 0;
460 g_playerLight
: Boolean = false;
462 procedure g_ResetDynlights ();
466 if not gwin_has_stencil
then begin g_dynLightCount
:= 0; exit
; end;
468 for idx
:= 0 to g_dynLightCount
-1 do
470 if g_dynLights
[idx
].exploCount
= -666 then
477 Inc(g_dynLights
[idx
].exploCount
);
478 if (g_dynLights
[idx
].exploCount
< 10) then
480 g_dynLights
[idx
].radius
:= g_dynLights
[idx
].exploRadius
+g_dynLights
[idx
].exploCount
*8;
481 g_dynLights
[idx
].a
:= 0.4+g_dynLights
[idx
].exploCount
/10;
482 if (g_dynLights
[idx
].a
> 0.8) then g_dynLights
[idx
].a
:= 0.8;
483 if lnum
<> idx
then g_dynLights
[lnum
] := g_dynLights
[idx
];
488 g_dynLightCount
:= lnum
;
491 procedure g_AddDynLight (x
, y
, radius
: Integer; r
, g
, b
, a
: Single);
493 if not gwin_has_stencil
then exit
;
494 if g_dynLightCount
= length(g_dynLights
) then SetLength(g_dynLights
, g_dynLightCount
+1024);
495 g_dynLights
[g_dynLightCount
].x
:= x
;
496 g_dynLights
[g_dynLightCount
].y
:= y
;
497 g_dynLights
[g_dynLightCount
].radius
:= radius
;
498 g_dynLights
[g_dynLightCount
].r
:= r
;
499 g_dynLights
[g_dynLightCount
].g
:= g
;
500 g_dynLights
[g_dynLightCount
].b
:= b
;
501 g_dynLights
[g_dynLightCount
].a
:= a
;
502 g_dynLights
[g_dynLightCount
].exploCount
:= -666;
503 Inc(g_dynLightCount
);
506 procedure g_DynLightExplosion (x
, y
, radius
: Integer; r
, g
, b
: Single);
508 if not gwin_has_stencil
then exit
;
509 if g_dynLightCount
= length(g_dynLights
) then SetLength(g_dynLights
, g_dynLightCount
+1024);
510 g_dynLights
[g_dynLightCount
].x
:= x
;
511 g_dynLights
[g_dynLightCount
].y
:= y
;
512 g_dynLights
[g_dynLightCount
].radius
:= 0;
513 g_dynLights
[g_dynLightCount
].exploRadius
:= radius
;
514 g_dynLights
[g_dynLightCount
].r
:= r
;
515 g_dynLights
[g_dynLightCount
].g
:= g
;
516 g_dynLights
[g_dynLightCount
].b
:= b
;
517 g_dynLights
[g_dynLightCount
].a
:= 0;
518 g_dynLights
[g_dynLightCount
].exploCount
:= 0;
519 Inc(g_dynLightCount
);
523 // ////////////////////////////////////////////////////////////////////////// //
524 function calcProfilesHeight (prof
: TProfiler
): Integer;
527 if (prof
= nil) then exit
;
528 if (length(prof
.bars
) = 0) then exit
;
529 result
:= length(prof
.bars
)*(16+2);
533 function drawProfiles (x
, y
: Integer; prof
: TProfiler
): Integer;
540 if (prof
= nil) then exit
;
542 if (length(prof
.bars
) = 0) then exit
;
544 hgt
:= calcProfilesHeight(prof
);
545 if (x
< 0) then x
:= gScreenWidth
-(wdt
-1)+x
;
546 if (y
< 0) then y
:= gScreenHeight
-(hgt
-1)+y
;
548 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
549 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
550 e_DarkenQuadWH(x
, y
, wdt
, hgt
, 150);
553 for ii
:= 0 to High(prof
.bars
) do
555 e_TextureFontPrintEx(x
+2+4*prof
.bars
[ii
].level
, yy
, Format('%s: %d', [prof
.bars
[ii
].name
, prof
.bars
[ii
].value
]), gStdFont
, 255, 255, 0, 1, false);
562 // ////////////////////////////////////////////////////////////////////////// //
564 TEndCustomGameStat
= record
565 PlayerStat
: TPlayerStatArray
;
569 Map
, MapName
: String;
572 TEndSingleGameStat
= record
573 PlayerStat
: Array [0..1] of record
579 TotalSecrets
: Integer;
582 TLoadingStat
= record
586 Msgs
: Array of String;
588 PBarWasHere
: Boolean; // did we draw a progress bar for this message?
591 TParamStrValue
= record
596 TParamStrValues
= Array of TParamStrValue
;
599 INTER_ACTION_TEXT
= 1;
600 INTER_ACTION_PIC
= 2;
601 INTER_ACTION_MUSIC
= 3;
605 FPSCounter
, UPSCounter
: Word;
606 FPSTime
, UPSTime
: LongWord
;
607 DataLoaded
: Boolean = False;
608 IsDrawStat
: Boolean = False;
609 CustomStat
: TEndCustomGameStat
;
610 SingleStat
: TEndSingleGameStat
;
611 LoadingStat
: TLoadingStat
;
612 EndingGameCounter
: Byte = 0;
615 MessageLineLength
: Integer = 80;
616 MapList
: SSArray
= nil;
617 MapIndex
: Integer = -1;
618 InterReadyTime
: Integer = -1;
619 StatShotDone
: Boolean = False;
620 StatFilename
: string = ''; // used by stat screenshot to save with the same name as the csv
621 StatDate
: string = '';
627 text: Array of ShortString
;
628 anim
: Array of ShortString
;
629 pic
: Array of ShortString
;
630 mus
: Array of ShortString
;
632 triggers
: Array of record
634 actions
: Array of record
635 action
, p1
, p2
: Integer;
638 cur_trigger
: Integer;
651 function Compare(a
, b
: TPlayerStat
): Integer;
653 if a
.Spectator
then Result
:= 1
654 else if b
.Spectator
then Result
:= -1
655 else if a
.Frags
< b
.Frags
then Result
:= 1
656 else if a
.Frags
> b
.Frags
then Result
:= -1
657 else if a
.Deaths
< b
.Deaths
then Result
:= -1
658 else if a
.Deaths
> b
.Deaths
then Result
:= 1
659 else if a
.Kills
< b
.Kills
then Result
:= -1
663 procedure SortGameStat(var stat
: TPlayerStatArray
);
668 if stat
= nil then Exit
;
670 for I
:= High(stat
) downto Low(stat
) do
671 for J
:= Low(stat
) to High(stat
) - 1 do
672 if Compare(stat
[J
], stat
[J
+ 1]) = 1 then
675 stat
[J
] := stat
[J
+ 1];
680 // saves a shitty CSV containing the game stats passed to it
681 procedure SaveGameStat(Stat
: TEndCustomGameStat
; Path
: string);
684 dir
, fname
, map
, mode
, etime
, flags
, strf
: String;
689 dir
:= e_GetWriteableDir(StatsDirs
);
690 // stats are placed in stats/yy/mm/dd/*.csv
691 fname
:= e_CatPath(dir
, Path
);
692 ForceDirectories(fname
); // ensure yy/mm/dd exists within the stats dir
693 fname
:= e_CatPath(fname
, StatFilename
+ '.csv');
694 AssignFile(s
, fname
);
696 SetTextCodePage(s
, CP_UTF8
);
698 // line 1: stats ver, datetime, server name, map name, game mode, time limit, score limit, dmflags, game time, num players
699 if g_Game_IsNet
then fname
:= NetServerName
else fname
:= '';
700 map
:= g_ExtractWadNameNoPath(gMapInfo
.Map
) + ':/' + g_ExtractFileName(gMapInfo
.Map
);
701 mode
:= g_Game_ModeToText(Stat
.GameMode
);
702 etime
:= Format('%d:%.2d:%.2d', [
703 Stat
.GameTime
div 1000 div 3600,
704 (Stat
.GameTime
div 1000 div 60) mod 60,
705 Stat
.GameTime
div 1000 mod 60
709 for flag
in gGameSettings
.Options
do
712 System
.WriteStr(strf
, flag
); // FIXME: rename our utils.WriteStr()
716 WriteLn(s
, 'stats_ver,datetime,server,map,mode,timelimit,scorelimit,dmflags,time,num_players');
717 WriteLn(s
, Format('%d,%s,%s,%s,%s,%u,%u,"%s",%s,%d', [
723 gGameSettings
.TimeLimit
,
724 gGameSettings
.ScoreLimit
,
727 Length(Stat
.PlayerStat
)
729 // line 2: game specific shit
730 // if it's a team game: red score, blue score
731 // if it's a coop game: monsters killed, monsters total, secrets found, secrets total
733 if Stat
.GameMode
in [GM_TDM
, GM_CTF
] then
735 Format('red_score,blue_score' + LineEnding
+ '%d,%d', [Stat
.TeamStat
[TEAM_RED
].Score
, Stat
.TeamStat
[TEAM_BLUE
].Score
]))
736 else if Stat
.GameMode
in [GM_COOP
, GM_SINGLE
] then
738 Format('mon_killed,mon_total,secrets_found,secrets_total' + LineEnding
+ '%d,%d,%d,%d',[gCoopMonstersKilled
, gTotalMonsters
, gCoopSecretsFound
, gSecretsCount
]));
739 // lines 3-...: team, player name, frags, deaths
740 WriteLn(s
, 'team,name,frags,deaths');
741 for I
:= Low(Stat
.PlayerStat
) to High(Stat
.PlayerStat
) do
742 with Stat
.PlayerStat
[I
] do
743 WriteLn(s
, Format('%d,%s,%d,%d', [Team
, dquoteStr(Name
), Frags
, Deaths
]));
745 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_WRITE
], [fname
]));
748 g_Console_Add('could not create gamestats file "' + fname
+ '"');
753 procedure ClearDebugCvars();
755 g_debug_Sounds
:= False;
756 g_debug_Frames
:= False;
757 g_debug_WinMsgs
:= False;
758 g_debug_MonsterOff
:= False;
759 g_debug_BotAIOff
:= 0;
760 g_debug_HealthBar
:= False;
761 g_Debug_Player
:= False;
764 function g_Game_ModeToText(Mode
: Byte): string;
768 GM_DM
: Result
:= _lc
[I_MENU_GAME_TYPE_DM
];
769 GM_TDM
: Result
:= _lc
[I_MENU_GAME_TYPE_TDM
];
770 GM_CTF
: Result
:= _lc
[I_MENU_GAME_TYPE_CTF
];
771 GM_COOP
: Result
:= _lc
[I_MENU_GAME_TYPE_COOP
];
772 GM_SINGLE
: Result
:= _lc
[I_MENU_GAME_TYPE_SINGLE
];
776 function g_Game_TextToMode(Mode
: string): Byte;
779 Mode
:= UpperCase(Mode
);
780 if Mode
= _lc
[I_MENU_GAME_TYPE_DM
] then
785 if Mode
= _lc
[I_MENU_GAME_TYPE_TDM
] then
790 if Mode
= _lc
[I_MENU_GAME_TYPE_CTF
] then
795 if Mode
= _lc
[I_MENU_GAME_TYPE_COOP
] then
800 if Mode
= _lc
[I_MENU_GAME_TYPE_SINGLE
] then
807 function g_Game_IsNet(): Boolean;
809 Result
:= (gGameSettings
.GameType
in [GT_SERVER
, GT_CLIENT
]);
812 function g_Game_IsServer(): Boolean;
814 Result
:= (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
, GT_SERVER
]);
817 function g_Game_IsClient(): Boolean;
819 Result
:= (gGameSettings
.GameType
= GT_CLIENT
);
822 function g_Game_GetMegaWADInfo(WAD
: String): TMegaWADInfo
;
829 Result
.name
:= ExtractFileName(WAD
);
830 Result
.description
:= '';
833 w
:= TWADFile
.Create();
836 if not w
.GetResource('INTERSCRIPT', p
, len
) then
842 cfg
:= TConfig
.CreateMem(p
, len
);
843 Result
.name
:= cfg
.ReadStr('megawad', 'name', ExtractFileName(WAD
));
844 Result
.description
:= cfg
.ReadStr('megawad', 'description', '');
845 Result
.author
:= cfg
.ReadStr('megawad', 'author', '');
846 Result
.pic
:= cfg
.ReadStr('megawad', 'pic', '');
852 procedure g_Game_FreeWAD();
856 for a
:= 0 to High(MegaWAD
.res
.pic
) do
857 if MegaWAD
.res
.pic
[a
] <> '' then
858 g_Texture_Delete(MegaWAD
.res
.pic
[a
]);
860 for a
:= 0 to High(MegaWAD
.res
.mus
) do
861 if MegaWAD
.res
.mus
[a
] <> '' then
862 g_Sound_Delete(MegaWAD
.res
.mus
[a
]);
864 MegaWAD
.res
.pic
:= nil;
865 MegaWAD
.res
.text := nil;
866 MegaWAD
.res
.anim
:= nil;
867 MegaWAD
.res
.mus
:= nil;
868 MegaWAD
.triggers
:= nil;
870 g_Texture_Delete('TEXTURE_endpic');
871 g_Sound_Delete('MUSIC_endmus');
873 ZeroMemory(@MegaWAD
, SizeOf(MegaWAD
));
874 gGameSettings
.WAD
:= '';
877 procedure g_Game_LoadWAD(WAD
: string);
886 gGameSettings
.WAD
:= WAD
;
887 if not (gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
]) then
890 MegaWAD
.info
:= g_Game_GetMegaWADInfo(WAD
);
892 w
:= TWADFile
.Create();
895 if not w
.GetResource('INTERSCRIPT', p
, len
) then
901 cfg
:= TConfig
.CreateMem(p
, len
);
906 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
907 if s = '' then Break;
910 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
911 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
913 g_Texture_CreateWADEx(s, s);
919 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
920 if s = '' then Break;
923 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
924 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
926 g_Music_CreateWADEx(s, s);
929 MegaWAD
.endpic
:= cfg
.ReadStr('megawad', 'endpic', '');
930 if MegaWAD
.endpic
<> '' then
932 TEXTUREFILTER
:= GL_LINEAR
;
933 s
:= e_GetResourcePath(WadDirs
, MegaWAD
.endpic
, WAD
);
934 g_Texture_CreateWADEx('TEXTURE_endpic', s
);
935 TEXTUREFILTER
:= GL_NEAREST
;
937 MegaWAD
.endmus
:= cfg
.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
938 if MegaWAD
.endmus
<> '' then
940 s
:= e_GetResourcePath(WadDirs
, MegaWAD
.endmus
, WAD
);
941 g_Sound_CreateWADEx('MUSIC_endmus', s
, True);
949 {procedure start_trigger(t: string);
953 function next_trigger(): Boolean;
957 procedure DisableCheats();
963 if gPlayer1
<> nil then gPlayer1
.GodMode
:= False;
964 if gPlayer2
<> nil then gPlayer2
.GodMode
:= False;
965 if gPlayer1
<> nil then gPlayer1
.NoTarget
:= False;
966 if gPlayer2
<> nil then gPlayer2
.NoTarget
:= False;
968 {$IF DEFINED(D2F_DEBUG)}
969 if gPlayer1
<> nil then gPlayer1
.NoTarget
:= True;
970 gAimLine
:= g_dbg_aimline_on
;
974 procedure g_Game_ExecuteEvent(Name
: String);
980 if gEvents
= nil then
982 for a
:= 0 to High(gEvents
) do
983 if gEvents
[a
].Name
= Name
then
985 if gEvents
[a
].Command
<> '' then
986 g_Console_Process(gEvents
[a
].Command
, True);
991 function g_Game_DelayEvent(DEType
: Byte; Time
: LongWord
; Num
: Integer = 0; Str
: String = ''): Integer;
996 if gDelayedEvents
<> nil then
997 for a
:= 0 to High(gDelayedEvents
) do
998 if not gDelayedEvents
[a
].Pending
then
1005 SetLength(gDelayedEvents
, Length(gDelayedEvents
) + 1);
1006 n
:= High(gDelayedEvents
);
1008 gDelayedEvents
[n
].Pending
:= True;
1009 gDelayedEvents
[n
].DEType
:= DEType
;
1010 gDelayedEvents
[n
].DENum
:= Num
;
1011 gDelayedEvents
[n
].DEStr
:= Str
;
1012 if DEType
= DE_GLOBEVENT
then
1013 gDelayedEvents
[n
].Time
:= (sys_GetTicks() {div 1000}) + Time
1015 gDelayedEvents
[n
].Time
:= gTime
+ Time
;
1019 procedure EndGame();
1025 if g_Game_IsNet
and g_Game_IsServer
then
1026 MH_SEND_GameEvent(NET_EV_MAPEND
, Byte(gMissionFailed
));
1029 gPauseMain
:= false;
1030 gPauseHolmes
:= false;
1033 g_Game_StopAllSounds(False);
1038 EndingGameCounter
:= 0;
1039 g_ActiveWindow
:= nil;
1041 gLMSRespawn
:= LMS_RESPAWN_NONE
;
1042 gLMSRespawnTime
:= 0;
1045 EXIT_SIMPLE
: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
1050 begin // Ýòî áûë òåñò
1054 begin // Âûõîä â ãëàâíîå ìåíþ
1055 gMusic
.SetByName('MUSIC_MENU');
1057 if gState
<> STATE_SLIST
then
1059 g_GUI_ShowWindow('MainMenu');
1060 gState
:= STATE_MENU
;
1063 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
1064 slReturnPressed
:= True;
1065 if g_Net_Slist_Fetch(slCurrent
) then
1067 if slCurrent
= nil then
1068 slWaitStr
:= _lc
[I_NET_SLIST_NOSERVERS
];
1071 slWaitStr
:= _lc
[I_NET_SLIST_ERROR
];
1072 g_Serverlist_GenerateTable(slCurrent
, slTable
);
1075 g_Game_ExecuteEvent('ongameend');
1079 EXIT_RESTART
: // Íà÷àòü óðîâåíü ñíà÷àëà
1081 if not g_Game_IsClient
then g_Game_Restart();
1084 EXIT_ENDLEVELCUSTOM
: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
1086 // Ñòàòèñòèêà Ñâîåé èãðû:
1087 FileName
:= g_ExtractWadName(gMapInfo
.Map
);
1089 CustomStat
.GameTime
:= gTime
;
1090 CustomStat
.Map
:= ExtractFileName(FileName
)+':'+g_ExtractFileName(gMapInfo
.Map
); //ResName;
1091 CustomStat
.MapName
:= gMapInfo
.Name
;
1092 CustomStat
.GameMode
:= gGameSettings
.GameMode
;
1093 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1094 CustomStat
.TeamStat
:= gTeamStat
;
1096 CustomStat
.PlayerStat
:= nil;
1098 // Ñòàòèñòèêà èãðîêîâ:
1099 if gPlayers
<> nil then
1101 for a
:= 0 to High(gPlayers
) do
1102 if gPlayers
[a
] <> nil then
1104 SetLength(CustomStat
.PlayerStat
, Length(CustomStat
.PlayerStat
)+1);
1105 with CustomStat
.PlayerStat
[High(CustomStat
.PlayerStat
)] do
1108 Name
:= gPlayers
[a
].Name
;
1109 Frags
:= gPlayers
[a
].Frags
;
1110 Deaths
:= gPlayers
[a
].Death
;
1111 Kills
:= gPlayers
[a
].Kills
;
1112 Team
:= gPlayers
[a
].Team
;
1113 Color
:= gPlayers
[a
].Model
.Color
;
1114 Spectator
:= gPlayers
[a
].FSpectator
;
1118 SortGameStat(CustomStat
.PlayerStat
);
1120 if (gSaveStats
or gScreenshotStats
) and (Length(CustomStat
.PlayerStat
) > 1) then
1123 if g_Game_IsNet
then StatFilename
:= NetServerName
else StatFilename
:= 'local';
1124 StatDate
:= FormatDateTime('yymmdd_hhnnss', t
);
1125 StatFilename
:= StatFilename
+ '_' + CustomStat
.Map
+ '_' + g_Game_ModeToText(CustomStat
.GameMode
);
1126 StatFilename
:= sanitizeFilename(StatFilename
) + '_' + StatDate
;
1128 SaveGameStat(CustomStat
, FormatDateTime('yyyy"/"mm"/"dd', t
));
1131 StatShotDone
:= False;
1134 g_Game_ExecuteEvent('onmapend');
1135 if not g_Game_IsClient
then g_Player_ResetReady
;
1136 gInterReadyCount
:= 0;
1138 // Çàòóõàþùèé ýêðàí:
1139 EndingGameCounter
:= 255;
1140 gState
:= STATE_FOLD
;
1142 if gDefInterTime
< 0 then
1143 gInterEndTime
:= IfThen((gGameSettings
.GameType
= GT_SERVER
) and (gPlayer1
= nil), 15000, 25000)
1145 gInterEndTime
:= gDefInterTime
* 1000;
1148 EXIT_ENDLEVELSINGLE
: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
1150 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
1151 SingleStat
.GameTime
:= gTime
;
1152 SingleStat
.TwoPlayers
:= gPlayer2
<> nil;
1153 SingleStat
.TotalSecrets
:= gSecretsCount
;
1154 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
1155 SingleStat
.PlayerStat
[0].Kills
:= gPlayer1
.MonsterKills
;
1156 SingleStat
.PlayerStat
[0].Secrets
:= gPlayer1
.Secrets
;
1157 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
1158 if SingleStat
.TwoPlayers
then
1160 SingleStat
.PlayerStat
[1].Kills
:= gPlayer2
.MonsterKills
;
1161 SingleStat
.PlayerStat
[1].Secrets
:= gPlayer2
.Secrets
;
1164 g_Game_ExecuteEvent('onmapend');
1167 if gNextMap
<> '' then
1169 gMusic
.SetByName('MUSIC_INTERMUS');
1171 gState
:= STATE_INTERSINGLE
;
1174 g_Game_ExecuteEvent('oninter');
1176 else // Áîëüøå íåò êàðò
1178 // Çàòóõàþùèé ýêðàí:
1179 EndingGameCounter
:= 255;
1180 gState
:= STATE_FOLD
;
1185 // Îêîí÷àíèå îáðàáîòàíî:
1186 if gExit
<> EXIT_QUIT
then
1190 procedure drawTime(X
, Y
: Integer); inline;
1192 e_TextureFontPrint(x
, y
,
1193 Format('%d:%.2d:%.2d', [
1194 gTime
div 1000 div 3600,
1195 (gTime
div 1000 div 60) mod 60,
1196 gTime
div 1000 mod 60
1201 procedure DrawStat();
1203 pc
, x
, y
, w
, h
: Integer;
1204 w1
, w2
, w3
, w4
: Integer;
1206 cw
, ch
, r
, g
, b
, rr
, gg
, bb
: Byte;
1209 stat
: TPlayerStatArray
;
1217 pc
:= g_Player_GetCount
;
1218 e_TextureFontGetSize(gStdFont
, cw
, ch
);
1220 w
:= gScreenWidth
-(gScreenWidth
div 5);
1221 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1224 h
:= 40+ch
*5+(ch
+8)*pc
;
1225 x
:= (gScreenWidth
div 2)-(w
div 2);
1226 y
:= (gScreenHeight
div 2)-(h
div 2);
1228 e_DrawFillQuad(x
, y
, x
+w
-1, y
+h
-1, 64, 64, 64, 32);
1229 e_DrawQuad(x
, y
, x
+w
-1, y
+h
-1, 255, 127, 0);
1231 drawTime(x
+w
-78, y
+8);
1233 wad
:= g_ExtractWadNameNoPath(gMapInfo
.Map
);
1234 map
:= g_ExtractFileName(gMapInfo
.Map
);
1235 mapstr
:= wad
+ ':\' + map
+ ' - ' + gMapInfo
.Name
;
1237 case gGameSettings
.GameMode
of
1240 if gGameSettings
.MaxLives
= 0 then
1241 s1
:= _lc
[I_GAME_DM
]
1243 s1
:= _lc
[I_GAME_LMS
];
1244 s2
:= Format(_lc
[I_GAME_FRAG_LIMIT
], [gGameSettings
.ScoreLimit
]);
1245 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1250 if gGameSettings
.MaxLives
= 0 then
1251 s1
:= _lc
[I_GAME_TDM
]
1253 s1
:= _lc
[I_GAME_TLMS
];
1254 s2
:= Format(_lc
[I_GAME_FRAG_LIMIT
], [gGameSettings
.ScoreLimit
]);
1255 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1260 s1
:= _lc
[I_GAME_CTF
];
1261 s2
:= Format(_lc
[I_GAME_SCORE_LIMIT
], [gGameSettings
.ScoreLimit
]);
1262 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1267 if gGameSettings
.MaxLives
= 0 then
1268 s1
:= _lc
[I_GAME_COOP
]
1270 s1
:= _lc
[I_GAME_SURV
];
1271 s2
:= _lc
[I_GAME_MONSTERS
] + ' ' + IntToStr(gCoopMonstersKilled
) + '/' + IntToStr(gTotalMonsters
);
1272 s3
:= _lc
[I_GAME_SECRETS
] + ' ' + IntToStr(gCoopSecretsFound
) + '/' + IntToStr(gSecretsCount
);
1283 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*cw
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
1285 e_TextureFontPrintEx(x
+(w
div 2)-(Length(mapstr
)*cw
div 2), _y
, mapstr
, gStdFont
, 200, 200, 200, 1);
1287 e_TextureFontPrintEx(x
+16, _y
, s2
, gStdFont
, 200, 200, 200, 1);
1289 e_TextureFontPrintEx(x
+w
-16-(Length(s3
))*cw
, _y
, s3
,
1290 gStdFont
, 200, 200, 200, 1);
1292 if NetMode
= NET_SERVER
then
1293 e_TextureFontPrintEx(x
+8, y
+ 8, _lc
[I_NET_SERVER
], gStdFont
, 255, 255, 255, 1)
1295 if NetMode
= NET_CLIENT
then
1296 e_TextureFontPrintEx(x
+8, y
+ 8,
1297 NetClientIP
+ ':' + IntToStr(NetClientPort
), gStdFont
, 255, 255, 255, 1);
1301 stat
:= g_Player_GetStats();
1304 w2
:= (w
-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1305 w3
:= (w
-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1307 w1
:= w
-16-w2
-w3
-w4
; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1309 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1313 for a
:= TEAM_RED
to TEAM_BLUE
do
1315 if a
= TEAM_RED
then
1317 s1
:= _lc
[I_GAME_TEAM_RED
];
1324 s1
:= _lc
[I_GAME_TEAM_BLUE
];
1330 e_TextureFontPrintEx(x
+16, _y
, s1
, gStdFont
, r
, g
, b
, 1);
1331 e_TextureFontPrintEx(x
+w1
+16, _y
, IntToStr(gTeamStat
[a
].Score
),
1332 gStdFont
, r
, g
, b
, 1);
1334 _y
:= _y
+ch
+(ch
div 4);
1335 e_DrawLine(1, x
+16, _y
, x
+w
-16, _y
, r
, g
, b
);
1336 _y
:= _y
+(ch
div 4);
1338 for aa
:= 0 to High(stat
) do
1339 if stat
[aa
].Team
= a
then
1355 namestr
:= Format('[%5d] %s', [UID
, Name
])
1359 e_TextureFontPrintEx(x
+16, _y
, namestr
, gStdFont
, rr
, gg
, bb
, 1);
1361 e_TextureFontPrintEx(x
+w1
+16, _y
, Format(_lc
[I_GAME_PING_MS
], [Ping
, Loss
]), gStdFont
, rr
, gg
, bb
, 1);
1363 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
, IntToStr(Frags
), gStdFont
, rr
, gg
, bb
, 1);
1365 e_TextureFontPrintEx(x
+w1
+w2
+w3
+16, _y
, IntToStr(Deaths
), gStdFont
, rr
, gg
, bb
, 1);
1372 else if gGameSettings
.GameMode
in [GM_DM
, GM_COOP
] then
1375 e_TextureFontPrintEx(x
+16, _y
, _lc
[I_GAME_PLAYER_NAME
], gStdFont
, 255, 127, 0, 1);
1376 e_TextureFontPrintEx(x
+16+w1
, _y
, _lc
[I_GAME_PING
], gStdFont
, 255, 127, 0, 1);
1377 e_TextureFontPrintEx(x
+16+w1
+w2
, _y
, _lc
[I_GAME_FRAGS
], gStdFont
, 255, 127, 0, 1);
1378 e_TextureFontPrintEx(x
+16+w1
+w2
+w3
, _y
, _lc
[I_GAME_DEATHS
], gStdFont
, 255, 127, 0, 1);
1381 for aa
:= 0 to High(stat
) do
1395 namestr
:= Format('[%5d] %s', [UID
, Name
])
1399 e_DrawFillQuad(x
+16, _y
+4, x
+32-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
1400 e_DrawQuad(x
+16, _y
+4, x
+32-1, _y
+16+4-1, 192, 192, 192);
1402 e_TextureFontPrintEx(x
+16+16+8, _y
+4, namestr
, gStdFont
, r
, g
, 0, 1);
1404 e_TextureFontPrintEx(x
+w1
+16, _y
+4, Format(_lc
[I_GAME_PING_MS
], [Ping
, Loss
]), gStdFont
, r
, g
, 0, 1);
1406 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
+4, IntToStr(Frags
), gStdFont
, r
, g
, 0, 1);
1408 e_TextureFontPrintEx(x
+w1
+w2
+w3
+16, _y
+4, IntToStr(Deaths
), gStdFont
, r
, g
, 0, 1);
1414 procedure g_Game_Init();
1417 knownFiles
: array of AnsiString
= nil;
1419 wext
, s
: AnsiString
;
1424 gTempDelete
:= False;
1426 sfsGCDisable(); // temporary disable removing of temporary volumes
1429 TEXTUREFILTER
:= GL_LINEAR
;
1430 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD
+':TEXTURES\TITLE');
1431 g_Texture_CreateWADEx('INTER', GameWAD
+':TEXTURES\INTER');
1432 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD
+':TEXTURES\ENDGAME_EN');
1433 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD
+':TEXTURES\ENDGAME_RU');
1434 TEXTUREFILTER
:= GL_NEAREST
;
1436 LoadStdFont('STDTXT', 'STDFONT', gStdFont
);
1437 LoadFont('MENUTXT', 'MENUFONT', gMenuFont
);
1438 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont
);
1440 g_Game_ClearLoading();
1441 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION
]), 0, False);
1442 g_Game_SetLoadingText('', 0, False);
1444 g_Game_SetLoadingText(_lc
[I_LOAD_CONSOLE
], 0, False);
1447 g_Game_SetLoadingText(_lc
[I_LOAD_MODELS
], 0, False);
1448 g_PlayerModel_LoadData();
1450 // load models from all possible wad types, in all known directories
1451 // this does a loosy job (linear search, ooph!), but meh
1452 for wext
in wadExtensions
do
1454 for f
:= High(ModelDirs
) downto Low(ModelDirs
) do
1456 if (FindFirst(ModelDirs
[f
]+DirectorySeparator
+'*'+wext
, faAnyFile
, SR
) = 0) then
1460 for s
in knownFiles
do
1462 if (strEquCI1251(forceFilenameExt(SR
.Name
, ''), forceFilenameExt(ExtractFileName(s
), ''))) then
1470 SetLength(knownFiles
, length(knownFiles
)+1);
1471 knownFiles
[High(knownFiles
)] := ModelDirs
[f
]+DirectorySeparator
+SR
.Name
;
1473 until (FindNext(SR
) <> 0);
1479 if (length(knownFiles
) = 0) then raise Exception
.Create('no player models found!');
1481 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
);
1482 for s
in knownFiles
do
1484 if not g_PlayerModel_Load(s
) then e_LogWritefln('Error loading model "%s"', [s
], TMsgType
.Warning
);
1488 gPauseMain
:= false;
1489 gPauseHolmes
:= false;
1492 {e_MouseInfo.Accel := 1.0;}
1494 g_Game_SetLoadingText(_lc
[I_LOAD_GAME_DATA
], 0, False);
1497 g_Game_SetLoadingText(_lc
[I_LOAD_MUSIC
], 0, False);
1498 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD
+':MUSIC\INTERMUS', True);
1499 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD
+':MUSIC\MENU', True);
1500 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD
+':MUSIC\ROUNDMUS', True, True);
1501 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD
+':MUSIC\ENDMUS', True);
1504 g_Game_SetLoadingText(_lc
[I_LOAD_MENUS
], 0, False);
1508 gMusic
:= TMusic
.Create();
1509 gMusic
.SetByName('MUSIC_MENU');
1512 gGameSettings
.WarmupTime
:= 30;
1514 gState
:= STATE_MENU
;
1516 SetLength(gEvents
, 6);
1517 gEvents
[0].Name
:= 'ongamestart';
1518 gEvents
[1].Name
:= 'ongameend';
1519 gEvents
[2].Name
:= 'onmapstart';
1520 gEvents
[3].Name
:= 'onmapend';
1521 gEvents
[4].Name
:= 'oninter';
1522 gEvents
[5].Name
:= 'onwadend';
1524 sfsGCEnable(); // enable releasing unused volumes
1528 procedure g_Game_Free(freeTextures
: Boolean=true);
1530 if NetMode
= NET_CLIENT
then g_Net_Disconnect();
1531 if NetMode
= NET_SERVER
then g_Net_Host_Die();
1533 g_Map_Free(freeTextures
);
1535 g_Player_RemoveAllCorpses();
1537 gGameSettings
.GameType
:= GT_NONE
;
1538 if gGameSettings
.GameMode
= GM_SINGLE
then
1539 gGameSettings
.GameMode
:= GM_DM
;
1540 gSwitchGameMode
:= gGameSettings
.GameMode
;
1543 gExitByTrigger
:= False;
1546 function IsActivePlayer(p
: TPlayer
): Boolean;
1551 Result
:= (not p
.FDummy
) and (not p
.FSpectator
);
1554 function GetActivePlayer_ByID(ID
: Integer): TPlayer
;
1561 if gPlayers
= nil then
1563 for a
:= Low(gPlayers
) to High(gPlayers
) do
1564 if IsActivePlayer(gPlayers
[a
]) then
1566 if gPlayers
[a
].UID
<> ID
then
1568 Result
:= gPlayers
[a
];
1573 function GetActivePlayerID_Next(Skip
: Integer = -1): Integer;
1579 if gPlayers
= nil then
1583 for a
:= Low(gPlayers
) to High(gPlayers
) do
1584 if IsActivePlayer(gPlayers
[a
]) then
1586 SetLength(ids
, Length(ids
) + 1);
1587 ids
[High(ids
)] := gPlayers
[a
].UID
;
1588 if gPlayers
[a
].UID
= Skip
then
1591 if Length(ids
) = 0 then
1596 Result
:= ids
[(idx
+ 1) mod Length(ids
)];
1599 function GetActivePlayerID_Prev(Skip
: Integer = -1): Integer;
1605 if gPlayers
= nil then
1609 for a
:= Low(gPlayers
) to High(gPlayers
) do
1610 if IsActivePlayer(gPlayers
[a
]) then
1612 SetLength(ids
, Length(ids
) + 1);
1613 ids
[High(ids
)] := gPlayers
[a
].UID
;
1614 if gPlayers
[a
].UID
= Skip
then
1617 if Length(ids
) = 0 then
1620 Result
:= ids
[Length(ids
) - 1]
1622 Result
:= ids
[(Length(ids
) - 1 + idx
) mod Length(ids
)];
1625 function GetActivePlayerID_Random(Skip
: Integer = -1): Integer;
1631 if gPlayers
= nil then
1635 for a
:= Low(gPlayers
) to High(gPlayers
) do
1636 if IsActivePlayer(gPlayers
[a
]) then
1638 SetLength(ids
, Length(ids
) + 1);
1639 ids
[High(ids
)] := gPlayers
[a
].UID
;
1640 if gPlayers
[a
].UID
= Skip
then
1643 if Length(ids
) = 0 then
1645 if Length(ids
) = 1 then
1650 Result
:= ids
[Random(Length(ids
))];
1652 while (idx
<> -1) and (Result
= Skip
) and (a
> 0) do
1654 Result
:= ids
[Random(Length(ids
))];
1659 function GetRandomSpectMode(Current
: Byte): Byte;
1666 0: Result
:= SPECT_STATS
;
1667 1: Result
:= SPECT_MAPVIEW
;
1668 2: Result
:= SPECT_MAPVIEW
;
1669 3: Result
:= SPECT_PLAYERS
;
1670 4: Result
:= SPECT_PLAYERS
;
1671 5: Result
:= SPECT_PLAYERS
;
1672 6: Result
:= SPECT_PLAYERS
;
1674 if (Current
in [SPECT_STATS
, SPECT_MAPVIEW
]) and (Current
= Result
) then
1678 procedure ProcessPlayerControls (plr
: TPlayer
; p
: Integer; var MoveButton
: Byte);
1684 if (plr
= nil) then exit
;
1685 if (p
= 2) then time
:= 1000 else time
:= 1;
1686 strafeDir
:= MoveButton
shr 4;
1687 MoveButton
:= MoveButton
and $0F;
1689 if gPlayerAction
[p
, ACTION_MOVELEFT
] and (not gPlayerAction
[p
, ACTION_MOVERIGHT
]) then
1690 MoveButton
:= 1 // Íàæàòà òîëüêî "Âëåâî"
1691 else if (not gPlayerAction
[p
, ACTION_MOVELEFT
]) and gPlayerAction
[p
, ACTION_MOVERIGHT
] then
1692 MoveButton
:= 2 // Íàæàòà òîëüêî "Âïðàâî"
1693 else if (not gPlayerAction
[p
, ACTION_MOVELEFT
]) and (not gPlayerAction
[p
, ACTION_MOVERIGHT
]) then
1694 MoveButton
:= 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1696 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1697 if MoveButton
= 1 then
1698 plr
.PressKey(KEY_LEFT
, time
)
1699 else if MoveButton
= 2 then
1700 plr
.PressKey(KEY_RIGHT
, time
);
1702 // if we have "strafe" key, turn off old strafe mechanics
1703 if gPlayerAction
[p
, ACTION_STRAFE
] then
1705 // new strafe mechanics
1706 if (strafeDir
= 0) then
1707 strafeDir
:= MoveButton
; // start strafing
1708 // now set direction according to strafe (reversed)
1709 if (strafeDir
= 2) then
1710 plr
.SetDirection(TDirection
.D_LEFT
)
1711 else if (strafeDir
= 1) then
1712 plr
.SetDirection(TDirection
.D_RIGHT
)
1716 strafeDir
:= 0; // not strafing anymore
1717 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1718 if (MoveButton
= 2) and gPlayerAction
[p
, ACTION_MOVELEFT
] then
1719 plr
.SetDirection(TDirection
.D_LEFT
)
1720 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1721 else if (MoveButton
= 1) and gPlayerAction
[p
, ACTION_MOVERIGHT
] then
1722 plr
.SetDirection(TDirection
.D_RIGHT
)
1723 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1724 else if MoveButton
<> 0 then
1725 plr
.SetDirection(TDirection(MoveButton
-1))
1728 // fix movebutton state
1729 MoveButton
:= MoveButton
or (strafeDir
shl 4);
1731 // Îñòàëüíûå êëàâèøè:
1732 if gPlayerAction
[p
, ACTION_JUMP
] then plr
.PressKey(KEY_JUMP
, time
);
1733 if gPlayerAction
[p
, ACTION_LOOKUP
] then plr
.PressKey(KEY_UP
, time
);
1734 if gPlayerAction
[p
, ACTION_LOOKDOWN
] then plr
.PressKey(KEY_DOWN
, time
);
1735 if gPlayerAction
[p
, ACTION_ATTACK
] then plr
.PressKey(KEY_FIRE
);
1736 if gPlayerAction
[p
, ACTION_ACTIVATE
] then plr
.PressKey(KEY_OPEN
);
1738 for i
:= WP_FACT
to WP_LACT
do
1740 if gWeaponAction
[p
, i
] then
1742 plr
.ProcessWeaponAction(i
);
1743 gWeaponAction
[p
, i
] := False
1747 for i
:= WP_FIRST
to WP_LAST
do
1749 if gSelectWeapon
[p
, i
] then
1751 plr
.QueueWeaponSwitch(i
); // all choices are passed there, and god will take the best
1752 gSelectWeapon
[p
, i
] := False
1756 // HACK: add dynlight here
1757 if gwin_k8_enable_light_experiments
then
1759 if e_KeyPressed(IK_F8
) and gGameOn
and (not gConsoleShow
) and (g_ActiveWindow
= nil) then
1761 g_playerLight
:= true;
1763 if e_KeyPressed(IK_F9
) and gGameOn
and (not gConsoleShow
) and (g_ActiveWindow
= nil) then
1765 g_playerLight
:= false;
1769 if gwin_has_stencil
and g_playerLight
then g_AddDynLight(plr
.GameX
+32, plr
.GameY
+40, 128, 1, 1, 0, 0.6);
1772 // HACK: don't have a "key was pressed" function
1773 procedure InterReady();
1775 if InterReadyTime
> gTime
then Exit
;
1776 InterReadyTime
:= gTime
+ 3000;
1777 MC_SEND_CheatRequest(NET_CHEAT_READY
);
1780 procedure g_Game_PreUpdate();
1782 // these are in separate PreUpdate functions because they can interact during Update()
1783 // and are synced over the net
1784 // we don't care that much about corpses and gibs
1785 g_Player_PreUpdate();
1786 g_Monsters_PreUpdate();
1787 g_Items_PreUpdate();
1788 g_Weapon_PreUpdate();
1791 procedure g_Game_Update();
1793 Msg
: g_gui
.TMessage
;
1799 function sendMonsPos (mon
: TMonster
): Boolean;
1801 result
:= false; // don't stop
1802 // this will also reset "need-send" flag
1803 if mon
.gncNeedSend
then
1805 MH_SEND_MonsterPos(mon
.UID
);
1807 else if (mon
.MonsterType
= MONSTER_BARREL
) then
1809 if (mon
.GameVelX
<> 0) or (mon
.GameVelY
<> 0) then MH_SEND_MonsterPos(mon
.UID
);
1811 else if (mon
.MonsterState
<> MONSTATE_SLEEP
) then
1813 if (mon
.MonsterState
<> MONSTATE_DEAD
) or (mon
.GameVelX
<> 0) or (mon
.GameVelY
<> 0) then MH_SEND_MonsterPos(mon
.UID
);
1817 function sendMonsPosUnexpected (mon
: TMonster
): Boolean;
1819 result
:= false; // don't stop
1820 // this will also reset "need-send" flag
1821 if mon
.gncNeedSend
then MH_SEND_MonsterPos(mon
.UID
);
1824 function sendItemPos (it
: PItem
): Boolean;
1826 result
:= false; // don't stop
1829 MH_SEND_ItemPos(it
.myId
);
1830 it
.needSend
:= False;
1835 reliableUpdate
: Boolean;
1840 // Ïîðà âûêëþ÷àòü èãðó:
1841 if gExit
= EXIT_QUIT
then
1843 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1847 if gExit
= EXIT_QUIT
then
1851 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî
1852 // no need to, as we'll do it in event handler
1854 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1857 if (NetMode
= NET_NONE
) and (g_Game_IsNet
) and (gGameOn
or (gState
in [STATE_FOLD
, STATE_INTERCUSTOM
])) then
1859 gExit
:= EXIT_SIMPLE
;
1864 // process master server communications
1865 g_Net_Slist_Pulse();
1868 STATE_INTERSINGLE
, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1869 STATE_INTERCUSTOM
, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1870 STATE_INTERTEXT
, // Òåêñò ìåæäó óðîâíÿìè
1871 STATE_INTERPIC
: // Êàðòèíêà ìåæäó óðîâíÿìè
1873 if g_Game_IsNet
and g_Game_IsServer
then
1875 gInterTime
:= gInterTime
+ GAME_TICK
;
1876 a
:= Min((gInterEndTime
- gInterTime
) div 1000 + 1, 255);
1877 if a
<> gServInterTime
then
1879 gServInterTime
:= a
;
1880 MH_SEND_TimeSync(gServInterTime
);
1884 if (not g_Game_IsClient
) and
1888 e_KeyPressed(IK_RETURN
) or e_KeyPressed(IK_KPRETURN
) or e_KeyPressed(IK_SPACE
) or
1889 e_KeyPressed(VK_FIRE
) or e_KeyPressed(VK_OPEN
) or
1890 e_KeyPressed(JOY0_ATTACK
) or e_KeyPressed(JOY1_ATTACK
) or
1891 e_KeyPressed(JOY2_ATTACK
) or e_KeyPressed(JOY3_ATTACK
)
1893 and (not gJustChatted
) and (not gConsoleShow
) and (not gChatShow
)
1894 and (g_ActiveWindow
= nil)
1896 or (g_Game_IsNet
and ((gInterTime
> gInterEndTime
) or ((gInterReadyCount
>= NetClientCount
) and (NetClientCount
> 0))))
1899 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1900 g_Game_StopAllSounds(True);
1902 if gMapOnce
then // Ýòî áûë òåñò
1903 gExit
:= EXIT_SIMPLE
1905 if gNextMap
<> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1906 g_Game_ChangeMap(gNextMap
)
1907 else // Ñëåäóþùåé êàðòû íåò
1909 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
] then
1911 // Âûõîä â ãëàâíîå ìåíþ:
1913 g_GUI_ShowWindow('MainMenu');
1914 gMusic
.SetByName('MUSIC_MENU');
1916 gState
:= STATE_MENU
;
1919 // Ôèíàëüíàÿ êàðòèíêà:
1920 g_Game_ExecuteEvent('onwadend');
1922 if not gMusic
.SetByName('MUSIC_endmus') then
1923 gMusic
.SetByName('MUSIC_STDENDMUS');
1925 gState
:= STATE_ENDPIC
;
1927 g_Game_ExecuteEvent('ongameend');
1932 else if g_Game_IsClient
and
1935 e_KeyPressed(IK_RETURN
) or e_KeyPressed(IK_KPRETURN
) or e_KeyPressed(IK_SPACE
) or
1936 e_KeyPressed(VK_FIRE
) or e_KeyPressed(VK_OPEN
) or
1937 e_KeyPressed(JOY0_ATTACK
) or e_KeyPressed(JOY1_ATTACK
) or
1938 e_KeyPressed(JOY2_ATTACK
) or e_KeyPressed(JOY3_ATTACK
)
1940 and (not gJustChatted
) and (not gConsoleShow
) and (not gChatShow
)
1941 and (g_ActiveWindow
= nil)
1949 if gState
= STATE_INTERTEXT
then
1950 if InterText
.counter
> 0 then
1951 InterText
.counter
:= InterText
.counter
- 1;
1954 STATE_FOLD
: // Çàòóõàíèå ýêðàíà
1956 if EndingGameCounter
= 0 then
1958 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1959 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
] then
1961 gState
:= STATE_INTERCUSTOM
;
1962 InterReadyTime
:= -1;
1963 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then
1965 g_Game_ExecuteEvent('onwadend');
1966 if not gMusic
.SetByName('MUSIC_endmus') then
1967 gMusic
.SetByName('MUSIC_STDENDMUS');
1970 gMusic
.SetByName('MUSIC_ROUNDMUS');
1974 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1976 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 if (gSoundTriggerTime
> 8) then
2266 g_Game_UpdateTriggerSounds();
2267 gSoundTriggerTime
:= 0;
2271 Inc(gSoundTriggerTime
);
2274 if (NetMode
= NET_SERVER
) then
2276 Inc(NetTimeToUpdate
);
2277 Inc(NetTimeToReliable
);
2279 // send monster updates
2280 if (NetTimeToReliable
>= NetRelupdRate
) or (NetTimeToUpdate
>= NetUpdateRate
) then
2282 // send all monsters (periodic sync)
2283 reliableUpdate
:= (NetTimeToReliable
>= NetRelupdRate
);
2285 for I
:= 0 to High(gPlayers
) do
2287 if (gPlayers
[I
] <> nil) then MH_SEND_PlayerPos(reliableUpdate
, gPlayers
[I
].UID
);
2290 g_Mons_ForEach(sendMonsPos
);
2292 // update flags that aren't stationary
2293 if gGameSettings
.GameMode
= GM_CTF
then
2294 for I
:= FLAG_RED
to FLAG_BLUE
do
2295 if gFlags
[I
].NeedSend
then
2297 gFlags
[I
].NeedSend
:= False;
2301 // update items that aren't stationary
2302 g_Items_ForEachAlive(sendItemPos
);
2304 if reliableUpdate
then
2306 NetTimeToReliable
:= 0;
2307 NetTimeToUpdate
:= NetUpdateRate
;
2311 NetTimeToUpdate
:= 0;
2316 // send only mosters with some unexpected changes
2317 g_Mons_ForEach(sendMonsPosUnexpected
);
2320 // send unexpected platform changes
2321 g_Map_NetSendInterestingPanels();
2323 g_Net_Slist_ServerUpdate();
2325 if NetUseMaster then
2327 if (gTime >= NetTimeToMaster) or g_Net_Slist_IsConnectionInProgress then
2329 if (not g_Net_Slist_IsConnectionActive) then g_Net_Slist_Connect(false); // non-blocking connection to the master
2331 NetTimeToMaster := gTime + NetMasterRate;
2336 else if (NetMode
= NET_CLIENT
) then
2338 MC_SEND_PlayerPos();
2340 end; // if gameOn ...
2342 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
2343 if g_ActiveWindow
<> nil then
2345 w
:= e_GetFirstKeyPressed();
2347 if (w
<> IK_INVALID
) then
2349 Msg
.Msg
:= MESSAGE_DIKEY
;
2351 g_ActiveWindow
.OnMessage(Msg
);
2354 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
2355 if g_ActiveWindow
<> nil then
2356 g_ActiveWindow
.Update();
2358 // Íóæíî ñìåíèòü ðàçðåøåíèå:
2359 if gResolutionChange
then
2361 e_WriteLog('Changing resolution', TMsgType
.Notify
);
2362 g_Game_ChangeResolution(gRC_Width
, gRC_Height
, gRC_FullScreen
, gRC_Maximized
);
2363 gResolutionChange
:= False;
2364 g_ActiveWindow
:= nil;
2367 // Íóæíî ñìåíèòü ÿçûê:
2368 if gLanguageChange
then
2370 //e_WriteLog('Read language file', MSG_NOTIFY);
2371 //g_Language_Load(DataDir + gLanguage + '.txt');
2372 g_Language_Set(gLanguage
);
2376 gLanguageChange
:= False;
2380 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
2381 if e_KeyPressed(IK_F10
) and
2383 (not gConsoleShow
) and
2384 (g_ActiveWindow
= nil) then
2389 Time
:= sys_GetTicks() {div 1000};
2391 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
2392 if gDelayedEvents
<> nil then
2393 for a
:= 0 to High(gDelayedEvents
) do
2394 if gDelayedEvents
[a
].Pending
and
2396 ((gDelayedEvents
[a
].DEType
= DE_GLOBEVENT
) and (gDelayedEvents
[a
].Time
<= Time
)) or
2397 ((gDelayedEvents
[a
].DEType
> DE_GLOBEVENT
) and (gDelayedEvents
[a
].Time
<= gTime
))
2400 case gDelayedEvents
[a
].DEType
of
2402 g_Game_ExecuteEvent(gDelayedEvents
[a
].DEStr
);
2405 g_Game_Announce_GoodShot(gDelayedEvents
[a
].DENum
);
2409 g_Game_Announce_KillCombo(gDelayedEvents
[a
].DENum
);
2410 if g_Game_IsNet
and g_Game_IsServer
then
2411 MH_SEND_GameEvent(NET_EV_KILLCOMBO
, gDelayedEvents
[a
].DENum
);
2415 g_Game_Announce_BodyKill(gDelayedEvents
[a
].DENum
);
2417 gDelayedEvents
[a
].Pending
:= False;
2420 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2421 UPSCounter
:= UPSCounter
+ 1;
2422 if Time
- UPSTime
>= 1000 then
2431 g_Weapon_AddDynLights();
2432 g_Items_AddDynLights();
2436 procedure g_Game_LoadChatSounds(Resource
: string);
2439 FileName
, Snd
: string;
2441 len
, cnt
, tags
, i
, j
: Integer;
2444 FileName
:= g_ExtractWadName(Resource
);
2446 WAD
:= TWADFile
.Create();
2447 WAD
.ReadFile(FileName
);
2449 if not WAD
.GetResource(g_ExtractFilePathName(Resource
), p
, len
) then
2456 cfg
:= TConfig
.CreateMem(p
, len
);
2457 cnt
:= cfg
.ReadInt('ChatSounds', 'Count', 0);
2459 SetLength(gChatSounds
, cnt
);
2460 for i
:= 0 to Length(gChatSounds
) - 1 do
2462 gChatSounds
[i
].Sound
:= nil;
2463 Snd
:= Trim(cfg
.ReadStr(IntToStr(i
), 'Sound', ''));
2464 tags
:= cfg
.ReadInt(IntToStr(i
), 'Tags', 0);
2465 if (Snd
= '') or (Tags
<= 0) then
2467 g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i
), GameWAD
+':'+Snd
);
2468 gChatSounds
[i
].Sound
:= TPlayableSound
.Create();
2469 gChatSounds
[i
].Sound
.SetByName('SOUND_CHAT_MACRO' + IntToStr(i
));
2470 SetLength(gChatSounds
[i
].Tags
, tags
);
2471 for j
:= 0 to tags
- 1 do
2472 gChatSounds
[i
].Tags
[j
] := toLowerCase1251(cfg
.ReadStr(IntToStr(i
), 'Tag' + IntToStr(j
), ''));
2473 gChatSounds
[i
].FullWord
:= cfg
.ReadBool(IntToStr(i
), 'FullWord', False);
2480 procedure g_Game_FreeChatSounds();
2484 for i
:= 0 to Length(gChatSounds
) - 1 do
2486 gChatSounds
[i
].Sound
.Free();
2487 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i
));
2489 SetLength(gChatSounds
, 0);
2493 procedure g_Game_LoadData();
2500 if DataLoaded
then Exit
;
2502 e_WriteLog('Loading game data...', TMsgType
.Notify
);
2504 g_Texture_CreateWADEx('NOTEXTURE', GameWAD
+':TEXTURES\NOTEXTURE');
2505 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD
+':TEXTURES\HUD');
2506 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD
+':TEXTURES\AIRBAR');
2507 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD
+':TEXTURES\JETBAR');
2508 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD
+':TEXTURES\HUDBG');
2509 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD
+':TEXTURES\ARMORHUD');
2510 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD
+':TEXTURES\FLAGHUD_R_BASE');
2511 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD
+':TEXTURES\FLAGHUD_R_STOLEN');
2512 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD
+':TEXTURES\FLAGHUD_R_DROP');
2513 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD
+':TEXTURES\FLAGHUD_B_BASE');
2514 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD
+':TEXTURES\FLAGHUD_B_STOLEN');
2515 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD
+':TEXTURES\FLAGHUD_B_DROP');
2516 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD
+':TEXTURES\TALKBUBBLE');
2517 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD
+':TEXTURES\PENTA');
2518 g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD
+':TEXTURES\PLRIND');
2521 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD
+':TEXTURES\LLEFT') then hasPBarGfx
:= false;
2522 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD
+':TEXTURES\LMARKER') then hasPBarGfx
:= false;
2523 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD
+':TEXTURES\LMIDDLE') then hasPBarGfx
:= false;
2524 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD
+':TEXTURES\LRIGHT') then hasPBarGfx
:= false;
2528 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl
, hl
);
2529 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr
, hr
);
2530 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb
, hb
);
2531 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm
, hm
);
2532 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
2538 hasPBarGfx
:= false;
2542 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD
+':TEXTURES\TELEPORT', 64, 64, 10, False);
2543 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD
+':WEAPONS\PUNCH', 64, 64, 4, False);
2544 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD
+':WEAPONS\PUNCH_UP', 64, 64, 4, False);
2545 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD
+':WEAPONS\PUNCH_DN', 64, 64, 4, False);
2546 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD
+':WEAPONS\PUNCHB', 64, 64, 4, False);
2547 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD
+':WEAPONS\PUNCHB_UP', 64, 64, 4, False);
2548 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD
+':WEAPONS\PUNCHB_DN', 64, 64, 4, False);
2549 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD
+':SOUNDS\TELEPORT');
2550 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD
+':SOUNDS\NOTELEPORT');
2551 g_Sound_CreateWADEx('SOUND_GAME_SECRET', GameWAD
+':SOUNDS\SECRET');
2552 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD
+':SOUNDS\DOOROPEN');
2553 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD
+':SOUNDS\DOORCLOSE');
2554 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD
+':SOUNDS\BULK1');
2555 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD
+':SOUNDS\BULK2');
2556 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD
+':SOUNDS\BUBBLE1');
2557 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD
+':SOUNDS\BUBBLE2');
2558 g_Sound_CreateWADEx('SOUND_GAME_BURNING', GameWAD
+':SOUNDS\BURNING');
2559 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD
+':SOUNDS\SWITCH1');
2560 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD
+':SOUNDS\SWITCH0');
2561 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD
+':SOUNDS\RADIO');
2562 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD
+':SOUNDS\GOOD1');
2563 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD
+':SOUNDS\GOOD2');
2564 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD
+':SOUNDS\GOOD3');
2565 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD
+':SOUNDS\GOOD4');
2566 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD
+':SOUNDS\KILL2X');
2567 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD
+':SOUNDS\KILL3X');
2568 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD
+':SOUNDS\KILL4X');
2569 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD
+':SOUNDS\KILLMX');
2570 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD
+':SOUNDS\MUHAHA1');
2571 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD
+':SOUNDS\MUHAHA2');
2572 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD
+':SOUNDS\MUHAHA3');
2573 g_Sound_CreateWADEx('SOUND_CTF_GET1', GameWAD
+':SOUNDS\GETFLAG1');
2574 g_Sound_CreateWADEx('SOUND_CTF_GET2', GameWAD
+':SOUNDS\GETFLAG2');
2575 g_Sound_CreateWADEx('SOUND_CTF_LOST1', GameWAD
+':SOUNDS\LOSTFLG1');
2576 g_Sound_CreateWADEx('SOUND_CTF_LOST2', GameWAD
+':SOUNDS\LOSTFLG2');
2577 g_Sound_CreateWADEx('SOUND_CTF_RETURN1', GameWAD
+':SOUNDS\RETFLAG1');
2578 g_Sound_CreateWADEx('SOUND_CTF_RETURN2', GameWAD
+':SOUNDS\RETFLAG2');
2579 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE1', GameWAD
+':SOUNDS\CAPFLAG1');
2580 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE2', GameWAD
+':SOUNDS\CAPFLAG2');
2582 goodsnd
[0] := TPlayableSound
.Create();
2583 goodsnd
[1] := TPlayableSound
.Create();
2584 goodsnd
[2] := TPlayableSound
.Create();
2585 goodsnd
[3] := TPlayableSound
.Create();
2587 goodsnd
[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2588 goodsnd
[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2589 goodsnd
[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2590 goodsnd
[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2592 killsnd
[0] := TPlayableSound
.Create();
2593 killsnd
[1] := TPlayableSound
.Create();
2594 killsnd
[2] := TPlayableSound
.Create();
2595 killsnd
[3] := TPlayableSound
.Create();
2597 killsnd
[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2598 killsnd
[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2599 killsnd
[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2600 killsnd
[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2602 hahasnd
[0] := TPlayableSound
.Create();
2603 hahasnd
[1] := TPlayableSound
.Create();
2604 hahasnd
[2] := TPlayableSound
.Create();
2606 hahasnd
[0].SetByName('SOUND_ANNOUNCER_MUHAHA1');
2607 hahasnd
[1].SetByName('SOUND_ANNOUNCER_MUHAHA2');
2608 hahasnd
[2].SetByName('SOUND_ANNOUNCER_MUHAHA3');
2610 sound_get_flag
[0] := TPlayableSound
.Create();
2611 sound_get_flag
[1] := TPlayableSound
.Create();
2612 sound_lost_flag
[0] := TPlayableSound
.Create();
2613 sound_lost_flag
[1] := TPlayableSound
.Create();
2614 sound_ret_flag
[0] := TPlayableSound
.Create();
2615 sound_ret_flag
[1] := TPlayableSound
.Create();
2616 sound_cap_flag
[0] := TPlayableSound
.Create();
2617 sound_cap_flag
[1] := TPlayableSound
.Create();
2619 sound_get_flag
[0].SetByName('SOUND_CTF_GET1');
2620 sound_get_flag
[1].SetByName('SOUND_CTF_GET2');
2621 sound_lost_flag
[0].SetByName('SOUND_CTF_LOST1');
2622 sound_lost_flag
[1].SetByName('SOUND_CTF_LOST2');
2623 sound_ret_flag
[0].SetByName('SOUND_CTF_RETURN1');
2624 sound_ret_flag
[1].SetByName('SOUND_CTF_RETURN2');
2625 sound_cap_flag
[0].SetByName('SOUND_CTF_CAPTURE1');
2626 sound_cap_flag
[1].SetByName('SOUND_CTF_CAPTURE2');
2628 g_Game_LoadChatSounds(GameWAD
+':CHATSND\SNDCFG');
2630 g_Game_SetLoadingText(_lc
[I_LOAD_ITEMS_DATA
], 0, False);
2633 g_Game_SetLoadingText(_lc
[I_LOAD_WEAPONS_DATA
], 0, False);
2634 g_Weapon_LoadData();
2636 g_Monsters_LoadData();
2641 procedure g_Game_FreeData();
2643 if not DataLoaded
then Exit
;
2646 g_Weapon_FreeData();
2647 g_Monsters_FreeData();
2649 e_WriteLog('Releasing game data...', TMsgType
.Notify
);
2651 g_Texture_Delete('NOTEXTURE');
2652 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2653 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2654 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2655 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2656 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2657 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2658 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2659 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2660 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2661 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2662 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2663 g_Frames_DeleteByName('FRAMES_TELEPORT');
2664 g_Frames_DeleteByName('FRAMES_PUNCH');
2665 g_Frames_DeleteByName('FRAMES_PUNCH_UP');
2666 g_Frames_DeleteByName('FRAMES_PUNCH_DN');
2667 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK');
2668 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP');
2669 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
2670 g_Sound_Delete('SOUND_GAME_TELEPORT');
2671 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2672 g_Sound_Delete('SOUND_GAME_SECRET');
2673 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2674 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2675 g_Sound_Delete('SOUND_GAME_BULK1');
2676 g_Sound_Delete('SOUND_GAME_BULK2');
2677 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2678 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2679 g_Sound_Delete('SOUND_GAME_BURNING');
2680 g_Sound_Delete('SOUND_GAME_SWITCH1');
2681 g_Sound_Delete('SOUND_GAME_SWITCH0');
2688 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2689 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2690 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2691 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2698 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2699 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2700 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2701 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2707 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1');
2708 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2');
2709 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3');
2711 sound_get_flag
[0].Free();
2712 sound_get_flag
[1].Free();
2713 sound_lost_flag
[0].Free();
2714 sound_lost_flag
[1].Free();
2715 sound_ret_flag
[0].Free();
2716 sound_ret_flag
[1].Free();
2717 sound_cap_flag
[0].Free();
2718 sound_cap_flag
[1].Free();
2720 g_Sound_Delete('SOUND_CTF_GET1');
2721 g_Sound_Delete('SOUND_CTF_GET2');
2722 g_Sound_Delete('SOUND_CTF_LOST1');
2723 g_Sound_Delete('SOUND_CTF_LOST2');
2724 g_Sound_Delete('SOUND_CTF_RETURN1');
2725 g_Sound_Delete('SOUND_CTF_RETURN2');
2726 g_Sound_Delete('SOUND_CTF_CAPTURE1');
2727 g_Sound_Delete('SOUND_CTF_CAPTURE2');
2729 g_Game_FreeChatSounds();
2731 DataLoaded
:= False;
2734 procedure DrawCustomStat();
2740 ww2
, hh2
, r
, g
, b
, rr
, gg
, bb
: Byte;
2741 s1
, s2
, topstr
: String;
2743 e_TextureFontGetSize(gStdFont
, ww2
, hh2
);
2747 if g_Console_Action(ACTION_SCORES
) then
2749 if not gStatsPressed
then
2751 gStatsOff
:= not gStatsOff
;
2752 gStatsPressed
:= True;
2756 gStatsPressed
:= False;
2760 s1
:= _lc
[I_MENU_INTER_NOTICE_TAB
];
2761 w
:= (Length(s1
) * ww2
) div 2;
2762 x
:= gScreenWidth
div 2 - w
;
2764 e_TextureFontPrint(x
, y
, s1
, gStdFont
);
2768 if (gGameSettings
.GameMode
= GM_COOP
) then
2770 if gMissionFailed
then
2771 topstr
:= _lc
[I_MENU_INTER_MISSION_FAIL
]
2773 topstr
:= _lc
[I_MENU_INTER_LEVEL_COMPLETE
];
2776 topstr
:= _lc
[I_MENU_INTER_ROUND_OVER
];
2778 e_CharFont_GetSize(gMenuFont
, topstr
, ww1
, hh1
);
2779 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(ww1
div 2), 16, topstr
);
2781 if g_Game_IsNet
then
2783 topstr
:= Format(_lc
[I_MENU_INTER_NOTICE_TIME
], [gServInterTime
]);
2784 if not gChatShow
then
2785 e_TextureFontPrintEx((gScreenWidth
div 2)-(Length(topstr
)*ww2
div 2),
2786 gScreenHeight
-(hh2
+4)*2, topstr
, gStdFont
, 255, 255, 255, 1);
2789 if g_Game_IsClient
then
2790 topstr
:= _lc
[I_MENU_INTER_NOTICE_MAP
]
2792 topstr
:= _lc
[I_MENU_INTER_NOTICE_SPACE
];
2793 if not gChatShow
then
2794 e_TextureFontPrintEx((gScreenWidth
div 2)-(Length(topstr
)*ww2
div 2),
2795 gScreenHeight
-(hh2
+4), topstr
, gStdFont
, 255, 255, 255, 1);
2800 w
:= gScreenWidth
-x
*2;
2806 e_DrawFillQuad(x
, y
, gScreenWidth
-x
-1, gScreenHeight
-y
-1, 64, 64, 64, 32);
2807 e_DrawQuad(x
, y
, gScreenWidth
-x
-1, gScreenHeight
-y
-1, 255, 127, 0);
2809 m
:= Max(Length(_lc
[I_MENU_MAP
])+1, Length(_lc
[I_GAME_GAME_TIME
])+1)*ww2
;
2811 case CustomStat
.GameMode
of
2814 if gGameSettings
.MaxLives
= 0 then
2815 s1
:= _lc
[I_GAME_DM
]
2817 s1
:= _lc
[I_GAME_LMS
];
2821 if gGameSettings
.MaxLives
= 0 then
2822 s1
:= _lc
[I_GAME_TDM
]
2824 s1
:= _lc
[I_GAME_TLMS
];
2826 GM_CTF
: s1
:= _lc
[I_GAME_CTF
];
2829 if gGameSettings
.MaxLives
= 0 then
2830 s1
:= _lc
[I_GAME_COOP
]
2832 s1
:= _lc
[I_GAME_SURV
];
2838 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
2842 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_MENU_MAP
], gStdFont
, 255, 127, 0, 1);
2843 e_TextureFontPrint(x
+8+m
, _y
, Format('%s - %s', [CustomStat
.Map
, CustomStat
.MapName
]), gStdFont
);
2846 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_GAME_TIME
], gStdFont
, 255, 127, 0, 1);
2847 e_TextureFontPrint(x
+8+m
, _y
, Format('%d:%.2d:%.2d', [CustomStat
.GameTime
div 1000 div 3600,
2848 (CustomStat
.GameTime
div 1000 div 60) mod 60,
2849 CustomStat
.GameTime
div 1000 mod 60]), gStdFont
);
2851 pc
:= Length(CustomStat
.PlayerStat
);
2852 if pc
= 0 then Exit
;
2854 if CustomStat
.GameMode
= GM_COOP
then
2856 m
:= Max(Length(_lc
[I_GAME_MONSTERS
])+1, Length(_lc
[I_GAME_SECRETS
])+1)*ww2
;
2858 s2
:= _lc
[I_GAME_MONSTERS
];
2859 e_TextureFontPrintEx(x
+8, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2860 e_TextureFontPrintEx(x
+8+m
, _y
, IntToStr(gCoopMonstersKilled
) + '/' + IntToStr(gTotalMonsters
), gStdFont
, 255, 255, 255, 1);
2862 s2
:= _lc
[I_GAME_SECRETS
];
2863 e_TextureFontPrintEx(x
+8, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2864 e_TextureFontPrintEx(x
+8+m
, _y
, IntToStr(gCoopSecretsFound
) + '/' + IntToStr(gSecretsCount
), gStdFont
, 255, 255, 255, 1);
2867 m
:= Max(Length(_lc
[I_GAME_MONSTERS_TOTAL
])+1, Length(_lc
[I_GAME_SECRETS_TOTAL
])+1)*ww2
;
2869 s2
:= _lc
[I_GAME_MONSTERS_TOTAL
];
2870 e_TextureFontPrintEx(x
+250, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2871 e_TextureFontPrintEx(x
+250+m
, _y
, IntToStr(gCoopTotalMonstersKilled
) + '/' + IntToStr(gCoopTotalMonsters
), gStdFont
, 255, 255, 255, 1);
2873 s2
:= _lc
[I_GAME_SECRETS_TOTAL
];
2874 e_TextureFontPrintEx(x
+250, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2875 e_TextureFontPrintEx(x
+250+m
, _y
, IntToStr(gCoopTotalSecretsFound
) + '/' + IntToStr(gCoopTotalSecrets
), gStdFont
, 255, 255, 255, 1);
2879 if CustomStat
.GameMode
in [GM_TDM
, GM_CTF
] then
2884 if TeamStat
[TEAM_RED
].Score
> TeamStat
[TEAM_BLUE
].Score
then s1
:= _lc
[I_GAME_WIN_RED
]
2885 else if TeamStat
[TEAM_BLUE
].Score
> TeamStat
[TEAM_RED
].Score
then s1
:= _lc
[I_GAME_WIN_BLUE
]
2886 else s1
:= _lc
[I_GAME_WIN_DRAW
];
2888 e_TextureFontPrintEx(x
+8+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
2891 for t
:= TEAM_RED
to TEAM_BLUE
do
2893 if t
= TEAM_RED
then
2895 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_TEAM_RED
],
2896 gStdFont
, 255, 0, 0, 1);
2897 e_TextureFontPrintEx(x
+w1
+8, _y
, IntToStr(CustomStat
.TeamStat
[TEAM_RED
].Score
),
2898 gStdFont
, 255, 0, 0, 1);
2905 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_TEAM_BLUE
],
2906 gStdFont
, 0, 0, 255, 1);
2907 e_TextureFontPrintEx(x
+w1
+8, _y
, IntToStr(CustomStat
.TeamStat
[TEAM_BLUE
].Score
),
2908 gStdFont
, 0, 0, 255, 1);
2914 e_DrawLine(1, x
+8, _y
+20, x
-8+w
, _y
+20, r
, g
, b
);
2917 for p
:= 0 to High(CustomStat
.PlayerStat
) do
2918 if CustomStat
.PlayerStat
[p
].Team
= t
then
2919 with CustomStat
.PlayerStat
[p
] do
2933 if (gPlayers
[Num
] <> nil) and (gPlayers
[Num
].FReady
) then
2934 e_TextureFontPrintEx(x
+16, _y
, Name
+ ' *', gStdFont
, rr
, gg
, bb
, 1)
2936 e_TextureFontPrintEx(x
+16, _y
, Name
, gStdFont
, rr
, gg
, bb
, 1);
2937 e_TextureFontPrintEx(x
+w1
+16, _y
, IntToStr(Frags
), gStdFont
, rr
, gg
, bb
, 1);
2938 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
, IntToStr(Deaths
), gStdFont
, rr
, gg
, bb
, 1);
2945 else if CustomStat
.GameMode
in [GM_DM
, GM_COOP
] then
2948 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_PLAYER_NAME
], gStdFont
, 255, 127, 0, 1);
2949 e_TextureFontPrintEx(x
+8+w1
, _y
, _lc
[I_GAME_FRAGS
], gStdFont
, 255, 127, 0, 1);
2950 e_TextureFontPrintEx(x
+8+w1
+w2
, _y
, _lc
[I_GAME_DEATHS
], gStdFont
, 255, 127, 0, 1);
2953 for p
:= 0 to High(CustomStat
.PlayerStat
) do
2954 with CustomStat
.PlayerStat
[p
] do
2956 e_DrawFillQuad(x
+8, _y
+4, x
+24-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
2963 if (gPlayers
[Num
] <> nil) and (gPlayers
[Num
].FReady
) then
2964 e_TextureFontPrintEx(x
+8+16+8, _y
+4, Name
+ ' *', gStdFont
, r
, r
, r
, 1, True)
2966 e_TextureFontPrintEx(x
+8+16+8, _y
+4, Name
, gStdFont
, r
, r
, r
, 1, True);
2967 e_TextureFontPrintEx(x
+w1
+8+16+8, _y
+4, IntToStr(Frags
), gStdFont
, r
, r
, r
, 1, True);
2968 e_TextureFontPrintEx(x
+w1
+w2
+8+16+8, _y
+4, IntToStr(Deaths
), gStdFont
, r
, r
, r
, 1, True);
2973 // HACK: take stats screenshot immediately after the first frame of the stats showing
2974 if gScreenshotStats
and (not StatShotDone
) and (Length(CustomStat
.PlayerStat
) > 1) then
2976 g_TakeScreenShot('stats/' + StatFilename
);
2977 StatShotDone
:= True;
2981 procedure DrawSingleStat();
2983 tm
, key_x
, val_x
, y
: Integer;
2987 procedure player_stat(n
: Integer);
2993 s1
:= Format(' %d ', [SingleStat
.PlayerStat
[n
].Kills
]);
2994 s2
:= Format(' %d', [gTotalMonsters
]);
2996 e_CharFont_Print(gMenuFont
, key_x
, y
, _lc
[I_MENU_INTER_KILLS
]);
2997 e_CharFont_PrintEx(gMenuFont
, val_x
, y
, s1
, _RGB(255, 0, 0));
2998 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
2999 e_CharFont_Print(gMenuFont
, val_x
+w1
, y
, '/');
3001 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3002 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
, s2
, _RGB(255, 0, 0));
3004 // "Kills-per-minute: ##.#":
3005 s1
:= _lc
[I_MENU_INTER_KPM
];
3007 kpm
:= (SingleStat
.PlayerStat
[n
].Kills
/ tm
) * 60
3009 kpm
:= SingleStat
.PlayerStat
[n
].Kills
;
3010 s2
:= Format(' %.1f', [kpm
]);
3012 e_CharFont_Print(gMenuFont
, key_x
, y
+32, s1
);
3013 e_CharFont_PrintEx(gMenuFont
, val_x
, y
+32, s2
, _RGB(255, 0, 0));
3015 // "Secrets found: # / #":
3016 s1
:= Format(' %d ', [SingleStat
.PlayerStat
[n
].Secrets
]);
3017 s2
:= Format(' %d', [SingleStat
.TotalSecrets
]);
3019 e_CharFont_Print(gMenuFont
, key_x
, y
+64, _lc
[I_MENU_INTER_SECRETS
]);
3020 e_CharFont_PrintEx(gMenuFont
, val_x
, y
+64, s1
, _RGB(255, 0, 0));
3021 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3022 e_CharFont_Print(gMenuFont
, val_x
+w1
, y
+64, '/');
3024 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3025 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
+64, s2
, _RGB(255, 0, 0));
3029 // "Level Complete":
3030 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_INTER_LEVEL_COMPLETE
], w1
, h
);
3031 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 32, _lc
[I_MENU_INTER_LEVEL_COMPLETE
]);
3033 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
3034 s1
:= _lc
[I_MENU_INTER_KPM
];
3035 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3038 e_CharFont_GetSize(gMenuFont
, s1
, w2
, h
);
3040 key_x
:= (gScreenWidth
-w1
-w2
) div 2;
3041 val_x
:= key_x
+ w1
;
3044 tm
:= SingleStat
.GameTime
div 1000;
3045 s1
:= _lc
[I_MENU_INTER_TIME
];
3046 s2
:= Format(' %d:%.2d:%.2d', [tm
div (60*60), (tm
mod (60*60)) div 60, tm
mod 60]);
3048 e_CharFont_Print(gMenuFont
, key_x
, 80, s1
);
3049 e_CharFont_PrintEx(gMenuFont
, val_x
, 80, s2
, _RGB(255, 0, 0));
3051 if SingleStat
.TwoPlayers
then
3054 s1
:= _lc
[I_MENU_PLAYER_1
];
3055 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3056 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 128, s1
);
3058 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3063 s1
:= _lc
[I_MENU_PLAYER_2
];
3064 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3065 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 288, s1
);
3067 // Ñòàòèñòèêà âòîðîãî èãðîêà:
3073 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3079 procedure DrawLoadingStat();
3080 procedure drawRect (x
, y
, w
, h
: Integer);
3082 if (w
< 1) or (h
< 1) then exit
;
3084 glVertex2f(x
+0.375, y
+0.375);
3085 glVertex2f(x
+w
+0.375, y
+0.375);
3086 glVertex2f(x
+w
+0.375, y
+h
+0.375);
3087 glVertex2f(x
+0.375, y
+h
+0.375);
3091 function drawPBar (cur
, total
: Integer; washere
: Boolean): Boolean;
3093 rectW
, rectH
: Integer;
3100 idl
, idr
, idb
, idm
: LongWord
;
3104 if (total
< 1) then exit
;
3105 if (cur
< 1) then exit
; // don't blink
3106 if (not washere
) and (cur
>= total
) then exit
; // don't blink
3107 //if (cur < 0) then cur := 0;
3108 //if (cur > total) then cur := total;
3111 if (hasPBarGfx
) then
3113 g_Texture_Get('UI_GFX_PBAR_LEFT', idl
);
3114 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl
, hl
);
3115 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr
);
3116 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr
, hr
);
3117 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb
);
3118 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb
, hb
);
3119 g_Texture_Get('UI_GFX_PBAR_MARKER', idm
);
3120 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm
, hm
);
3122 //rectW := gScreenWidth-360;
3123 rectW
:= trunc(624.0*gScreenWidth
/1024.0);
3126 x0
:= (gScreenWidth
-rectW
) div 2;
3127 y0
:= gScreenHeight
-rectH
-64;
3128 if (y0
< 2) then y0
:= 2;
3130 glEnable(GL_SCISSOR_TEST
);
3133 glScissor(x0
, gScreenHeight
-y0
-rectH
, rectW
, rectH
);
3134 e_DrawSize(idl
, x0
, y0
, 0, true, false, wl
, hl
);
3135 e_DrawSize(idr
, x0
+rectW
-wr
, y0
, 0, true, false, wr
, hr
);
3138 glScissor(x0
+wl
, gScreenHeight
-y0
-rectH
, rectW
-wl
-wr
, rectH
);
3140 while (f
< x0
+rectW
) do
3142 e_DrawSize(idb
, f
, y0
, 0, true, false, wb
, hb
);
3147 wdt
:= (rectW
-wl
-wr
)*cur
div total
;
3148 if (wdt
> rectW
-wl
-wr
) then wdt
:= rectW
-wr
-wr
;
3151 my
:= y0
; // don't be so smart, ketmar: +(rectH-wm) div 2;
3152 glScissor(x0
+wl
, gScreenHeight
-my
-rectH
, wdt
, hm
);
3156 e_DrawSize(idm
, f
, y0
, 0, true, false, wm
, hm
);
3162 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
3166 rectW
:= gScreenWidth
-64;
3169 x0
:= (gScreenWidth
-rectW
) div 2;
3170 y0
:= gScreenHeight
-rectH
-64;
3171 if (y0
< 2) then y0
:= 2;
3173 glDisable(GL_BLEND
);
3174 glDisable(GL_TEXTURE_2D
);
3176 //glClearColor(0, 0, 0, 0);
3177 //glClear(GL_COLOR_BUFFER_BIT);
3179 glColor4ub(127, 127, 127, 255);
3180 drawRect(x0
-2, y0
-2, rectW
+4, rectH
+4);
3182 glColor4ub(0, 0, 0, 255);
3183 drawRect(x0
-1, y0
-1, rectW
+2, rectH
+2);
3185 glColor4ub(127, 127, 127, 255);
3186 wdt
:= rectW
*cur
div total
;
3187 if (wdt
> rectW
) then wdt
:= rectW
;
3188 drawRect(x0
, y0
, wdt
, rectH
);
3197 if (Length(LoadingStat
.Msgs
) = 0) then exit
;
3199 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_LOADING
], ww
, hh
);
3200 yy
:= (gScreenHeight
div 3);
3201 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(ww
div 2), yy
-2*hh
, _lc
[I_MENU_LOADING
]);
3202 xx
:= (gScreenWidth
div 3);
3206 for i
:= 0 to NextMsg
-1 do
3208 if (i
= (NextMsg
-1)) and (MaxValue
> 0) then
3209 s
:= Format('%s: %d/%d', [Msgs
[i
], CurValue
, MaxValue
])
3213 e_CharFont_PrintEx(gMenuSmallFont
, xx
, yy
, s
, _RGB(255, 0, 0));
3214 yy
:= yy
+ LOADING_INTERLINE
;
3215 PBarWasHere
:= drawPBar(CurValue
, MaxValue
, PBarWasHere
);
3220 procedure DrawMenuBackground(tex
: AnsiString
);
3226 if g_Texture_Get(tex
, ID
) then
3228 e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
3229 e_GetTextureSize(ID
, @w
, @h
);
3231 w
:= round(w
* 1.333 * (gScreenHeight
/ h
))
3233 w
:= trunc(w
* (gScreenHeight
/ h
));
3234 e_DrawSize(ID
, (gScreenWidth
- w
) div 2, 0, 0, False, False, w
, gScreenHeight
);
3236 else e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
3239 procedure DrawMinimap(p
: TPlayer
; RenderRect
: e_graphics
.TRect
);
3241 a
, aX
, aY
, aX2
, aY2
, Scale
, ScaleSz
: Integer;
3243 function monDraw (mon
: TMonster
): Boolean;
3245 result
:= false; // don't stop
3250 // Ëåâûé âåðõíèé óãîë
3251 aX
:= Obj
.X
div ScaleSz
+ 1;
3252 aY
:= Obj
.Y
div ScaleSz
+ 1;
3254 aX2
:= max(Obj
.Rect
.Width
div ScaleSz
, 1);
3255 aY2
:= max(Obj
.Rect
.Height
div ScaleSz
, 1);
3256 // Ïðàâûé íèæíèé óãîë
3257 aX2
:= aX
+ aX2
- 1;
3258 aY2
:= aY
+ aY2
- 1;
3259 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 255, 0, 0);
3265 if (gMapInfo
.Width
> RenderRect
.Right
- RenderRect
.Left
) or
3266 (gMapInfo
.Height
> RenderRect
.Bottom
- RenderRect
.Top
) then
3269 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
3270 ScaleSz
:= 16 div Scale
;
3271 // Ðàçìåðû ìèíè-êàðòû:
3272 aX
:= max(gMapInfo
.Width
div ScaleSz
, 1);
3273 aY
:= max(gMapInfo
.Height
div ScaleSz
, 1);
3275 e_DrawFillQuad(0, 0, aX
-1, aY
-1, 0, 0, 0, 0);
3277 if gWalls
<> nil then
3280 for a
:= 0 to High(gWalls
) do
3282 if PanelType
<> 0 then
3284 // Ëåâûé âåðõíèé óãîë:
3285 aX
:= X
div ScaleSz
;
3286 aY
:= Y
div ScaleSz
;
3288 aX2
:= max(Width
div ScaleSz
, 1);
3289 aY2
:= max(Height
div ScaleSz
, 1);
3290 // Ïðàâûé íèæíèé óãîë:
3291 aX2
:= aX
+ aX2
- 1;
3292 aY2
:= aY
+ aY2
- 1;
3295 PANEL_WALL
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 208, 208, 208, 0);
3296 PANEL_OPENDOOR
, PANEL_CLOSEDOOR
:
3297 if Enabled
then e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 160, 160, 160, 0);
3301 if gSteps
<> nil then
3304 for a
:= 0 to High(gSteps
) do
3306 if PanelType
<> 0 then
3308 // Ëåâûé âåðõíèé óãîë:
3309 aX
:= X
div ScaleSz
;
3310 aY
:= Y
div ScaleSz
;
3312 aX2
:= max(Width
div ScaleSz
, 1);
3313 aY2
:= max(Height
div ScaleSz
, 1);
3314 // Ïðàâûé íèæíèé óãîë:
3315 aX2
:= aX
+ aX2
- 1;
3316 aY2
:= aY
+ aY2
- 1;
3318 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 128, 128, 128, 0);
3321 if gLifts
<> nil then
3324 for a
:= 0 to High(gLifts
) do
3326 if PanelType
<> 0 then
3328 // Ëåâûé âåðõíèé óãîë:
3329 aX
:= X
div ScaleSz
;
3330 aY
:= Y
div ScaleSz
;
3332 aX2
:= max(Width
div ScaleSz
, 1);
3333 aY2
:= max(Height
div ScaleSz
, 1);
3334 // Ïðàâûé íèæíèé óãîë:
3335 aX2
:= aX
+ aX2
- 1;
3336 aY2
:= aY
+ aY2
- 1;
3339 LIFTTYPE_UP
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 116, 72, 36, 0);
3340 LIFTTYPE_DOWN
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 116, 124, 96, 0);
3341 LIFTTYPE_LEFT
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 200, 80, 4, 0);
3342 LIFTTYPE_RIGHT
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 252, 140, 56, 0);
3346 if gWater
<> nil then
3349 for a
:= 0 to High(gWater
) do
3351 if PanelType
<> 0 then
3353 // Ëåâûé âåðõíèé óãîë:
3354 aX
:= X
div ScaleSz
;
3355 aY
:= Y
div ScaleSz
;
3357 aX2
:= max(Width
div ScaleSz
, 1);
3358 aY2
:= max(Height
div ScaleSz
, 1);
3359 // Ïðàâûé íèæíèé óãîë:
3360 aX2
:= aX
+ aX2
- 1;
3361 aY2
:= aY
+ aY2
- 1;
3363 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 0, 192, 0);
3366 if gAcid1
<> nil then
3368 // Ðèñóåì êèñëîòó 1:
3369 for a
:= 0 to High(gAcid1
) do
3371 if PanelType
<> 0 then
3373 // Ëåâûé âåðõíèé óãîë:
3374 aX
:= X
div ScaleSz
;
3375 aY
:= Y
div ScaleSz
;
3377 aX2
:= max(Width
div ScaleSz
, 1);
3378 aY2
:= max(Height
div ScaleSz
, 1);
3379 // Ïðàâûé íèæíèé óãîë:
3380 aX2
:= aX
+ aX2
- 1;
3381 aY2
:= aY
+ aY2
- 1;
3383 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 176, 0, 0);
3386 if gAcid2
<> nil then
3388 // Ðèñóåì êèñëîòó 2:
3389 for a
:= 0 to High(gAcid2
) do
3391 if PanelType
<> 0 then
3393 // Ëåâûé âåðõíèé óãîë:
3394 aX
:= X
div ScaleSz
;
3395 aY
:= Y
div ScaleSz
;
3397 aX2
:= max(Width
div ScaleSz
, 1);
3398 aY2
:= max(Height
div ScaleSz
, 1);
3399 // Ïðàâûé íèæíèé óãîë:
3400 aX2
:= aX
+ aX2
- 1;
3401 aY2
:= aY
+ aY2
- 1;
3403 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 176, 0, 0, 0);
3406 if gPlayers
<> nil then
3409 for a
:= 0 to High(gPlayers
) do
3410 if gPlayers
[a
] <> nil then with gPlayers
[a
] do
3412 // Ëåâûé âåðõíèé óãîë:
3413 aX
:= Obj
.X
div ScaleSz
+ 1;
3414 aY
:= Obj
.Y
div ScaleSz
+ 1;
3416 aX2
:= max(Obj
.Rect
.Width
div ScaleSz
, 1);
3417 aY2
:= max(Obj
.Rect
.Height
div ScaleSz
, 1);
3418 // Ïðàâûé íèæíèé óãîë:
3419 aX2
:= aX
+ aX2
- 1;
3420 aY2
:= aY
+ aY2
- 1;
3422 if gPlayers
[a
] = p
then
3423 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 255, 0, 0)
3426 TEAM_RED
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 0, 0, 0);
3427 TEAM_BLUE
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 0, 255, 0);
3428 else e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 128, 0, 0);
3433 g_Mons_ForEach(monDraw
);
3438 procedure renderAmbientQuad (hasAmbient
: Boolean; constref ambColor
: TDFColor
);
3440 if not hasAmbient
then exit
;
3441 e_AmbientQuad(sX
, sY
, sWidth
, sHeight
, ambColor
.r
, ambColor
.g
, ambColor
.b
, ambColor
.a
);
3445 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3446 //FIXME: broken for splitscreen mode
3447 procedure renderDynLightsInternal ();
3449 //hasAmbient: Boolean;
3450 //ambColor: TDFColor;
3452 lx
, ly
, lrad
: Integer;
3453 scxywh
: array[0..3] of GLint
;
3456 if e_NoGraphics
then exit
;
3458 //TODO: lights should be in separate grid, i think
3459 // but on the other side: grid may be slower for dynlights, as their lifetime is short
3460 if (not gwin_k8_enable_light_experiments
) or (not gwin_has_stencil
) or (g_dynLightCount
< 1) then exit
;
3463 //ambColor := gCurrentMap['light_ambient'].rgba;
3464 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3466 { // this will multiply incoming color to alpha from framebuffer
3468 glBlendFunc(GL_DST_ALPHA, GL_ONE);
3472 * light rendering: (INVALID!)
3473 * glStencilFunc(GL_EQUAL, 0, $ff);
3475 * glClear(GL_STENCIL_BUFFER_BIT);
3476 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3477 * draw shadow volume into stencil buffer
3478 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3479 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
3481 * draw color-less quad with light alpha (WARNING! don't touch color!)
3482 * glEnable(GL_BLEND);
3483 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
3484 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
3486 wassc
:= (glIsEnabled(GL_SCISSOR_TEST
) <> 0);
3487 if wassc
then glGetIntegerv(GL_SCISSOR_BOX
, @scxywh
[0]) else glGetIntegerv(GL_VIEWPORT
, @scxywh
[0]);
3489 // setup OpenGL parameters
3490 glStencilMask($FFFFFFFF);
3491 glStencilFunc(GL_ALWAYS
, 0, $FFFFFFFF);
3492 glEnable(GL_STENCIL_TEST
);
3493 glEnable(GL_SCISSOR_TEST
);
3494 glClear(GL_STENCIL_BUFFER_BIT
);
3495 glStencilFunc(GL_EQUAL
, 0, $ff);
3497 for lln
:= 0 to g_dynLightCount
-1 do
3499 lx
:= g_dynLights
[lln
].x
;
3500 ly
:= g_dynLights
[lln
].y
;
3501 lrad
:= g_dynLights
[lln
].radius
;
3502 if (lrad
< 3) then continue
;
3504 if (lx
-sX
+lrad
< 0) then continue
;
3505 if (ly
-sY
+lrad
< 0) then continue
;
3506 if (lx
-sX
-lrad
>= gPlayerScreenSize
.X
) then continue
;
3507 if (ly
-sY
-lrad
>= gPlayerScreenSize
.Y
) then continue
;
3509 // set scissor to optimize drawing
3510 if (g_dbg_scale
= 1.0) then
3512 glScissor((lx
-sX
)-lrad
+2, gPlayerScreenSize
.Y
-(ly
-sY
)-lrad
-1+2, lrad
*2-4, lrad
*2-4);
3516 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
3518 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3519 if (g_dbg_scale
<> 1.0) then glClear(GL_STENCIL_BUFFER_BIT
);
3520 glStencilOp(GL_KEEP
, GL_KEEP
, GL_INCR
);
3521 // draw extruded panels
3522 glDisable(GL_TEXTURE_2D
);
3523 glDisable(GL_BLEND
);
3524 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
); // no need to modify color buffer
3525 if (lrad
> 4) then g_Map_DrawPanelShadowVolumes(lx
, ly
, lrad
);
3526 // render light texture
3527 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
); // modify color buffer
3528 glStencilOp(GL_ZERO
, GL_ZERO
, GL_ZERO
); // draw light, and clear stencil buffer
3531 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
3532 glEnable(GL_TEXTURE_2D
);
3533 // color and opacity
3534 glColor4f(g_dynLights
[lln
].r
, g_dynLights
[lln
].g
, g_dynLights
[lln
].b
, g_dynLights
[lln
].a
);
3535 glBindTexture(GL_TEXTURE_2D
, g_Texture_Light());
3537 glTexCoord2f(0.0, 0.0); glVertex2i(lx
-lrad
, ly
-lrad
); // top-left
3538 glTexCoord2f(1.0, 0.0); glVertex2i(lx
+lrad
, ly
-lrad
); // top-right
3539 glTexCoord2f(1.0, 1.0); glVertex2i(lx
+lrad
, ly
+lrad
); // bottom-right
3540 glTexCoord2f(0.0, 1.0); glVertex2i(lx
-lrad
, ly
+lrad
); // bottom-left
3545 glDisable(GL_STENCIL_TEST
);
3546 glDisable(GL_BLEND
);
3547 glDisable(GL_SCISSOR_TEST
);
3548 //glScissor(0, 0, sWidth, sHeight);
3550 glScissor(scxywh
[0], scxywh
[1], scxywh
[2], scxywh
[3]);
3551 if wassc
then glEnable(GL_SCISSOR_TEST
) else glDisable(GL_SCISSOR_TEST
);
3555 function fixViewportForScale (): Boolean;
3557 nx0
, ny0
, nw
, nh
: Integer;
3560 if (g_dbg_scale
<> 1.0) then
3563 nx0
:= round(sX
-(gPlayerScreenSize
.X
-(sWidth
*g_dbg_scale
))/2/g_dbg_scale
);
3564 ny0
:= round(sY
-(gPlayerScreenSize
.Y
-(sHeight
*g_dbg_scale
))/2/g_dbg_scale
);
3565 nw
:= round(sWidth
/g_dbg_scale
);
3566 nh
:= round(sHeight
/g_dbg_scale
);
3575 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3576 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3577 procedure renderMapInternal (backXOfs
, backYOfs
: Integer; setTransMatrix
: Boolean);
3579 TDrawCB
= procedure ();
3582 hasAmbient
: Boolean;
3584 doAmbient
: Boolean = false;
3586 procedure drawPanelType (profname
: AnsiString
; panType
: DWord
; doDraw
: Boolean);
3591 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
3592 if gdbg_map_use_accel_render
then
3594 tagmask
:= panelTypeToTag(panType
);
3595 while (gDrawPanelList
.count
> 0) do
3597 pan
:= TPanel(gDrawPanelList
.front());
3598 if ((pan
.tag
and tagmask
) = 0) then break
;
3599 if doDraw
then pan
.Draw(doAmbient
, ambColor
);
3600 gDrawPanelList
.popFront();
3605 if doDraw
then g_Map_DrawPanels(panType
, hasAmbient
, ambColor
);
3607 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3610 procedure drawOther (profname
: AnsiString
; cb
: TDrawCB
);
3612 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
3613 if assigned(cb
) then cb();
3614 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3618 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('total');
3620 // our accelerated renderer will collect all panels to gDrawPanelList
3621 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3622 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('collect');
3623 if gdbg_map_use_accel_render
then
3625 g_Map_CollectDrawPanels(sX
, sY
, sWidth
, sHeight
);
3627 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3629 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('skyback');
3630 g_Map_DrawBack(backXOfs
, backYOfs
);
3631 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3633 if setTransMatrix
then
3635 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3636 glScalef(g_dbg_scale
, g_dbg_scale
, 1.0);
3637 glTranslatef(-sX
, -sY
, 0);
3641 ambColor
:= gCurrentMap
['light_ambient'].rgba
;
3642 hasAmbient
:= (not ambColor
.isOpaque
) or (not ambColor
.isBlack
);
3647 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3648 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3649 glClear(GL_COLOR_BUFFER_BIT);
3652 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3655 drawPanelType('*back', PANEL_BACK
, g_rlayer_back
);
3656 drawPanelType('*step', PANEL_STEP
, g_rlayer_step
);
3657 drawOther('items', @g_Items_Draw
);
3658 drawOther('weapons', @g_Weapon_Draw
);
3659 drawOther('shells', @g_Player_DrawShells
);
3660 drawOther('drawall', @g_Player_DrawAll
);
3661 drawOther('corpses', @g_Player_DrawCorpses
);
3662 drawPanelType('*wall', PANEL_WALL
, g_rlayer_wall
);
3663 drawOther('monsters', @g_Monsters_Draw
);
3664 drawOther('itemdrop', @g_Items_DrawDrop
);
3665 drawPanelType('*door', PANEL_CLOSEDOOR
, g_rlayer_door
);
3666 drawOther('gfx', @g_GFX_Draw
);
3667 drawOther('flags', @g_Map_DrawFlags
);
3668 drawPanelType('*acid1', PANEL_ACID1
, g_rlayer_acid1
);
3669 drawPanelType('*acid2', PANEL_ACID2
, g_rlayer_acid2
);
3670 drawPanelType('*water', PANEL_WATER
, g_rlayer_water
);
3671 drawOther('dynlights', @renderDynLightsInternal
);
3673 if hasAmbient
{and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3675 renderAmbientQuad(hasAmbient
, ambColor
);
3679 drawPanelType('*fore', PANEL_FORE
, g_rlayer_fore
);
3682 if g_debug_HealthBar
then
3684 g_Monsters_DrawHealth();
3685 g_Player_DrawHealth();
3688 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainEnd(); // map rendering
3692 procedure DrawMapView(x
, y
, w
, h
: Integer);
3699 bx
:= Round(x
/(gMapInfo
.Width
- w
)*(gBackSize
.X
- w
));
3700 by
:= Round(y
/(gMapInfo
.Height
- h
)*(gBackSize
.Y
- h
));
3707 fixViewportForScale();
3708 renderMapInternal(-bx
, -by
, true);
3714 procedure DrawPlayer(p
: TPlayer
);
3716 px
, py
, a
, b
, c
, d
, i
, fX
, fY
: Integer;
3720 if (p
= nil) or (p
.FDummy
) then
3723 g_Map_DrawBack(0, 0);
3728 if (profileFrameDraw
= nil) then profileFrameDraw
:= TProfiler
.Create('RENDER', g_profile_history_size
);
3729 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainBegin(g_profile_frame_draw
);
3735 camObj
:= p
.getCameraObj();
3736 camObj
.lerp(gLerpFactor
, fX
, fY
);
3737 px
:= fX
+ PLAYER_RECT_CX
;
3738 py
:= fY
+ PLAYER_RECT_CY
+nlerp(p
.SlopeOld
, camObj
.slopeUpLeft
, gLerpFactor
);
3740 if (g_dbg_scale
= 1.0) and (not g_dbg_ignore_bounds
) then
3742 if (px
> (gPlayerScreenSize
.X
div 2)) then a
:= -px
+(gPlayerScreenSize
.X
div 2) else a
:= 0;
3743 if (py
> (gPlayerScreenSize
.Y
div 2)) then b
:= -py
+(gPlayerScreenSize
.Y
div 2) else b
:= 0;
3745 if (px
> gMapInfo
.Width
-(gPlayerScreenSize
.X
div 2)) then a
:= -gMapInfo
.Width
+gPlayerScreenSize
.X
;
3746 if (py
> gMapInfo
.Height
-(gPlayerScreenSize
.Y
div 2)) then b
:= -gMapInfo
.Height
+gPlayerScreenSize
.Y
;
3748 if (gMapInfo
.Width
= gPlayerScreenSize
.X
) then a
:= 0
3749 else if (gMapInfo
.Width
< gPlayerScreenSize
.X
) then
3752 a
:= (gPlayerScreenSize
.X
-gMapInfo
.Width
) div 2;
3755 if (gMapInfo
.Height
= gPlayerScreenSize
.Y
) then b
:= 0
3756 else if (gMapInfo
.Height
< gPlayerScreenSize
.Y
) then
3759 b
:= (gPlayerScreenSize
.Y
-gMapInfo
.Height
) div 2;
3764 // scaled, ignore level bounds
3765 a
:= -px
+(gPlayerScreenSize
.X
div 2);
3766 b
:= -py
+(gPlayerScreenSize
.Y
div 2);
3771 sWidth
:= gPlayerScreenSize
.X
;
3772 sHeight
:= gPlayerScreenSize
.Y
;
3773 fixViewportForScale();
3775 i
:= py
- (sY
+ sHeight
div 2);
3776 if (p
.IncCam
> 0) then
3778 // clamp to level bounds
3779 if (sY
- p
.IncCam
< 0) then
3780 p
.IncCam
:= nclamp(sY
, 0, 120);
3781 // clamp around player position
3783 p
.IncCam
:= nclamp(p
.IncCam
, 0, max(0, 120 - i
));
3785 else if (p
.IncCam
< 0) then
3787 // clamp to level bounds
3788 if (sY
+ sHeight
- p
.IncCam
> gMapInfo
.Height
) then
3789 p
.IncCam
:= nclamp(sY
+ sHeight
- gMapInfo
.Height
, -120, 0);
3790 // clamp around player position
3792 p
.IncCam
:= nclamp(p
.IncCam
, min(0, -120 - i
), 0);
3795 sY
:= sY
- nlerp(p
.IncCamOld
, p
.IncCam
, gLerpFactor
);
3797 if (not g_dbg_ignore_bounds
) then
3799 if (sX
+sWidth
> gMapInfo
.Width
) then sX
:= gMapInfo
.Width
-sWidth
;
3800 if (sY
+sHeight
> gMapInfo
.Height
) then sY
:= gMapInfo
.Height
-sHeight
;
3801 if (sX
< 0) then sX
:= 0;
3802 if (sY
< 0) then sY
:= 0;
3805 if (gBackSize
.X
<= gPlayerScreenSize
.X
) or (gMapInfo
.Width
<= sWidth
) then c
:= 0 else c
:= trunc((gBackSize
.X
-gPlayerScreenSize
.X
)*sX
/(gMapInfo
.Width
-sWidth
));
3806 if (gBackSize
.Y
<= gPlayerScreenSize
.Y
) or (gMapInfo
.Height
<= sHeight
) then d
:= 0 else d
:= trunc((gBackSize
.Y
-gPlayerScreenSize
.Y
)*sY
/(gMapInfo
.Height
-sHeight
));
3808 //r_smallmap_h: 0: left; 1: center; 2: right
3809 //r_smallmap_v: 0: top; 1: center; 2: bottom
3811 if (gMapInfo
.Width
= sWidth
) then
3815 else if (gMapInfo
.Width
< sWidth
) then
3817 case r_smallmap_h
of
3818 1: sX
:= -((sWidth
-gMapInfo
.Width
) div 2); // center
3819 2: sX
:= -(sWidth
-gMapInfo
.Width
); // right
3820 else sX
:= 0; // left
3824 if (gMapInfo
.Height
= sHeight
) then
3828 else if (gMapInfo
.Height
< sHeight
) then
3830 case r_smallmap_v
of
3831 1: sY
:= -((sHeight
-gMapInfo
.Height
) div 2); // center
3832 2: sY
:= -(sHeight
-gMapInfo
.Height
); // bottom
3833 else sY
:= 0; // top
3839 p
.viewPortW
:= sWidth
;
3840 p
.viewPortH
:= sHeight
;
3842 {$IFDEF ENABLE_HOLMES}
3843 if (p
= gPlayer1
) then
3845 g_Holmes_plrViewPos(sX
, sY
);
3846 g_Holmes_plrViewSize(sWidth
, sHeight
);
3850 renderMapInternal(-c
, -d
, true);
3852 if (gGameSettings
.GameMode
<> GM_SINGLE
) and (gPlayerIndicator
> 0) then
3853 case gPlayerIndicator
of
3855 p
.DrawIndicator(_RGB(255, 255, 255));
3858 for i
:= 0 to High(gPlayers
) do
3859 if gPlayers
[i
] <> nil then
3860 if gPlayers
[i
] = p
then p
.DrawIndicator(_RGB(255, 255, 255))
3861 else if (gPlayers
[i
].Team
= p
.Team
) and (gPlayers
[i
].Team
<> TEAM_NONE
) then
3862 if gPlayerIndicatorStyle
= 1 then
3863 gPlayers
[i
].DrawIndicator(_RGB(192, 192, 192))
3864 else gPlayers
[i
].DrawIndicator(gPlayers
[i
].GetColor
);
3868 for a := 0 to High(gCollideMap) do
3869 for b := 0 to High(gCollideMap[a]) do
3872 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3874 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3878 1: e_DrawPoint(1, b, a, 200, 200, 200);
3879 2: e_DrawPoint(1, b, a, 64, 64, 255);
3880 3: e_DrawPoint(1, b, a, 255, 0, 255);
3890 if gShowMap
then DrawMinimap(p
, _TRect(0, 0, 128, 128));
3891 if g_Debug_Player
then
3892 g_Player_DrawDebug(p
);
3896 procedure drawProfilers ();
3901 if g_profile_frame_draw
and (profileFrameDraw
<> nil) then
3902 px
-= drawProfiles(px
, py
, profileFrameDraw
);
3904 if g_profile_collision
and (profMapCollision
<> nil) then
3906 px
-= drawProfiles(px
, py
, profMapCollision
);
3907 py
-= calcProfilesHeight(profMonsLOS
);
3910 if g_profile_los
and (profMonsLOS
<> nil) then
3912 px
-= drawProfiles(px
, py
, profMonsLOS
);
3913 py
-= calcProfilesHeight(profMonsLOS
);
3917 procedure g_Game_Draw();
3924 plView1
, plView2
: TPlayer
;
3927 if gExit
= EXIT_QUIT
then Exit
;
3929 Time
:= sys_GetTicks() {div 1000};
3930 FPSCounter
:= FPSCounter
+1;
3931 if Time
- FPSTime
>= 1000 then
3938 e_SetRendertarget(True);
3939 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
3941 if gGameOn
or (gState
= STATE_FOLD
) then
3943 if (gPlayer1
<> nil) and (gPlayer2
<> nil) then
3945 gSpectMode
:= SPECT_NONE
;
3946 if not gRevertPlayers
then
3948 plView1
:= gPlayer1
;
3949 plView2
:= gPlayer2
;
3953 plView1
:= gPlayer2
;
3954 plView2
:= gPlayer1
;
3958 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
3960 gSpectMode
:= SPECT_NONE
;
3961 if gPlayer2
= nil then
3964 plView1
:= gPlayer2
;
3973 if (plView1
= nil) and (plView2
= nil) and (gSpectMode
= SPECT_NONE
) then
3974 gSpectMode
:= SPECT_STATS
;
3976 if gSpectMode
= SPECT_PLAYERS
then
3977 if gPlayers
<> nil then
3979 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
3980 if plView1
= nil then
3982 gSpectPID1
:= GetActivePlayerID_Next();
3983 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
3985 if gSpectViewTwo
then
3987 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
3988 if plView2
= nil then
3990 gSpectPID2
:= GetActivePlayerID_Next();
3991 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
3996 if gSpectMode
= SPECT_MAPVIEW
then
3998 // Ðåæèì ïðîñìîòðà êàðòû
4000 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
4001 DrawMapView(gSpectX
, gSpectY
, gScreenWidth
, gScreenHeight
);
4002 gHearPoint1
.Active
:= True;
4003 gHearPoint1
.Coords
.X
:= gScreenWidth
div 2 + gSpectX
;
4004 gHearPoint1
.Coords
.Y
:= gScreenHeight
div 2 + gSpectY
;
4005 gHearPoint2
.Active
:= False;
4009 Split
:= (plView1
<> nil) and (plView2
<> nil);
4011 // Òî÷êè ñëóõà èãðîêîâ
4012 if plView1
<> nil then
4014 gHearPoint1
.Active
:= True;
4015 gHearPoint1
.Coords
.X
:= plView1
.GameX
+ PLAYER_RECT
.Width
;
4016 gHearPoint1
.Coords
.Y
:= plView1
.GameY
+ PLAYER_RECT
.Height
DIV 2;
4018 gHearPoint1
.Active
:= False;
4019 if plView2
<> nil then
4021 gHearPoint2
.Active
:= True;
4022 gHearPoint2
.Coords
.X
:= plView2
.GameX
+ PLAYER_RECT
.Width
;
4023 gHearPoint2
.Coords
.Y
:= plView2
.GameY
+ PLAYER_RECT
.Height
DIV 2;
4025 gHearPoint2
.Active
:= False;
4027 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4028 gPlayerScreenSize
.X
:= gScreenWidth
-196;
4031 gPlayerScreenSize
.Y
:= gScreenHeight
div 2;
4032 if gScreenHeight
mod 2 = 0 then
4033 Dec(gPlayerScreenSize
.Y
);
4036 gPlayerScreenSize
.Y
:= gScreenHeight
;
4039 if gScreenHeight
mod 2 = 0 then
4040 e_SetViewPort(0, gPlayerScreenSize
.Y
+2, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
)
4042 e_SetViewPort(0, gPlayerScreenSize
.Y
+1, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
4044 DrawPlayer(plView1
);
4045 gPlayer1ScreenCoord
.X
:= sX
;
4046 gPlayer1ScreenCoord
.Y
:= sY
;
4050 e_SetViewPort(0, 0, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
4052 DrawPlayer(plView2
);
4053 gPlayer2ScreenCoord
.X
:= sX
;