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}
26 g_basic
, g_player
, e_graphics
, g_res_downloader
,
27 g_gui
, utils
, md5
, mempool
, xprofiler
,
32 //RESERVED = 0, // FIXME: reuse for something
51 TGameOptions
= set of TGameOption
;
53 TGameSettings
= record
60 ItemRespawnTime
: Word;
61 ItemRespawnRandom
: Word;
62 PowerupRespawnTime
: Word;
63 PowerupRespawnRandom
: Word;
65 Options
: TGameOptions
;
74 TDelayedEvent
= record
84 Sound
: TPlayableSound
;
85 Tags
: Array of String;
90 TPlayerSettings
= record
95 // ones below are sent only to the server
97 WeaponPreferences
: Array[WP_FIRST
..WP_LAST
+1] of Byte;
102 TMegaWADInfo
= record
114 function g_Game_IsNet(): Boolean;
115 function g_Game_IsServer(): Boolean;
116 function g_Game_IsClient(): Boolean;
117 procedure g_Game_Init();
118 procedure g_Game_Free (freeTextures
: Boolean=true);
119 procedure g_Game_LoadData();
120 procedure g_Game_FreeData();
121 procedure g_Game_Update();
122 procedure g_Game_PreUpdate();
123 procedure g_Game_Draw();
124 procedure g_Game_Quit();
125 procedure g_Game_SetupScreenSize();
126 procedure g_Game_ChangeResolution(newWidth
, newHeight
: Word; nowFull
, nowMax
: Boolean);
127 function g_Game_ModeToText(Mode
: Byte): string;
128 function g_Game_TextToMode(Mode
: string): Byte;
129 procedure g_Game_ExecuteEvent(Name
: String);
130 function g_Game_DelayEvent(DEType
: Byte; Time
: LongWord
; Num
: Integer = 0; Str
: String = ''): Integer;
131 procedure g_Game_AddPlayer(Team
: Byte = TEAM_NONE
);
132 procedure g_Game_RemovePlayer();
133 procedure g_Game_Spectate();
134 procedure g_Game_SpectateCenterView();
135 procedure g_Game_StartSingle(Map
: String; TwoPlayers
: Boolean; nPlayers
: Byte);
136 procedure g_Game_StartCustom(Map
: String; GameMode
: Byte; TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte; Options
: TGameOptions
; nPlayers
: Byte);
137 procedure g_Game_StartServer(Map
: String; GameMode
: Byte; TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte; Options
: TGameOptions
; nPlayers
: Byte; IPAddr
: LongWord
; Port
: Word);
138 procedure g_Game_StartClient(Addr
: String; Port
: Word; PW
: String);
139 procedure g_Game_Restart();
140 procedure g_Game_RestartLevel();
141 procedure g_Game_RestartRound(NoMapRestart
: Boolean = False);
142 function g_Game_ClientWAD (NewWAD
: String; const WHash
: TMD5Digest
): AnsiString
;
143 function g_Game_StartMap(asMegawad
: Boolean; Map
: String; Force
: Boolean = False; const oldMapPath
: AnsiString
=''): Boolean;
144 procedure g_Game_ChangeMap(const MapPath
: String);
145 procedure g_Game_ExitLevel(const Map
: AnsiString
);
146 function g_Game_GetFirstMap(WAD
: String): String;
147 function g_Game_GetNextMap(): String;
148 procedure g_Game_NextLevel();
149 procedure g_Game_Pause(Enable
: Boolean);
150 procedure g_Game_HolmesPause(Enable
: Boolean);
151 procedure g_Game_InGameMenu(Show
: Boolean);
152 function g_Game_IsWatchedPlayer(UID
: Word): Boolean;
153 function g_Game_IsWatchedTeam(Team
: Byte): Boolean;
154 procedure g_Game_Message(Msg
: String; Time
: Word);
156 {$IFDEF ENABLE_SOUND}
157 procedure g_Game_PauseAllSounds(Enable
: Boolean);
158 procedure g_Game_StopAllSounds(all
: Boolean);
159 procedure g_Game_UpdateTriggerSounds();
160 procedure g_Game_ChatSound(Text: String; Taunt
: Boolean = True);
161 procedure g_Game_Announce_GoodShot(SpawnerUID
: Word);
162 procedure g_Game_Announce_KillCombo(Param
: Integer);
163 procedure g_Game_Announce_BodyKill(SpawnerUID
: Word);
166 function g_Game_GetMegaWADInfo(WAD
: String; cfg
: TConfig
= nil): TMegaWADInfo
;
167 procedure g_Game_Effect_Bubbles(fX
, fY
: Integer; count
: Word; devX
, devY
: Byte; Silent
: Boolean = False);
168 procedure g_Game_StartVote(Command
, Initiator
: string);
169 procedure g_Game_CheckVote
;
170 procedure g_TakeScreenShot(Filename
: string = '');
171 procedure g_FatalError(Text: String);
172 procedure g_SimpleError(Text: String);
173 function g_Game_IsTestMap(): Boolean;
174 procedure g_Game_DeleteTestMap();
175 procedure GameCVars(P
: SSArray
);
176 procedure PlayerSettingsCVars(P
: SSArray
);
177 procedure SystemCommands(P
: SSArray
);
178 procedure GameCommands(P
: SSArray
);
179 procedure GameCheats(P
: SSArray
);
180 procedure DebugCommands(P
: SSArray
);
181 procedure g_Game_Process_Params
;
182 procedure g_Game_SetLoadingText(Text: String; Max
: Integer; reWrite
: Boolean);
183 procedure g_Game_StepLoading(Value
: Integer = -1);
184 procedure g_Game_ClearLoading();
185 procedure g_Game_SetDebugMode();
186 procedure DrawLoadingStat();
187 procedure DrawMenuBackground(tex
: AnsiString
);
189 { procedure SetWinPause(Enable: Boolean); }
194 LOADING_SHOW_STEP
= 100;
195 LOADING_INTERLINE
= 20;
210 MESSAGE_DIKEY
= WM_USER
+ 1;
215 EXIT_ENDLEVELSINGLE
= 4;
216 EXIT_ENDLEVELCUSTOM
= 5;
221 STATE_INTERCUSTOM
= 3;
222 STATE_INTERSINGLE
= 4;
228 LMS_RESPAWN_NONE
= 0;
229 LMS_RESPAWN_WARMUP
= 1;
230 LMS_RESPAWN_FINAL
= 2;
247 CONFIG_FILENAME
= 'Doom2DF.cfg';
248 TEST_MAP_NAME
= '$$$_TEST_$$$';
249 STD_PLAYER_MODEL
= 'Doomer';
250 DEFAULT_PLAYERS
= {$IFNDEF HEADLESS}1{$ELSE}0{$ENDIF};
251 STATFILE_VERSION
= $03;
255 gGameSettings
: TGameSettings
;
256 gPlayer1Settings
: TPlayerSettings
;
257 gPlayer2Settings
: TPlayerSettings
;
259 gPlayerScreenSize
: TDFPoint
;
260 gPlayer1ScreenCoord
: TDFPoint
;
261 gPlayer2ScreenCoord
: TDFPoint
;
264 gPlayerDrawn
: TPlayer
;
266 gLerpFactor
: Single = 1.0;
267 gSwitchGameMode
: Byte = GM_DM
;
268 gHearPoint1
, gHearPoint2
: THearPoint
;
269 {$IFDEF ENABLE_SOUND}
270 gSoundEffectsDF
: Boolean;
271 gSoundTriggerTime
: Word;
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 gUseChatSounds
: Boolean = True;
280 gChatSounds
: array of TChatSound
;
283 gMusicName
: String = '';
284 gMusicPlay
: Boolean = False;
285 gMusicPos
: LongWord
= 0;
286 gMusicPause
: Boolean = False;
288 gAnnouncer
: Integer = ANNOUNCE_NONE
;
289 gBodyKillEvent
: Integer = -1;
290 gDefInterTime
: ShortInt
= -1;
291 gInterEndTime
: LongWord
;
292 gInterTime
: LongWord
;
293 gServInterTime
: Byte;
294 gGameStartTime
: LongWord
;
295 gTotalMonsters
: Integer;
297 gPauseHolmes
: Boolean;
300 gShowScore
: Boolean = True;
301 gShowStat
: Boolean = True;
303 gShowKillMsg
: Boolean = True;
304 gShowLives
: Boolean = True;
308 gState
: Byte = STATE_NONE
;
310 sWidth
, sHeight
: Word;
311 gSpectMode
: Byte = SPECT_NONE
;
312 gSpectHUD
: Boolean = True;
313 gSpectKeyPress
: Boolean;
316 gSpectStep
: Byte = 8;
317 gSpectViewTwo
: Boolean;
318 gSpectPID1
: Integer = -1;
319 gSpectPID2
: Integer = -1;
321 gSpectAutoNext
: LongWord
;
322 gSpectAutoStepX
: Integer;
323 gSpectAutoStepY
: Integer;
324 gLoadGameMode
: Boolean;
327 gMapToDelete
: String;
328 gTempDelete
: Boolean;
332 gResolutionChange
: Boolean;
333 gRC_Width
, gRC_Height
: Integer;
334 gRC_FullScreen
, gRC_Maximized
: Boolean;
335 gLanguageChange
: Boolean;
337 g_debug_Sounds
: Boolean;
338 g_debug_Frames
: Boolean;
339 g_debug_WinMsgs
: Boolean;
340 g_debug_MonsterOff
: Boolean;
341 g_debug_BotAIOff
: Byte;
342 g_debug_HealthBar
: Boolean;
343 g_Debug_Player
: Boolean;
344 gCoopMonstersKilled
: Word;
345 gCoopSecretsFound
: Word;
346 gCoopTotalMonstersKilled
: Word;
347 gCoopTotalSecretsFound
: Word;
348 gCoopTotalMonsters
: Word;
349 gCoopTotalSecrets
: Word;
351 gStatsPressed
: Boolean;
352 gExitByTrigger
: Boolean;
354 gLMSRespawn
: Byte = LMS_RESPAWN_NONE
;
355 gLMSRespawnTime
: Cardinal;
356 gLMSSoftSpawn
: Boolean;
357 gMissionFailed
: Boolean;
358 gVoteInProgress
: Boolean;
359 gVotePassed
: Boolean;
360 gVoteCommand
: String;
361 gVoteTimer
: Cardinal;
362 gVoteCmdTimer
: Cardinal;
364 gVoteTimeout
: Cardinal = 30;
366 gVotesEnabled
: Boolean = True;
367 gEvents
: array of TGameEvent
;
368 gDelayedEvents
: array of TDelayedEvent
;
369 gWeaponAction
: array [0..1, WP_FACT
..WP_LACT
] of Boolean; // [player, weapon_action]
370 gSelectWeapon
: array [0..1, WP_FIRST
..WP_LAST
] of Boolean; // [player, weapon]
371 gInterReadyCount
: Integer;
372 gMaxBots
: Integer = 127;
374 g_dbg_ignore_bounds
: Boolean;
375 r_smallmap_h
: Integer; // 0: left; 1: center; 2: right
376 r_smallmap_v
: Integer = 2; // 0: top; 1: center; 2: bottom
378 // move button values:
379 // bits 0-1: l/r state:
380 // 0: neither left, nor right pressed
383 // bits 4-5: l/r state when strafe was pressed
387 g_profile_frame_update
: Boolean;
388 g_profile_frame_draw
: Boolean;
389 g_profile_collision
: Boolean;
390 g_profile_los
: Boolean;
391 g_profile_history_size
: Integer = 1000;
393 g_rlayer_back
: Boolean = True;
394 g_rlayer_step
: Boolean = True;
395 g_rlayer_wall
: Boolean = True;
396 g_rlayer_door
: Boolean = True;
397 g_rlayer_acid1
: Boolean = True;
398 g_rlayer_acid2
: Boolean = True;
399 g_rlayer_water
: Boolean = True;
400 g_rlayer_fore
: Boolean = True;
403 procedure g_ResetDynlights ();
404 procedure g_AddDynLight (x
, y
, radius
: Integer; r
, g
, b
, a
: Single);
405 procedure g_DynLightExplosion (x
, y
, radius
: Integer; r
, g
, b
: Single);
407 function conIsCheatsEnabled (): Boolean; inline;
408 function gPause (): Boolean; inline;
413 {$INCLUDE ../nogl/noGLuses.inc}
414 {$IFDEF ENABLE_HOLMES}
417 e_texture
, e_res
, g_textures
, g_window
, g_menu
,
418 e_input
, e_log
, g_console
, g_items
, g_map
, g_panel
,
419 g_playermodel
, g_gfx
, g_options
, Math
,
420 g_triggers
, g_monsters
,
421 g_language
, g_net
, g_main
, g_phys
,
422 ENet
, e_msg
, g_netmsg
, g_netmaster
,
423 sfs
, wadreader
, g_system
, Generics
.Collections
, Generics
.Defaults
;
427 profileFrameDraw
: TProfiler
;
429 // ////////////////////////////////////////////////////////////////////////// //
430 function gPause (): Boolean; inline;
432 Result
:= gPauseMain
or gPauseHolmes
;
435 function conIsCheatsEnabled (): Boolean; inline;
438 if g_Game_IsNet
then exit
;
439 if not gDebugMode
then
441 //if not gCheats then exit;
442 if not (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
]) then exit
;
443 if not (gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
]) then exit
;
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 µs', [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
;
609 CustomStat
: TEndCustomGameStat
;
610 SingleStat
: TEndSingleGameStat
;
611 LoadingStat
: TLoadingStat
;
612 EndingGameCounter
: Byte;
615 MessageLineLength
: Integer = 80;
616 MapIndex
: Integer = -1;
617 InterReadyTime
: Integer = -1;
618 StatShotDone
: Boolean;
619 StatFilename
: String; // used by stat screenshot to save with the same name as the csv
626 text: Array of ShortString
;
627 anim
: Array of ShortString
;
628 pic
: Array of ShortString
;
629 mus
: Array of ShortString
;
631 triggers
: Array of record
633 actions
: Array of record
634 action
, p1
, p2
: Integer;
637 cur_trigger
: Integer;
650 function Compare(a
, b
: TPlayerStat
): Integer;
652 if a
.Spectator
then Result
:= 1
653 else if b
.Spectator
then Result
:= -1
654 else if a
.Frags
< b
.Frags
then Result
:= 1
655 else if a
.Frags
> b
.Frags
then Result
:= -1
656 else if a
.Deaths
< b
.Deaths
then Result
:= -1
657 else if a
.Deaths
> b
.Deaths
then Result
:= 1
658 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
:= ConcatPaths([dir
, Path
]);
692 ForceDirectories(fname
); // ensure yy/mm/dd exists within the stats dir
693 fname
:= ConcatPaths([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; cfg
: TConfig
): TMegaWADInfo
;
830 w
:= TWADFile
.Create();
833 w
.GetResource('INTERSCRIPT', p
, len
);
838 Result
.name
:= ExtractFileName(WAD
);
842 cfg
:= TConfig
.CreateMem(p
, len
);
846 Result
.name
:= cfg
.ReadStr('megawad', 'name', ExtractFileName(WAD
));
847 Result
.description
:= cfg
.ReadStr('megawad', 'description', '');
848 Result
.author
:= cfg
.ReadStr('megawad', 'author', '');
849 Result
.pic
:= cfg
.ReadStr('megawad', 'pic', '');
855 procedure g_Game_FreeCurrentWAD();
859 for a
:= 0 to High(MegaWAD
.res
.pic
) do
860 if MegaWAD
.res
.pic
[a
] <> '' then
861 g_Texture_Delete(MegaWAD
.res
.pic
[a
]);
863 {$IFDEF ENABLE_SOUND}
864 for a
:= 0 to High(MegaWAD
.res
.mus
) do
865 if MegaWAD
.res
.mus
[a
] <> '' then
866 g_Sound_Delete(MegaWAD
.res
.mus
[a
]);
869 MegaWAD
.res
.pic
:= nil;
870 MegaWAD
.res
.text := nil;
871 MegaWAD
.res
.anim
:= nil;
872 MegaWAD
.res
.mus
:= nil;
873 MegaWAD
.triggers
:= nil;
875 g_Texture_Delete('TEXTURE_endpic');
877 {$IFDEF ENABLE_SOUND}
878 g_Sound_Delete('MUSIC_endmus');
881 ZeroMemory(@MegaWAD
, SizeOf(MegaWAD
));
882 gGameSettings
.WAD
:= '';
885 procedure g_Game_SetCurrentWAD(WAD
: string);
893 g_Game_FreeCurrentWAD();
894 gGameSettings
.WAD
:= WAD
;
895 if not (gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
]) then
898 w
:= TWADFile
.Create();
900 w
.GetResource('INTERSCRIPT', p
, len
);
903 if p
= nil then Exit
;
904 cfg
:= TConfig
.CreateMem(p
, len
);
906 MegaWAD
.info
:= g_Game_GetMegaWADInfo(WAD
, cfg
);
908 MegaWAD
.endpic
:= cfg
.ReadStr('megawad', 'endpic', '');
909 if MegaWAD
.endpic
<> '' then
911 TEXTUREFILTER
:= GL_LINEAR
;
912 s
:= e_GetResourcePath(WadDirs
, MegaWAD
.endpic
, WAD
);
913 g_Texture_CreateWADEx('TEXTURE_endpic', s
);
914 TEXTUREFILTER
:= GL_NEAREST
;
916 MegaWAD
.endmus
:= cfg
.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
918 {$IFDEF ENABLE_SOUND}
919 if MegaWAD
.endmus
<> '' then
921 s
:= e_GetResourcePath(WadDirs
, MegaWAD
.endmus
, WAD
);
922 g_Sound_CreateWADEx('MUSIC_endmus', s
, True);
929 {procedure start_trigger(t: string);
933 function next_trigger(): Boolean;
937 procedure DisableCheats();
943 if gPlayer1
<> nil then gPlayer1
.GodMode
:= False;
944 if gPlayer2
<> nil then gPlayer2
.GodMode
:= False;
945 if gPlayer1
<> nil then gPlayer1
.NoTarget
:= False;
946 if gPlayer2
<> nil then gPlayer2
.NoTarget
:= False;
948 {$IF DEFINED(D2F_DEBUG)}
949 if gPlayer1
<> nil then gPlayer1
.NoTarget
:= True;
950 gAimLine
:= g_dbg_aimline_on
;
954 procedure g_Game_ExecuteEvent(Name
: String);
960 if gEvents
= nil then
962 for a
:= 0 to High(gEvents
) do
963 if gEvents
[a
].Name
= Name
then
965 if gEvents
[a
].Command
<> '' then
966 g_Console_Process(gEvents
[a
].Command
, True);
971 function g_Game_DelayEvent(DEType
: Byte; Time
: LongWord
; Num
: Integer = 0; Str
: String = ''): Integer;
976 if gDelayedEvents
<> nil then
977 for a
:= 0 to High(gDelayedEvents
) do
978 if not gDelayedEvents
[a
].Pending
then
985 SetLength(gDelayedEvents
, Length(gDelayedEvents
) + 1);
986 n
:= High(gDelayedEvents
);
988 gDelayedEvents
[n
].Pending
:= True;
989 gDelayedEvents
[n
].DEType
:= DEType
;
990 gDelayedEvents
[n
].DENum
:= Num
;
991 gDelayedEvents
[n
].DEStr
:= Str
;
992 if DEType
= DE_GLOBEVENT
then
993 gDelayedEvents
[n
].Time
:= (sys_GetTicks() {div 1000}) + Time
995 gDelayedEvents
[n
].Time
:= gTime
+ Time
;
1005 if g_Game_IsNet
and g_Game_IsServer
then
1006 MH_SEND_GameEvent(NET_EV_MAPEND
, Byte(gMissionFailed
));
1009 gPauseMain
:= false;
1010 gPauseHolmes
:= false;
1013 {$IFDEF ENABLE_SOUND}
1014 g_Game_StopAllSounds(False);
1020 EndingGameCounter
:= 0;
1021 g_ActiveWindow
:= nil;
1023 gLMSRespawn
:= LMS_RESPAWN_NONE
;
1024 gLMSRespawnTime
:= 0;
1027 EXIT_SIMPLE
: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
1032 begin // Ýòî áûë òåñò
1036 begin // Âûõîä â ãëàâíîå ìåíþ
1037 {$IFDEF ENABLE_SOUND}
1038 gMusic
.SetByName('MUSIC_MENU');
1041 if gState
<> STATE_SLIST
then
1043 g_GUI_ShowWindow('MainMenu');
1044 gState
:= STATE_MENU
;
1047 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
1048 slReturnPressed
:= True;
1049 if g_Net_Slist_Fetch(slCurrent
) then
1051 if slCurrent
= nil then
1052 slWaitStr
:= _lc
[I_NET_SLIST_NOSERVERS
];
1055 slWaitStr
:= _lc
[I_NET_SLIST_ERROR
];
1056 g_Serverlist_GenerateTable(slCurrent
, slTable
);
1059 g_Game_ExecuteEvent('ongameend');
1063 EXIT_RESTART
: // Íà÷àòü óðîâåíü ñíà÷àëà
1065 if not g_Game_IsClient
then g_Game_Restart();
1068 EXIT_ENDLEVELCUSTOM
: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
1070 // Ñòàòèñòèêà Ñâîåé èãðû:
1071 FileName
:= g_ExtractWadName(gMapInfo
.Map
);
1073 CustomStat
.GameTime
:= gTime
;
1074 CustomStat
.Map
:= ExtractFileName(FileName
)+':'+g_ExtractFileName(gMapInfo
.Map
); //ResName;
1075 CustomStat
.MapName
:= gMapInfo
.Name
;
1076 CustomStat
.GameMode
:= gGameSettings
.GameMode
;
1077 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1078 CustomStat
.TeamStat
:= gTeamStat
;
1080 CustomStat
.PlayerStat
:= nil;
1082 // Ñòàòèñòèêà èãðîêîâ:
1083 if gPlayers
<> nil then
1085 for a
:= 0 to High(gPlayers
) do
1086 if gPlayers
[a
] <> nil then
1088 SetLength(CustomStat
.PlayerStat
, Length(CustomStat
.PlayerStat
)+1);
1089 with CustomStat
.PlayerStat
[High(CustomStat
.PlayerStat
)] do
1092 Name
:= gPlayers
[a
].Name
;
1093 Frags
:= gPlayers
[a
].Frags
;
1094 Deaths
:= gPlayers
[a
].Death
;
1095 Kills
:= gPlayers
[a
].Kills
;
1096 Team
:= gPlayers
[a
].Team
;
1097 Color
:= gPlayers
[a
].Model
.Color
;
1098 Spectator
:= gPlayers
[a
].FSpectator
;
1102 SortGameStat(CustomStat
.PlayerStat
);
1104 if (gSaveStats
or gScreenshotStats
) and (Length(CustomStat
.PlayerStat
) > 1) then
1107 if g_Game_IsNet
then StatFilename
:= NetServerName
else StatFilename
:= 'local';
1108 StatDate
:= FormatDateTime('yymmdd_hhnnss', t
);
1109 StatFilename
:= StatFilename
+ '_' + CustomStat
.Map
+ '_' + g_Game_ModeToText(CustomStat
.GameMode
);
1110 StatFilename
:= sanitizeFilename(StatFilename
) + '_' + StatDate
;
1112 SaveGameStat(CustomStat
, FormatDateTime('yyyy"/"mm"/"dd', t
));
1115 StatShotDone
:= False;
1118 g_Game_ExecuteEvent('onmapend');
1119 if not g_Game_IsClient
then g_Player_ResetReady
;
1120 gInterReadyCount
:= 0;
1122 // Çàòóõàþùèé ýêðàí:
1123 EndingGameCounter
:= 255;
1124 gState
:= STATE_FOLD
;
1126 if gDefInterTime
< 0 then
1127 gInterEndTime
:= IfThen((gGameSettings
.GameType
= GT_SERVER
) and (gPlayer1
= nil), 15000, 25000)
1129 gInterEndTime
:= gDefInterTime
* 1000;
1132 EXIT_ENDLEVELSINGLE
: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
1134 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
1135 SingleStat
.GameTime
:= gTime
;
1136 SingleStat
.TwoPlayers
:= gPlayer2
<> nil;
1137 SingleStat
.TotalSecrets
:= gSecretsCount
;
1138 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
1139 SingleStat
.PlayerStat
[0].Kills
:= gPlayer1
.MonsterKills
;
1140 SingleStat
.PlayerStat
[0].Secrets
:= gPlayer1
.Secrets
;
1141 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
1142 if SingleStat
.TwoPlayers
then
1144 SingleStat
.PlayerStat
[1].Kills
:= gPlayer2
.MonsterKills
;
1145 SingleStat
.PlayerStat
[1].Secrets
:= gPlayer2
.Secrets
;
1148 g_Game_ExecuteEvent('onmapend');
1151 if gNextMap
<> '' then
1153 {$IFDEF ENABLE_SOUND}
1154 gMusic
.SetByName('MUSIC_INTERMUS');
1157 gState
:= STATE_INTERSINGLE
;
1160 g_Game_ExecuteEvent('oninter');
1162 else // Áîëüøå íåò êàðò
1164 // Çàòóõàþùèé ýêðàí:
1165 EndingGameCounter
:= 255;
1166 gState
:= STATE_FOLD
;
1171 // Îêîí÷àíèå îáðàáîòàíî:
1172 if gExit
<> EXIT_QUIT
then
1176 procedure drawTime(X
, Y
: Integer); inline;
1178 e_TextureFontPrint(x
, y
,
1179 Format('%d:%.2d:%.2d', [
1180 gTime
div 1000 div 3600,
1181 (gTime
div 1000 div 60) mod 60,
1182 gTime
div 1000 mod 60
1187 procedure DrawStat();
1189 pc
, x
, y
, w
, h
: Integer;
1190 w1
, w2
, w3
, w4
: Integer;
1192 cw
, ch
, r
, g
, b
, rr
, gg
, bb
: Byte;
1195 stat
: TPlayerStatArray
;
1203 pc
:= g_Player_GetCount
;
1204 e_TextureFontGetSize(gStdFont
, cw
, ch
);
1206 w
:= gScreenWidth
-(gScreenWidth
div 5);
1207 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1210 h
:= 40+ch
*5+(ch
+8)*pc
;
1211 x
:= (gScreenWidth
div 2)-(w
div 2);
1212 y
:= (gScreenHeight
div 2)-(h
div 2);
1214 e_DrawFillQuad(x
, y
, x
+w
-1, y
+h
-1, 64, 64, 64, 32);
1215 e_DrawQuad(x
, y
, x
+w
-1, y
+h
-1, 255, 127, 0);
1217 drawTime(x
+w
-78, y
+8);
1219 wad
:= g_ExtractWadNameNoPath(gMapInfo
.Map
);
1220 map
:= g_ExtractFileName(gMapInfo
.Map
);
1221 mapstr
:= wad
+ ':\' + map
+ ' - ' + gMapInfo
.Name
;
1223 case gGameSettings
.GameMode
of
1226 if gGameSettings
.MaxLives
= 0 then
1227 s1
:= _lc
[I_GAME_DM
]
1229 s1
:= _lc
[I_GAME_LMS
];
1230 s2
:= Format(_lc
[I_GAME_FRAG_LIMIT
], [gGameSettings
.ScoreLimit
]);
1231 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1236 if gGameSettings
.MaxLives
= 0 then
1237 s1
:= _lc
[I_GAME_TDM
]
1239 s1
:= _lc
[I_GAME_TLMS
];
1240 s2
:= Format(_lc
[I_GAME_FRAG_LIMIT
], [gGameSettings
.ScoreLimit
]);
1241 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1246 s1
:= _lc
[I_GAME_CTF
];
1247 s2
:= Format(_lc
[I_GAME_SCORE_LIMIT
], [gGameSettings
.ScoreLimit
]);
1248 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1253 if gGameSettings
.MaxLives
= 0 then
1254 s1
:= _lc
[I_GAME_COOP
]
1256 s1
:= _lc
[I_GAME_SURV
];
1257 s2
:= _lc
[I_GAME_MONSTERS
] + ' ' + IntToStr(gCoopMonstersKilled
) + '/' + IntToStr(gTotalMonsters
);
1258 s3
:= _lc
[I_GAME_SECRETS
] + ' ' + IntToStr(gCoopSecretsFound
) + '/' + IntToStr(gSecretsCount
);
1269 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*cw
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
1271 e_TextureFontPrintEx(x
+(w
div 2)-(Length(mapstr
)*cw
div 2), _y
, mapstr
, gStdFont
, 200, 200, 200, 1);
1273 e_TextureFontPrintEx(x
+16, _y
, s2
, gStdFont
, 200, 200, 200, 1);
1275 e_TextureFontPrintEx(x
+w
-16-(Length(s3
))*cw
, _y
, s3
,
1276 gStdFont
, 200, 200, 200, 1);
1278 if NetMode
= NET_SERVER
then
1279 e_TextureFontPrintEx(x
+8, y
+ 8, _lc
[I_NET_SERVER
], gStdFont
, 255, 255, 255, 1)
1281 if NetMode
= NET_CLIENT
then
1282 e_TextureFontPrintEx(x
+8, y
+ 8,
1283 NetClientIP
+ ':' + IntToStr(NetClientPort
), gStdFont
, 255, 255, 255, 1);
1287 stat
:= g_Player_GetStats();
1290 w2
:= (w
-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1291 w3
:= (w
-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1293 w1
:= w
-16-w2
-w3
-w4
; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1295 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1299 for a
:= TEAM_RED
to TEAM_BLUE
do
1301 if a
= TEAM_RED
then
1303 s1
:= _lc
[I_GAME_TEAM_RED
];
1310 s1
:= _lc
[I_GAME_TEAM_BLUE
];
1316 e_TextureFontPrintEx(x
+16, _y
, s1
, gStdFont
, r
, g
, b
, 1);
1317 e_TextureFontPrintEx(x
+w1
+16, _y
, IntToStr(gTeamStat
[a
].Score
),
1318 gStdFont
, r
, g
, b
, 1);
1320 _y
:= _y
+ch
+(ch
div 4);
1321 e_DrawLine(1, x
+16, _y
, x
+w
-16, _y
, r
, g
, b
);
1322 _y
:= _y
+(ch
div 4);
1324 for aa
:= 0 to High(stat
) do
1325 if stat
[aa
].Team
= a
then
1341 namestr
:= Format('[%5d] %s', [UID
, Name
])
1345 e_TextureFontPrintEx(x
+16, _y
, namestr
, gStdFont
, rr
, gg
, bb
, 1);
1347 e_TextureFontPrintEx(x
+w1
+16, _y
, Format(_lc
[I_GAME_PING_MS
], [Ping
, Loss
]), gStdFont
, rr
, gg
, bb
, 1);
1349 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
, IntToStr(Frags
), gStdFont
, rr
, gg
, bb
, 1);
1351 e_TextureFontPrintEx(x
+w1
+w2
+w3
+16, _y
, IntToStr(Deaths
), gStdFont
, rr
, gg
, bb
, 1);
1358 else if gGameSettings
.GameMode
in [GM_DM
, GM_COOP
] then
1361 e_TextureFontPrintEx(x
+16, _y
, _lc
[I_GAME_PLAYER_NAME
], gStdFont
, 255, 127, 0, 1);
1362 e_TextureFontPrintEx(x
+16+w1
, _y
, _lc
[I_GAME_PING
], gStdFont
, 255, 127, 0, 1);
1363 e_TextureFontPrintEx(x
+16+w1
+w2
, _y
, _lc
[I_GAME_FRAGS
], gStdFont
, 255, 127, 0, 1);
1364 e_TextureFontPrintEx(x
+16+w1
+w2
+w3
, _y
, _lc
[I_GAME_DEATHS
], gStdFont
, 255, 127, 0, 1);
1367 for aa
:= 0 to High(stat
) do
1381 namestr
:= Format('[%5d] %s', [UID
, Name
])
1385 e_DrawFillQuad(x
+16, _y
+4, x
+32-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
1386 e_DrawQuad(x
+16, _y
+4, x
+32-1, _y
+16+4-1, 192, 192, 192);
1388 e_TextureFontPrintEx(x
+16+16+8, _y
+4, namestr
, gStdFont
, r
, g
, 0, 1);
1390 e_TextureFontPrintEx(x
+w1
+16, _y
+4, Format(_lc
[I_GAME_PING_MS
], [Ping
, Loss
]), gStdFont
, r
, g
, 0, 1);
1392 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
+4, IntToStr(Frags
), gStdFont
, r
, g
, 0, 1);
1394 e_TextureFontPrintEx(x
+w1
+w2
+w3
+16, _y
+4, IntToStr(Deaths
), gStdFont
, r
, g
, 0, 1);
1400 procedure g_Game_Init();
1403 knownFiles
: array of AnsiString
= nil;
1405 wext
, s
: AnsiString
;
1410 gTempDelete
:= False;
1412 sfsGCDisable(); // temporary disable removing of temporary volumes
1415 TEXTUREFILTER
:= GL_LINEAR
;
1416 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD
+':TEXTURES\TITLE');
1417 g_Texture_CreateWADEx('INTER', GameWAD
+':TEXTURES\INTER');
1418 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD
+':TEXTURES\ENDGAME_EN');
1419 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD
+':TEXTURES\ENDGAME_RU');
1420 TEXTUREFILTER
:= GL_NEAREST
;
1422 LoadStdFont('STDTXT', 'STDFONT', gStdFont
);
1423 LoadFont('MENUTXT', 'MENUFONT', gMenuFont
);
1424 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont
);
1426 g_Game_ClearLoading();
1427 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION
]), 0, False);
1428 g_Game_SetLoadingText('', 0, False);
1430 g_Game_SetLoadingText(_lc
[I_LOAD_CONSOLE
], 0, False);
1433 g_Game_SetLoadingText(_lc
[I_LOAD_MODELS
], 0, False);
1434 g_PlayerModel_LoadData();
1436 // load models from all possible wad types, in all known directories
1437 // this does a loosy job (linear search, ooph!), but meh
1438 for wext
in wadExtensions
do
1440 for f
:= High(ModelDirs
) downto Low(ModelDirs
) do
1442 if (FindFirst(ModelDirs
[f
]+DirectorySeparator
+'*'+wext
, faAnyFile
, SR
) = 0) then
1446 for s
in knownFiles
do
1448 if (strEquCI1251(forceFilenameExt(SR
.Name
, ''), forceFilenameExt(ExtractFileName(s
), ''))) then
1456 SetLength(knownFiles
, length(knownFiles
)+1);
1457 knownFiles
[High(knownFiles
)] := ModelDirs
[f
]+DirectorySeparator
+SR
.Name
;
1459 until (FindNext(SR
) <> 0);
1465 if (length(knownFiles
) = 0) then raise Exception
.Create('no player models found!');
1467 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
);
1468 for s
in knownFiles
do
1470 if not g_PlayerModel_Load(s
) then e_LogWritefln('Error loading model "%s"', [s
], TMsgType
.Warning
);
1474 gPauseMain
:= false;
1475 gPauseHolmes
:= false;
1478 {e_MouseInfo.Accel := 1.0;}
1480 g_Game_SetLoadingText(_lc
[I_LOAD_GAME_DATA
], 0, False);
1483 {$IFDEF ENABLE_SOUND}
1484 g_Game_SetLoadingText(_lc
[I_LOAD_MUSIC
], 0, False);
1485 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD
+':MUSIC\INTERMUS', True);
1486 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD
+':MUSIC\MENU', True);
1487 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD
+':MUSIC\ROUNDMUS', True, True);
1488 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD
+':MUSIC\ENDMUS', True);
1492 g_Game_SetLoadingText(_lc
[I_LOAD_MENUS
], 0, False);
1496 {$IFDEF ENABLE_SOUND}
1497 gMusic
:= TMusic
.Create();
1498 gMusic
.SetByName('MUSIC_MENU');
1502 gMusicPlay
:= False;
1504 gMusicPause
:= False;
1507 gGameSettings
.WarmupTime
:= 30;
1509 gState
:= STATE_MENU
;
1511 SetLength(gEvents
, 6);
1512 gEvents
[0].Name
:= 'ongamestart';
1513 gEvents
[1].Name
:= 'ongameend';
1514 gEvents
[2].Name
:= 'onmapstart';
1515 gEvents
[3].Name
:= 'onmapend';
1516 gEvents
[4].Name
:= 'oninter';
1517 gEvents
[5].Name
:= 'onwadend';
1519 sfsGCEnable(); // enable releasing unused volumes
1523 procedure g_Game_Free(freeTextures
: Boolean=true);
1525 e_WriteLog('g_Game_Free: completion of the gameplay', TMsgType
.Notify
);
1526 if NetMode
= NET_CLIENT
then g_Net_Disconnect();
1527 if NetMode
= NET_SERVER
then g_Net_Host_Die();
1529 g_Map_Free(freeTextures
);
1531 g_Player_RemoveAllCorpses();
1533 gGameSettings
.GameType
:= GT_NONE
;
1534 if gGameSettings
.GameMode
= GM_SINGLE
then
1535 gGameSettings
.GameMode
:= GM_DM
;
1536 gSwitchGameMode
:= gGameSettings
.GameMode
;
1539 gExitByTrigger
:= False;
1542 function IsActivePlayer(p
: TPlayer
): Boolean;
1547 Result
:= (not p
.FDummy
) and (not p
.FSpectator
);
1550 function GetActivePlayer_ByID(ID
: Integer): TPlayer
;
1557 if gPlayers
= nil then
1559 for a
:= Low(gPlayers
) to High(gPlayers
) do
1560 if IsActivePlayer(gPlayers
[a
]) then
1562 if gPlayers
[a
].UID
<> ID
then
1564 Result
:= gPlayers
[a
];
1569 function GetActivePlayerID_Next(Skip
: Integer = -1): Integer;
1575 if gPlayers
= nil then
1579 for a
:= Low(gPlayers
) to High(gPlayers
) do
1580 if IsActivePlayer(gPlayers
[a
]) then
1582 SetLength(ids
, Length(ids
) + 1);
1583 ids
[High(ids
)] := gPlayers
[a
].UID
;
1584 if gPlayers
[a
].UID
= Skip
then
1587 if Length(ids
) = 0 then
1592 Result
:= ids
[(idx
+ 1) mod Length(ids
)];
1595 function GetActivePlayerID_Prev(Skip
: Integer = -1): Integer;
1601 if gPlayers
= nil then
1605 for a
:= Low(gPlayers
) to High(gPlayers
) do
1606 if IsActivePlayer(gPlayers
[a
]) then
1608 SetLength(ids
, Length(ids
) + 1);
1609 ids
[High(ids
)] := gPlayers
[a
].UID
;
1610 if gPlayers
[a
].UID
= Skip
then
1613 if Length(ids
) = 0 then
1616 Result
:= ids
[Length(ids
) - 1]
1618 Result
:= ids
[(Length(ids
) - 1 + idx
) mod Length(ids
)];
1621 function GetActivePlayerID_Random(Skip
: Integer = -1): Integer;
1627 if gPlayers
= nil then
1631 for a
:= Low(gPlayers
) to High(gPlayers
) do
1632 if IsActivePlayer(gPlayers
[a
]) then
1634 SetLength(ids
, Length(ids
) + 1);
1635 ids
[High(ids
)] := gPlayers
[a
].UID
;
1636 if gPlayers
[a
].UID
= Skip
then
1639 if Length(ids
) = 0 then
1641 if Length(ids
) = 1 then
1646 Result
:= ids
[Random(Length(ids
))];
1648 while (idx
<> -1) and (Result
= Skip
) and (a
> 0) do
1650 Result
:= ids
[Random(Length(ids
))];
1655 function GetRandomSpectMode(Current
: Byte): Byte;
1662 0: Result
:= SPECT_STATS
;
1663 1: Result
:= SPECT_MAPVIEW
;
1664 2: Result
:= SPECT_MAPVIEW
;
1665 3: Result
:= SPECT_PLAYERS
;
1666 4: Result
:= SPECT_PLAYERS
;
1667 5: Result
:= SPECT_PLAYERS
;
1668 6: Result
:= SPECT_PLAYERS
;
1670 if (Current
in [SPECT_STATS
, SPECT_MAPVIEW
]) and (Current
= Result
) then
1674 procedure ProcessPlayerControls (plr
: TPlayer
; p
: Integer; var MoveButton
: Byte);
1680 if (plr
= nil) then exit
;
1681 if (p
= 2) then time
:= 1000 else time
:= 1;
1682 strafeDir
:= MoveButton
shr 4;
1683 MoveButton
:= MoveButton
and $0F;
1685 if gPlayerAction
[p
, ACTION_MOVELEFT
] and (not gPlayerAction
[p
, ACTION_MOVERIGHT
]) then
1686 MoveButton
:= 1 // Íàæàòà òîëüêî "Âëåâî"
1687 else if (not gPlayerAction
[p
, ACTION_MOVELEFT
]) and gPlayerAction
[p
, ACTION_MOVERIGHT
] then
1688 MoveButton
:= 2 // Íàæàòà òîëüêî "Âïðàâî"
1689 else if (not gPlayerAction
[p
, ACTION_MOVELEFT
]) and (not gPlayerAction
[p
, ACTION_MOVERIGHT
]) then
1690 MoveButton
:= 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1692 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1693 if MoveButton
= 1 then
1694 plr
.PressKey(KEY_LEFT
, time
)
1695 else if MoveButton
= 2 then
1696 plr
.PressKey(KEY_RIGHT
, time
);
1698 // if we have "strafe" key, turn off old strafe mechanics
1699 if gPlayerAction
[p
, ACTION_STRAFE
] then
1701 // new strafe mechanics
1702 if (strafeDir
= 0) then
1703 strafeDir
:= MoveButton
; // start strafing
1704 // now set direction according to strafe (reversed)
1705 if (strafeDir
= 2) then
1706 plr
.SetDirection(TDirection
.D_LEFT
)
1707 else if (strafeDir
= 1) then
1708 plr
.SetDirection(TDirection
.D_RIGHT
)
1712 strafeDir
:= 0; // not strafing anymore
1713 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1714 if (MoveButton
= 2) and gPlayerAction
[p
, ACTION_MOVELEFT
] then
1715 plr
.SetDirection(TDirection
.D_LEFT
)
1716 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1717 else if (MoveButton
= 1) and gPlayerAction
[p
, ACTION_MOVERIGHT
] then
1718 plr
.SetDirection(TDirection
.D_RIGHT
)
1719 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1720 else if MoveButton
<> 0 then
1721 plr
.SetDirection(TDirection(MoveButton
-1))
1724 // fix movebutton state
1725 MoveButton
:= MoveButton
or (strafeDir
shl 4);
1727 // Îñòàëüíûå êëàâèøè:
1728 if gPlayerAction
[p
, ACTION_JUMP
] then plr
.PressKey(KEY_JUMP
, time
);
1729 if gPlayerAction
[p
, ACTION_LOOKUP
] then plr
.PressKey(KEY_UP
, time
);
1730 if gPlayerAction
[p
, ACTION_LOOKDOWN
] then plr
.PressKey(KEY_DOWN
, time
);
1731 if gPlayerAction
[p
, ACTION_ATTACK
] then plr
.PressKey(KEY_FIRE
);
1732 if gPlayerAction
[p
, ACTION_ACTIVATE
] then plr
.PressKey(KEY_OPEN
);
1734 for i
:= WP_FACT
to WP_LACT
do
1736 if gWeaponAction
[p
, i
] then
1738 plr
.ProcessWeaponAction(i
);
1739 gWeaponAction
[p
, i
] := False
1743 for i
:= WP_FIRST
to WP_LAST
do
1745 if gSelectWeapon
[p
, i
] then
1747 plr
.QueueWeaponSwitch(i
); // all choices are passed there, and god will take the best
1748 gSelectWeapon
[p
, i
] := False
1752 // HACK: add dynlight here
1753 if gwin_k8_enable_light_experiments
then
1755 if e_KeyPressed(IK_F8
) and gGameOn
and (not gConsoleShow
) and (g_ActiveWindow
= nil) then
1757 g_playerLight
:= true;
1759 if e_KeyPressed(IK_F9
) and gGameOn
and (not gConsoleShow
) and (g_ActiveWindow
= nil) then
1761 g_playerLight
:= false;
1765 if gwin_has_stencil
and g_playerLight
then g_AddDynLight(plr
.GameX
+32, plr
.GameY
+40, 128, 1, 1, 0, 0.6);
1768 // HACK: don't have a "key was pressed" function
1769 procedure InterReady();
1771 if InterReadyTime
> gTime
then Exit
;
1772 InterReadyTime
:= gTime
+ 3000;
1773 MC_SEND_CheatRequest(NET_CHEAT_READY
);
1776 procedure g_Game_PreUpdate();
1778 // these are in separate PreUpdate functions because they can interact during Update()
1779 // and are synced over the net
1780 // we don't care that much about corpses and gibs
1781 g_Player_PreUpdate();
1782 g_Monsters_PreUpdate();
1783 g_Items_PreUpdate();
1784 g_Weapon_PreUpdate();
1787 procedure g_Game_Update();
1789 Msg
: g_gui
.TMessage
;
1795 function sendMonsPos (mon
: TMonster
): Boolean;
1797 result
:= false; // don't stop
1798 // this will also reset "need-send" flag
1799 if mon
.gncNeedSend
then
1801 MH_SEND_MonsterPos(mon
.UID
);
1803 else if (mon
.MonsterType
= MONSTER_BARREL
) then
1805 if (mon
.GameVelX
<> 0) or (mon
.GameVelY
<> 0) then MH_SEND_MonsterPos(mon
.UID
);
1807 else if (mon
.MonsterState
<> MONSTATE_SLEEP
) then
1809 if (mon
.MonsterState
<> MONSTATE_DEAD
) or (mon
.GameVelX
<> 0) or (mon
.GameVelY
<> 0) then MH_SEND_MonsterPos(mon
.UID
);
1813 function sendMonsPosUnexpected (mon
: TMonster
): Boolean;
1815 result
:= false; // don't stop
1816 // this will also reset "need-send" flag
1817 if mon
.gncNeedSend
then MH_SEND_MonsterPos(mon
.UID
);
1820 function sendItemPos (it
: PItem
): Boolean;
1822 result
:= false; // don't stop
1825 MH_SEND_ItemPos(it
.myId
);
1826 it
.needSend
:= False;
1831 reliableUpdate
: Boolean;
1836 // Ïîðà âûêëþ÷àòü èãðó:
1837 if gExit
= EXIT_QUIT
then
1839 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1843 if gExit
= EXIT_QUIT
then
1847 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî
1848 // no need to, as we'll do it in event handler
1850 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1853 if (NetMode
= NET_NONE
) and (g_Game_IsNet
) and (gGameOn
or (gState
in [STATE_FOLD
, STATE_INTERCUSTOM
])) then
1855 gExit
:= EXIT_SIMPLE
;
1860 // process master server communications
1861 g_Net_Slist_Pulse();
1864 STATE_INTERSINGLE
, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1865 STATE_INTERCUSTOM
, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1866 STATE_INTERTEXT
, // Òåêñò ìåæäó óðîâíÿìè
1867 STATE_INTERPIC
: // Êàðòèíêà ìåæäó óðîâíÿìè
1869 if g_Game_IsNet
and g_Game_IsServer
then
1871 gInterTime
:= gInterTime
+ GAME_TICK
;
1872 a
:= Min((gInterEndTime
- gInterTime
) div 1000 + 1, 255);
1873 if a
<> gServInterTime
then
1875 gServInterTime
:= a
;
1876 MH_SEND_TimeSync(gServInterTime
);
1880 if (not g_Game_IsClient
) and
1884 e_KeyPressed(IK_RETURN
) or e_KeyPressed(IK_KPRETURN
) or e_KeyPressed(IK_SPACE
) or e_KeyPressed(IK_SELECT
) or
1885 e_KeyPressed(VK_FIRE
) or e_KeyPressed(VK_OPEN
) or
1886 e_KeyPressed(JOY0_ATTACK
) or e_KeyPressed(JOY1_ATTACK
) or
1887 e_KeyPressed(JOY2_ATTACK
) or e_KeyPressed(JOY3_ATTACK
)
1889 and (not gJustChatted
) and (not gConsoleShow
) and (not gChatShow
)
1890 and (g_ActiveWindow
= nil)
1892 or (g_Game_IsNet
and ((gInterTime
> gInterEndTime
) or ((gInterReadyCount
>= NetClientCount
) and (NetClientCount
> 0))))
1895 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1896 {$IFDEF ENABLE_SOUND}
1897 g_Game_StopAllSounds(True);
1900 if gMapOnce
then // Ýòî áûë òåñò
1901 gExit
:= EXIT_SIMPLE
1903 if gNextMap
<> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1904 g_Game_ChangeMap(gNextMap
)
1905 else // Ñëåäóþùåé êàðòû íåò
1907 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
] then
1909 // Âûõîä â ãëàâíîå ìåíþ:
1911 g_GUI_ShowWindow('MainMenu');
1912 {$IFDEF ENABLE_SOUND}
1913 gMusic
.SetByName('MUSIC_MENU');
1916 gState
:= STATE_MENU
;
1919 // Ôèíàëüíàÿ êàðòèíêà:
1920 g_Game_ExecuteEvent('onwadend');
1922 {$IFDEF ENABLE_SOUND}
1923 if not gMusic
.SetByName('MUSIC_endmus') then
1924 gMusic
.SetByName('MUSIC_STDENDMUS');
1927 gState
:= STATE_ENDPIC
;
1929 g_Game_ExecuteEvent('ongameend');
1934 else if g_Game_IsClient
and
1937 e_KeyPressed(IK_RETURN
) or e_KeyPressed(IK_KPRETURN
) or e_KeyPressed(IK_SPACE
) or e_KeyPressed(IK_SELECT
) or
1938 e_KeyPressed(VK_FIRE
) or e_KeyPressed(VK_OPEN
) or
1939 e_KeyPressed(JOY0_ATTACK
) or e_KeyPressed(JOY1_ATTACK
) or
1940 e_KeyPressed(JOY2_ATTACK
) or e_KeyPressed(JOY3_ATTACK
)
1942 and (not gJustChatted
) and (not gConsoleShow
) and (not gChatShow
)
1943 and (g_ActiveWindow
= nil)
1951 if gState
= STATE_INTERTEXT
then
1952 if InterText
.counter
> 0 then
1953 InterText
.counter
:= InterText
.counter
- 1;
1956 STATE_FOLD
: // Çàòóõàíèå ýêðàíà
1958 if EndingGameCounter
= 0 then
1960 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1961 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
] then
1963 gState
:= STATE_INTERCUSTOM
;
1964 InterReadyTime
:= -1;
1965 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then
1967 g_Game_ExecuteEvent('onwadend');
1968 {$IFDEF ENABLE_SOUND}
1969 if not gMusic
.SetByName('MUSIC_endmus') then
1970 gMusic
.SetByName('MUSIC_STDENDMUS');
1975 {$IFDEF ENABLE_SOUND}
1976 gMusic
.SetByName('MUSIC_ROUNDMUS');
1979 {$IFDEF ENABLE_SOUND}
1984 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1986 {$IFDEF ENABLE_SOUND}
1987 gMusic
.SetByName('MUSIC_INTERMUS');
1990 gState
:= STATE_INTERSINGLE
;
1993 g_Game_ExecuteEvent('oninter');
1996 DecMin(EndingGameCounter
, 6, 0);
1999 STATE_ENDPIC
: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
2001 if gMapOnce
then // Ýòî áûë òåñò
2003 gExit
:= EXIT_SIMPLE
;
2009 g_Serverlist_Control(slCurrent
, slTable
);
2012 // Ñòàòèñòèêà ïî Tab:
2014 IsDrawStat
:= (not gConsoleShow
) and (not gChatShow
) and (gGameSettings
.GameType
<> GT_SINGLE
) and g_Console_Action(ACTION_SCORES
);
2017 if gGameOn
and not gPause
and (gState
<> STATE_FOLD
) then
2019 // Âðåìÿ += 28 ìèëëèñåêóíä:
2020 gTime
:= gTime
+ GAME_TICK
;
2022 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
2023 if MessageTime
= 0 then
2025 if MessageTime
> 0 then
2026 MessageTime
:= MessageTime
- 1;
2028 if (g_Game_IsServer
) then
2030 // Áûë çàäàí ëèìèò âðåìåíè:
2031 if (gGameSettings
.TimeLimit
> 0) then
2032 if (gTime
- gGameStartTime
) div 1000 >= gGameSettings
.TimeLimit
then
2033 begin // Îí ïðîøåë => êîíåö óðîâíÿ
2038 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
2039 if (gLMSRespawn
> LMS_RESPAWN_NONE
) and (gLMSRespawnTime
< gTime
) then
2040 g_Game_RestartRound(gLMSSoftSpawn
);
2042 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
2043 if gVoteInProgress
and (gVoteTimer
< gTime
) then
2045 else if gVotePassed
and (gVoteCmdTimer
< gTime
) then
2047 g_Console_Process(gVoteCommand
);
2049 gVotePassed
:= False;
2052 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
2053 if gFlags
[FLAG_RED
].State
= FLAG_STATE_CAPTURED
then
2054 gFlags
[FLAG_RED
].CaptureTime
:= gFlags
[FLAG_RED
].CaptureTime
+ GAME_TICK
;
2055 if gFlags
[FLAG_BLUE
].State
= FLAG_STATE_CAPTURED
then
2056 gFlags
[FLAG_BLUE
].CaptureTime
:= gFlags
[FLAG_BLUE
].CaptureTime
+ GAME_TICK
;
2058 // Áûë çàäàí ëèìèò ïîáåä:
2059 if (gGameSettings
.ScoreLimit
> 0) then
2063 if gGameSettings
.GameMode
= GM_DM
then
2064 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
2065 for i
:= 0 to High(gPlayers
) do
2066 if gPlayers
[i
] <> nil then
2067 if gPlayers
[i
].Frags
> b
then
2068 b
:= gPlayers
[i
].Frags
;
2071 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
2072 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
2073 b
:= Max(gTeamStat
[TEAM_RED
].Score
, gTeamStat
[TEAM_BLUE
].Score
);
2076 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
2077 if b
>= gGameSettings
.ScoreLimit
then
2084 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
2085 if gPlayer1
<> nil then gPlayer1
.ReleaseKeys();
2086 if gPlayer2
<> nil then gPlayer2
.ReleaseKeys();
2087 if (not gConsoleShow
) and (not gChatShow
) and (g_ActiveWindow
= nil) then
2089 ProcessPlayerControls(gPlayer1
, 0, P1MoveButton
);
2090 ProcessPlayerControls(gPlayer2
, 1, P2MoveButton
);
2091 end // if not console
2094 if g_Game_IsNet
and (gPlayer1
<> nil) then gPlayer1
.PressKey(KEY_CHAT
, 10000);
2096 // process weapon switch queue
2100 if (gPlayer1
= nil) and (gPlayer2
= nil) and
2101 (not gConsoleShow
) and (not gChatShow
) and (g_ActiveWindow
= nil) then
2103 if not gSpectKeyPress
then
2105 if gPlayerAction
[0, ACTION_JUMP
] and (not gSpectAuto
) then
2107 // switch spect mode
2109 SPECT_NONE
: ; // not spectator
2111 SPECT_MAPVIEW
: Inc(gSpectMode
);
2112 SPECT_PLAYERS
: gSpectMode
:= SPECT_STATS
; // reset to 1
2114 gSpectKeyPress
:= True;
2116 if (gSpectMode
= SPECT_MAPVIEW
)
2117 and (not gSpectAuto
) then
2119 if gPlayerAction
[0, ACTION_MOVELEFT
] then
2120 gSpectX
:= Max(gSpectX
- gSpectStep
, 0);
2121 if gPlayerAction
[0, ACTION_MOVERIGHT
] then
2122 gSpectX
:= Min(gSpectX
+ gSpectStep
, gMapInfo
.Width
- gScreenWidth
);
2123 if gPlayerAction
[0, ACTION_LOOKUP
] then
2124 gSpectY
:= Max(gSpectY
- gSpectStep
, 0);
2125 if gPlayerAction
[0, ACTION_LOOKDOWN
] then
2126 gSpectY
:= Min(gSpectY
+ gSpectStep
, gMapInfo
.Height
- gScreenHeight
);
2127 if gWeaponAction
[0, WP_PREV
] then
2130 if gSpectStep
> 4 then gSpectStep
:= gSpectStep
shr 1;
2131 gWeaponAction
[0, WP_PREV
] := False;
2133 if gWeaponAction
[0, WP_NEXT
] then
2136 if gSpectStep
< 64 then gSpectStep
:= gSpectStep
shl 1;
2137 gWeaponAction
[0, WP_NEXT
] := False;
2140 if (gSpectMode
= SPECT_PLAYERS
)
2141 and (not gSpectAuto
) then
2143 if gPlayerAction
[0, ACTION_LOOKUP
] then
2146 gSpectViewTwo
:= True;
2147 gSpectKeyPress
:= True;
2149 if gPlayerAction
[0, ACTION_LOOKDOWN
] then
2151 // remove second view
2152 gSpectViewTwo
:= False;
2153 gSpectKeyPress
:= True;
2155 if gPlayerAction
[0, ACTION_MOVELEFT
] then
2157 // prev player (view 1)
2158 gSpectPID1
:= GetActivePlayerID_Prev(gSpectPID1
);
2159 gSpectKeyPress
:= True;
2161 if gPlayerAction
[0, ACTION_MOVERIGHT
] then
2163 // next player (view 1)
2164 gSpectPID1
:= GetActivePlayerID_Next(gSpectPID1
);
2165 gSpectKeyPress
:= True;
2167 if gWeaponAction
[0, WP_PREV
] then
2169 // prev player (view 2)
2170 gSpectPID2
:= GetActivePlayerID_Prev(gSpectPID2
);
2171 gWeaponAction
[0, WP_PREV
] := False;
2173 if gWeaponAction
[0, WP_NEXT
] then
2175 // next player (view 2)
2176 gSpectPID2
:= GetActivePlayerID_Next(gSpectPID2
);
2177 gWeaponAction
[0, WP_NEXT
] := False;
2180 if gPlayerAction
[0, ACTION_ATTACK
] then
2182 if (gSpectMode
= SPECT_STATS
) and (not gSpectAuto
) then
2185 gSpectAutoNext
:= 0;
2186 gSpectViewTwo
:= False;
2187 gSpectKeyPress
:= True;
2192 gSpectMode
:= SPECT_STATS
;
2193 gSpectAuto
:= False;
2194 gSpectKeyPress
:= True;
2199 if (not gPlayerAction
[0, ACTION_JUMP
]) and
2200 (not gPlayerAction
[0, ACTION_ATTACK
]) and
2201 (not gPlayerAction
[0, ACTION_MOVELEFT
]) and
2202 (not gPlayerAction
[0, ACTION_MOVERIGHT
]) and
2203 (not gPlayerAction
[0, ACTION_LOOKUP
]) and
2204 (not gPlayerAction
[0, ACTION_LOOKDOWN
]) then
2205 gSpectKeyPress
:= False;
2209 if gSpectMode
= SPECT_MAPVIEW
then
2211 i
:= Min(Max(gSpectX
+ gSpectAutoStepX
, 0), gMapInfo
.Width
- gScreenWidth
);
2213 gSpectAutoNext
:= gTime
2216 i
:= Min(Max(gSpectY
+ gSpectAutoStepY
, 0), gMapInfo
.Height
- gScreenHeight
);
2218 gSpectAutoNext
:= gTime
2222 if gSpectAutoNext
<= gTime
then
2224 if gSpectAutoNext
> 0 then
2226 gSpectMode
:= GetRandomSpectMode(gSpectMode
);
2230 gSpectX
:= Random(gMapInfo
.Width
- gScreenWidth
);
2231 gSpectY
:= Random(gMapInfo
.Height
- gScreenHeight
);
2232 gSpectAutoStepX
:= Random(9) - 4;
2233 gSpectAutoStepY
:= Random(9) - 4;
2234 if ((gSpectX
< 800) and (gSpectAutoStepX
< 0)) or
2235 ((gSpectX
> gMapInfo
.Width
- gScreenWidth
- 800) and (gSpectAutoStepX
> 0)) then
2236 gSpectAutoStepX
:= gSpectAutoStepX
* -1;
2237 if ((gSpectY
< 800) and (gSpectAutoStepY
< 0)) or
2238 ((gSpectY
> gMapInfo
.Height
- gScreenHeight
- 800) and (gSpectAutoStepY
> 0)) then
2239 gSpectAutoStepY
:= gSpectAutoStepY
* -1;
2243 gSpectPID1
:= GetActivePlayerID_Random(gSpectPID1
);
2248 SPECT_STATS
: gSpectAutoNext
:= gTime
+ (Random(3) + 5) * 1000;
2249 SPECT_MAPVIEW
: gSpectAutoNext
:= gTime
+ (Random(4) + 7) * 1000;
2250 SPECT_PLAYERS
: gSpectAutoNext
:= gTime
+ (Random(7) + 8) * 1000;
2256 // Îáíîâëÿåì âñå îñòàëüíîå:
2259 g_Triggers_Update();
2261 g_Monsters_Update();
2263 g_Player_UpdateAll();
2264 g_Player_UpdatePhysicalObjects();
2266 // server: send newly spawned monsters unconditionally
2267 if (gGameSettings
.GameType
= GT_SERVER
) then
2269 if (Length(gMonstersSpawned
) > 0) then
2271 for I
:= 0 to High(gMonstersSpawned
) do MH_SEND_MonsterSpawn(gMonstersSpawned
[I
]);
2272 SetLength(gMonstersSpawned
, 0);
2276 {$IFDEF ENABLE_SOUND}
2277 if (gSoundTriggerTime
> 8) then
2279 g_Game_UpdateTriggerSounds();
2280 gSoundTriggerTime
:= 0;
2284 Inc(gSoundTriggerTime
);
2288 if (NetMode
= NET_SERVER
) then
2290 Inc(NetTimeToUpdate
);
2291 Inc(NetTimeToReliable
);
2293 // send monster updates
2294 if (NetTimeToReliable
>= NetRelupdRate
) or (NetTimeToUpdate
>= NetUpdateRate
) then
2296 // send all monsters (periodic sync)
2297 reliableUpdate
:= (NetTimeToReliable
>= NetRelupdRate
);
2299 for I
:= 0 to High(gPlayers
) do
2301 if (gPlayers
[I
] <> nil) then MH_SEND_PlayerPos(reliableUpdate
, gPlayers
[I
].UID
);
2304 g_Mons_ForEach(sendMonsPos
);
2306 // update flags that aren't stationary
2307 if gGameSettings
.GameMode
= GM_CTF
then
2308 for I
:= FLAG_RED
to FLAG_BLUE
do
2309 if gFlags
[I
].NeedSend
then
2311 gFlags
[I
].NeedSend
:= False;
2315 // update items that aren't stationary
2316 g_Items_ForEachAlive(sendItemPos
);
2318 if reliableUpdate
then
2320 NetTimeToReliable
:= 0;
2321 NetTimeToUpdate
:= NetUpdateRate
;
2325 NetTimeToUpdate
:= 0;
2330 // send only mosters with some unexpected changes
2331 g_Mons_ForEach(sendMonsPosUnexpected
);
2334 // send unexpected platform changes
2335 g_Map_NetSendInterestingPanels();
2337 g_Net_Slist_ServerUpdate();
2339 if NetUseMaster then
2341 if (gTime >= NetTimeToMaster) or g_Net_Slist_IsConnectionInProgress then
2343 if (not g_Net_Slist_IsConnectionActive) then g_Net_Slist_Connect(false); // non-blocking connection to the master
2345 NetTimeToMaster := gTime + NetMasterRate;
2350 else if (NetMode
= NET_CLIENT
) then
2352 MC_SEND_PlayerPos();
2354 end; // if gameOn ...
2356 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
2357 if g_ActiveWindow
<> nil then
2359 w
:= e_GetFirstKeyPressed();
2361 if (w
<> IK_INVALID
) then
2363 Msg
.Msg
:= MESSAGE_DIKEY
;
2365 g_ActiveWindow
.OnMessage(Msg
);
2368 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
2369 if g_ActiveWindow
<> nil then
2370 g_ActiveWindow
.Update();
2372 // Íóæíî ñìåíèòü ðàçðåøåíèå:
2373 if gResolutionChange
then
2375 e_WriteLog('Changing resolution', TMsgType
.Notify
);
2376 g_Game_ChangeResolution(gRC_Width
, gRC_Height
, gRC_FullScreen
, gRC_Maximized
);
2377 gResolutionChange
:= False;
2378 g_ActiveWindow
:= nil;
2381 // Íóæíî ñìåíèòü ÿçûê:
2382 if gLanguageChange
then
2384 //e_WriteLog('Read language file', MSG_NOTIFY);
2385 //g_Language_Load(DataDir + gLanguage + '.txt');
2386 g_Language_Set(gLanguage
);
2390 gLanguageChange
:= False;
2394 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
2395 if e_KeyPressed(IK_F10
) and
2397 (not gConsoleShow
) and
2398 (g_ActiveWindow
= nil) then
2403 Time
:= sys_GetTicks() {div 1000};
2405 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
2406 if gDelayedEvents
<> nil then
2407 for a
:= 0 to High(gDelayedEvents
) do
2408 if gDelayedEvents
[a
].Pending
and
2410 ((gDelayedEvents
[a
].DEType
= DE_GLOBEVENT
) and (gDelayedEvents
[a
].Time
<= Time
)) or
2411 ((gDelayedEvents
[a
].DEType
> DE_GLOBEVENT
) and (gDelayedEvents
[a
].Time
<= gTime
))
2414 case gDelayedEvents
[a
].DEType
of
2416 g_Game_ExecuteEvent(gDelayedEvents
[a
].DEStr
);
2420 {$IFDEF ENABLE_SOUND}
2421 g_Game_Announce_GoodShot(gDelayedEvents
[a
].DENum
);
2427 {$IFDEF ENABLE_SOUND}
2428 g_Game_Announce_KillCombo(gDelayedEvents
[a
].DENum
);
2430 if g_Game_IsNet
and g_Game_IsServer
then
2431 MH_SEND_GameEvent(NET_EV_KILLCOMBO
, gDelayedEvents
[a
].DENum
);
2436 {$IFDEF ENABLE_SOUND}
2437 g_Game_Announce_BodyKill(gDelayedEvents
[a
].DENum
);
2441 gDelayedEvents
[a
].Pending
:= False;
2444 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2445 UPSCounter
:= UPSCounter
+ 1;
2446 if Time
- UPSTime
>= 1000 then
2455 g_Weapon_AddDynLights();
2456 g_Items_AddDynLights();
2460 {$IFDEF ENABLE_SOUND}
2461 procedure g_Game_LoadChatSounds(Resource
: string);
2464 FileName
, Snd
: string;
2466 len
, cnt
, tags
, i
, j
: Integer;
2469 FileName
:= g_ExtractWadName(Resource
);
2471 WAD
:= TWADFile
.Create();
2472 WAD
.ReadFile(FileName
);
2474 if not WAD
.GetResource(g_ExtractFilePathName(Resource
), p
, len
) then
2482 cfg
:= TConfig
.CreateMem(p
, len
);
2484 cnt
:= cfg
.ReadInt('ChatSounds', 'Count', 0);
2486 SetLength(gChatSounds
, cnt
);
2487 for i
:= 0 to Length(gChatSounds
) - 1 do
2489 gChatSounds
[i
].Sound
:= nil;
2490 Snd
:= Trim(cfg
.ReadStr(IntToStr(i
), 'Sound', ''));
2491 tags
:= cfg
.ReadInt(IntToStr(i
), 'Tags', 0);
2492 if (Snd
= '') or (Tags
<= 0) then
2494 g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i
), GameWAD
+':'+Snd
);
2495 gChatSounds
[i
].Sound
:= TPlayableSound
.Create();
2496 gChatSounds
[i
].Sound
.SetByName('SOUND_CHAT_MACRO' + IntToStr(i
));
2497 SetLength(gChatSounds
[i
].Tags
, tags
);
2498 for j
:= 0 to tags
- 1 do
2499 gChatSounds
[i
].Tags
[j
] := toLowerCase1251(cfg
.ReadStr(IntToStr(i
), 'Tag' + IntToStr(j
), ''));
2500 gChatSounds
[i
].FullWord
:= cfg
.ReadBool(IntToStr(i
), 'FullWord', False);
2506 procedure g_Game_FreeChatSounds();
2510 for i
:= 0 to Length(gChatSounds
) - 1 do
2512 gChatSounds
[i
].Sound
.Free();
2513 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i
));
2515 SetLength(gChatSounds
, 0);
2520 procedure g_Game_LoadData();
2527 if DataLoaded
then Exit
;
2529 e_WriteLog('Loading game data...', TMsgType
.Notify
);
2531 g_Texture_CreateWADEx('NOTEXTURE', GameWAD
+':TEXTURES\NOTEXTURE');
2532 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD
+':TEXTURES\HUD');
2533 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD
+':TEXTURES\AIRBAR');
2534 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD
+':TEXTURES\JETBAR');
2535 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD
+':TEXTURES\HUDBG');
2536 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD
+':TEXTURES\ARMORHUD');
2537 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD
+':TEXTURES\FLAGHUD_R_BASE');
2538 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD
+':TEXTURES\FLAGHUD_R_STOLEN');
2539 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD
+':TEXTURES\FLAGHUD_R_DROP');
2540 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD
+':TEXTURES\FLAGHUD_B_BASE');
2541 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD
+':TEXTURES\FLAGHUD_B_STOLEN');
2542 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD
+':TEXTURES\FLAGHUD_B_DROP');
2543 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD
+':TEXTURES\TALKBUBBLE');
2544 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD
+':TEXTURES\PENTA');
2545 g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD
+':TEXTURES\PLRIND');
2548 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD
+':TEXTURES\LLEFT') then hasPBarGfx
:= false;
2549 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD
+':TEXTURES\LMARKER') then hasPBarGfx
:= false;
2550 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD
+':TEXTURES\LMIDDLE') then hasPBarGfx
:= false;
2551 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD
+':TEXTURES\LRIGHT') then hasPBarGfx
:= false;
2555 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl
, hl
);
2556 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr
, hr
);
2557 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb
, hb
);
2558 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm
, hm
);
2559 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
2565 hasPBarGfx
:= false;
2569 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD
+':TEXTURES\TELEPORT', 64, 64, 10, False);
2570 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD
+':WEAPONS\PUNCH', 64, 64, 4, False);
2571 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD
+':WEAPONS\PUNCH_UP', 64, 64, 4, False);
2572 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD
+':WEAPONS\PUNCH_DN', 64, 64, 4, False);
2573 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD
+':WEAPONS\PUNCHB', 64, 64, 4, False);
2574 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD
+':WEAPONS\PUNCHB_UP', 64, 64, 4, False);
2575 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD
+':WEAPONS\PUNCHB_DN', 64, 64, 4, False);
2577 {$IFDEF ENABLE_SOUND}
2578 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD
+':SOUNDS\TELEPORT');
2579 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD
+':SOUNDS\NOTELEPORT');
2580 g_Sound_CreateWADEx('SOUND_GAME_SECRET', GameWAD
+':SOUNDS\SECRET');
2581 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD
+':SOUNDS\DOOROPEN');
2582 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD
+':SOUNDS\DOORCLOSE');
2583 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD
+':SOUNDS\BULK1');
2584 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD
+':SOUNDS\BULK2');
2585 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD
+':SOUNDS\BUBBLE1');
2586 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD
+':SOUNDS\BUBBLE2');
2587 g_Sound_CreateWADEx('SOUND_GAME_BURNING', GameWAD
+':SOUNDS\BURNING');
2588 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD
+':SOUNDS\SWITCH1');
2589 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD
+':SOUNDS\SWITCH0');
2590 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD
+':SOUNDS\RADIO');
2591 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD
+':SOUNDS\GOOD1');
2592 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD
+':SOUNDS\GOOD2');
2593 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD
+':SOUNDS\GOOD3');
2594 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD
+':SOUNDS\GOOD4');
2595 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD
+':SOUNDS\KILL2X');
2596 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD
+':SOUNDS\KILL3X');
2597 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD
+':SOUNDS\KILL4X');
2598 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD
+':SOUNDS\KILLMX');
2599 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD
+':SOUNDS\MUHAHA1');
2600 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD
+':SOUNDS\MUHAHA2');
2601 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD
+':SOUNDS\MUHAHA3');
2602 g_Sound_CreateWADEx('SOUND_CTF_GET1', GameWAD
+':SOUNDS\GETFLAG1');
2603 g_Sound_CreateWADEx('SOUND_CTF_GET2', GameWAD
+':SOUNDS\GETFLAG2');
2604 g_Sound_CreateWADEx('SOUND_CTF_LOST1', GameWAD
+':SOUNDS\LOSTFLG1');
2605 g_Sound_CreateWADEx('SOUND_CTF_LOST2', GameWAD
+':SOUNDS\LOSTFLG2');
2606 g_Sound_CreateWADEx('SOUND_CTF_RETURN1', GameWAD
+':SOUNDS\RETFLAG1');
2607 g_Sound_CreateWADEx('SOUND_CTF_RETURN2', GameWAD
+':SOUNDS\RETFLAG2');
2608 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE1', GameWAD
+':SOUNDS\CAPFLAG1');
2609 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE2', GameWAD
+':SOUNDS\CAPFLAG2');
2611 goodsnd
[0] := TPlayableSound
.Create();
2612 goodsnd
[1] := TPlayableSound
.Create();
2613 goodsnd
[2] := TPlayableSound
.Create();
2614 goodsnd
[3] := TPlayableSound
.Create();
2616 goodsnd
[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2617 goodsnd
[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2618 goodsnd
[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2619 goodsnd
[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2621 killsnd
[0] := TPlayableSound
.Create();
2622 killsnd
[1] := TPlayableSound
.Create();
2623 killsnd
[2] := TPlayableSound
.Create();
2624 killsnd
[3] := TPlayableSound
.Create();
2626 killsnd
[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2627 killsnd
[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2628 killsnd
[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2629 killsnd
[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2631 hahasnd
[0] := TPlayableSound
.Create();
2632 hahasnd
[1] := TPlayableSound
.Create();
2633 hahasnd
[2] := TPlayableSound
.Create();
2635 hahasnd
[0].SetByName('SOUND_ANNOUNCER_MUHAHA1');
2636 hahasnd
[1].SetByName('SOUND_ANNOUNCER_MUHAHA2');
2637 hahasnd
[2].SetByName('SOUND_ANNOUNCER_MUHAHA3');
2639 sound_get_flag
[0] := TPlayableSound
.Create();
2640 sound_get_flag
[1] := TPlayableSound
.Create();
2641 sound_lost_flag
[0] := TPlayableSound
.Create();
2642 sound_lost_flag
[1] := TPlayableSound
.Create();
2643 sound_ret_flag
[0] := TPlayableSound
.Create();
2644 sound_ret_flag
[1] := TPlayableSound
.Create();
2645 sound_cap_flag
[0] := TPlayableSound
.Create();
2646 sound_cap_flag
[1] := TPlayableSound
.Create();
2648 sound_get_flag
[0].SetByName('SOUND_CTF_GET1');
2649 sound_get_flag
[1].SetByName('SOUND_CTF_GET2');
2650 sound_lost_flag
[0].SetByName('SOUND_CTF_LOST1');
2651 sound_lost_flag
[1].SetByName('SOUND_CTF_LOST2');
2652 sound_ret_flag
[0].SetByName('SOUND_CTF_RETURN1');
2653 sound_ret_flag
[1].SetByName('SOUND_CTF_RETURN2');
2654 sound_cap_flag
[0].SetByName('SOUND_CTF_CAPTURE1');
2655 sound_cap_flag
[1].SetByName('SOUND_CTF_CAPTURE2');
2657 g_Game_LoadChatSounds(GameWAD
+':CHATSND\SNDCFG');
2660 g_Game_SetLoadingText(_lc
[I_LOAD_ITEMS_DATA
], 0, False);
2663 g_Game_SetLoadingText(_lc
[I_LOAD_WEAPONS_DATA
], 0, False);
2664 g_Weapon_LoadData();
2666 g_Monsters_LoadData();
2671 procedure g_Game_FreeData();
2673 if not DataLoaded
then Exit
;
2676 g_Weapon_FreeData();
2677 g_Monsters_FreeData();
2679 e_WriteLog('Releasing game data...', TMsgType
.Notify
);
2681 g_Texture_Delete('NOTEXTURE');
2682 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2683 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2684 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2685 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2686 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2687 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2688 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2689 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2690 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2691 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2692 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2693 g_Frames_DeleteByName('FRAMES_TELEPORT');
2694 g_Frames_DeleteByName('FRAMES_PUNCH');
2695 g_Frames_DeleteByName('FRAMES_PUNCH_UP');
2696 g_Frames_DeleteByName('FRAMES_PUNCH_DN');
2697 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK');
2698 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP');
2699 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
2701 {$IFDEF ENABLE_SOUND}
2702 g_Sound_Delete('SOUND_GAME_TELEPORT');
2703 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2704 g_Sound_Delete('SOUND_GAME_SECRET');
2705 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2706 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2707 g_Sound_Delete('SOUND_GAME_BULK1');
2708 g_Sound_Delete('SOUND_GAME_BULK2');
2709 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2710 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2711 g_Sound_Delete('SOUND_GAME_BURNING');
2712 g_Sound_Delete('SOUND_GAME_SWITCH1');
2713 g_Sound_Delete('SOUND_GAME_SWITCH0');
2720 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2721 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2722 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2723 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2730 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2731 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2732 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2733 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2739 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1');
2740 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2');
2741 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3');
2743 sound_get_flag
[0].Free();
2744 sound_get_flag
[1].Free();
2745 sound_lost_flag
[0].Free();
2746 sound_lost_flag
[1].Free();
2747 sound_ret_flag
[0].Free();
2748 sound_ret_flag
[1].Free();
2749 sound_cap_flag
[0].Free();
2750 sound_cap_flag
[1].Free();
2752 g_Sound_Delete('SOUND_CTF_GET1');
2753 g_Sound_Delete('SOUND_CTF_GET2');
2754 g_Sound_Delete('SOUND_CTF_LOST1');
2755 g_Sound_Delete('SOUND_CTF_LOST2');
2756 g_Sound_Delete('SOUND_CTF_RETURN1');
2757 g_Sound_Delete('SOUND_CTF_RETURN2');
2758 g_Sound_Delete('SOUND_CTF_CAPTURE1');
2759 g_Sound_Delete('SOUND_CTF_CAPTURE2');
2761 g_Game_FreeChatSounds();
2764 DataLoaded
:= False;
2767 procedure DrawCustomStat();
2773 ww2
, hh2
, r
, g
, b
, rr
, gg
, bb
: Byte;
2774 s1
, s2
, topstr
: String;
2776 e_TextureFontGetSize(gStdFont
, ww2
, hh2
);
2780 if g_Console_Action(ACTION_SCORES
) then
2782 if not gStatsPressed
then
2784 gStatsOff
:= not gStatsOff
;
2785 gStatsPressed
:= True;
2789 gStatsPressed
:= False;
2793 s1
:= _lc
[I_MENU_INTER_NOTICE_TAB
];
2794 w
:= (Length(s1
) * ww2
) div 2;
2795 x
:= gScreenWidth
div 2 - w
;
2797 e_TextureFontPrint(x
, y
, s1
, gStdFont
);
2801 if (gGameSettings
.GameMode
= GM_COOP
) then
2803 if gMissionFailed
then
2804 topstr
:= _lc
[I_MENU_INTER_MISSION_FAIL
]
2806 topstr
:= _lc
[I_MENU_INTER_LEVEL_COMPLETE
];
2809 topstr
:= _lc
[I_MENU_INTER_ROUND_OVER
];
2811 e_CharFont_GetSize(gMenuFont
, topstr
, ww1
, hh1
);
2812 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(ww1
div 2), 16, topstr
);
2814 if g_Game_IsNet
then
2816 topstr
:= Format(_lc
[I_MENU_INTER_NOTICE_TIME
], [gServInterTime
]);
2817 if not gChatShow
then
2818 e_TextureFontPrintEx((gScreenWidth
div 2)-(Length(topstr
)*ww2
div 2),
2819 gScreenHeight
-(hh2
+4)*2, topstr
, gStdFont
, 255, 255, 255, 1);
2822 if g_Game_IsClient
then
2823 topstr
:= _lc
[I_MENU_INTER_NOTICE_MAP
]
2825 topstr
:= _lc
[I_MENU_INTER_NOTICE_SPACE
];
2826 if not gChatShow
then
2827 e_TextureFontPrintEx((gScreenWidth
div 2)-(Length(topstr
)*ww2
div 2),
2828 gScreenHeight
-(hh2
+4), topstr
, gStdFont
, 255, 255, 255, 1);
2833 w
:= gScreenWidth
-x
*2;
2839 e_DrawFillQuad(x
, y
, gScreenWidth
-x
-1, gScreenHeight
-y
-1, 64, 64, 64, 32);
2840 e_DrawQuad(x
, y
, gScreenWidth
-x
-1, gScreenHeight
-y
-1, 255, 127, 0);
2842 m
:= Max(Length(_lc
[I_MENU_MAP
])+1, Length(_lc
[I_GAME_GAME_TIME
])+1)*ww2
;
2844 case CustomStat
.GameMode
of
2847 if gGameSettings
.MaxLives
= 0 then
2848 s1
:= _lc
[I_GAME_DM
]
2850 s1
:= _lc
[I_GAME_LMS
];
2854 if gGameSettings
.MaxLives
= 0 then
2855 s1
:= _lc
[I_GAME_TDM
]
2857 s1
:= _lc
[I_GAME_TLMS
];
2859 GM_CTF
: s1
:= _lc
[I_GAME_CTF
];
2862 if gGameSettings
.MaxLives
= 0 then
2863 s1
:= _lc
[I_GAME_COOP
]
2865 s1
:= _lc
[I_GAME_SURV
];
2871 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
2875 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_MENU_MAP
], gStdFont
, 255, 127, 0, 1);
2876 e_TextureFontPrint(x
+8+m
, _y
, Format('%s - %s', [CustomStat
.Map
, CustomStat
.MapName
]), gStdFont
);
2879 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_GAME_TIME
], gStdFont
, 255, 127, 0, 1);
2880 e_TextureFontPrint(x
+8+m
, _y
, Format('%d:%.2d:%.2d', [CustomStat
.GameTime
div 1000 div 3600,
2881 (CustomStat
.GameTime
div 1000 div 60) mod 60,
2882 CustomStat
.GameTime
div 1000 mod 60]), gStdFont
);
2884 pc
:= Length(CustomStat
.PlayerStat
);
2885 if pc
= 0 then Exit
;
2887 if CustomStat
.GameMode
= GM_COOP
then
2889 m
:= Max(Length(_lc
[I_GAME_MONSTERS
])+1, Length(_lc
[I_GAME_SECRETS
])+1)*ww2
;
2891 s2
:= _lc
[I_GAME_MONSTERS
];
2892 e_TextureFontPrintEx(x
+8, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2893 e_TextureFontPrintEx(x
+8+m
, _y
, IntToStr(gCoopMonstersKilled
) + '/' + IntToStr(gTotalMonsters
), gStdFont
, 255, 255, 255, 1);
2895 s2
:= _lc
[I_GAME_SECRETS
];
2896 e_TextureFontPrintEx(x
+8, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2897 e_TextureFontPrintEx(x
+8+m
, _y
, IntToStr(gCoopSecretsFound
) + '/' + IntToStr(gSecretsCount
), gStdFont
, 255, 255, 255, 1);
2900 m
:= Max(Length(_lc
[I_GAME_MONSTERS_TOTAL
])+1, Length(_lc
[I_GAME_SECRETS_TOTAL
])+1)*ww2
;
2902 s2
:= _lc
[I_GAME_MONSTERS_TOTAL
];
2903 e_TextureFontPrintEx(x
+250, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2904 e_TextureFontPrintEx(x
+250+m
, _y
, IntToStr(gCoopTotalMonstersKilled
) + '/' + IntToStr(gCoopTotalMonsters
), gStdFont
, 255, 255, 255, 1);
2906 s2
:= _lc
[I_GAME_SECRETS_TOTAL
];
2907 e_TextureFontPrintEx(x
+250, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2908 e_TextureFontPrintEx(x
+250+m
, _y
, IntToStr(gCoopTotalSecretsFound
) + '/' + IntToStr(gCoopTotalSecrets
), gStdFont
, 255, 255, 255, 1);
2912 if CustomStat
.GameMode
in [GM_TDM
, GM_CTF
] then
2917 if TeamStat
[TEAM_RED
].Score
> TeamStat
[TEAM_BLUE
].Score
then s1
:= _lc
[I_GAME_WIN_RED
]
2918 else if TeamStat
[TEAM_BLUE
].Score
> TeamStat
[TEAM_RED
].Score
then s1
:= _lc
[I_GAME_WIN_BLUE
]
2919 else s1
:= _lc
[I_GAME_WIN_DRAW
];
2921 e_TextureFontPrintEx(x
+8+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
2924 for t
:= TEAM_RED
to TEAM_BLUE
do
2926 if t
= TEAM_RED
then
2928 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_TEAM_RED
],
2929 gStdFont
, 255, 0, 0, 1);
2930 e_TextureFontPrintEx(x
+w1
+8, _y
, IntToStr(CustomStat
.TeamStat
[TEAM_RED
].Score
),
2931 gStdFont
, 255, 0, 0, 1);
2938 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_TEAM_BLUE
],
2939 gStdFont
, 0, 0, 255, 1);
2940 e_TextureFontPrintEx(x
+w1
+8, _y
, IntToStr(CustomStat
.TeamStat
[TEAM_BLUE
].Score
),
2941 gStdFont
, 0, 0, 255, 1);
2947 e_DrawLine(1, x
+8, _y
+20, x
-8+w
, _y
+20, r
, g
, b
);
2950 for p
:= 0 to High(CustomStat
.PlayerStat
) do
2951 if CustomStat
.PlayerStat
[p
].Team
= t
then
2952 with CustomStat
.PlayerStat
[p
] do
2966 if (gPlayers
[Num
] <> nil) and (gPlayers
[Num
].FReady
) then
2967 e_TextureFontPrintEx(x
+16, _y
, Name
+ ' *', gStdFont
, rr
, gg
, bb
, 1)
2969 e_TextureFontPrintEx(x
+16, _y
, Name
, gStdFont
, rr
, gg
, bb
, 1);
2970 e_TextureFontPrintEx(x
+w1
+16, _y
, IntToStr(Frags
), gStdFont
, rr
, gg
, bb
, 1);
2971 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
, IntToStr(Deaths
), gStdFont
, rr
, gg
, bb
, 1);
2978 else if CustomStat
.GameMode
in [GM_DM
, GM_COOP
] then
2981 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_PLAYER_NAME
], gStdFont
, 255, 127, 0, 1);
2982 e_TextureFontPrintEx(x
+8+w1
, _y
, _lc
[I_GAME_FRAGS
], gStdFont
, 255, 127, 0, 1);
2983 e_TextureFontPrintEx(x
+8+w1
+w2
, _y
, _lc
[I_GAME_DEATHS
], gStdFont
, 255, 127, 0, 1);
2986 for p
:= 0 to High(CustomStat
.PlayerStat
) do
2987 with CustomStat
.PlayerStat
[p
] do
2989 e_DrawFillQuad(x
+8, _y
+4, x
+24-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
2996 if (gPlayers
[Num
] <> nil) and (gPlayers
[Num
].FReady
) then
2997 e_TextureFontPrintEx(x
+8+16+8, _y
+4, Name
+ ' *', gStdFont
, r
, r
, r
, 1, True)
2999 e_TextureFontPrintEx(x
+8+16+8, _y
+4, Name
, gStdFont
, r
, r
, r
, 1, True);
3000 e_TextureFontPrintEx(x
+w1
+8+16+8, _y
+4, IntToStr(Frags
), gStdFont
, r
, r
, r
, 1, True);
3001 e_TextureFontPrintEx(x
+w1
+w2
+8+16+8, _y
+4, IntToStr(Deaths
), gStdFont
, r
, r
, r
, 1, True);
3006 // HACK: take stats screenshot immediately after the first frame of the stats showing
3007 if gScreenshotStats
and (not StatShotDone
) and (Length(CustomStat
.PlayerStat
) > 1) then
3009 g_TakeScreenShot('stats/' + StatFilename
);
3010 StatShotDone
:= True;
3014 procedure DrawSingleStat();
3016 tm
, key_x
, val_x
, y
: Integer;
3020 procedure player_stat(n
: Integer);
3026 s1
:= Format(' %d ', [SingleStat
.PlayerStat
[n
].Kills
]);
3027 s2
:= Format(' %d', [gTotalMonsters
]);
3029 e_CharFont_Print(gMenuFont
, key_x
, y
, _lc
[I_MENU_INTER_KILLS
]);
3030 e_CharFont_PrintEx(gMenuFont
, val_x
, y
, s1
, _RGB(255, 0, 0));
3031 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3032 e_CharFont_Print(gMenuFont
, val_x
+w1
, y
, '/');
3034 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3035 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
, s2
, _RGB(255, 0, 0));
3037 // "Kills-per-minute: ##.#":
3038 s1
:= _lc
[I_MENU_INTER_KPM
];
3040 kpm
:= (SingleStat
.PlayerStat
[n
].Kills
/ tm
) * 60
3042 kpm
:= SingleStat
.PlayerStat
[n
].Kills
;
3043 s2
:= Format(' %.1f', [kpm
]);
3045 e_CharFont_Print(gMenuFont
, key_x
, y
+32, s1
);
3046 e_CharFont_PrintEx(gMenuFont
, val_x
, y
+32, s2
, _RGB(255, 0, 0));
3048 // "Secrets found: # / #":
3049 s1
:= Format(' %d ', [SingleStat
.PlayerStat
[n
].Secrets
]);
3050 s2
:= Format(' %d', [SingleStat
.TotalSecrets
]);
3052 e_CharFont_Print(gMenuFont
, key_x
, y
+64, _lc
[I_MENU_INTER_SECRETS
]);
3053 e_CharFont_PrintEx(gMenuFont
, val_x
, y
+64, s1
, _RGB(255, 0, 0));
3054 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3055 e_CharFont_Print(gMenuFont
, val_x
+w1
, y
+64, '/');
3057 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3058 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
+64, s2
, _RGB(255, 0, 0));
3062 // "Level Complete":
3063 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_INTER_LEVEL_COMPLETE
], w1
, h
);
3064 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 32, _lc
[I_MENU_INTER_LEVEL_COMPLETE
]);
3066 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
3067 s1
:= _lc
[I_MENU_INTER_KPM
];
3068 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3071 e_CharFont_GetSize(gMenuFont
, s1
, w2
, h
);
3073 key_x
:= (gScreenWidth
-w1
-w2
) div 2;
3074 val_x
:= key_x
+ w1
;
3077 tm
:= SingleStat
.GameTime
div 1000;
3078 s1
:= _lc
[I_MENU_INTER_TIME
];
3079 s2
:= Format(' %d:%.2d:%.2d', [tm
div (60*60), (tm
mod (60*60)) div 60, tm
mod 60]);
3081 e_CharFont_Print(gMenuFont
, key_x
, 80, s1
);
3082 e_CharFont_PrintEx(gMenuFont
, val_x
, 80, s2
, _RGB(255, 0, 0));
3084 if SingleStat
.TwoPlayers
then
3087 s1
:= _lc
[I_MENU_PLAYER_1
];
3088 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3089 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 128, s1
);
3091 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3096 s1
:= _lc
[I_MENU_PLAYER_2
];
3097 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3098 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 288, s1
);
3100 // Ñòàòèñòèêà âòîðîãî èãðîêà:
3106 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3112 procedure DrawLoadingStat();
3113 procedure drawRect (x
, y
, w
, h
: Integer);
3115 if (w
< 1) or (h
< 1) then exit
;
3117 glVertex2f(x
+0.375, y
+0.375);
3118 glVertex2f(x
+w
+0.375, y
+0.375);
3119 glVertex2f(x
+w
+0.375, y
+h
+0.375);
3120 glVertex2f(x
+0.375, y
+h
+0.375);
3124 function drawPBar (cur
, total
: Integer; washere
: Boolean): Boolean;
3126 rectW
, rectH
: Integer;
3133 idl
, idr
, idb
, idm
: LongWord
;
3137 if (total
< 1) then exit
;
3138 if (cur
< 1) then exit
; // don't blink
3139 if (not washere
) and (cur
>= total
) then exit
; // don't blink
3140 //if (cur < 0) then cur := 0;
3141 //if (cur > total) then cur := total;
3144 if (hasPBarGfx
) then
3146 g_Texture_Get('UI_GFX_PBAR_LEFT', idl
);
3147 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl
, hl
);
3148 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr
);
3149 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr
, hr
);
3150 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb
);
3151 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb
, hb
);
3152 g_Texture_Get('UI_GFX_PBAR_MARKER', idm
);
3153 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm
, hm
);
3155 //rectW := gScreenWidth-360;
3156 rectW
:= trunc(624.0*gScreenWidth
/1024.0);
3159 x0
:= (gScreenWidth
-rectW
) div 2;
3160 y0
:= gScreenHeight
-rectH
-64;
3161 if (y0
< 2) then y0
:= 2;
3163 glEnable(GL_SCISSOR_TEST
);
3166 glScissor(x0
, gScreenHeight
-y0
-rectH
, rectW
, rectH
);
3167 e_DrawSize(idl
, x0
, y0
, 0, true, false, wl
, hl
);
3168 e_DrawSize(idr
, x0
+rectW
-wr
, y0
, 0, true, false, wr
, hr
);
3171 glScissor(x0
+wl
, gScreenHeight
-y0
-rectH
, rectW
-wl
-wr
, rectH
);
3173 while (f
< x0
+rectW
) do
3175 e_DrawSize(idb
, f
, y0
, 0, true, false, wb
, hb
);
3180 wdt
:= (rectW
-wl
-wr
)*cur
div total
;
3181 if (wdt
> rectW
-wl
-wr
) then wdt
:= rectW
-wr
-wr
;
3184 my
:= y0
; // don't be so smart, ketmar: +(rectH-wm) div 2;
3185 glScissor(x0
+wl
, gScreenHeight
-my
-rectH
, wdt
, hm
);
3189 e_DrawSize(idm
, f
, y0
, 0, true, false, wm
, hm
);
3195 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
3199 rectW
:= gScreenWidth
-64;
3202 x0
:= (gScreenWidth
-rectW
) div 2;
3203 y0
:= gScreenHeight
-rectH
-64;
3204 if (y0
< 2) then y0
:= 2;
3206 glDisable(GL_BLEND
);
3207 glDisable(GL_TEXTURE_2D
);
3209 //glClearColor(0, 0, 0, 0);
3210 //glClear(GL_COLOR_BUFFER_BIT);
3212 glColor4ub(127, 127, 127, 255);
3213 drawRect(x0
-2, y0
-2, rectW
+4, rectH
+4);
3215 glColor4ub(0, 0, 0, 255);
3216 drawRect(x0
-1, y0
-1, rectW
+2, rectH
+2);
3218 glColor4ub(127, 127, 127, 255);
3219 wdt
:= rectW
*cur
div total
;
3220 if (wdt
> rectW
) then wdt
:= rectW
;
3221 drawRect(x0
, y0
, wdt
, rectH
);
3230 if (Length(LoadingStat
.Msgs
) = 0) then exit
;
3232 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_LOADING
], ww
, hh
);
3233 yy
:= (gScreenHeight
div 3);
3234 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(ww
div 2), yy
-2*hh
, _lc
[I_MENU_LOADING
]);
3235 xx
:= (gScreenWidth
div 3);
3239 for i
:= 0 to NextMsg
-1 do
3241 if (i
= (NextMsg
-1)) and (MaxValue
> 0) then
3242 s
:= Format('%s: %d/%d', [Msgs
[i
], CurValue
, MaxValue
])
3246 e_CharFont_PrintEx(gMenuSmallFont
, xx
, yy
, s
, _RGB(255, 0, 0));
3247 yy
:= yy
+ LOADING_INTERLINE
;
3248 PBarWasHere
:= drawPBar(CurValue
, MaxValue
, PBarWasHere
);
3253 procedure DrawMenuBackground(tex
: AnsiString
);
3259 if g_Texture_Get(tex
, ID
) then
3261 e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
3262 e_GetTextureSize(ID
, @w
, @h
);
3264 w
:= round(w
* 1.333 * (gScreenHeight
/ h
))
3266 w
:= trunc(w
* (gScreenHeight
/ h
));
3267 e_DrawSize(ID
, (gScreenWidth
- w
) div 2, 0, 0, False, False, w
, gScreenHeight
);
3269 else e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
3272 procedure DrawMinimap(p
: TPlayer
; RenderRect
: e_graphics
.TRect
);
3274 a
, aX
, aY
, aX2
, aY2
, Scale
, ScaleSz
: Integer;
3276 function monDraw (mon
: TMonster
): Boolean;
3278 result
:= false; // don't stop
3283 // Ëåâûé âåðõíèé óãîë
3284 aX
:= Obj
.X
div ScaleSz
+ 1;
3285 aY
:= Obj
.Y
div ScaleSz
+ 1;
3287 aX2
:= max(Obj
.Rect
.Width
div ScaleSz
, 1);
3288 aY2
:= max(Obj
.Rect
.Height
div ScaleSz
, 1);
3289 // Ïðàâûé íèæíèé óãîë
3290 aX2
:= aX
+ aX2
- 1;
3291 aY2
:= aY
+ aY2
- 1;
3292 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 255, 0, 0);
3298 if (gMapInfo
.Width
> RenderRect
.Right
- RenderRect
.Left
) or
3299 (gMapInfo
.Height
> RenderRect
.Bottom
- RenderRect
.Top
) then
3302 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
3303 ScaleSz
:= 16 div Scale
;
3304 // Ðàçìåðû ìèíè-êàðòû:
3305 aX
:= max(gMapInfo
.Width
div ScaleSz
, 1);
3306 aY
:= max(gMapInfo
.Height
div ScaleSz
, 1);
3308 e_DrawFillQuad(0, 0, aX
-1, aY
-1, 0, 0, 0, 0);
3310 if gWalls
<> nil then
3313 for a
:= 0 to High(gWalls
) do
3315 if PanelType
<> 0 then
3317 // Ëåâûé âåðõíèé óãîë:
3318 aX
:= X
div ScaleSz
;
3319 aY
:= Y
div ScaleSz
;
3321 aX2
:= max(Width
div ScaleSz
, 1);
3322 aY2
:= max(Height
div ScaleSz
, 1);
3323 // Ïðàâûé íèæíèé óãîë:
3324 aX2
:= aX
+ aX2
- 1;
3325 aY2
:= aY
+ aY2
- 1;
3328 PANEL_WALL
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 208, 208, 208, 0);
3329 PANEL_OPENDOOR
, PANEL_CLOSEDOOR
:
3330 if Enabled
then e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 160, 160, 160, 0);
3334 if gSteps
<> nil then
3337 for a
:= 0 to High(gSteps
) do
3339 if PanelType
<> 0 then
3341 // Ëåâûé âåðõíèé óãîë:
3342 aX
:= X
div ScaleSz
;
3343 aY
:= Y
div ScaleSz
;
3345 aX2
:= max(Width
div ScaleSz
, 1);
3346 aY2
:= max(Height
div ScaleSz
, 1);
3347 // Ïðàâûé íèæíèé óãîë:
3348 aX2
:= aX
+ aX2
- 1;
3349 aY2
:= aY
+ aY2
- 1;
3351 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 128, 128, 128, 0);
3354 if gLifts
<> nil then
3357 for a
:= 0 to High(gLifts
) do
3359 if PanelType
<> 0 then
3361 // Ëåâûé âåðõíèé óãîë:
3362 aX
:= X
div ScaleSz
;
3363 aY
:= Y
div ScaleSz
;
3365 aX2
:= max(Width
div ScaleSz
, 1);
3366 aY2
:= max(Height
div ScaleSz
, 1);
3367 // Ïðàâûé íèæíèé óãîë:
3368 aX2
:= aX
+ aX2
- 1;
3369 aY2
:= aY
+ aY2
- 1;
3372 LIFTTYPE_UP
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 116, 72, 36, 0);
3373 LIFTTYPE_DOWN
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 116, 124, 96, 0);
3374 LIFTTYPE_LEFT
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 200, 80, 4, 0);
3375 LIFTTYPE_RIGHT
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 252, 140, 56, 0);
3379 if gWater
<> nil then
3382 for a
:= 0 to High(gWater
) do
3384 if PanelType
<> 0 then
3386 // Ëåâûé âåðõíèé óãîë:
3387 aX
:= X
div ScaleSz
;
3388 aY
:= Y
div ScaleSz
;
3390 aX2
:= max(Width
div ScaleSz
, 1);
3391 aY2
:= max(Height
div ScaleSz
, 1);
3392 // Ïðàâûé íèæíèé óãîë:
3393 aX2
:= aX
+ aX2
- 1;
3394 aY2
:= aY
+ aY2
- 1;
3396 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 0, 192, 0);
3399 if gAcid1
<> nil then
3401 // Ðèñóåì êèñëîòó 1:
3402 for a
:= 0 to High(gAcid1
) do
3404 if PanelType
<> 0 then
3406 // Ëåâûé âåðõíèé óãîë:
3407 aX
:= X
div ScaleSz
;
3408 aY
:= Y
div ScaleSz
;
3410 aX2
:= max(Width
div ScaleSz
, 1);
3411 aY2
:= max(Height
div ScaleSz
, 1);
3412 // Ïðàâûé íèæíèé óãîë:
3413 aX2
:= aX
+ aX2
- 1;
3414 aY2
:= aY
+ aY2
- 1;
3416 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 176, 0, 0);
3419 if gAcid2
<> nil then
3421 // Ðèñóåì êèñëîòó 2:
3422 for a
:= 0 to High(gAcid2
) do
3424 if PanelType
<> 0 then
3426 // Ëåâûé âåðõíèé óãîë:
3427 aX
:= X
div ScaleSz
;
3428 aY
:= Y
div ScaleSz
;
3430 aX2
:= max(Width
div ScaleSz
, 1);
3431 aY2
:= max(Height
div ScaleSz
, 1);
3432 // Ïðàâûé íèæíèé óãîë:
3433 aX2
:= aX
+ aX2
- 1;
3434 aY2
:= aY
+ aY2
- 1;
3436 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 176, 0, 0, 0);
3439 if gPlayers
<> nil then
3442 for a
:= 0 to High(gPlayers
) do
3443 if gPlayers
[a
] <> nil then with gPlayers
[a
] do
3445 // Ëåâûé âåðõíèé óãîë:
3446 aX
:= Obj
.X
div ScaleSz
+ 1;
3447 aY
:= Obj
.Y
div ScaleSz
+ 1;
3449 aX2
:= max(Obj
.Rect
.Width
div ScaleSz
, 1);
3450 aY2
:= max(Obj
.Rect
.Height
div ScaleSz
, 1);
3451 // Ïðàâûé íèæíèé óãîë:
3452 aX2
:= aX
+ aX2
- 1;
3453 aY2
:= aY
+ aY2
- 1;
3455 if gPlayers
[a
] = p
then
3456 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 255, 0, 0)
3459 TEAM_RED
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 0, 0, 0);
3460 TEAM_BLUE
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 0, 255, 0);
3461 else e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 128, 0, 0);
3466 g_Mons_ForEach(monDraw
);
3471 procedure renderAmbientQuad (hasAmbient
: Boolean; constref ambColor
: TDFColor
);
3473 if not hasAmbient
then exit
;
3474 e_AmbientQuad(sX
, sY
, sWidth
, sHeight
, ambColor
.r
, ambColor
.g
, ambColor
.b
, ambColor
.a
);
3478 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3479 //FIXME: broken for splitscreen mode
3480 procedure renderDynLightsInternal ();
3482 //hasAmbient: Boolean;
3483 //ambColor: TDFColor;
3485 lx
, ly
, lrad
: Integer;
3486 scxywh
: array[0..3] of GLint
;
3489 if e_NoGraphics
then exit
;
3491 //TODO: lights should be in separate grid, i think
3492 // but on the other side: grid may be slower for dynlights, as their lifetime is short
3493 if (not gwin_k8_enable_light_experiments
) or (not gwin_has_stencil
) or (g_dynLightCount
< 1) then exit
;
3496 //ambColor := gCurrentMap['light_ambient'].rgba;
3497 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3499 { // this will multiply incoming color to alpha from framebuffer
3501 glBlendFunc(GL_DST_ALPHA, GL_ONE);
3505 * light rendering: (INVALID!)
3506 * glStencilFunc(GL_EQUAL, 0, $ff);
3508 * glClear(GL_STENCIL_BUFFER_BIT);
3509 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3510 * draw shadow volume into stencil buffer
3511 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3512 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
3514 * draw color-less quad with light alpha (WARNING! don't touch color!)
3515 * glEnable(GL_BLEND);
3516 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
3517 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
3519 wassc
:= (glIsEnabled(GL_SCISSOR_TEST
) <> 0);
3520 if wassc
then glGetIntegerv(GL_SCISSOR_BOX
, @scxywh
[0]) else glGetIntegerv(GL_VIEWPORT
, @scxywh
[0]);
3522 // setup OpenGL parameters
3523 glStencilMask($FFFFFFFF);
3524 glStencilFunc(GL_ALWAYS
, 0, $FFFFFFFF);
3525 glEnable(GL_STENCIL_TEST
);
3526 glEnable(GL_SCISSOR_TEST
);
3527 glClear(GL_STENCIL_BUFFER_BIT
);
3528 glStencilFunc(GL_EQUAL
, 0, $ff);
3530 for lln
:= 0 to g_dynLightCount
-1 do
3532 lx
:= g_dynLights
[lln
].x
;
3533 ly
:= g_dynLights
[lln
].y
;
3534 lrad
:= g_dynLights
[lln
].radius
;
3535 if (lrad
< 3) then continue
;
3537 if (lx
-sX
+lrad
< 0) then continue
;
3538 if (ly
-sY
+lrad
< 0) then continue
;
3539 if (lx
-sX
-lrad
>= gPlayerScreenSize
.X
) then continue
;
3540 if (ly
-sY
-lrad
>= gPlayerScreenSize
.Y
) then continue
;
3542 // set scissor to optimize drawing
3543 if (g_dbg_scale
= 1.0) then
3545 glScissor((lx
-sX
)-lrad
+2, gPlayerScreenSize
.Y
-(ly
-sY
)-lrad
-1+2, lrad
*2-4, lrad
*2-4);
3549 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
3551 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3552 if (g_dbg_scale
<> 1.0) then glClear(GL_STENCIL_BUFFER_BIT
);
3553 glStencilOp(GL_KEEP
, GL_KEEP
, GL_INCR
);
3554 // draw extruded panels
3555 glDisable(GL_TEXTURE_2D
);
3556 glDisable(GL_BLEND
);
3557 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
); // no need to modify color buffer
3558 if (lrad
> 4) then g_Map_DrawPanelShadowVolumes(lx
, ly
, lrad
);
3559 // render light texture
3560 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
); // modify color buffer
3561 glStencilOp(GL_ZERO
, GL_ZERO
, GL_ZERO
); // draw light, and clear stencil buffer
3564 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
3565 // color and opacity
3566 glColor4f(g_dynLights
[lln
].r
, g_dynLights
[lln
].g
, g_dynLights
[lln
].b
, g_dynLights
[lln
].a
);
3567 glBindTexture(GL_TEXTURE_2D
, g_Texture_Light());
3568 glEnable(GL_TEXTURE_2D
);
3570 glTexCoord2f(0.0, 0.0); glVertex2i(lx
-lrad
, ly
-lrad
); // top-left
3571 glTexCoord2f(1.0, 0.0); glVertex2i(lx
+lrad
, ly
-lrad
); // top-right
3572 glTexCoord2f(1.0, 1.0); glVertex2i(lx
+lrad
, ly
+lrad
); // bottom-right
3573 glTexCoord2f(0.0, 1.0); glVertex2i(lx
-lrad
, ly
+lrad
); // bottom-left
3575 glDisable(GL_TEXTURE_2D
);
3579 glDisable(GL_STENCIL_TEST
);
3580 glDisable(GL_BLEND
);
3581 glDisable(GL_SCISSOR_TEST
);
3582 //glScissor(0, 0, sWidth, sHeight);
3584 glScissor(scxywh
[0], scxywh
[1], scxywh
[2], scxywh
[3]);
3585 if wassc
then glEnable(GL_SCISSOR_TEST
) else glDisable(GL_SCISSOR_TEST
);
3589 function fixViewportForScale (): Boolean;
3591 nx0
, ny0
, nw
, nh
: Integer;
3594 if (g_dbg_scale
<> 1.0) then
3597 nx0
:= round(sX
-(gPlayerScreenSize
.X
-(sWidth
*g_dbg_scale
))/2/g_dbg_scale
);
3598 ny0
:= round(sY
-(gPlayerScreenSize
.Y
-(sHeight
*g_dbg_scale
))/2/g_dbg_scale
);
3599 nw
:= round(sWidth
/g_dbg_scale
);
3600 nh
:= round(sHeight
/g_dbg_scale
);
3609 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3610 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3611 procedure renderMapInternal (backXOfs
, backYOfs
: Integer; setTransMatrix
: Boolean);
3613 TDrawCB
= procedure ();
3616 hasAmbient
: Boolean;
3618 doAmbient
: Boolean = false;
3620 procedure drawPanelType (profname
: AnsiString
; panType
: DWord
; doDraw
: Boolean);
3625 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
3626 if gdbg_map_use_accel_render
then
3628 tagmask
:= panelTypeToTag(panType
);
3629 while (gDrawPanelList
.count
> 0) do
3631 pan
:= TPanel(gDrawPanelList
.front());
3632 if ((pan
.tag
and tagmask
) = 0) then break
;
3633 if doDraw
then pan
.Draw(doAmbient
, ambColor
);
3634 gDrawPanelList
.popFront();
3639 if doDraw
then g_Map_DrawPanels(panType
, hasAmbient
, ambColor
);
3641 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3644 procedure drawOther (profname
: AnsiString
; cb
: TDrawCB
);
3646 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
3647 if assigned(cb
) then cb();
3648 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3652 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('total');
3654 // our accelerated renderer will collect all panels to gDrawPanelList
3655 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3656 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('collect');
3657 if gdbg_map_use_accel_render
then
3659 g_Map_CollectDrawPanels(sX
, sY
, sWidth
, sHeight
);
3661 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3663 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('skyback');
3664 g_Map_DrawBack(backXOfs
, backYOfs
);
3665 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3667 if setTransMatrix
then
3669 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3670 glScalef(g_dbg_scale
, g_dbg_scale
, 1.0);
3671 glTranslatef(-sX
, -sY
, 0);
3675 ambColor
:= gCurrentMap
['light_ambient'].rgba
;
3676 hasAmbient
:= (not ambColor
.isOpaque
) or (not ambColor
.isBlack
);
3681 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3682 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3683 glClear(GL_COLOR_BUFFER_BIT);
3686 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3689 drawPanelType('*back', PANEL_BACK
, g_rlayer_back
);
3690 drawPanelType('*step', PANEL_STEP
, g_rlayer_step
);
3691 drawOther('items', @g_Items_Draw
);
3692 drawOther('weapons', @g_Weapon_Draw
);
3693 drawOther('shells', @g_Player_DrawShells
);
3694 drawOther('drawall', @g_Player_DrawAll
);
3695 drawOther('corpses', @g_Player_DrawCorpses
);
3696 drawPanelType('*wall', PANEL_WALL
, g_rlayer_wall
);
3697 drawOther('monsters', @g_Monsters_Draw
);
3698 drawOther('itemdrop', @g_Items_DrawDrop
);
3699 drawPanelType('*door', PANEL_CLOSEDOOR
, g_rlayer_door
);
3700 drawOther('gfx', @g_GFX_Draw
);
3701 drawOther('flags', @g_Map_DrawFlags
);
3702 drawPanelType('*acid1', PANEL_ACID1
, g_rlayer_acid1
);
3703 drawPanelType('*acid2', PANEL_ACID2
, g_rlayer_acid2
);
3704 drawPanelType('*water', PANEL_WATER
, g_rlayer_water
);
3705 drawOther('dynlights', @renderDynLightsInternal
);
3707 if hasAmbient
{and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3709 renderAmbientQuad(hasAmbient
, ambColor
);
3713 drawPanelType('*fore', PANEL_FORE
, g_rlayer_fore
);
3716 if g_debug_HealthBar
then
3718 g_Monsters_DrawHealth();
3719 g_Player_DrawHealth();
3722 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainEnd(); // map rendering
3726 procedure DrawMapView(x
, y
, w
, h
: Integer);
3733 bx
:= Round(x
/(gMapInfo
.Width
- w
)*(gBackSize
.X
- w
));
3734 by
:= Round(y
/(gMapInfo
.Height
- h
)*(gBackSize
.Y
- h
));
3741 fixViewportForScale();
3742 renderMapInternal(-bx
, -by
, true);
3748 procedure DrawPlayer(p
: TPlayer
);
3750 px
, py
, a
, b
, c
, d
, i
, fX
, fY
: Integer;
3754 if (p
= nil) or (p
.FDummy
) then
3757 g_Map_DrawBack(0, 0);
3762 if (profileFrameDraw
= nil) then profileFrameDraw
:= TProfiler
.Create('RENDER', g_profile_history_size
);
3763 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainBegin(g_profile_frame_draw
);
3769 camObj
:= p
.getCameraObj();
3770 camObj
.lerp(gLerpFactor
, fX
, fY
);
3771 px
:= fX
+ PLAYER_RECT_CX
;
3772 py
:= fY
+ PLAYER_RECT_CY
+nlerp(p
.SlopeOld
, camObj
.slopeUpLeft
, gLerpFactor
);
3774 if (g_dbg_scale
= 1.0) and (not g_dbg_ignore_bounds
) then
3776 if (px
> (gPlayerScreenSize
.X
div 2)) then a
:= -px
+(gPlayerScreenSize
.X
div 2) else a
:= 0;
3777 if (py
> (gPlayerScreenSize
.Y
div 2)) then b
:= -py
+(gPlayerScreenSize
.Y
div 2) else b
:= 0;
3779 if (px
> gMapInfo
.Width
-(gPlayerScreenSize
.X
div 2)) then a
:= -gMapInfo
.Width
+gPlayerScreenSize
.X
;
3780 if (py
> gMapInfo
.Height
-(gPlayerScreenSize
.Y
div 2)) then b
:= -gMapInfo
.Height
+gPlayerScreenSize
.Y
;
3782 if (gMapInfo
.Width
= gPlayerScreenSize
.X
) then a
:= 0
3783 else if (gMapInfo
.Width
< gPlayerScreenSize
.X
) then
3786 a
:= (gPlayerScreenSize
.X
-gMapInfo
.Width
) div 2;
3789 if (gMapInfo
.Height
= gPlayerScreenSize
.Y
) then b
:= 0
3790 else if (gMapInfo
.Height
< gPlayerScreenSize
.Y
) then
3793 b
:= (gPlayerScreenSize
.Y
-gMapInfo
.Height
) div 2;
3798 // scaled, ignore level bounds
3799 a
:= -px
+(gPlayerScreenSize
.X
div 2);
3800 b
:= -py
+(gPlayerScreenSize
.Y
div 2);
3805 sWidth
:= gPlayerScreenSize
.X
;
3806 sHeight
:= gPlayerScreenSize
.Y
;
3807 fixViewportForScale();
3809 i
:= py
- (sY
+ sHeight
div 2);
3810 if (p
.IncCam
> 0) then
3812 // clamp to level bounds
3813 if (sY
- p
.IncCam
< 0) then
3814 p
.IncCam
:= nclamp(sY
, 0, 120);
3815 // clamp around player position
3817 p
.IncCam
:= nclamp(p
.IncCam
, 0, max(0, 120 - i
));
3819 else if (p
.IncCam
< 0) then
3821 // clamp to level bounds
3822 if (sY
+ sHeight
- p
.IncCam
> gMapInfo
.Height
) then
3823 p
.IncCam
:= nclamp(sY
+ sHeight
- gMapInfo
.Height
, -120, 0);
3824 // clamp around player position
3826 p
.IncCam
:= nclamp(p
.IncCam
, min(0, -120 - i
), 0);
3829 sY
:= sY
- nlerp(p
.IncCamOld
, p
.IncCam
, gLerpFactor
);
3831 if (not g_dbg_ignore_bounds
) then
3833 if (sX
+sWidth
> gMapInfo
.Width
) then sX
:= gMapInfo
.Width
-sWidth
;
3834 if (sY
+sHeight
> gMapInfo
.Height
) then sY
:= gMapInfo
.Height
-sHeight
;
3835 if (sX
< 0) then sX
:= 0;
3836 if (sY
< 0) then sY
:= 0;
3839 if (gBackSize
.X
<= gPlayerScreenSize
.X
) or (gMapInfo
.Width
<= sWidth
) then c
:= 0 else c
:= trunc((gBackSize
.X
-gPlayerScreenSize
.X
)*sX
/(gMapInfo
.Width
-sWidth
));
3840 if (gBackSize
.Y
<= gPlayerScreenSize
.Y
) or (gMapInfo
.Height
<= sHeight
) then d
:= 0 else d
:= trunc((gBackSize
.Y
-gPlayerScreenSize
.Y
)*sY
/(gMapInfo
.Height
-sHeight
));
3842 //r_smallmap_h: 0: left; 1: center; 2: right
3843 //r_smallmap_v: 0: top; 1: center; 2: bottom
3845 if (gMapInfo
.Width
= sWidth
) then
3849 else if (gMapInfo
.Width
< sWidth
) then
3851 case r_smallmap_h
of
3852 1: sX
:= -((sWidth
-gMapInfo
.Width
) div 2); // center
3853 2: sX
:= -(sWidth
-gMapInfo
.Width
); // right
3854 else sX
:= 0; // left
3858 if (gMapInfo
.Height
= sHeight
) then
3862 else if (gMapInfo
.Height
< sHeight
) then
3864 case r_smallmap_v
of
3865 1: sY
:= -((sHeight
-gMapInfo
.Height
) div 2); // center
3866 2: sY
:= -(sHeight
-gMapInfo
.Height
); // bottom
3867 else sY
:= 0; // top
3873 p
.viewPortW
:= sWidth
;
3874 p
.viewPortH
:= sHeight
;
3876 {$IFDEF ENABLE_HOLMES}
3877 if (p
= gPlayer1
) then
3879 g_Holmes_plrViewPos(sX
, sY
);
3880 g_Holmes_plrViewSize(sWidth
, sHeight
);
3884 renderMapInternal(-c
, -d
, true);
3886 if (gGameSettings
.GameMode
<> GM_SINGLE
) and (gPlayerIndicator
> 0) then
3887 case gPlayerIndicator
of
3889 p
.DrawIndicator(_RGB(255, 255, 255));
3892 for i
:= 0 to High(gPlayers
) do
3893 if gPlayers
[i
] <> nil then
3894 if gPlayers
[i
] = p
then p
.DrawIndicator(_RGB(255, 255, 255))
3895 else if (gPlayers
[i
].Team
= p
.Team
) and (gPlayers
[i
].Team
<> TEAM_NONE
) then
3896 if gPlayerIndicatorStyle
= 1 then
3897 gPlayers
[i
].DrawIndicator(_RGB(192, 192, 192))
3898 else gPlayers
[i
].DrawIndicator(gPlayers
[i
].GetColor
);
3902 for a := 0 to High(gCollideMap) do
3903 for b := 0 to High(gCollideMap[a]) do
3906 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3908 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3912 1: e_DrawPoint(1, b, a, 200, 200, 200);
3913 2: e_DrawPoint(1, b, a, 64, 64, 255);
3914 3: e_DrawPoint(1, b, a, 255, 0, 255);
3924 if gShowMap
then DrawMinimap(p
, _TRect(0, 0, 128, 128));
3925 if g_Debug_Player
then
3926 g_Player_DrawDebug(p
);
3930 procedure drawProfilers ();
3935 if g_profile_frame_draw
and (profileFrameDraw
<> nil) then
3936 px
-= drawProfiles(px
, py
, profileFrameDraw
);
3938 if g_profile_collision
and (profMapCollision
<> nil) then
3940 px
-= drawProfiles(px
, py
, profMapCollision
);
3941 py
-= calcProfilesHeight(profMonsLOS
);
3944 if g_profile_los
and (profMonsLOS
<> nil) then
3946 px
-= drawProfiles(px
, py
, profMonsLOS
);
3947 py
-= calcProfilesHeight(profMonsLOS
);
3951 procedure g_Game_Draw();
3958 plView1
, plView2
: TPlayer
;
3961 if gExit
= EXIT_QUIT
then Exit
;
3963 Time
:= sys_GetTicks() {div 1000};
3964 FPSCounter
:= FPSCounter
+1;
3965 if Time
- FPSTime
>= 1000 then
3972 e_SetRendertarget(True);
3973 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
3975 if gGameOn
or (gState
= STATE_FOLD
) then
3977 if (gPlayer1
<> nil) and (gPlayer2
<> nil) then
3979 gSpectMode
:= SPECT_NONE
;
3980 if not gSwapPlayers
then
3982 plView1
:= gPlayer1
;
3983 plView2
:= gPlayer2
;
3987 plView1
:= gPlayer2
;
3988 plView2
:= gPlayer1
;
3992 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
3994 gSpectMode
:= SPECT_NONE
;
3995 if gPlayer2
= nil then
3998 plView1
:= gPlayer2
;
4007 if (plView1
= nil) and (plView2
= nil) and (gSpectMode
= SPECT_NONE
) then
4008 gSpectMode
:= SPECT_STATS
;
4010 if gSpectMode
= SPECT_PLAYERS
then
4011 if gPlayers
<> nil then
4013 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
4014 if plView1
= nil then
4016 gSpectPID1
:= GetActivePlayerID_Next();
4017 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
4019 if gSpectViewTwo
then
4021 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
4022 if plView2
= nil then
4024 gSpectPID2
:= GetActivePlayerID_Next();
4025 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
4030 if gSpectMode
= SPECT_MAPVIEW
then
4032 // Ðåæèì ïðîñìîòðà êàðòû
4034 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
4035 DrawMapView(gSpectX
, gSpectY
, gScreenWidth
, gScreenHeight
);
4036 gHearPoint1
.Active
:= True;
4037 gHearPoint1
.Coords
.X
:= gScreenWidth
div 2 + gSpectX
;
4038 gHearPoint1
.Coords
.Y
:= gScreenHeight
div 2 + gSpectY
;
4039 gHearPoint2
.Active
:= False;
4043 Split
:= (plView1
<> nil) and (plView2
<> nil);
4045 // Òî÷êè ñëóõà èãðîêîâ
4046 if plView1
<> nil then
4048 gHearPoint1
.Active
:= True;
4049 gHearPoint1
.Coords
.X
:= plView1
.GameX
+ PLAYER_RECT
.Width
;
4050 gHearPoint1
.Coords
.Y
:= plView1
.GameY
+ PLAYER_RECT
.Height
DIV 2;
4052 gHearPoint1
.Active
:= False;
4053 if plView2
<> nil then
4055 gHearPoint2
.Active
:= True;
4056 gHearPoint2
.Coords
.X
:= plView2
.GameX
+ PLAYER_RECT
.Width
;
4057 gHearPoint2
.Coords
.Y
:= plView2
.GameY
+ PLAYER_RECT
.Height
DIV 2;
4059 gHearPoint2
.Active
:= False;
4061 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4062 gPlayerScreenSize
.X
:= gScreenWidth
-196;
4065 gPlayerScreenSize
.Y
:= gScreenHeight
div 2;
4066 if gScreenHeight
mod 2 = 0 then
4067 Dec(gPlayerScreenSize
.Y
);
4070 gPlayerScreenSize
.Y
:= gScreenHeight
;
4073 if gScreenHeight
mod 2 = 0 then
4074 e_SetViewPort(0, gPlayerScreenSize
.Y
+2, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
)
4076 e_SetViewPort(0, gPlayerScreenSize
.Y
+1, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
4078 DrawPlayer(plView1
);
4079 gPlayer1ScreenCoord
.X
:= sX
;
4080 gPlayer1ScreenCoord
.Y
:= sY
;
4084 e_SetViewPort(0, 0, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
4086 DrawPlayer(plView2
);
4087 gPlayer2ScreenCoord
.X
:= sX
;
4088 gPlayer2ScreenCoord
.Y
:= sY
;
4091 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
4094 e_DrawLine(2, 0, gScreenHeight
div 2, gScreenWidth
, gScreenHeight
div 2, 0, 0, 0);
4097 {$IFDEF ENABLE_HOLMES}
4099 if (g_holmes_enabled
) then g_Holmes_Draw();
4102 if MessageText
<> '' then
4106 e_CharFont_GetSizeFmt(gMenuFont
, MessageText
, w
, h
);
4108 e_CharFont_PrintFmt(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
4109 (gScreenHeight
div 2)-(h
div 2), MessageText
)
4111 e_CharFont_PrintFmt(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
4112 Round(gScreenHeight
/ 2.75)-(h
div 2), MessageText
);
4115 if IsDrawStat
or (gSpectMode
= SPECT_STATS
) then
4118 if gSpectHUD
and (not gChatShow
) and (gSpectMode
<> SPECT_NONE
) and (not gSpectAuto
) then
4120 // Draw spectator GUI
4123 e_TextureFontGetSize(gStdFont
, ww
, hh
);
4126 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Stats', gStdFont
, 255, 255, 255, 1);
4128 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Observe Map', gStdFont
, 255, 255, 255, 1);
4130 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Watch Players', gStdFont
, 255, 255, 255, 1);
4132 e_TextureFontPrintEx(2*ww
, gScreenHeight
- (hh
+2), '< jump >', gStdFont
, 255, 255, 255, 1);
4133 if gSpectMode
= SPECT_STATS
then
4135 e_TextureFontPrintEx(16*ww
, gScreenHeight
- (hh
+2)*2, 'Autoview', gStdFont
, 255, 255, 255, 1);
4136 e_TextureFontPrintEx(16*ww
, gScreenHeight
- (hh
+2), '< fire >', gStdFont
, 255, 255, 255, 1);
4138 if gSpectMode
= SPECT_MAPVIEW
then
4140 e_TextureFontPrintEx(22*ww
, gScreenHeight
- (hh
+2)*2, '[-]', gStdFont
, 255, 255, 255, 1);
4141 e_TextureFontPrintEx(26*ww
, gScreenHeight
- (hh
+2)*2, 'Step ' + IntToStr(gSpectStep
), gStdFont
, 255, 255, 255, 1);
4142 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2)*2, '[+]', gStdFont
, 255, 255, 255, 1);
4143 e_TextureFontPrintEx(18*ww
, gScreenHeight
- (hh
+2), '<prev weap>', gStdFont
, 255, 255, 255, 1);
4144 e_TextureFontPrintEx(30*ww
, gScreenHeight
- (hh
+2), '<next weap>', gStdFont
, 255, 255, 255, 1);
4146 if gSpectMode
= SPECT_PLAYERS
then
4148 e_TextureFontPrintEx(22*ww
, gScreenHeight
- (hh
+2)*2, 'Player 1', gStdFont
, 255, 255, 255, 1);
4149 e_TextureFontPrintEx(20*ww
, gScreenHeight
- (hh
+2), '<left/right>', gStdFont
, 255, 255, 255, 1);
4150 if gSpectViewTwo
then
4152 e_TextureFontPrintEx(37*ww
, gScreenHeight
- (hh
+2)*2, 'Player 2', gStdFont
, 255, 255, 255, 1);
4153 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2), '<prev w/next w>', gStdFont
, 255, 255, 255, 1);
4154 e_TextureFontPrintEx(52*ww
, gScreenHeight
- (hh
+2)*2, '2x View', gStdFont
, 255, 255, 255, 1);
4155 e_TextureFontPrintEx(51*ww
, gScreenHeight
- (hh
+2), '<up/down>', gStdFont
, 255, 255, 255, 1);
4159 e_TextureFontPrintEx(35*ww
, gScreenHeight
- (hh
+2)*2, '2x View', gStdFont
, 255, 255, 255, 1);
4160 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2), '<up/down>', gStdFont
, 255, 255, 255, 1);
4166 if gPauseMain
and gGameOn
and (g_ActiveWindow
= nil) then
4168 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4169 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4171 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_PAUSE
], w
, h
);
4172 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
4173 (gScreenHeight
div 2)-(h
div 2), _lc
[I_MENU_PAUSE
]);
4178 if (gState
= STATE_MENU
) then
4180 if (g_ActiveWindow
= nil) or (g_ActiveWindow
.BackTexture
= '') then DrawMenuBackground('MENU_BACKGROUND');
4181 // F3 at menu will show game loading dialog
4182 if e_KeyPressed(IK_F3
) then g_Menu_Show_LoadMenu(true);
4183 if (g_ActiveWindow
<> nil) then
4185 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4186 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4190 // F3 at titlepic will show game loading dialog
4191 if e_KeyPressed(IK_F3
) then
4193 g_Menu_Show_LoadMenu(true);
4194 if (g_ActiveWindow
<> nil) then e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4199 if gState
= STATE_FOLD
then
4201 e_DrawFillQuad(0, 0, gScreenWidth
-1, gScreenHeight
-1, 0, 0, 0, EndingGameCounter
);
4204 if gState
= STATE_INTERCUSTOM
then
4206 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then
4208 back
:= 'TEXTURE_endpic';
4209 if not g_Texture_Get(back
, ID
) then
4210 back
:= _lc
[I_TEXTURE_ENDPIC
];
4215 DrawMenuBackground(back
);
4219 if g_ActiveWindow
<> nil then
4221 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4222 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4226 if gState
= STATE_INTERSINGLE
then
4228 if EndingGameCounter
> 0 then
4230 e_DrawFillQuad(0, 0, gScreenWidth
-1, gScreenHeight
-1, 0, 0, 0, EndingGameCounter
);
4236 DrawMenuBackground(back
);
4240 if g_ActiveWindow
<> nil then
4242 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4243 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4248 if gState
= STATE_ENDPIC
then
4251 if g_Texture_Get('TEXTURE_endpic', ID
) then DrawMenuBackground('TEXTURE_endpic')
4252 else DrawMenuBackground(_lc
[I_TEXTURE_ENDPIC
]);
4254 if g_ActiveWindow
<> nil then
4256 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4257 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4261 if gState
= STATE_SLIST
then
4263 // if g_Texture_Get('MENU_BACKGROUND', ID) then
4265 // e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
4266 // //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4268 DrawMenuBackground('MENU_BACKGROUND');
4269 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4270 g_Serverlist_Draw(slCurrent
, slTable
);
4274 if g_ActiveWindow
<> nil then
4278 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4279 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4281 g_ActiveWindow
.Draw();
4288 {$IFDEF ENABLE_SOUND}
4289 if g_debug_Sounds
and gGameOn
then
4291 for w
:= 0 to High(e_SoundsArray
) do
4292 for h
:= 0 to e_SoundsArray
[w
].nRefs
do
4293 e_DrawPoint(1, w
+100, h
+100, 255, 0, 0);
4299 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS
]), gStdFont
);
4300 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS
]), gStdFont
);
4303 if gGameOn
and gShowTime
then
4304 drawTime(gScreenWidth
-72, gScreenHeight
-16);
4306 if gGameOn
then drawProfilers();
4308 // TODO: draw this after the FBO and remap mouse click coordinates
4310 {$IFDEF ENABLE_HOLMES}
4314 // blit framebuffer to screen
4316 e_SetRendertarget(False);
4317 e_SetViewPort(0, 0, gWinSizeX
, gWinSizeY
);
4318 e_BlitFramebuffer(gWinSizeX
, gWinSizeY
);
4320 // draw the overlay stuff on top of it
4325 // FIXME: This cannot be called from anywhere other than ProcessMessages(), because otherwise
4326 // remaining events in the system queue may cause use-after-free! Do 'gExit := EXIT_QUIT' instead.
4327 procedure g_Game_Quit();
4329 e_WriteLog('g_Game_Quit: cleanup assets before shutting down', TMsgType
.Notify
);
4330 {$IFDEF ENABLE_SOUND}
4331 g_Game_StopAllSounds(True);
4335 gMusicPlay
:= False;
4337 gMusicPause
:= False;
4340 g_PlayerModel_FreeData();
4341 g_Texture_DeleteAll();
4342 g_Frames_DeleteAll();
4347 if NetInitDone
then g_Net_Free
;
4349 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
4350 if gMapToDelete
<> '' then
4351 g_Game_DeleteTestMap();
4353 sys_RequestQuit(); // FIXME: this posts an event that have no sense anymore at this moment.
4356 procedure g_FatalError(Text: String);
4358 g_Console_Add(Format(_lc
[I_FATAL_ERROR
], [Text]), True);
4359 e_WriteLog(Format(_lc
[I_FATAL_ERROR
], [Text]), TMsgType
.Warning
);
4361 gExit
:= EXIT_SIMPLE
;
4362 if gGameOn
then EndGame
;
4365 procedure g_SimpleError(Text: String);
4367 g_Console_Add(Format(_lc
[I_SIMPLE_ERROR
], [Text]), True);
4368 e_WriteLog(Format(_lc
[I_SIMPLE_ERROR
], [Text]), TMsgType
.Warning
);
4371 procedure g_Game_SetupScreenSize();
4373 RES_FACTOR
= 4.0 / 3.0;
4379 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4380 gPlayerScreenSize
.X
:= gScreenWidth
-196;
4381 if (gPlayer1
<> nil) and (gPlayer2
<> nil) then
4382 gPlayerScreenSize
.Y
:= gScreenHeight
div 2
4384 gPlayerScreenSize
.Y
:= gScreenHeight
;
4386 // Ðàçìåð çàäíåãî ïëàíà:
4387 if BackID
<> DWORD(-1) then
4390 if (gScreenWidth
*s
> gMapInfo
.Width
) or
4391 (gScreenHeight
*s
> gMapInfo
.Height
) then
4393 gBackSize
.X
:= gScreenWidth
;
4394 gBackSize
.Y
:= gScreenHeight
;
4398 e_GetTextureSize(BackID
, @bw
, @bh
);
4399 rf
:= Single(bw
) / Single(bh
);
4400 if (rf
> RES_FACTOR
) then bw
:= Round(Single(bh
) * RES_FACTOR
)
4401 else if (rf
< RES_FACTOR
) then bh
:= Round(Single(bw
) / RES_FACTOR
);
4402 s
:= Max(gScreenWidth
/ bw
, gScreenHeight
/ bh
);
4403 if (s
< 1.0) then s
:= 1.0;
4404 gBackSize
.X
:= Round(bw
*s
);
4405 gBackSize
.Y
:= Round(bh
*s
);
4410 procedure g_Game_ChangeResolution(newWidth
, newHeight
: Word; nowFull
, nowMax
: Boolean);
4412 sys_SetDisplayMode(newWidth
, newHeight
, gBPP
, nowFull
, nowMax
);
4415 procedure g_Game_AddPlayer(Team
: Byte = TEAM_NONE
);
4417 if ((not gGameOn
) and (gState
<> STATE_INTERCUSTOM
))
4418 or (not (gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
])) then
4421 if (gGameSettings
.MaxLives
> 0) and (gLMSRespawn
= LMS_RESPAWN_NONE
) then
4424 if gPlayer1
= nil then
4426 if g_Game_IsClient
then
4428 if NetPlrUID1
> -1 then
4429 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE
);
4433 if not (Team
in [TEAM_RED
, TEAM_BLUE
]) then
4434 Team
:= gPlayer1Settings
.Team
;
4436 // Ñîçäàíèå ïåðâîãî èãðîêà:
4437 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4438 gPlayer1Settings
.Color
,
4440 if gPlayer1
= nil then
4441 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]))
4444 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4445 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4446 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4447 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4448 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4449 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [gPlayer1
.Name
]), True);
4450 if g_Game_IsServer
and g_Game_IsNet
then
4451 MH_SEND_PlayerCreate(gPlayer1
.UID
);
4452 gPlayer1
.Respawn(False, True);
4453 g_Net_Slist_ServerPlayerComes();
4458 if gPlayer2
= nil then
4460 if g_Game_IsClient
then
4462 if NetPlrUID2
> -1 then
4463 gPlayer2
:= g_Player_Get(NetPlrUID2
);
4467 if not (Team
in [TEAM_RED
, TEAM_BLUE
]) then
4468 Team
:= gPlayer2Settings
.Team
;
4470 // Ñîçäàíèå âòîðîãî èãðîêà:
4471 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4472 gPlayer2Settings
.Color
,
4474 if gPlayer2
= nil then
4475 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]))
4478 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4479 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4480 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4481 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4482 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4483 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [gPlayer2
.Name
]), True);
4484 if g_Game_IsServer
and g_Game_IsNet
then
4485 MH_SEND_PlayerCreate(gPlayer2
.UID
);
4486 gPlayer2
.Respawn(False, True);
4487 g_Net_Slist_ServerPlayerComes();
4494 procedure g_Game_RemovePlayer();
4498 if ((not gGameOn
) and (gState
<> STATE_INTERCUSTOM
))
4499 or (not (gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
])) then
4504 if g_Game_IsServer
then
4507 Pl
.Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
4508 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [Pl
.Name
]), True);
4509 g_Player_Remove(Pl
.UID
);
4510 g_Net_Slist_ServerPlayerLeaves();
4514 gSpectLatchPID2
:= Pl
.UID
;
4522 if g_Game_IsServer
then
4525 Pl
.Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
4526 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [Pl
.Name
]), True);
4527 g_Player_Remove(Pl
.UID
);
4528 g_Net_Slist_ServerPlayerLeaves();
4531 gSpectLatchPID1
:= Pl
.UID
;
4533 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE
);
4537 g_Net_Slist_ServerPlayerLeaves();
4540 procedure g_Game_Spectate();
4542 g_Game_RemovePlayer();
4543 if gPlayer1
<> nil then
4544 g_Game_RemovePlayer();
4547 procedure g_Game_SpectateCenterView();
4549 gSpectX
:= Max(gMapInfo
.Width
div 2 - gScreenWidth
div 2, 0);
4550 gSpectY
:= Max(gMapInfo
.Height
div 2 - gScreenHeight
div 2, 0);
4553 procedure g_Game_StartSingle(Map
: String; TwoPlayers
: Boolean; nPlayers
: Byte);
4560 e_WriteLog('Starting singleplayer game...', TMsgType
.Notify
);
4562 g_Game_ClearLoading();
4565 gGameSettings
:= Default(TGameSettings
);
4568 gGameSettings
.GameType
:= GT_SINGLE
;
4569 gGameSettings
.MaxLives
:= 0;
4570 gGameSettings
.Options
:= [TGameOption
.ALLOW_EXIT
, TGameOption
.MONSTERS
,
4571 TGameOption
.BOTS_VS_MONSTERS
, TGameOption
.TEAM_HIT_PROJECTILE
, TGameOption
.TEAM_HIT_TRACE
,
4572 TGameOption
.POWERUP_RANDOM
, TGameOption
.ITEM_ALL_RANDOM
, TGameOption
.ITEM_LIFE_RANDOM
,
4573 TGameOption
.ITEM_AMMO_RANDOM
, TGameOption
.ITEM_WEAPON_RANDOM
];
4574 gSwitchGameMode
:= GM_SINGLE
;
4576 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4577 gLMSRespawnTime
:= 0;
4578 gSpectLatchPID1
:= 0;
4579 gSpectLatchPID2
:= 0;
4581 g_Game_ExecuteEvent('ongamestart');
4583 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4584 g_Game_SetupScreenSize();
4586 // Ñîçäàíèå ïåðâîãî èãðîêà:
4587 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4588 gPlayer1Settings
.Color
,
4589 gPlayer1Settings
.Team
, False));
4590 if gPlayer1
= nil then
4592 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4596 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4597 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4598 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4599 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4600 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4603 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
4606 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4607 gPlayer2Settings
.Color
,
4608 gPlayer2Settings
.Team
, False));
4609 if gPlayer2
= nil then
4611 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]));
4615 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4616 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4617 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4618 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4619 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4623 // Çàãðóçêà è çàïóñê êàðòû:
4624 if not g_Game_StartMap(false{asMegawad}, MAP
, True) then
4626 if (Pos(':\', Map
) > 0) or (Pos(':/', Map
) > 0) then tmps
:= Map
else tmps
:= gGameSettings
.WAD
+ ':\' + MAP
;
4627 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [tmps
]));
4631 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4635 for i
:= nPl
+1 to nPlayers
do
4636 g_Player_Create(STD_PLAYER_MODEL
, _RGB(0, 0, 0), 0, True);
4639 procedure g_Game_StartCustom(Map
: String; GameMode
: Byte;
4640 TimeLimit
, ScoreLimit
: Word;
4642 Options
: TGameOptions
; nPlayers
: Byte);
4648 e_WriteLog('Starting custom game...', TMsgType
.Notify
);
4650 g_Game_ClearLoading();
4653 gGameSettings
.GameType
:= GT_CUSTOM
;
4654 gGameSettings
.GameMode
:= GameMode
;
4655 gSwitchGameMode
:= GameMode
;
4656 gGameSettings
.TimeLimit
:= TimeLimit
;
4657 gGameSettings
.ScoreLimit
:= ScoreLimit
;
4658 gGameSettings
.MaxLives
:= IfThen(GameMode
= GM_CTF
, 0, MaxLives
);
4659 gGameSettings
.Options
:= Options
;
4661 gCoopTotalMonstersKilled
:= 0;
4662 gCoopTotalSecretsFound
:= 0;
4663 gCoopTotalMonsters
:= 0;
4664 gCoopTotalSecrets
:= 0;
4668 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4669 gLMSRespawnTime
:= 0;
4670 gSpectLatchPID1
:= 0;
4671 gSpectLatchPID2
:= 0;
4673 g_Game_ExecuteEvent('ongamestart');
4675 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4676 g_Game_SetupScreenSize();
4678 // Ðåæèì íàáëþäàòåëÿ:
4679 if nPlayers
= 0 then
4686 if nPlayers
>= 1 then
4688 // Ñîçäàíèå ïåðâîãî èãðîêà:
4689 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4690 gPlayer1Settings
.Color
,
4691 gPlayer1Settings
.Team
, False));
4692 if gPlayer1
= nil then
4694 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4698 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4699 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4700 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4701 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4702 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4706 if nPlayers
>= 2 then
4708 // Ñîçäàíèå âòîðîãî èãðîêà:
4709 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4710 gPlayer2Settings
.Color
,
4711 gPlayer2Settings
.Team
, False));
4712 if gPlayer2
= nil then
4714 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]));
4718 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4719 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4720 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4721 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4722 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4726 // Çàãðóçêà è çàïóñê êàðòû:
4727 if not g_Game_StartMap(true{asMegawad}, Map
, True) then
4729 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [Map
]));
4733 // Íåò òî÷åê ïîÿâëåíèÿ:
4734 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1
) +
4735 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2
) +
4736 g_Map_GetPointCount(RESPAWNPOINT_DM
) +
4737 g_Map_GetPointCount(RESPAWNPOINT_RED
)+
4738 g_Map_GetPointCount(RESPAWNPOINT_BLUE
)) < 1 then
4740 g_FatalError(_lc
[I_GAME_ERROR_GET_SPAWN
]);
4744 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4748 for i
:= nPl
+1 to nPlayers
do
4749 g_Player_Create(STD_PLAYER_MODEL
, _RGB(0, 0, 0), 0, True);
4752 procedure g_Game_StartServer(Map
: String; GameMode
: Byte;
4753 TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte;
4754 Options
: TGameOptions
; nPlayers
: Byte;
4755 IPAddr
: LongWord
; Port
: Word);
4758 g_Net_Slist_ServerClosed();
4760 e_WriteLog('Starting net game (server)...', TMsgType
.Notify
);
4762 g_Game_ClearLoading();
4767 gGameSettings
.GameType
:= GT_SERVER
;
4768 gGameSettings
.GameMode
:= GameMode
;
4769 gSwitchGameMode
:= GameMode
;
4770 gGameSettings
.TimeLimit
:= TimeLimit
;
4771 gGameSettings
.ScoreLimit
:= ScoreLimit
;
4772 gGameSettings
.MaxLives
:= IfThen(GameMode
= GM_CTF
, 0, MaxLives
);
4773 gGameSettings
.Options
:= Options
;
4775 gCoopTotalMonstersKilled
:= 0;
4776 gCoopTotalSecretsFound
:= 0;
4777 gCoopTotalMonsters
:= 0;
4778 gCoopTotalSecrets
:= 0;
4782 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4783 gLMSRespawnTime
:= 0;
4784 gSpectLatchPID1
:= 0;
4785 gSpectLatchPID2
:= 0;
4787 g_Game_ExecuteEvent('ongamestart');
4789 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
4790 g_Game_SetupScreenSize();
4792 // Ðåæèì íàáëþäàòåëÿ:
4793 if nPlayers
= 0 then
4799 if nPlayers
>= 1 then
4801 // Ñîçäàíèå ïåðâîãî èãðîêà:
4802 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4803 gPlayer1Settings
.Color
,
4804 gPlayer1Settings
.Team
, False));
4805 if gPlayer1
= nil then
4807 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4811 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4812 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4813 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4814 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4815 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4818 if nPlayers
>= 2 then
4820 // Ñîçäàíèå âòîðîãî èãðîêà:
4821 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4822 gPlayer2Settings
.Color
,
4823 gPlayer2Settings
.Team
, False));
4824 if gPlayer2
= nil then
4826 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]));
4830 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4831 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4832 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4833 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4834 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4837 g_Game_SetLoadingText(_lc
[I_LOAD_HOST
], 0, False);
4838 if NetForwardPorts
then
4839 g_Game_SetLoadingText(_lc
[I_LOAD_PORTS
], 0, False);
4842 if not g_Net_Host(IPAddr
, Port
, NetMaxClients
) then
4844 g_FatalError(_lc
[I_NET_MSG
] + Format(_lc
[I_NET_ERR_HOST
], [Port
]));
4848 g_Net_Slist_Set(NetMasterList
);
4850 g_Net_Slist_ServerStarted();
4852 // Çàãðóçêà è çàïóñê êàðòû:
4853 if not g_Game_StartMap(false{asMegawad}, Map
, True) then
4855 g_Net_Slist_ServerClosed();
4856 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [Map
]));
4860 // Íåò òî÷åê ïîÿâëåíèÿ:
4861 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1
) +
4862 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2
) +
4863 g_Map_GetPointCount(RESPAWNPOINT_DM
) +
4864 g_Map_GetPointCount(RESPAWNPOINT_RED
)+
4865 g_Map_GetPointCount(RESPAWNPOINT_BLUE
)) < 1 then
4867 g_Net_Slist_ServerClosed();
4868 g_FatalError(_lc
[I_GAME_ERROR_GET_SPAWN
]);
4872 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4875 g_Net_Slist_ServerMapStarted();
4876 NetState
:= NET_STATE_GAME
;
4879 procedure g_Game_StartClient(Addr
: String; Port
: Word; PW
: String);
4894 e_WriteLog('Starting net game (client)...', TMsgType
.Notify
);
4895 e_WriteLog('NET: Trying to connect to ' + Addr
+ ':' + IntToStr(Port
) + '...', TMsgType
.Notify
);
4897 g_Game_ClearLoading();
4902 gGameSettings
.GameType
:= GT_CLIENT
;
4904 gCoopTotalMonstersKilled
:= 0;
4905 gCoopTotalSecretsFound
:= 0;
4906 gCoopTotalMonsters
:= 0;
4907 gCoopTotalSecrets
:= 0;
4911 g_Game_ExecuteEvent('ongamestart');
4913 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4914 g_Game_SetupScreenSize();
4916 NetState
:= NET_STATE_AUTH
;
4918 g_Game_SetLoadingText(_lc
[I_LOAD_CONNECT
], 0, False);
4920 // create (or update) map/resource databases
4921 g_Res_CreateDatabases(true);
4923 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4924 gLMSRespawnTime
:= 0;
4925 gSpectLatchPID1
:= 0;
4926 gSpectLatchPID2
:= 0;
4929 if not g_Net_Connect(Addr
, Port
) then
4931 g_FatalError(_lc
[I_NET_MSG
] + _lc
[I_NET_ERR_CONN
]);
4932 NetState
:= NET_STATE_NONE
;
4936 g_Game_SetLoadingText(_lc
[I_LOAD_SEND_INFO
], 0, False);
4938 g_Game_SetLoadingText(_lc
[I_LOAD_WAIT_INFO
], 0, False);
4943 // fuck! https://www.mail-archive.com/enet-discuss@cubik.org/msg00852.html
4944 // tl;dr: on shitdows, we can get -1 sometimes, and it is *NOT* a failure.
4945 // thank you, enet. let's ignore failures altogether then.
4946 while (enet_host_service(NetHost
, @NetEvent
, 50) > 0) do
4948 if (NetEvent
.kind
= ENET_EVENT_TYPE_RECEIVE
) then
4950 Ptr
:= NetEvent
.packet
^.data
;
4951 if not InMsg
.Init(Ptr
, NetEvent
.packet
^.dataLength
, True) then
4953 enet_packet_destroy(NetEvent
.packet
);
4957 InMsg
.ReadLongWord(); // skip size
4958 MID
:= InMsg
.ReadByte();
4960 if (MID
= NET_MSG_INFO
) and (State
= 0) then
4962 NetMyID
:= InMsg
.ReadByte();
4963 NetPlrUID1
:= InMsg
.ReadWord();
4965 WadName
:= InMsg
.ReadString();
4966 Map
:= InMsg
.ReadString();
4968 gWADHash
:= InMsg
.ReadMD5();
4970 gGameSettings
.GameMode
:= InMsg
.ReadByte();
4971 gSwitchGameMode
:= gGameSettings
.GameMode
;
4972 gGameSettings
.ScoreLimit
:= InMsg
.ReadWord();
4973 gGameSettings
.TimeLimit
:= InMsg
.ReadWord();
4974 gGameSettings
.MaxLives
:= InMsg
.ReadByte();
4975 gGameSettings
.Options
:= TGameOptions(InMsg
.ReadLongWord());
4976 T
:= InMsg
.ReadLongWord();
4978 //newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4979 //if newResPath = '' then
4981 //g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4982 newResPath
:= g_Res_DownloadMapWAD(ExtractFileName(WadName
), gWADHash
);
4983 if newResPath
= '' then
4985 g_FatalError(_lc
[I_NET_ERR_HASH
]);
4986 enet_packet_destroy(NetEvent
.packet
);
4987 NetState
:= NET_STATE_NONE
;
4990 e_LogWritefln('using downloaded map wad [%s] for [%s]`', [newResPath
, WadName
], TMsgType
.Notify
);
4992 //newResPath := ExtractRelativePath(MapsDir, newResPath);
4995 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4996 gPlayer1Settings
.Color
,
4997 gPlayer1Settings
.Team
, False));
4999 if gPlayer1
= nil then
5001 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
5003 enet_packet_destroy(NetEvent
.packet
);
5004 NetState
:= NET_STATE_NONE
;
5008 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
5009 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
5010 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
5011 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
5012 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
5013 gPlayer1
.UID
:= NetPlrUID1
;
5014 gPlayer1
.Reset(True);
5016 if not g_Game_StartMap(false{asMegawad}, newResPath
+ ':\' + Map
, True) then
5018 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [WadName
+ ':\' + Map
]));
5020 enet_packet_destroy(NetEvent
.packet
);
5021 NetState
:= NET_STATE_NONE
;
5029 enet_packet_destroy(NetEvent
.packet
);
5033 enet_packet_destroy(NetEvent
.packet
);
5037 if (NetEvent
.kind
= ENET_EVENT_TYPE_DISCONNECT
) then
5040 if (NetEvent
.data
<= NET_DISC_MAX
) then
5041 g_Console_Add(_lc
[I_NET_MSG_ERROR
] + _lc
[I_NET_ERR_CONN
] + ' ' +
5042 _lc
[TStrings_Locale(Cardinal(I_NET_DISC_NONE
) + NetEvent
.data
)], True);
5049 ProcessLoading(True);
5050 if g_Net_UserRequestExit() then
5059 g_FatalError(_lc
[I_NET_MSG
] + _lc
[I_NET_ERR_CONN
]);
5060 NetState
:= NET_STATE_NONE
;
5065 NetState
:= NET_STATE_GAME
;
5066 MC_SEND_FullStateRequest
;
5067 e_WriteLog('NET: Connection successful.', TMsgType
.Notify
);
5071 lastAsMegaWad
: Boolean = false;
5073 procedure g_Game_ChangeMap(const MapPath
: String);
5077 g_Game_ClearLoading();
5079 Force
:= gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
];
5080 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
5081 if gExitByTrigger
then
5084 gExitByTrigger
:= False;
5086 if not g_Game_StartMap(lastAsMegaWad
, MapPath
, Force
) then
5087 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [MapPath
]));
5090 procedure g_Game_Restart();
5094 if g_Game_IsClient
then
5096 map
:= g_ExtractFileName(gMapInfo
.Map
);
5097 e_LogWritefln('g_Game_Restart: map = "%s" gCurrentMapFileName = "%s"', [map
, gCurrentMapFileName
]);
5101 g_Game_ClearLoading();
5102 g_Game_StartMap(lastAsMegaWad
, Map
, True, gCurrentMapFileName
);
5105 function g_Game_StartMap (asMegawad
: Boolean; Map
: String; Force
: Boolean = False; const oldMapPath
: AnsiString
=''): Boolean;
5107 NewWAD
, ResName
: String;
5111 g_Map_Free((Map
<> gCurrentMapFileName
) and (oldMapPath
<> gCurrentMapFileName
));
5112 g_Player_RemoveAllCorpses();
5114 if (not g_Game_IsClient
) and
5115 (gSwitchGameMode
<> gGameSettings
.GameMode
) and
5116 (gGameSettings
.GameMode
<> GM_SINGLE
) then
5118 if gSwitchGameMode
= GM_CTF
then
5119 gGameSettings
.MaxLives
:= 0;
5120 gGameSettings
.GameMode
:= gSwitchGameMode
;
5123 gSwitchGameMode
:= gGameSettings
.GameMode
;
5125 g_Player_ResetTeams();
5127 lastAsMegaWad
:= asMegawad
;
5128 if isWadPath(Map
) then
5130 NewWAD
:= g_ExtractWadName(Map
);
5131 ResName
:= g_ExtractFileName(Map
);
5132 if g_Game_IsServer
then
5134 nws
:= findDiskWad(NewWAD
);
5135 //writeln('000: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']');
5138 if (length(nws
) = 0) then nws
:= e_FindWad(MegawadDirs
, NewWAD
);
5139 if (length(nws
) = 0) then nws
:= e_FindWad(MapDirs
, NewWAD
);
5143 if (length(nws
) = 0) then nws
:= e_FindWad(MapDirs
, NewWAD
);
5144 if (length(nws
) = 0) then nws
:= e_FindWad(MegawadDirs
, NewWAD
);
5146 //if (length(nws) = 0) then nws := e_FindWad(MapDownloadDirs, NewWAD);
5147 //writeln('001: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']');
5149 if (length(nws
) = 0) then
5151 ResName
:= ''; // failed
5156 if (g_Game_IsNet
) then gWADHash
:= MD5File(nws
);
5157 //writeln('********: nws=', nws, ' : Map=', Map, ' : nw=', NewWAD, ' : resname=', ResName);
5158 g_Game_SetCurrentWAD(NewWAD
);
5163 // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART
5164 NewWAD
:= g_Game_ClientWAD(NewWAD
, gWADHash
);
5169 NewWAD
:= gGameSettings
.WAD
;
5175 //writeln('********: gsw=', gGameSettings.WAD, '; rn=', ResName);
5177 if (ResName
<> '') and (NewWAD
<> '') then
5179 //result := g_Map_Load(gGameSettings.WAD + ':\' + ResName);
5180 result
:= g_Map_Load(NewWAD
+':\'+ResName
);
5184 g_Player_ResetAll(Force
or gLastMap
, gGameSettings
.GameType
= GT_SINGLE
);
5186 gState
:= STATE_NONE
;
5187 g_ActiveWindow
:= nil;
5193 if gGameSettings
.GameMode
= GM_CTF
then
5195 g_Map_ResetFlag(FLAG_RED
);
5196 g_Map_ResetFlag(FLAG_BLUE
);
5197 // CTF, à ôëàãîâ íåò:
5198 if not g_Map_HaveFlagPoints() then
5199 g_SimpleError(_lc
[I_GAME_ERROR_CTF
]);
5204 gState
:= STATE_MENU
;
5209 gPauseMain
:= false;
5210 gPauseHolmes
:= false;
5211 NetTimeToUpdate
:= 1;
5212 NetTimeToReliable
:= 0;
5213 NetTimeToMaster
:= NetMasterRate
;
5214 gSpectLatchPID1
:= 0;
5215 gSpectLatchPID2
:= 0;
5216 gMissionFailed
:= False;
5219 gCoopMonstersKilled
:= 0;
5220 gCoopSecretsFound
:= 0;
5222 gVoteInProgress
:= False;
5223 gVotePassed
:= False;
5229 if not gGameOn
then Exit
;
5231 g_Game_SpectateCenterView();
5233 if g_Game_IsServer
then
5235 if (gGameSettings
.MaxLives
> 0) and (gGameSettings
.WarmupTime
> 0) then
5237 gLMSRespawn
:= LMS_RESPAWN_WARMUP
;
5238 gLMSRespawnTime
:= gTime
+ gGameSettings
.WarmupTime
*1000;
5239 gLMSSoftSpawn
:= True;
5240 if g_Game_IsNet
then
5241 MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, gLMSRespawnTime
- gTime
);
5245 gLMSRespawn
:= LMS_RESPAWN_NONE
;
5246 gLMSRespawnTime
:= 0;
5250 if NetMode
= NET_SERVER
then
5252 // reset full state flags
5253 if NetClients
<> nil then
5254 for I
:= 0 to High(NetClients
) do
5255 NetClients
[I
].FullUpdateSent
:= False;
5257 MH_SEND_GameEvent(NET_EV_MAPSTART
, gGameSettings
.GameMode
, Map
);
5260 g_Net_Slist_ServerMapStarted();
5262 if NetClients
<> nil then
5263 for I
:= 0 to High(NetClients
) do
5264 if NetClients
[I
].Used
then
5266 NetClients
[I
].Voted
:= False;
5267 if NetClients
[I
].RequestedFullUpdate
then
5269 MH_SEND_Everything((NetClients
[I
].State
= NET_STATE_AUTH
), I
);
5270 NetClients
[I
].RequestedFullUpdate
:= False;
5274 g_Net_UnbanNonPerm();
5279 gCoopTotalMonstersKilled
:= 0;
5280 gCoopTotalSecretsFound
:= 0;
5281 gCoopTotalMonsters
:= 0;
5282 gCoopTotalSecrets
:= 0;
5286 g_Game_ExecuteEvent('onmapstart');
5289 procedure g_Game_ExitLevel(const Map
: AnsiString
);
5293 gCoopTotalMonstersKilled
:= gCoopTotalMonstersKilled
+ gCoopMonstersKilled
;
5294 gCoopTotalSecretsFound
:= gCoopTotalSecretsFound
+ gCoopSecretsFound
;
5295 gCoopTotalMonsters
:= gCoopTotalMonsters
+ gTotalMonsters
;
5296 gCoopTotalSecrets
:= gCoopTotalSecrets
+ gSecretsCount
;
5298 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
5299 if gGameSettings
.GameType
= GT_SINGLE
then
5300 gExit
:= EXIT_ENDLEVELSINGLE
5301 else // Âûøëè â âûõîä â Ñâîåé èãðå
5303 gExit
:= EXIT_ENDLEVELCUSTOM
;
5304 if gGameSettings
.GameMode
= GM_COOP
then
5305 g_Player_RememberAll
;
5307 if not g_Map_Exist(gGameSettings
.WAD
+ ':\' + gNextMap
) then
5310 if gGameSettings
.GameMode
= GM_COOP
then
5313 gStatsPressed
:= True;
5314 gNextMap
:= 'MAP01';
5316 if not g_Map_Exist(gGameSettings
.WAD
+ ':\' + gNextMap
) then
5319 if g_Game_IsNet
then
5321 MH_SEND_GameStats();
5322 MH_SEND_CoopStats();
5328 procedure g_Game_RestartLevel();
5332 if gGameSettings
.GameMode
= GM_SINGLE
then
5337 gExit
:= EXIT_ENDLEVELCUSTOM
;
5338 Map
:= g_ExtractFileName(gMapInfo
.Map
);
5342 function g_Game_ClientWAD (NewWAD
: String; const WHash
: TMD5Digest
): AnsiString
;
5344 gWAD
{, xwad}: String;
5347 if not g_Game_IsClient
then Exit
;
5348 //e_LogWritefln('*** g_Game_ClientWAD: `%s`', [NewWAD]);
5350 gWAD
:= g_Res_DownloadMapWAD(ExtractFileName(NewWAD
), WHash
);
5355 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_WAD
], [ExtractFileName(NewWAD
)]));
5359 e_LogWritefln('using downloaded client map wad [%s] for [%s]', [gWAD
, NewWAD
], TMsgType
.Notify
);
5362 g_Game_SetCurrentWAD(NewWAD
);
5366 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then Exit;
5367 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
5370 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
5371 gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash);
5375 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
5379 NewWAD := ExtractRelativePath(MapsDir, gWAD);
5380 g_Game_SetCurrentWAD(NewWAD);
5384 procedure g_Game_RestartRound(NoMapRestart
: Boolean = False);
5386 i
, n
, nb
, nr
: Integer;
5388 if not g_Game_IsServer
then Exit
;
5389 if gLMSRespawn
= LMS_RESPAWN_NONE
then Exit
;
5390 gLMSRespawn
:= LMS_RESPAWN_NONE
;
5391 gLMSRespawnTime
:= 0;
5394 if (gGameSettings
.GameMode
= GM_COOP
) and not NoMapRestart
then
5396 gMissionFailed
:= True;
5397 g_Game_RestartLevel
;
5401 n
:= 0; nb
:= 0; nr
:= 0;
5402 for i
:= Low(gPlayers
) to High(gPlayers
) do
5403 if (gPlayers
[i
] <> nil) and
5404 ((not gPlayers
[i
].FSpectator
) or gPlayers
[i
].FWantsInGame
or
5405 (gPlayers
[i
] is TBot
)) then
5408 if gPlayers
[i
].Team
= TEAM_RED
then Inc(nr
)
5409 else if gPlayers
[i
].Team
= TEAM_BLUE
then Inc(nb
)
5412 if (n
< 1) or ((gGameSettings
.GameMode
= GM_TDM
) and ((nr
= 0) or (nb
= 0))) then
5414 // wait a second until the fuckers finally decide to join
5415 gLMSRespawn
:= LMS_RESPAWN_WARMUP
;
5416 gLMSRespawnTime
:= gTime
+ gGameSettings
.WarmupTime
*1000;
5417 gLMSSoftSpawn
:= NoMapRestart
;
5418 if g_Game_IsNet
then
5419 MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, gLMSRespawnTime
- gTime
);
5423 g_Player_RemoveAllCorpses
;
5424 g_Game_Message(_lc
[I_MESSAGE_LMS_START
], 144);
5425 if g_Game_IsNet
then
5426 MH_SEND_GameEvent(NET_EV_LMS_START
);
5428 for i
:= Low(gPlayers
) to High(gPlayers
) do
5430 if gPlayers
[i
] = nil then continue
;
5431 if gPlayers
[i
] is TBot
then gPlayers
[i
].FWantsInGame
:= True;
5432 // don't touch normal spectators
5433 if gPlayers
[i
].FSpectator
and not gPlayers
[i
].FWantsInGame
then
5435 gPlayers
[i
].FNoRespawn
:= True;
5436 gPlayers
[i
].Lives
:= 0;
5437 if g_Game_IsNet
then
5438 MH_SEND_PlayerStats(gPlayers
[I
].UID
);
5441 gPlayers
[i
].FNoRespawn
:= False;
5442 gPlayers
[i
].Lives
:= gGameSettings
.MaxLives
;
5443 gPlayers
[i
].Respawn(False, True);
5444 if gGameSettings
.GameMode
= GM_COOP
then
5446 gPlayers
[i
].Frags
:= 0;
5447 gPlayers
[i
].RestoreState
;
5449 if (gPlayer1
= nil) and (gSpectLatchPID1
> 0) then
5450 gPlayer1
:= g_Player_Get(gSpectLatchPID1
);
5451 if (gPlayer2
= nil) and (gSpectLatchPID2
> 0) then
5452 gPlayer2
:= g_Player_Get(gSpectLatchPID2
);
5455 g_Items_RestartRound();
5457 gLMSSoftSpawn
:= False;
5460 function g_Game_GetFirstMap(WAD
: String): String;
5466 MapList
:= g_Map_GetMapsList(WAD
);
5467 if MapList
= nil then
5470 // TODO: Replace with simple minimum lookup.
5471 specialize TArrayHelper
<ShortString
>.Sort(MapList
,
5472 specialize TComparer
<ShortString
>.Construct(@ShortCompareText
));
5473 Result
:= MapList
[Low(MapList
)];
5475 // BD: why should we check for a map we just found?
5476 // just to guarantee its presence at the archive root? but nobody does the opposite anyway.
5477 //if not g_Map_Exist(WAD + ':\' + Result) then
5481 function g_Game_GetNextMap(): String;
5489 MapList
:= g_Map_GetMapsList(gGameSettings
.WAD
);
5490 if MapList
= nil then
5493 Map
:= g_ExtractFileName(gMapInfo
.Map
);
5494 specialize TArrayHelper
<ShortString
>.Sort(MapList
,
5495 specialize TComparer
<ShortString
>.Construct(@ShortCompareText
));
5498 for I
:= Low(MapList
) to High(MapList
) do
5499 if Map
= MapList
[I
] then
5505 if MapIndex
<> -255 then
5507 if MapIndex
= High(MapList
)
5508 then Result
:= MapList
[Low(MapList
)]
5509 else Result
:= MapList
[MapIndex
+ 1];
5511 if not g_Map_Exist(gGameSettings
.WAD
+ ':\' + Result
) then Result
:= Map
;
5515 procedure g_Game_NextLevel();
5517 if gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
, GM_COOP
] then
5518 gExit
:= EXIT_ENDLEVELCUSTOM
5521 gExit
:= EXIT_ENDLEVELSINGLE
;
5525 if gNextMap
<> '' then Exit
;
5526 gNextMap
:= g_Game_GetNextMap();
5529 function g_Game_IsTestMap(): Boolean;
5531 result
:= StrEquCI1251(TEST_MAP_NAME
, g_ExtractFileName(gMapInfo
.Map
));
5534 procedure g_Game_DeleteTestMap();
5537 //MapName: AnsiString;
5545 a
:= Pos('.wad:\', toLowerCase1251(gMapToDelete
));
5546 if (a
= 0) then a
:= Pos('.wad:/', toLowerCase1251(gMapToDelete
));
5547 if (a
= 0) then exit
;
5549 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
5550 WadName
:= Copy(gMapToDelete
, 1, a
+3);
5551 Delete(gMapToDelete
, 1, a
+5);
5552 gMapToDelete
:= UpperCase(gMapToDelete
);
5554 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
5557 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
5558 if MapName <> TEST_MAP_NAME then
5561 if not gTempDelete then
5563 time := g_GetFileTime(WadName);
5564 WAD := TWADFile.Create();
5567 if not WAD.ReadFile(WadName) then
5568 begin // Íåò òàêîãî WAD-ôàéëà
5573 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
5575 MapList := WAD.GetResourcesList('');
5577 if MapList <> nil then
5578 for a := 0 to High(MapList) do
5579 if MapList[a] = MapName then
5581 // Óäàëÿåì è ñîõðàíÿåì:
5582 WAD.RemoveResource('', MapName);
5583 WAD.SaveTo(WadName);
5588 g_SetFileTime(WadName, time);
5591 if gTempDelete
then DeleteFile(WadName
);
5594 procedure GameCVars(P
: SSArray
);
5597 stat
: TPlayerStatArray
;
5601 procedure ParseGameFlag(Flag
: TGameOption
; OffMsg
, OnMsg
: TStrings_Locale
; OnMapChange
: Boolean = False);
5605 if Length(P
) <= 1 then
5606 x
:= Flag
in gsGameFlags
5612 then gsGameFlags
+= [Flag
]
5613 else gsGameFlags
-= [Flag
];
5615 if g_Game_IsServer
then
5618 then gGameSettings
.Options
+= [Flag
]
5619 else gGameSettings
.Options
-= [Flag
];
5620 if g_Game_IsNet
then MH_SEND_GameSettings
;
5625 then g_Console_Add(_lc
[OnMsg
])
5626 else g_Console_Add(_lc
[OffMsg
]);
5628 if OnMapChange
and g_Game_IsServer
then
5629 g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5634 cmd
:= LowerCase(P
[0]);
5638 if (Length(P
) > 1) then
5640 a
:= g_Game_TextToMode(P
[1]);
5641 if a
= GM_SINGLE
then a
:= GM_COOP
;
5642 gsGameMode
:= g_Game_ModeToText(a
);
5643 if g_Game_IsServer
then
5645 gSwitchGameMode
:= a
;
5646 if (gGameOn
and (gGameSettings
.GameMode
= GM_SINGLE
)) or
5647 (gState
= STATE_INTERSINGLE
) then
5648 gSwitchGameMode
:= GM_SINGLE
;
5650 gGameSettings
.GameMode
:= gSwitchGameMode
;
5654 if gSwitchGameMode
= gGameSettings
.GameMode
then
5655 g_Console_Add(Format(_lc
[I_MSG_GAMEMODE_CURRENT
],
5656 [g_Game_ModeToText(gGameSettings
.GameMode
)]))
5658 g_Console_Add(Format(_lc
[I_MSG_GAMEMODE_CHANGE
],
5659 [g_Game_ModeToText(gGameSettings
.GameMode
),
5660 g_Game_ModeToText(gSwitchGameMode
)]));
5664 ParseGameFlag(TGameOption
.TEAM_DAMAGE
, I_MSG_FRIENDLY_FIRE_OFF
, I_MSG_FRIENDLY_FIRE_ON
);
5665 'g_friendly_absorb_damage':
5666 ParseGameFlag(TGameOption
.TEAM_ABSORB_DAMAGE
, I_MSG_FRIENDLY_ABSORB_DAMAGE_OFF
, I_MSG_FRIENDLY_ABSORB_DAMAGE_ON
);
5667 'g_friendly_hit_trace':
5668 ParseGameFlag(TGameOption
.TEAM_HIT_TRACE
, I_MSG_FRIENDLY_HIT_TRACE_OFF
, I_MSG_FRIENDLY_HIT_TRACE_ON
);
5669 'g_friendly_hit_projectile':
5670 ParseGameFlag(TGameOption
.TEAM_HIT_PROJECTILE
, I_MSG_FRIENDLY_PROJECT_TRACE_OFF
, I_MSG_FRIENDLY_PROJECT_TRACE_ON
);
5671 'g_items_all_respawn_random':
5672 ParseGameFlag(TGameOption
.ITEM_ALL_RANDOM
, I_MSG_ITEM_ALL_RANDOM_OFF
, I_MSG_ITEM_ALL_RANDOM_ON
, False);
5673 'g_items_help_respawn_random':
5674 ParseGameFlag(TGameOption
.ITEM_LIFE_RANDOM
, I_MSG_ITEM_LIFE_RANDOM_OFF
, I_MSG_ITEM_LIFE_RANDOM_ON
, False);
5675 'g_items_ammo_respawn_random':
5676 ParseGameFlag(TGameOption
.ITEM_AMMO_RANDOM
, I_MSG_ITEM_AMMO_RANDOM_OFF
, I_MSG_ITEM_AMMO_RANDOM_ON
, False);
5677 'g_items_weapon_respawn_random':
5678 ParseGameFlag(TGameOption
.ITEM_WEAPON_RANDOM
, I_MSG_ITEM_WEAPON_RANDOM_OFF
, I_MSG_ITEM_WEAPON_RANDOM_ON
);
5679 'g_powerup_randomize_respawn':
5680 ParseGameFlag(TGameOption
.POWERUP_RANDOM
, I_MSG_POWERUP_RANDOM_OFF
, I_MSG_POWERUP_RANDOM_ON
, False);
5682 ParseGameFlag(TGameOption
.WEAPONS_STAY
, I_MSG_WEAPONSTAY_OFF
, I_MSG_WEAPONSTAY_ON
);
5684 ParseGameFlag(TGameOption
.ALLOW_EXIT
, I_MSG_ALLOWEXIT_OFF
, I_MSG_ALLOWEXIT_ON
, True);
5686 ParseGameFlag(TGameOption
.MONSTERS
, I_MSG_ALLOWMON_OFF
, I_MSG_ALLOWMON_ON
, True);
5688 ParseGameFlag(TGameOption
.ALLOW_DROP_FLAG
, I_MSG_ALLOWDROPFLAG_OFF
, I_MSG_ALLOWDROPFLAG_ON
);
5690 ParseGameFlag(TGameOption
.THROW_FLAG
, I_MSG_THROWFLAG_OFF
, I_MSG_THROWFLAG_ON
);
5692 ParseGameFlag(TGameOption
.BOTS_VS_PLAYERS
, I_MSG_BOTSVSPLAYERS_OFF
, I_MSG_BOTSVSPLAYERS_ON
);
5694 ParseGameFlag(TGameOption
.BOTS_VS_MONSTERS
, I_MSG_BOTSVSMONSTERS_OFF
, I_MSG_BOTSVSMONSTERS_ON
);
5696 ParseGameFlag(TGameOption
.DM_KEYS
, I_MSG_DMKEYS_OFF
, I_MSG_DMKEYS_ON
, True);
5698 'g_gameflags': begin
5699 if Length(P
) > 1 then
5701 gsGameFlags
:= TGameOptions(StrToDWordDef(P
[1], LongWord(gsGameFlags
)));
5702 if g_Game_IsServer
then
5704 gGameSettings
.Options
:= gsGameFlags
;
5705 if g_Game_IsNet
then MH_SEND_GameSettings
;
5709 g_Console_Add(Format('%s %u', [cmd
, LongWord(gsGameFlags
)]));
5712 'g_warmup_time': begin
5713 if Length(P
) > 1 then
5715 gsWarmupTime
:= nclamp(StrToIntDef(P
[1], gsWarmupTime
), 0, $FFFF);
5716 if g_Game_IsServer
then
5718 gGameSettings
.WarmupTime
:= gsWarmupTime
;
5719 // extend warmup if it's already going
5720 if gLMSRespawn
= LMS_RESPAWN_WARMUP
then
5722 gLMSRespawnTime
:= gTime
+ gsWarmupTime
* 1000;
5723 if g_Game_IsNet
then MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, gLMSRespawnTime
- gTime
);
5725 if g_Game_IsNet
then MH_SEND_GameSettings
;
5729 g_Console_Add(Format(_lc
[I_MSG_WARMUP
], [Integer(gsWarmupTime
)]));
5730 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5733 'g_spawn_invul': begin
5734 if Length(P
) > 1 then
5736 gsSpawnInvul
:= nclamp(StrToIntDef(P
[1], gsSpawnInvul
), 0, $FFFF);
5737 if g_Game_IsServer
then
5739 gGameSettings
.SpawnInvul
:= gsSpawnInvul
;
5740 if g_Game_IsNet
then MH_SEND_GameSettings
;
5744 g_Console_Add(Format('%s %d', [cmd
, Integer(gsSpawnInvul
)]));
5747 'g_item_respawn_time': begin
5748 if Length(P
) > 1 then
5750 gsItemRespawnTime
:= nclamp(StrToIntDef(P
[1], gsItemRespawnTime
), 0, $FFFF);
5751 if g_Game_IsServer
then
5753 gGameSettings
.ItemRespawnTime
:= gsItemRespawnTime
;
5754 if g_Game_IsNet
then MH_SEND_GameSettings
;
5758 g_Console_Add(Format('%s %d', [cmd
, Integer(gsItemRespawnTime
)]));
5759 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5762 'g_item_time_random': begin
5763 if Length(P
) > 1 then
5765 gsItemRespawnRandom
:= nclamp(StrToIntDef(P
[1], gsItemRespawnRandom
), 0, $FFFF);
5766 if g_Game_IsServer
then
5768 gGameSettings
.ItemRespawnRandom
:= gsItemRespawnRandom
;
5769 if g_Game_IsNet
then MH_SEND_GameSettings
;
5773 g_Console_Add(Format('%s %d', [cmd
, Integer(gsItemRespawnRandom
)]));
5774 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5777 'g_powerup_respawn_time': begin
5778 if Length(P
) > 1 then
5780 gsPowerupRespawnTime
:= nclamp(StrToIntDef(P
[1], gsPowerupRespawnTime
), 0, $FFFF);
5781 if g_Game_IsServer
then
5783 gGameSettings
.PowerupRespawnTime
:= gsPowerupRespawnTime
;
5784 if g_Game_IsNet
then MH_SEND_GameSettings
;
5788 g_Console_Add(Format('%s %d', [cmd
, Integer(gsPowerupRespawnTime
)]));
5789 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5792 'g_powerup_time_random': begin
5793 if Length(P
) > 1 then
5795 gsPowerupRespawnRandom
:= nclamp(StrToIntDef(P
[1], gsPowerupRespawnRandom
), 0, $FFFF);
5796 if g_Game_IsServer
then
5798 gGameSettings
.PowerupRespawnRandom
:= gsPowerupRespawnRandom
;
5799 if g_Game_IsNet
then MH_SEND_GameSettings
;
5803 g_Console_Add(Format('%s %d', [cmd
, Integer(gsPowerupRespawnRandom
)]));
5804 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5807 'sv_intertime': begin
5808 if (Length(P
) > 1) then
5809 gDefInterTime
:= Min(Max(StrToIntDef(P
[1], gDefInterTime
), -1), 120);
5811 g_Console_Add(cmd
+ ' = ' + IntToStr(gDefInterTime
));
5814 'g_max_particles': begin
5815 if Length(p
) = 2 then
5817 a
:= Max(0, StrToIntDef(p
[1], 0));
5820 else if Length(p
) = 1 then
5822 e_LogWritefln('%s', [g_GFX_GetMax()])
5826 e_LogWritefln('usage: %s <n>', [cmd
])
5830 'g_max_shells': begin
5831 if Length(p
) = 2 then
5833 a
:= Max(0, StrToIntDef(p
[1], 0));
5836 else if Length(p
) = 1 then
5838 e_LogWritefln('%s', [g_Shells_GetMax()])
5842 e_LogWritefln('usage: %s <n>', [cmd
])
5847 if Length(p
) = 2 then
5849 a
:= Max(0, StrToIntDef(p
[1], 0));
5852 else if Length(p
) = 1 then
5854 e_LogWritefln('%s', [g_Gibs_GetMax()])
5858 e_LogWritefln('usage: %s <n>', [cmd
])
5862 'g_max_corpses': begin
5863 if Length(p
) = 2 then
5865 a
:= Max(0, StrToIntDef(p
[1], 0));
5868 else if Length(p
) = 1 then
5870 e_LogWritefln('%s', [g_Corpses_GetMax()])
5874 e_LogWritefln('usage: %s <n>', [cmd
])
5878 'g_force_model': begin
5879 if Length(p
) = 2 then
5881 a
:= StrToIntDef(p
[1], 0);
5882 g_Force_Model_Set(a
);
5883 if (g_Force_Model_Get() <> 0) and (gPlayers
<> nil) then
5885 for a
:= Low(gPlayers
) to High(gPlayers
) do
5887 if (gPlayers
[a
] <> nil) then
5889 if (gPlayers
[a
].UID
= gPlayer1
.UID
) then
5891 else if (gPlayer2
<> nil) and (gPlayers
[a
].UID
= gPlayer2
.UID
) then
5893 gPlayers
[a
].setModel(g_Forced_Model_GetName());
5897 else if (g_Force_Model_Get() = 0) and (gPlayers
<> nil) then
5899 for a
:= Low(gPlayers
) to High(gPlayers
) do
5901 if (gPlayers
[a
] <> nil) then
5903 if (gPlayers
[a
].UID
= gPlayer1
.UID
) then
5905 else if (gPlayer2
<> nil) and (gPlayers
[a
].UID
= gPlayer2
.UID
) then
5907 gPlayers
[a
].setModel(gPlayers
[a
].FActualModelName
);
5914 'g_force_model_name': begin
5915 if (Length(P
) > 1) then
5917 cmd
:= b_Text_Unformat(P
[1]);
5918 g_Forced_Model_SetName(cmd
);
5919 if (g_Force_Model_Get() <> 0) and (gPlayers
<> nil) then
5921 for a
:= Low(gPlayers
) to High(gPlayers
) do
5923 if (gPlayers
[a
] <> nil) then
5925 if (gPlayers
[a
].UID
= gPlayer1
.UID
) then
5927 else if (gPlayer2
<> nil) and (gPlayers
[a
].UID
= gPlayer2
.UID
) then
5929 gPlayers
[a
].setModel(g_Forced_Model_GetName());
5936 'g_scorelimit': begin
5937 if Length(P
) > 1 then
5939 gsScoreLimit
:= nclamp(StrToIntDef(P
[1], gsScoreLimit
), 0, $FFFF);
5941 if g_Game_IsServer
then
5944 if gGameSettings
.GameMode
= GM_DM
then
5946 stat
:= g_Player_GetStats();
5948 for a
:= 0 to High(stat
) do
5949 if stat
[a
].Frags
> b
then
5953 b
:= Max(gTeamStat
[TEAM_RED
].Score
, gTeamStat
[TEAM_BLUE
].Score
);
5955 // if someone has a higher score, set it to that instead
5956 gsScoreLimit
:= max(gsScoreLimit
, b
);
5957 gGameSettings
.ScoreLimit
:= gsScoreLimit
;
5959 if g_Game_IsNet
then MH_SEND_GameSettings
;
5963 g_Console_Add(Format(_lc
[I_MSG_SCORE_LIMIT
], [Integer(gsScoreLimit
)]));
5966 'g_timelimit': begin
5967 if Length(P
) > 1 then
5969 gsTimeLimit
:= nclamp(StrToIntDef(P
[1], gsTimeLimit
), 0, $FFFF);
5970 if g_Game_IsServer
then
5972 gGameSettings
.TimeLimit
:= gsTimeLimit
;
5973 if g_Game_IsNet
then MH_SEND_GameSettings
;
5976 g_Console_Add(Format(_lc
[I_MSG_TIME_LIMIT
],
5977 [gsTimeLimit
div 3600,
5978 (gsTimeLimit
div 60) mod 60,
5979 gsTimeLimit
mod 60]));
5983 if Length(P
) > 1 then
5984 gMaxBots
:= nclamp(StrToIntDef(P
[1], gMaxBots
), 0, 127);
5985 g_Console_Add('g_max_bots = ' + IntToStr(gMaxBots
));
5989 if Length(P
) > 1 then
5991 gsMaxLives
:= nclamp(StrToIntDef(P
[1], gsMaxLives
), 0, $FFFF);
5992 if g_Game_IsServer
then
5994 gGameSettings
.MaxLives
:= gsMaxLives
;
5995 if g_Game_IsNet
then MH_SEND_GameSettings
;
5999 g_Console_Add(Format(_lc
[I_MSG_LIVES
], [Integer(gsMaxLives
)]));
6004 procedure PlayerSettingsCVars(P
: SSArray
);
6009 function ParseTeam(s
: string): Byte;
6013 'red', '1': result
:= TEAM_RED
;
6014 'blue', '2': result
:= TEAM_BLUE
;
6015 else result
:= TEAM_NONE
;
6019 cmd
:= LowerCase(P
[0]);
6023 if (Length(P
) > 1) then
6025 gPlayer1Settings
.Name
:= b_Text_Unformat(P
[1]);
6026 if g_Game_IsClient
then
6027 MC_SEND_PlayerSettings
6028 else if gGameOn
and (gPlayer1
<> nil) then
6030 gPlayer1
.Name
:= b_Text_Unformat(P
[1]);
6031 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer1
.UID
);
6037 if (Length(P
) > 1) then
6039 gPlayer2Settings
.Name
:= b_Text_Unformat(P
[1]);
6040 if g_Game_IsClient
then
6041 MC_SEND_PlayerSettings
6042 else if gGameOn
and (gPlayer2
<> nil) then
6044 gPlayer2
.Name
:= b_Text_Unformat(P
[1]);
6045 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer2
.UID
);
6051 if Length(P
) > 3 then
6053 gPlayer1Settings
.Color
:= _RGB(EnsureRange(StrToIntDef(P
[1], 0), 0, 255),
6054 EnsureRange(StrToIntDef(P
[2], 0), 0, 255),
6055 EnsureRange(StrToIntDef(P
[3], 0), 0, 255));
6056 if g_Game_IsClient
then
6057 MC_SEND_PlayerSettings
6058 else if gGameOn
and (gPlayer1
<> nil) then
6060 gPlayer1
.SetColor(gPlayer1Settings
.Color
);
6061 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer1
.UID
);
6067 if Length(P
) > 3 then
6069 gPlayer2Settings
.Color
:= _RGB(EnsureRange(StrToIntDef(P
[1], 0), 0, 255),
6070 EnsureRange(StrToIntDef(P
[2], 0), 0, 255),
6071 EnsureRange(StrToIntDef(P
[3], 0), 0, 255));
6072 if g_Game_IsClient
then
6073 MC_SEND_PlayerSettings
6074 else if gGameOn
and (gPlayer2
<> nil) then
6076 gPlayer2
.SetColor(gPlayer2Settings
.Color
);
6077 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer2
.UID
);
6083 if (Length(P
) > 1) then
6085 gPlayer1Settings
.Model
:= P
[1];
6086 if g_Game_IsClient
then
6087 MC_SEND_PlayerSettings
6088 else if gGameOn
and (gPlayer1
<> nil) then
6090 gPlayer1
.FActualModelName
:= gPlayer1Settings
.Model
;
6091 gPlayer1
.SetModel(gPlayer1Settings
.Model
);
6092 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer1
.UID
);
6098 if (Length(P
) > 1) then
6100 gPlayer2Settings
.Model
:= P
[1];
6101 if g_Game_IsClient
then
6102 MC_SEND_PlayerSettings
6103 else if gGameOn
and (gPlayer2
<> nil) then
6105 gPlayer2
.FActualModelName
:= gPlayer2Settings
.Model
;
6106 gPlayer2
.SetModel(gPlayer2Settings
.Model
);
6107 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer2
.UID
);
6113 // TODO: switch teams if in game or store this separately
6114 if (Length(P
) > 1) then
6116 team
:= ParseTeam(P
[1]);
6117 if team
= TEAM_NONE
then
6118 g_Console_Add('expected ''red'', ''blue'', 1 or 2')
6119 else if not gGameOn
and not g_Game_IsNet
then
6120 gPlayer1Settings
.Team
:= team
6122 g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
6127 // TODO: switch teams if in game or store this separately
6128 if (Length(P
) > 1) then
6130 team
:= ParseTeam(P
[1]);
6131 if team
= TEAM_NONE
then
6132 g_Console_Add('expected ''red'', ''blue'', 1 or 2')
6133 else if not gGameOn
and not g_Game_IsNet
then
6134 gPlayer2Settings
.Team
:= team
6136 g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
6141 if (Length(P
) = 2) then
6142 gPlayer1Settings
.WeaponSwitch
:= EnsureRange(StrTointDef(P
[1], 0), 0, 2);
6146 if (Length(P
) = 2) then
6147 gPlayer2Settings
.WeaponSwitch
:= EnsureRange(StrTointDef(P
[1], 0), 0, 2);
6151 if (Length(P
) = 2) then
6152 gPlayer1Settings
.SwitchToEmpty
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6156 if (Length(P
) = 2) then
6157 gPlayer2Settings
.SwitchToEmpty
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6161 if (Length(P
) = 2) then
6162 gPlayer1Settings
.SkipIronFist
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6166 if (Length(P
) = 2) then
6167 gPlayer2Settings
.SkipIronFist
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6169 'p1_priority_ironfist':
6171 if (Length(P
) = 2) then
6172 gPlayer1Settings
.WeaponPreferences
[WEAPON_IRONFIST
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6174 'p2_priority_ironfist':
6176 if (Length(P
) = 2) then
6177 gPlayer2Settings
.WeaponPreferences
[WEAPON_IRONFIST
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6181 if (Length(P
) = 2) then
6182 gPlayer1Settings
.WeaponPreferences
[WEAPON_SAW
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6186 if (Length(P
) = 2) then
6187 gPlayer2Settings
.WeaponPreferences
[WEAPON_SAW
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6189 'p1_priority_pistol':
6191 if (Length(P
) = 2) then
6192 gPlayer1Settings
.WeaponPreferences
[WEAPON_PISTOL
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6194 'p2_priority_pistol':
6196 if (Length(P
) = 2) then
6197 gPlayer2Settings
.WeaponPreferences
[WEAPON_PISTOL
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6199 'p1_priority_shotgun1':
6201 if (Length(P
) = 2) then
6202 gPlayer1Settings
.WeaponPreferences
[WEAPON_SHOTGUN1
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6204 'p2_priority_shotgun1':
6206 if (Length(P
) = 2) then
6207 gPlayer2Settings
.WeaponPreferences
[WEAPON_SHOTGUN1
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6209 'p1_priority_shotgun2':
6211 if (Length(P
) = 2) then
6212 gPlayer1Settings
.WeaponPreferences
[WEAPON_SHOTGUN2
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6214 'p2_priority_shotgun2':
6216 if (Length(P
) = 2) then
6217 gPlayer2Settings
.WeaponPreferences
[WEAPON_SHOTGUN2
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6219 'p1_priority_chaingun':
6221 if (Length(P
) = 2) then
6222 gPlayer1Settings
.WeaponPreferences
[WEAPON_CHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6224 'p2_priority_chaingun':
6226 if (Length(P
) = 2) then
6227 gPlayer2Settings
.WeaponPreferences
[WEAPON_CHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6229 'p1_priority_rocketlauncher':
6231 if (Length(P
) = 2) then
6232 gPlayer1Settings
.WeaponPreferences
[WEAPON_ROCKETLAUNCHER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6234 'p2_priority_rocketlauncher':
6236 if (Length(P
) = 2) then
6237 gPlayer2Settings
.WeaponPreferences
[WEAPON_ROCKETLAUNCHER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6239 'p1_priority_plasma':
6241 if (Length(P
) = 2) then
6242 gPlayer1Settings
.WeaponPreferences
[WEAPON_PLASMA
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6244 'p2_priority_plasma':
6246 if (Length(P
) = 2) then
6247 gPlayer2Settings
.WeaponPreferences
[WEAPON_PLASMA
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6251 if (Length(P
) = 2) then
6252 gPlayer1Settings
.WeaponPreferences
[WEAPON_BFG
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6256 if (Length(P
) = 2) then
6257 gPlayer2Settings
.WeaponPreferences
[WEAPON_BFG
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6259 'p1_priority_superchaingun':
6261 if (Length(P
) = 2) then
6262 gPlayer1Settings
.WeaponPreferences
[WEAPON_SUPERCHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6264 'p2_priority_superchaingun':
6266 if (Length(P
) = 2) then
6267 gPlayer2Settings
.WeaponPreferences
[WEAPON_SUPERCHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6269 'p1_priority_flamethrower':
6271 if (Length(P
) = 2) then
6272 gPlayer1Settings
.WeaponPreferences
[WEAPON_FLAMETHROWER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6274 'p2_priority_flamethrower':
6276 if (Length(P
) = 2) then
6277 gPlayer2Settings
.WeaponPreferences
[WEAPON_FLAMETHROWER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6279 'p1_priority_berserk':
6281 if (Length(P
) = 2) then
6282 gPlayer1Settings
.WeaponPreferences
[WP_LAST
+1] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6284 'p2_priority_berserk':
6286 if (Length(P
) = 2) then
6287 gPlayer2Settings
.WeaponPreferences
[WP_LAST
+1] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6292 procedure PrintHeapStats();
6296 hs
:= GetFPCHeapStatus();
6297 e_LogWriteLn ('v===== heap status =====v');
6298 e_LogWriteFln('max heap size = %d k', [hs
.MaxHeapSize
div 1024]);
6299 e_LogWriteFln('max heap used = %d k', [hs
.MaxHeapUsed
div 1024]);
6300 e_LogWriteFln('cur heap size = %d k', [hs
.CurrHeapSize
div 1024]);
6301 e_LogWriteFln('cur heap used = %d k', [hs
.CurrHeapUsed
div 1024]);
6302 e_LogWriteFln('cur heap free = %d k', [hs
.CurrHeapFree
div 1024]);
6303 e_LogWriteLn ('^=======================^');
6306 procedure DebugCommands(P
: SSArray
);
6313 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
6314 if {gDebugMode}conIsCheatsEnabled
then
6316 cmd
:= LowerCase(P
[0]);
6317 if cmd
= 'd_window' then
6319 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth
, gScreenHeight
]));
6320 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX
, gWinSizeY
]));
6322 else if cmd
= 'd_sounds' then
6324 if (Length(P
) > 1) and
6325 ((P
[1] = '1') or (P
[1] = '0')) then
6326 g_Debug_Sounds
:= (P
[1][1] = '1');
6328 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds
)]));
6330 else if cmd
= 'd_frames' then
6332 if (Length(P
) > 1) and
6333 ((P
[1] = '1') or (P
[1] = '0')) then
6334 g_Debug_Frames
:= (P
[1][1] = '1');
6336 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames
)]));
6338 else if cmd
= 'd_winmsg' then
6340 if (Length(P
) > 1) and
6341 ((P
[1] = '1') or (P
[1] = '0')) then
6342 g_Debug_WinMsgs
:= (P
[1][1] = '1');
6344 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs
)]));
6346 else if (cmd
= 'd_monoff') and not g_Game_IsNet
then
6348 if (Length(P
) > 1) and
6349 ((P
[1] = '1') or (P
[1] = '0')) then
6350 g_Debug_MonsterOff
:= (P
[1][1] = '1');
6352 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff
)]));
6354 else if (cmd
= 'd_botoff') and not g_Game_IsNet
then
6356 if Length(P
) > 1 then
6358 '0': g_debug_BotAIOff
:= 0;
6359 '1': g_debug_BotAIOff
:= 1;
6360 '2': g_debug_BotAIOff
:= 2;
6361 '3': g_debug_BotAIOff
:= 3;
6364 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff
]));
6366 else if cmd
= 'd_monster' then
6368 if gGameOn
and (gPlayer1
<> nil) and (gPlayer1
.alive
) and (not g_Game_IsNet
) then
6369 if Length(P
) < 2 then
6371 g_Console_Add(cmd
+ ' [ID | Name] [behaviour]');
6372 g_Console_Add('ID | Name');
6373 for b
:= MONSTER_DEMON
to MONSTER_MAN
do
6374 g_Console_Add(Format('%2d | %s', [b
, g_Mons_NameByTypeId(b
)]));
6375 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
6378 a
:= StrToIntDef(P
[1], 0);
6379 if (a
< MONSTER_DEMON
) or (a
> MONSTER_MAN
) then
6380 a
:= g_Mons_TypeIdByName(P
[1]);
6382 if (a
< MONSTER_DEMON
) or (a
> MONSTER_MAN
) then
6383 g_Console_Add(Format(_lc
[I_MSG_NO_MONSTER
], [P
[1]]))
6386 with gPlayer1
.Obj
do
6388 mon
:= g_Monsters_Create(a
,
6389 X
+ Rect
.X
+ (Rect
.Width
div 2),
6390 Y
+ Rect
.Y
+ Rect
.Height
,
6391 gPlayer1
.Direction
, True);
6393 if (Length(P
) > 2) and (mon
<> nil) then
6395 if (CompareText(P
[2], 'normal') = 0) then mon
.MonsterBehaviour
:= BH_NORMAL
6396 else if (CompareText(P
[2], 'killer') = 0) then mon
.MonsterBehaviour
:= BH_KILLER
6397 else if (CompareText(P
[2], 'maniac') = 0) then mon
.MonsterBehaviour
:= BH_MANIAC
6398 else if (CompareText(P
[2], 'insane') = 0) then mon
.MonsterBehaviour
:= BH_INSANE
6399 else if (CompareText(P
[2], 'cannibal') = 0) then mon
.MonsterBehaviour
:= BH_CANNIBAL
6400 else if (CompareText(P
[2], 'good') = 0) then mon
.MonsterBehaviour
:= BH_GOOD
6401 else if (CompareText(P
[2], 'friend') = 0) then mon
.MonsterBehaviour
:= BH_GOOD
6402 else if (CompareText(P
[2], 'friendly') = 0) then mon
.MonsterBehaviour
:= BH_GOOD
6403 else mon
.MonsterBehaviour
:= Min(Max(StrToIntDef(P
[2], BH_NORMAL
), BH_NORMAL
), BH_GOOD
);
6408 else if (cmd
= 'd_health') then
6410 if (Length(P
) > 1) and
6411 ((P
[1] = '1') or (P
[1] = '0')) then
6412 g_debug_HealthBar
:= (P
[1][1] = '1');
6414 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar
)]));
6416 else if (cmd
= 'd_player') then
6418 if (Length(P
) > 1) and
6419 ((P
[1] = '1') or (P
[1] = '0')) then
6420 g_debug_Player
:= (P
[1][1] = '1');
6422 g_Console_Add(Format(cmd
+ ' is %d', [Byte(g_Debug_Player
)]));
6424 else if (cmd
= 'd_mem') then
6430 g_Console_Add(_lc
[I_MSG_NOT_DEBUG
]);
6434 procedure GameCheats(P
: SSArray
);
6440 if (not gGameOn
) or (not conIsCheatsEnabled
) then
6442 g_Console_Add('not available');
6448 g_Console_Add('where is the player?!');
6451 cmd
:= LowerCase(P
[0]);
6455 plr
.GodMode
:= not plr
.GodMode
;
6456 if plr
.GodMode
then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
6459 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
6460 if cmd
= 'give' then
6462 if length(P
) < 2 then begin g_Console_Add('give what?!'); exit
; end;
6463 for f
:= 1 to High(P
) do
6465 cmd
:= LowerCase(P
[f
]);
6466 if cmd
= 'health' then begin plr
.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue
; end;
6467 if (cmd
= 'all') {or (cmd = 'weapons')} then begin plr
.TankRamboCheats(False); g_Console_Add('player got the gifts'); continue
; end;
6468 if cmd
= 'exit' then
6470 if gTriggers
<> nil then
6472 for a
:= 0 to High(gTriggers
) do
6474 if gTriggers
[a
].TriggerType
= TRIGGER_EXIT
then
6476 g_Console_Add('player left the map');
6477 gExitByTrigger
:= True;
6478 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
6479 g_Game_ExitLevel(gTriggers
[a
].tgcMap
);
6487 if cmd
= 'air' then begin plr
.GiveItem(ITEM_OXYGEN
); g_Console_Add('player got some air'); continue
; end;
6488 if cmd
= 'jetpack' then begin plr
.GiveItem(ITEM_JETPACK
); g_Console_Add('player got a jetpack'); continue
; end;
6489 if cmd
= 'suit' then begin plr
.GiveItem(ITEM_SUIT
); g_Console_Add('player got an envirosuit'); continue
; end;
6490 if cmd
= 'berserk' then begin plr
.GiveItem(ITEM_MEDKIT_BLACK
); g_Console_Add('player got a berserk pack'); continue
; end;
6491 if cmd
= 'backpack' then begin plr
.GiveItem(ITEM_AMMO_BACKPACK
); g_Console_Add('player got a backpack'); continue
; end;
6493 if cmd
= 'helmet' then begin plr
.GiveItem(ITEM_HELMET
); g_Console_Add('player got a helmet'); continue
; end;
6494 if cmd
= 'bottle' then begin plr
.GiveItem(ITEM_BOTTLE
); g_Console_Add('player got a bottle of health'); continue
; end;
6496 if cmd
= 'stimpack' then begin plr
.GiveItem(ITEM_MEDKIT_SMALL
); g_Console_Add('player got a stimpack'); continue
; end;
6497 if (cmd
= 'medkit') or (cmd
= 'medikit') or (cmd
= 'medpack') or (cmd
= 'medipack') then begin plr
.GiveItem(ITEM_MEDKIT_LARGE
); g_Console_Add('player got a '+cmd
); continue
; end;
6499 if cmd
= 'greenarmor' then begin plr
.GiveItem(ITEM_ARMOR_GREEN
); g_Console_Add('player got a security armor'); continue
; end;
6500 if cmd
= 'bluearmor' then begin plr
.GiveItem(ITEM_ARMOR_BLUE
); g_Console_Add('player got a combat armor'); continue
; end;
6502 if (cmd
= 'soulsphere') or (cmd
= 'soul') then begin plr
.GiveItem(ITEM_SPHERE_BLUE
); g_Console_Add('player got a soul sphere'); continue
; end;
6503 if (cmd
= 'megasphere') or (cmd
= 'mega') then begin plr
.GiveItem(ITEM_SPHERE_WHITE
); g_Console_Add('player got a megasphere'); continue
; end;
6505 if (cmd
= 'invul') or (cmd
= 'invulnerability') then begin plr
.GiveItem(ITEM_INVUL
); g_Console_Add('player got invulnerability'); continue
; end;
6506 if (cmd
= 'invis') or (cmd
= 'invisibility') then begin plr
.GiveItem(ITEM_INVIS
); g_Console_Add('player got invisibility'); continue
; end;
6508 if cmd
= 'redkey' then begin plr
.GiveItem(ITEM_KEY_RED
); g_Console_Add('player got the red key'); continue
; end;
6509 if cmd
= 'greenkey' then begin plr
.GiveItem(ITEM_KEY_GREEN
); g_Console_Add('player got the green key'); continue
; end;
6510 if cmd
= 'bluekey' then begin plr
.GiveItem(ITEM_KEY_BLUE
); g_Console_Add('player got the blue key'); continue
; end;
6512 if (cmd
= 'shotgun') or (cmd
= 'sg') then begin plr
.GiveItem(ITEM_WEAPON_SHOTGUN1
); g_Console_Add('player got a shotgun'); continue
; end;
6513 if (cmd
= 'supershotgun') or (cmd
= 'ssg') then begin plr
.GiveItem(ITEM_WEAPON_SHOTGUN2
); g_Console_Add('player got a supershotgun'); continue
; end;
6514 if cmd
= 'chaingun' then begin plr
.GiveItem(ITEM_WEAPON_CHAINGUN
); g_Console_Add('player got a chaingun'); continue
; end;
6515 if (cmd
= 'launcher') or (cmd
= 'rocketlauncher') or (cmd
= 'rl') then begin plr
.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER
); g_Console_Add('player got a rocket launcher'); continue
; end;
6516 if cmd
= 'plasmagun' then begin plr
.GiveItem(ITEM_WEAPON_PLASMA
); g_Console_Add('player got a plasma gun'); continue
; end;
6517 if cmd
= 'bfg' then begin plr
.GiveItem(ITEM_WEAPON_BFG
); g_Console_Add('player got a BFG-9000'); continue
; end;
6519 if (cmd
= 'shotgunzz') or (cmd
= 'sgzz') then begin plr
.GiveItem(ITEM_WEAPON_SHOTGUN1
); plr
.GiveItem(ITEM_AMMO_SHELLS_BOX
); g_Console_Add('player got a shotgun'); continue
; end;
6520 if (cmd
= 'supershotgunzz') or (cmd
= 'ssgzz') then begin plr
.GiveItem(ITEM_WEAPON_SHOTGUN2
); plr
.GiveItem(ITEM_AMMO_SHELLS_BOX
); g_Console_Add('player got a supershotgun'); continue
; end;
6521 if cmd
= 'chaingunzz' then begin plr
.GiveItem(ITEM_WEAPON_CHAINGUN
); plr
.GiveItem(ITEM_AMMO_BULLETS_BOX
); g_Console_Add('player got a chaingun'); continue
; end;
6522 if (cmd
= 'launcherzz') or (cmd
= 'rocketlauncherzz') or (cmd
= 'rlzz') then begin plr
.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER
); plr
.GiveItem(ITEM_AMMO_ROCKET_BOX
); g_Console_Add('player got a rocket launcher'); continue
; end;
6523 if cmd
= 'plasmagunzz' then begin plr
.GiveItem(ITEM_WEAPON_PLASMA
); plr
.GiveItem(ITEM_AMMO_CELL_BIG
); g_Console_Add('player got a plasma gun'); continue
; end;
6524 if cmd
= 'bfgzz' then begin plr
.GiveItem(ITEM_WEAPON_BFG
); plr
.GiveItem(ITEM_AMMO_CELL_BIG
); g_Console_Add('player got a BFG-9000'); continue
; end;
6526 if cmd
= 'superchaingun' then begin plr
.GiveItem(ITEM_WEAPON_SUPERCHAINGUN
); g_Console_Add('player got a superchaingun'); continue
; end;
6527 if cmd
= 'superchaingunzz' then begin plr
.GiveItem(ITEM_WEAPON_SUPERCHAINGUN
); plr
.GiveItem(ITEM_AMMO_BULLETS_BOX
); g_Console_Add('player got a superchaingun'); continue
; end;
6529 if (cmd
= 'flamer') or (cmd
= 'flamethrower') or (cmd
= 'ft') then begin plr
.GiveItem(ITEM_WEAPON_FLAMETHROWER
); g_Console_Add('player got a flame thrower'); continue
; end;
6530 if (cmd
= 'flamerzz') or (cmd
= 'flamethrowerzz') or (cmd
= 'ftzz') then begin plr
.GiveItem(ITEM_WEAPON_FLAMETHROWER
); plr
.GiveItem(ITEM_AMMO_FUELCAN
); g_Console_Add('player got a flame thrower'); continue
; end;
6532 if cmd
= 'chainsaw' then begin plr
.GiveItem(ITEM_WEAPON_SAW
); g_Console_Add('player got a chainsaw'); continue
; end;
6534 if cmd
= 'ammo' then
6536 plr
.GiveItem(ITEM_AMMO_SHELLS_BOX
);
6537 plr
.GiveItem(ITEM_AMMO_BULLETS_BOX
);
6538 plr
.GiveItem(ITEM_AMMO_CELL_BIG
);
6539 plr
.GiveItem(ITEM_AMMO_ROCKET_BOX
);
6540 plr
.GiveItem(ITEM_AMMO_FUELCAN
);
6541 g_Console_Add('player got some ammo');
6545 if cmd
= 'clip' then begin plr
.GiveItem(ITEM_AMMO_BULLETS
); g_Console_Add('player got some bullets'); continue
; end;
6546 if cmd
= 'bullets' then begin plr
.GiveItem(ITEM_AMMO_BULLETS_BOX
); g_Console_Add('player got a box of bullets'); continue
; end;
6548 if cmd
= 'shells' then begin plr
.GiveItem(ITEM_AMMO_SHELLS
); g_Console_Add('player got some shells'); continue
; end;
6549 if cmd
= 'shellbox' then begin plr
.GiveItem(ITEM_AMMO_SHELLS_BOX
); g_Console_Add('player got a box of shells'); continue
; end;
6551 if cmd
= 'cells' then begin plr
.GiveItem(ITEM_AMMO_CELL
); g_Console_Add('player got some cells'); continue
; end;
6552 if cmd
= 'battery' then begin plr
.GiveItem(ITEM_AMMO_CELL_BIG
); g_Console_Add('player got cell battery'); continue
; end;
6554 if cmd
= 'rocket' then begin plr
.GiveItem(ITEM_AMMO_ROCKET
); g_Console_Add('player got a rocket'); continue
; end;
6555 if cmd
= 'rocketbox' then begin plr
.GiveItem(ITEM_AMMO_ROCKET_BOX
); g_Console_Add('player got some rockets'); continue
; end;
6557 if (cmd
= 'fuel') or (cmd
= 'fuelcan') then begin plr
.GiveItem(ITEM_AMMO_FUELCAN
); g_Console_Add('player got fuel canister'); continue
; end;
6559 if cmd
= 'weapons' then
6561 plr
.GiveItem(ITEM_WEAPON_SHOTGUN1
);
6562 plr
.GiveItem(ITEM_WEAPON_SHOTGUN2
);
6563 plr
.GiveItem(ITEM_WEAPON_CHAINGUN
);
6564 plr
.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER
);
6565 plr
.GiveItem(ITEM_WEAPON_PLASMA
);
6566 plr
.GiveItem(ITEM_WEAPON_BFG
);
6567 g_Console_Add('player got weapons');
6571 if cmd
= 'keys' then
6573 plr
.GiveItem(ITEM_KEY_RED
);
6574 plr
.GiveItem(ITEM_KEY_GREEN
);
6575 plr
.GiveItem(ITEM_KEY_BLUE
);
6576 g_Console_Add('player got all keys');
6580 g_Console_Add('i don''t know how to give '''+cmd
+'''!');
6585 if cmd
= 'open' then
6587 g_Console_Add('player activated sesame');
6588 g_Triggers_OpenAll();
6595 if gFly
then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
6599 if cmd
= 'noclip' then
6602 g_Console_Add('wall hardeness adjusted');
6606 if cmd
= 'notarget' then
6608 plr
.NoTarget
:= not plr
.NoTarget
;
6609 if plr
.NoTarget
then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
6613 if cmd
= 'noreload' then
6615 plr
.NoReload
:= not plr
.NoReload
;
6616 if plr
.NoReload
then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
6620 if cmd
= 'speedy' then
6622 MAX_RUNVEL
:= 32-MAX_RUNVEL
;
6623 g_Console_Add('speed adjusted');
6627 if cmd
= 'jumpy' then
6629 VEL_JUMP
:= 30-VEL_JUMP
;
6630 g_Console_Add('jump height adjusted');
6634 if cmd
= 'automap' then
6636 gShowMap
:= not gShowMap
;
6637 if gShowMap
then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
6641 if cmd
= 'aimline' then
6643 gAimLine
:= not gAimLine
;
6644 if gAimLine
then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
6649 procedure GameCommands(P
: SSArray
);
6655 pl
: pTNetClient
= nil;
6664 cmd
:= LowerCase(P
[0]);
6666 if cmd
= 'pause' then
6668 if (g_ActiveWindow
= nil) then
6669 g_Game_Pause(not gPauseMain
);
6671 else if cmd
= 'endgame' then
6672 gExit
:= EXIT_SIMPLE
6673 else if cmd
= 'restart' then
6675 if gGameOn
or (gState
in [STATE_INTERSINGLE
, STATE_INTERCUSTOM
]) then
6677 if g_Game_IsClient
then
6679 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6684 g_Console_Add(_lc
[I_MSG_NOT_GAME
]);
6686 else if cmd
= 'kick' then
6688 if g_Game_IsServer
then
6690 if Length(P
) < 2 then
6692 g_Console_Add('kick <name>');
6697 g_Console_Add('kick <name>');
6701 if g_Game_IsNet
then
6702 pl
:= g_Net_Client_ByName(P
[1]);
6705 s
:= g_Net_ClientName_ByID(pl
^.ID
);
6706 g_Net_Host_Kick(pl
^.ID
, NET_DISC_KICK
);
6707 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [s
]));
6708 MH_SEND_GameEvent(NET_EV_PLAYER_KICK
, 0, s
);
6709 g_Net_Slist_ServerPlayerLeaves();
6710 end else if gPlayers
<> nil then
6711 for a
:= Low(gPlayers
) to High(gPlayers
) do
6712 if gPlayers
[a
] <> nil then
6713 if Copy(LowerCase(gPlayers
[a
].Name
), 1, Length(P
[1])) = LowerCase(P
[1]) then
6715 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
6716 if not(gPlayers
[a
] is TBot
) and (gGameSettings
.GameType
= GT_SINGLE
) then
6718 gPlayers
[a
].Lives
:= 0;
6719 gPlayers
[a
].Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
6720 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [gPlayers
[a
].Name
]), True);
6721 g_Player_Remove(gPlayers
[a
].UID
);
6722 g_Net_Slist_ServerPlayerLeaves();
6723 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
6727 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
6729 else if cmd
= 'kick_id' then
6731 if g_Game_IsServer
and g_Game_IsNet
then
6733 if Length(P
) < 2 then
6735 g_Console_Add('kick_id <client ID>');
6740 g_Console_Add('kick_id <client ID>');
6744 a
:= StrToIntDef(P
[1], 0);
6745 if (NetClients
<> nil) and (a
<= High(NetClients
)) then
6747 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6749 s
:= g_Net_ClientName_ByID(NetClients
[a
].ID
);
6750 g_Net_Host_Kick(NetClients
[a
].ID
, NET_DISC_KICK
);
6751 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [s
]));
6752 MH_SEND_GameEvent(NET_EV_PLAYER_KICK
, 0, s
);
6753 g_Net_Slist_ServerPlayerLeaves();
6757 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6759 else if cmd
= 'kick_pid' then
6761 if g_Game_IsServer
and g_Game_IsNet
then
6763 if Length(P
) < 2 then
6765 g_Console_Add('kick_pid <player ID>');
6770 g_Console_Add('kick_pid <player ID>');
6774 a
:= StrToIntDef(P
[1], 0);
6775 pl
:= g_Net_Client_ByPlayer(a
);
6776 if (pl
<> nil) and pl
^.Used
and (pl
^.Peer
<> nil) then
6778 s
:= g_Net_ClientName_ByID(pl
^.ID
);
6779 g_Net_Host_Kick(pl
^.ID
, NET_DISC_KICK
);
6780 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [s
]));
6781 MH_SEND_GameEvent(NET_EV_PLAYER_KICK
, 0, s
);
6782 g_Net_Slist_ServerPlayerLeaves();
6785 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6787 else if cmd
= 'ban' then
6789 if g_Game_IsServer
and g_Game_IsNet
then
6791 if Length(P
) < 2 then
6793 g_Console_Add('ban <name>');
6798 g_Console_Add('ban <name>');
6802 pl
:= g_Net_Client_ByName(P
[1]);
6804 g_Net_Host_Ban(pl
, False)
6806 g_Console_Add(Format(_lc
[I_NET_ERR_NAME404
], [P
[1]]));
6808 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6810 else if cmd
= 'ban_id' then
6812 if g_Game_IsServer
and g_Game_IsNet
then
6814 if Length(P
) < 2 then
6816 g_Console_Add('ban_id <client ID>');
6821 g_Console_Add('ban_id <client ID>');
6825 a
:= StrToIntDef(P
[1], 0);
6826 if (NetClients
<> nil) and (a
<= High(NetClients
)) then
6827 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6828 g_Net_Host_Ban(pl
, False);
6830 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6832 else if cmd
= 'ban_pid' then
6834 if g_Game_IsServer
and g_Game_IsNet
then
6836 if Length(P
) < 2 then
6838 g_Console_Add('ban_pid <player ID>');
6843 g_Console_Add('ban_pid <player ID>');
6847 a
:= StrToIntDef(P
[1], 0);
6848 pl
:= g_Net_Client_ByPlayer(a
);
6849 if (pl
<> nil) and pl
^.Used
and (pl
^.Peer
<> nil) then
6850 g_Net_Host_Ban(pl
, False);
6852 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6854 else if cmd
= 'permban' then
6856 if g_Game_IsServer
and g_Game_IsNet
then
6858 if Length(P
) < 2 then
6860 g_Console_Add('permban <name>');
6865 g_Console_Add('permban <name>');
6869 pl
:= g_Net_Client_ByName(P
[1]);
6871 g_Net_Host_Ban(pl
, True)
6873 g_Console_Add(Format(_lc
[I_NET_ERR_NAME404
], [P
[1]]));
6875 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6877 else if cmd
= 'permban_id' then
6879 if g_Game_IsServer
and g_Game_IsNet
then
6881 if Length(P
) < 2 then
6883 g_Console_Add('permban_id <client ID>');
6888 g_Console_Add('permban_id <client ID>');
6892 a
:= StrToIntDef(P
[1], 0);
6893 if (NetClients
<> nil) and (a
<= High(NetClients
)) then
6894 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6895 g_Net_Host_Ban(@NetClients
[a
], True);
6897 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6899 else if cmd
= 'permban_pid' then
6901 if g_Game_IsServer
and g_Game_IsNet
then
6903 if Length(P
) < 2 then
6905 g_Console_Add('permban_pid <player ID>');
6910 g_Console_Add('permban_pid <player ID>');
6914 a
:= StrToIntDef(P
[1], 0);
6915 pl
:= g_Net_Client_ByPlayer(a
);
6916 if (pl
<> nil) and pl
^.Used
and (pl
^.Peer
<> nil) then
6917 g_Net_Host_Ban(pl
, True);
6919 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6921 else if cmd
= 'permban_ip' then
6923 if g_Game_IsServer
and g_Game_IsNet
then
6925 if Length(P
) < 2 then
6927 g_Console_Add('permban_ip <IP address>');
6932 g_Console_Add('permban_ip <IP address>');
6936 g_Net_BanAddress(P
[1]);
6937 g_Net_SaveBanList();
6938 g_Console_Add(Format(_lc
[I_PLAYER_BAN
], [P
[1]]));
6940 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6942 else if cmd
= 'unban' then
6944 if g_Game_IsServer
and g_Game_IsNet
then
6946 if Length(P
) < 2 then
6948 g_Console_Add('unban <IP Address>');
6953 g_Console_Add('unban <IP Address>');
6957 if g_Net_UnbanAddress(P
[1]) then
6959 g_Console_Add(Format(_lc
[I_MSG_UNBAN_OK
], [P
[1]]));
6960 g_Net_SaveBanList();
6962 g_Console_Add(Format(_lc
[I_MSG_UNBAN_FAIL
], [P
[1]]));
6964 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6966 else if cmd
= 'clientlist' then
6968 if g_Game_IsServer
and g_Game_IsNet
then
6971 if NetClients
<> nil then
6972 for a
:= Low(NetClients
) to High(NetClients
) do
6973 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6975 plr
:= g_Player_Get(NetClients
[a
].Player
);
6976 if plr
= nil then continue
;
6978 g_Console_Add(Format('#%2d: %-15s | %s', [a
,
6979 IpToStr(NetClients
[a
].Peer
^.address
.host
), plr
.Name
]));
6982 g_Console_Add(_lc
[I_MSG_NOCLIENTS
]);
6984 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6986 else if cmd
= 'connect' then
6988 if (NetMode
= NET_NONE
) then
6990 if Length(P
) < 2 then
6992 g_Console_Add('connect <IP> [port] [password]');
6997 g_Console_Add('connect <IP> [port] [password]');
7001 if Length(P
) > 2 then
7002 prt
:= StrToIntDef(P
[2], 25666)
7006 if Length(P
) > 3 then
7011 g_Game_StartClient(P
[1], prt
, pw
);
7014 else if cmd
= 'disconnect' then
7016 if (NetMode
= NET_CLIENT
) then
7019 else if cmd
= 'reconnect' then
7021 if (NetMode
= NET_SERVER
) then
7024 if (NetMode
= NET_CLIENT
) then
7027 gExit
:= EXIT_SIMPLE
;
7031 //TODO: Use last successful password to reconnect, instead of ''
7032 g_Game_StartClient(NetClientIP
, NetClientPort
, '');
7034 else if (cmd
= 'addbot') or
7035 (cmd
= 'bot_add') then
7038 1: g_Bot_Add(TEAM_NONE
, 2);
7039 2: g_Bot_Add(TEAM_NONE
, StrToIntDef(P
[1], 2));
7041 g_Bot_Add(TEAM_NONE
, StrToIntDef(P
[1], 2), StrToIntDef(P
[2], 100));
7044 else if cmd
= 'bot_addlist' then
7047 1: g_Bot_AddList(TEAM_NONE
, '');
7048 2: g_Bot_AddList(TEAM_NONE
, P
[1], StrToIntDef(P
[1], -1));
7050 if P
[2] = 'red' then
7052 else if P
[2] = 'blue' then
7058 then g_Bot_AddList(t
, P
[1], StrToIntDef(P
[1], -1))
7059 else g_Bot_AddList(t
, P
[1], StrToIntDef(P
[1], -1), StrToIntDef(P
[3], 100));
7062 else if cmd
= 'bot_removeall' then
7064 else if cmd
= 'chat' then
7066 if g_Game_IsNet
then
7068 if Length(P
) > 1 then
7070 for a
:= 1 to High(P
) do
7071 chstr
:= chstr
+ P
[a
] + ' ';
7073 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7075 if Length(chstr
) < 1 then
7077 g_Console_Add('chat <text>');
7081 chstr
:= b_Text_Format(chstr
);
7082 if g_Game_IsClient
then
7083 MC_SEND_Chat(chstr
, NET_CHAT_PLAYER
)
7085 MH_SEND_Chat(gPlayer1Settings
.Name
+ ': ' + chstr
, NET_CHAT_PLAYER
);
7088 g_Console_Add('chat <text>');
7090 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7092 else if cmd
= 'teamchat' then
7094 if g_Game_IsNet
and (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then
7096 if Length(P
) > 1 then
7098 for a
:= 1 to High(P
) do
7099 chstr
:= chstr
+ P
[a
] + ' ';
7101 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7103 if Length(chstr
) < 1 then
7105 g_Console_Add('teamchat <text>');
7109 chstr
:= b_Text_Format(chstr
);
7110 if g_Game_IsClient
then
7111 MC_SEND_Chat(chstr
, NET_CHAT_TEAM
)
7113 MH_SEND_Chat(gPlayer1Settings
.Name
+ ': ' + chstr
, NET_CHAT_TEAM
,
7114 gPlayer1Settings
.Team
);
7117 g_Console_Add('teamchat <text>');
7119 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7121 else if (cmd
= 'an') or (cmd
= 'announce') then
7123 if g_Game_IsNet
then
7125 if Length(P
) > 1 then
7127 for a
:= 1 to High(P
) do
7128 chstr
:= chstr
+ P
[a
] + ' ';
7130 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7132 if Length(chstr
) < 1 then
7134 g_Console_Add('announce <text>');
7138 chstr
:= 'centerprint 100 ' + b_Text_Format(chstr
);
7139 if g_Game_IsClient
then
7140 MC_SEND_RCONCommand(chstr
)
7142 g_Console_Process(chstr
, True);
7145 g_Console_Add('announce <text>');
7147 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7149 else if cmd
= 'game' then
7151 if gGameSettings
.GameType
<> GT_NONE
then
7153 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7156 if Length(P
) = 1 then
7158 g_Console_Add(cmd
+ ' <WAD> [MAP] [# players]');
7161 // game not started yet, load first map from some wad
7163 s
:= addWadExtension(P
[1]);
7164 found
:= e_FindResource(AllMapDirs
, s
);
7168 P
[1] := ExpandFileName(P
[1]);
7169 // if map not choosed then set first map
7170 if Length(P
) < 3 then
7173 P
[2] := g_Game_GetFirstMap(P
[1]);
7176 s
:= P
[1] + ':\' + UpperCase(P
[2]);
7178 if g_Map_Exist(s
) then
7182 with gGameSettings
do
7184 Options
:= gsGameFlags
;
7185 GameMode
:= g_Game_TextToMode(gsGameMode
);
7186 if gSwitchGameMode
<> GM_NONE
then
7187 GameMode
:= gSwitchGameMode
;
7188 if GameMode
= GM_NONE
then GameMode
:= GM_DM
;
7189 if GameMode
= GM_SINGLE
then GameMode
:= GM_COOP
;
7191 if Length(P
) >= 4 then
7192 b
:= StrToIntDef(P
[3], 1);
7193 g_Game_StartCustom(s
, GameMode
, TimeLimit
,
7194 ScoreLimit
, MaxLives
, Options
, b
);
7199 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[1]]))
7201 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [UpperCase(P
[2]), P
[1]]));
7203 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]));
7205 else if cmd
= 'host' then
7207 if gGameSettings
.GameType
<> GT_NONE
then
7209 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7212 if Length(P
) < 4 then
7214 g_Console_Add(cmd
+ ' <listen IP> <port> <WAD> [MAP] [# players]');
7217 if not StrToIp(P
[1], listen
) then
7219 prt
:= StrToIntDef(P
[2], 25666);
7221 s
:= addWadExtension(P
[3]);
7222 found
:= e_FindResource(AllMapDirs
, s
);
7226 // get first map in wad, if not specified
7227 if Length(P
) < 5 then
7230 P
[4] := g_Game_GetFirstMap(P
[1]);
7232 s
:= P
[3] + ':\' + UpperCase(P
[4]);
7233 if g_Map_Exist(s
) then
7237 with gGameSettings
do
7239 Options
:= gsGameFlags
;
7240 GameMode
:= g_Game_TextToMode(gsGameMode
);
7241 if gSwitchGameMode
<> GM_NONE
then GameMode
:= gSwitchGameMode
;
7242 if GameMode
= GM_NONE
then GameMode
:= GM_DM
;
7243 if GameMode
= GM_SINGLE
then GameMode
:= GM_COOP
;
7245 if Length(P
) >= 6 then
7246 b
:= StrToIntDef(P
[5], 0);
7247 g_Game_StartServer(s
, GameMode
, TimeLimit
, ScoreLimit
, MaxLives
, Options
, b
, listen
, prt
)
7253 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[3]]))
7255 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [UpperCase(P
[4]), P
[3]]))
7260 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[3]]))
7263 else if cmd
= 'map' then
7265 if Length(P
) = 1 then
7267 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7269 g_Console_Add(cmd
+ ' <MAP>');
7270 g_Console_Add(cmd
+ ' <WAD> [MAP]')
7274 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7277 else if not e_IsValidResourceName(P
[1]) then
7279 g_Console_Add('wad name must not be absolute or relative');
7283 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7285 if Length(P
) < 3 then
7287 // first param is map or wad
7288 s
:= UpperCase(P
[1]);
7289 if g_Map_Exist(gGameSettings
.WAD
+ ':\' + s
) then
7291 gExitByTrigger
:= False;
7294 // already in game, finish current map
7296 gExit
:= EXIT_ENDLEVELCUSTOM
;
7300 // intermission, so change map immediately
7307 found
:= e_FindResource(AllMapDirs
, s
);
7309 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [s
, 'WAD ' + P
[1]]));
7312 // no such map, found wad
7315 P
[1] := ExpandFileName(pw
);
7316 P
[2] := g_Game_GetFirstMap(P
[1]);
7317 s
:= P
[1] + ':\' + P
[2];
7318 if g_Map_Exist(s
) then
7320 gExitByTrigger
:= False;
7323 // already in game, finish current map
7325 gExit
:= EXIT_ENDLEVELCUSTOM
7329 // intermission, so change map immediately
7336 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[1]]))
7338 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7343 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7349 s
:= addWadExtension(P
[1]);
7350 found
:= e_FindResource(AllMapDirs
, s
);
7354 P
[2] := UpperCase(P
[2]);
7355 s
:= P
[1] + ':\' + P
[2];
7356 if g_Map_Exist(s
) then
7358 gExitByTrigger
:= False;
7362 gExit
:= EXIT_ENDLEVELCUSTOM
7371 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7376 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7382 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7386 else if cmd
= 'nextmap' then
7388 if not(gGameOn
or (gState
= STATE_INTERCUSTOM
)) then
7390 g_Console_Add(_lc
[I_MSG_NOT_GAME
])
7395 if Length(P
) = 1 then
7397 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7399 g_Console_Add(cmd
+ ' <MAP>');
7400 g_Console_Add(cmd
+ ' <WAD> [MAP]');
7405 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7408 else if not e_IsValidResourceName(P
[1]) then
7410 g_Console_Add('wad name must not be absolute or relative');
7415 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7417 if Length(P
) < 3 then
7419 // first param is map or wad
7420 s
:= UpperCase(P
[1]);
7421 if g_Map_Exist(gGameSettings
.WAD
+ ':\' + s
) then
7424 gExitByTrigger
:= False;
7430 // no such map, found wad
7431 pw
:= addWadExtension(P
[1]);
7432 found
:= e_FindResource(MapDirs
, pw
);
7434 found
:= e_FindResource(WadDirs
, pw
);
7436 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [s
, P
[1]]));
7439 // map not specified, select first map
7441 P
[2] := g_Game_GetFirstMap(P
[1]);
7442 s
:= P
[1] + ':\' + P
[2];
7443 if g_Map_Exist(s
) then
7445 gExitByTrigger
:= False;
7452 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[1]]))
7454 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7459 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7465 // specified two params wad + map
7466 pw
:= addWadExtension(P
[1]);
7467 found
:= e_FindResource(MapDirs
, pw
);
7469 found
:= e_FindResource(MapDirs
, pw
);
7473 P
[2] := UpperCase(P
[2]);
7474 s
:= P
[1] + ':\' + P
[2];
7475 if g_Map_Exist(s
) then
7477 gExitByTrigger
:= False;
7483 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7488 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7494 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7499 if gNextMap
= '' then
7500 g_Console_Add(_lc
[I_MSG_NEXTMAP_UNSET
])
7502 g_Console_Add(Format(_lc
[I_MSG_NEXTMAP_SET
], [gNextMap
]))
7506 else if (cmd
= 'endmap') or (cmd
= 'goodbye') then
7510 g_Console_Add(_lc
[I_MSG_NOT_GAME
])
7514 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7516 gExitByTrigger
:= False;
7517 // next map not specified, try to find trigger EXIT
7518 if (gNextMap
= '') and (gTriggers
<> nil) then
7520 for a
:= 0 to High(gTriggers
) do
7522 if gTriggers
[a
].TriggerType
= TRIGGER_EXIT
then
7524 gExitByTrigger
:= True;
7525 //gNextMap := gTriggers[a].Data.MapName;
7526 gNextMap
:= gTriggers
[a
].tgcMap
;
7531 if gNextMap
= '' then
7532 gNextMap
:= g_Game_GetNextMap();
7533 if not isWadPath(gNextMap
) then
7534 s
:= gGameSettings
.WAD
+ ':\' + gNextMap
7537 if g_Map_Exist(s
) then
7538 gExit
:= EXIT_ENDLEVELCUSTOM
7540 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [gNextMap
]))
7544 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7548 else if (cmd
= 'event') then
7550 if (Length(P
) <= 1) then
7552 for a
:= 0 to High(gEvents
) do
7553 if gEvents
[a
].Command
= '' then
7554 g_Console_Add(gEvents
[a
].Name
+ ' <none>')
7556 g_Console_Add(gEvents
[a
].Name
+ ' "' + gEvents
[a
].Command
+ '"');
7559 if (Length(P
) = 2) then
7561 for a
:= 0 to High(gEvents
) do
7562 if gEvents
[a
].Name
= P
[1] then
7563 if gEvents
[a
].Command
= '' then
7564 g_Console_Add(gEvents
[a
].Name
+ ' <none>')
7566 g_Console_Add(gEvents
[a
].Name
+ ' "' + gEvents
[a
].Command
+ '"');
7569 for a
:= 0 to High(gEvents
) do
7570 if gEvents
[a
].Name
= P
[1] then
7572 gEvents
[a
].Command
:= '';
7573 for b
:= 2 to High(P
) do
7574 if Pos(' ', P
[b
]) = 0 then
7575 gEvents
[a
].Command
:= gEvents
[a
].Command
+ ' ' + P
[b
]
7577 gEvents
[a
].Command
:= gEvents
[a
].Command
+ ' "' + P
[b
] + '"';
7578 gEvents
[a
].Command
:= Trim(gEvents
[a
].Command
);
7582 else if cmd
= 'suicide' then
7586 if g_Game_IsClient
then
7587 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE
)
7590 if gPlayer1
<> nil then
7591 gPlayer1
.Damage(SUICIDE_DAMAGE
, gPlayer1
.UID
, 0, 0, HIT_SELF
);
7592 if gPlayer2
<> nil then
7593 gPlayer2
.Damage(SUICIDE_DAMAGE
, gPlayer2
.UID
, 0, 0, HIT_SELF
);
7597 else if cmd
= 'screenshot' then
7601 else if (cmd
= 'weapnext') or (cmd
= 'weapprev') then
7603 a
:= 1 - (ord(cmd
[5]) - ord('n'));
7605 gWeaponAction
[0, WP_PREV
] := True;
7607 gWeaponAction
[0, WP_NEXT
] := True;
7609 else if cmd
= 'weapon' then
7611 if Length(p
) = 2 then
7613 a
:= WP_FIRST
+ StrToIntDef(p
[1], 0) - 1;
7614 if (a
>= WP_FIRST
) and (a
<= WP_LAST
) then
7615 gSelectWeapon
[0, a
] := True
7618 else if (cmd
= 'p1_weapnext') or (cmd
= 'p1_weapprev')
7619 or (cmd
= 'p2_weapnext') or (cmd
= 'p2_weapprev') then
7621 a
:= 1 - (ord(cmd
[8]) - ord('n'));
7622 b
:= ord(cmd
[2]) - ord('1');
7624 gWeaponAction
[b
, WP_PREV
] := True;
7626 gWeaponAction
[b
, WP_NEXT
] := True;
7628 else if (cmd
= 'p1_weapon') or (cmd
= 'p2_weapon') then
7630 if Length(p
) = 2 then
7632 a
:= WP_FIRST
+ StrToIntDef(p
[1], 0) - 1;
7633 b
:= ord(cmd
[2]) - ord('1');
7634 if (a
>= WP_FIRST
) and (a
<= WP_LAST
) then
7635 gSelectWeapon
[b
, a
] := True
7638 else if (cmd
= 'p1_weapbest') or (cmd
= 'p2_weapbest') then
7640 b
:= ord(cmd
[2]) - ord('1');
7642 gSelectWeapon
[b
, gPlayer1
.GetMorePrefered()] := True
7644 gSelectWeapon
[b
, gPlayer2
.GetMorePrefered()] := True;
7646 else if (cmd
= 'dropflag') then
7648 if g_Game_IsServer
then
7650 if gPlayer2
<> nil then gPlayer2
.TryDropFlag();
7651 if gPlayer1
<> nil then gPlayer1
.TryDropFlag();
7654 MC_SEND_CheatRequest(NET_CHEAT_DROPFLAG
);
7656 else if (cmd
= 'p1_dropflag') or (cmd
= 'p2_dropflag') then
7658 b
:= ord(cmd
[2]) - ord('1');
7659 if g_Game_IsServer
then
7661 if (b
= 1) and (gPlayer2
<> nil) then gPlayer2
.TryDropFlag()
7662 else if (b
= 0) and (gPlayer1
<> nil) then gPlayer1
.TryDropFlag();
7665 MC_SEND_CheatRequest(NET_CHEAT_DROPFLAG
);
7667 // Êîìàíäû Ñâîåé èãðû:
7668 else if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
] then
7670 if cmd
= 'bot_addred' then
7672 if Length(P
) > 1 then
7673 g_Bot_Add(TEAM_RED
, StrToIntDef(P
[1], 2))
7675 g_Bot_Add(TEAM_RED
, 2);
7677 else if cmd
= 'bot_addblue' then
7679 if Length(P
) > 1 then
7680 g_Bot_Add(TEAM_BLUE
, StrToIntDef(P
[1], 2))
7682 g_Bot_Add(TEAM_BLUE
, 2);
7684 else if cmd
= 'spectate' then
7690 else if cmd
= 'say' then
7692 if g_Game_IsServer
and g_Game_IsNet
then
7694 if Length(P
) > 1 then
7697 for a
:= 1 to High(P
) do
7698 chstr
:= chstr
+ P
[a
] + ' ';
7700 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7702 if Length(chstr
) < 1 then
7704 g_Console_Add('say <text>');
7708 chstr
:= b_Text_Format(chstr
);
7709 MH_SEND_Chat(chstr
, NET_CHAT_PLAYER
);
7711 else g_Console_Add('say <text>');
7713 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
7715 else if cmd
= 'tell' then
7717 if g_Game_IsServer
and g_Game_IsNet
then
7719 if (Length(P
) > 2) and (P
[1] <> '') then
7722 for a
:= 2 to High(P
) do
7723 chstr
:= chstr
+ P
[a
] + ' ';
7725 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7727 if Length(chstr
) < 1 then
7729 g_Console_Add('tell <playername> <text>');
7733 pl
:= g_Net_Client_ByName(P
[1]);
7735 MH_SEND_Chat(b_Text_Format(chstr
), NET_CHAT_PLAYER
, pl
^.ID
)
7737 g_Console_Add(Format(_lc
[I_NET_ERR_NAME404
], [P
[1]]));
7739 else g_Console_Add('tell <playername> <text>');
7741 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
7743 else if cmd
= 'centerprint' then
7745 if (Length(P
) > 2) and (P
[1] <> '') then
7748 for a
:= 2 to High(P
) do
7749 chstr
:= chstr
+ P
[a
] + ' ';
7751 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7753 if Length(chstr
) < 1 then
7755 g_Console_Add('centerprint <timeout> <text>');
7759 a
:= StrToIntDef(P
[1], 100);
7760 chstr
:= b_Text_Format(chstr
);
7761 g_Game_Message(chstr
, a
);
7762 if g_Game_IsNet
and g_Game_IsServer
then
7763 MH_SEND_GameEvent(NET_EV_BIGTEXT
, a
, chstr
);
7765 else g_Console_Add('centerprint <timeout> <text>');
7767 else if (cmd
= 'overtime') and not g_Game_IsClient
then
7769 if (Length(P
) = 1) or (StrToIntDef(P
[1], -1) <= 0) then
7771 // Äîïîëíèòåëüíîå âðåìÿ:
7772 gGameSettings
.TimeLimit
:= (gTime
- gGameStartTime
) div 1000 + Word(StrToIntDef(P
[1], 0));
7774 g_Console_Add(Format(_lc
[I_MSG_TIME_LIMIT
],
7775 [gGameSettings
.TimeLimit
div 3600,
7776 (gGameSettings
.TimeLimit
div 60) mod 60,
7777 gGameSettings
.TimeLimit
mod 60]));
7778 if g_Game_IsNet
then MH_SEND_GameSettings
;
7780 else if (cmd
= 'rcon_password') and g_Game_IsClient
then
7782 if (Length(P
) <= 1) then
7783 g_Console_Add('rcon_password <password>')
7785 MC_SEND_RCONPassword(P
[1]);
7787 else if cmd
= 'rcon' then
7789 if g_Game_IsClient
then
7791 if Length(P
) > 1 then
7794 for a
:= 1 to High(P
) do
7795 chstr
:= chstr
+ P
[a
] + ' ';
7797 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7799 if Length(chstr
) < 1 then
7801 g_Console_Add('rcon <command>');
7805 MC_SEND_RCONCommand(chstr
);
7807 else g_Console_Add('rcon <command>');
7810 else if cmd
= 'ready' then
7812 if g_Game_IsServer
and (gLMSRespawn
= LMS_RESPAWN_WARMUP
) then
7813 gLMSRespawnTime
:= gTime
+ 100;
7815 else if (cmd
= 'callvote') and g_Game_IsNet
then
7817 if Length(P
) > 1 then
7820 for a
:= 1 to High(P
) do begin
7821 if a
> 1 then chstr
:= chstr
+ ' ';
7822 chstr
:= chstr
+ P
[a
];
7825 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7827 if Length(chstr
) < 1 then
7829 g_Console_Add('callvote <command>');
7833 if g_Game_IsClient
then
7834 MC_SEND_Vote(True, chstr
)
7836 g_Game_StartVote(chstr
, gPlayer1Settings
.Name
);
7837 g_Console_Process('vote', True);
7840 g_Console_Add('callvote <command>');
7842 else if (cmd
= 'vote') and g_Game_IsNet
then
7844 if g_Game_IsClient
then
7846 else if gVoteInProgress
then
7848 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
7849 a
:= Floor((NetClientCount
+1)/2.0) + 1
7851 a
:= Floor(NetClientCount
/2.0) + 1;
7856 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_REVOKED
], [gPlayer1Settings
.Name
, gVoteCount
, a
]), True);
7857 MH_SEND_VoteEvent(NET_VE_REVOKE
, gPlayer1Settings
.Name
, 'a', gVoteCount
, a
);
7863 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_VOTE
], [gPlayer1Settings
.Name
, gVoteCount
, a
]), True);
7864 MH_SEND_VoteEvent(NET_VE_VOTE
, gPlayer1Settings
.Name
, 'a', gVoteCount
, a
);
7872 procedure SystemCommands(P
: SSArray
);
7876 cmd
:= LowerCase(P
[0]);
7885 gRC_Width
:= Max(1, gRC_Width
);
7886 gRC_Height
:= Max(1, gRC_Height
);
7887 gBPP
:= Max(1, gBPP
);
7888 if sys_SetDisplayMode(gRC_Width
, gRC_Height
, gBPP
, gRC_FullScreen
, gRC_Maximized
) = True then
7889 e_LogWriteln('resolution changed')
7891 e_LogWriteln('resolution not changed');
7892 sys_EnableVSync(gVSync
);
7896 if Length(p
) = 2 then
7898 gMaxFPS
:= StrToIntDef(p
[1], gMaxFPS
);
7900 gFrameTime
:= 1000 div gMaxFPS
7904 e_LogWritefln('r_maxfps %d', [gMaxFPS
]);
7908 if Length(p
) = 2 then
7910 gAskLanguage
:= true;
7911 gLanguage
:= LANGUAGE_ENGLISH
;
7912 case LowerCase(p
[1]) of
7915 gAskLanguage
:= false;
7916 gLanguage
:= LANGUAGE_ENGLISH
;
7920 gAskLanguage
:= false;
7921 gLanguage
:= LANGUAGE_RUSSIAN
;
7925 gAskLanguage
:= true;
7926 gLanguage
:= LANGUAGE_ENGLISH
;
7929 g_Language_Set(gLanguage
);
7933 e_LogWritefln('usage: %s <English|Russian|Ask>', [cmd
]);
7939 procedure g_TakeScreenShot(Filename
: String);
7943 dir
, date
, name
: String;
7945 if e_NoGraphics
then Exit
;
7947 dir
:= e_GetWriteableDir(ScreenshotDirs
);
7949 if Filename
= '' then
7952 DateTimeToString(date
, 'yyyy-mm-dd-hh-nn-ss', t
);
7953 Filename
:= 'screenshot-' + date
;
7956 name
:= Filename
+ '.png';
7957 // FIXME: hack for improper ConcatPaths(); see commit.
7958 if dir
<> '' then name
:= ConcatPaths([dir
, name
]);
7960 s
:= createDiskFile(name
);
7962 e_MakeScreenshot(s
, gWinSizeX
, gWinSizeY
);
7964 g_Console_Add(Format(_lc
[I_CONSOLE_SCREENSHOT
], [name
]))
7966 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_WRITE
], [name
]));
7971 g_Console_Add('oh shit, i can''t create screenshot!')
7975 procedure g_Game_InGameMenu(Show
: Boolean);
7977 if (g_ActiveWindow
= nil) and Show
then
7979 if gGameSettings
.GameType
= GT_SINGLE
then
7980 g_GUI_ShowWindow('GameSingleMenu')
7983 if g_Game_IsClient
then
7984 g_GUI_ShowWindow('GameClientMenu')
7986 if g_Game_IsNet
then
7987 g_GUI_ShowWindow('GameServerMenu')
7989 g_GUI_ShowWindow('GameCustomMenu');
7991 {$IFDEF ENABLE_SOUND}
7992 g_Sound_PlayEx('MENU_OPEN');
7995 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7996 if (not g_Game_IsNet
) then
8000 if (g_ActiveWindow
<> nil) and (not Show
) then
8002 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
8003 if (not g_Game_IsNet
) then
8004 g_Game_Pause(False);
8008 procedure g_Game_Pause (Enable
: Boolean);
8012 if not gGameOn
then exit
;
8014 if not (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
]) then exit
;
8017 gPauseMain
:= Enable
;
8019 {$IFDEF ENABLE_SOUND}
8020 if (gPause
<> oldPause
) then g_Game_PauseAllSounds(gPause
);
8024 procedure g_Game_HolmesPause (Enable
: Boolean);
8028 if not gGameOn
then exit
;
8029 if not (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
]) then exit
;
8032 gPauseHolmes
:= Enable
;
8034 {$IFDEF ENABLE_SOUND}
8035 if (gPause
<> oldPause
) then g_Game_PauseAllSounds(gPause
);
8039 {$IFDEF ENABLE_SOUND}
8040 procedure g_Game_PauseAllSounds(Enable
: Boolean);
8045 if gTriggers
<> nil then
8046 for i
:= 0 to High(gTriggers
) do
8047 with gTriggers
[i
] do
8048 if (TriggerType
= TRIGGER_SOUND
) and
8050 Sound
.IsPlaying() then
8052 Sound
.Pause(Enable
);
8056 if gPlayers
<> nil then
8057 for i
:= 0 to High(gPlayers
) do
8058 if gPlayers
[i
] <> nil then
8059 gPlayers
[i
].PauseSounds(Enable
);
8062 if gMusic
<> nil then
8063 gMusic
.Pause(Enable
);
8066 procedure g_Game_StopAllSounds(all
: Boolean);
8070 if gTriggers
<> nil then
8071 for i
:= 0 to High(gTriggers
) do
8072 with gTriggers
[i
] do
8073 if (TriggerType
= TRIGGER_SOUND
) and
8077 if gMusic
<> nil then
8084 procedure g_Game_UpdateTriggerSounds
;
8087 if gTriggers
<> nil then
8088 for i
:= 0 to High(gTriggers
) do
8089 with gTriggers
[i
] do
8090 if (TriggerType
= TRIGGER_SOUND
) and (Sound
<> nil) and tgcLocal
and Sound
.IsPlaying() then
8091 Sound
.SetCoordsRect(X
, Y
, Width
, Height
, tgcVolume
/ 255.0)
8095 function g_Game_IsWatchedPlayer(UID
: Word): Boolean;
8098 if (gPlayer1
<> nil) and (gPlayer1
.UID
= UID
) then
8103 if (gPlayer2
<> nil) and (gPlayer2
.UID
= UID
) then
8108 if gSpectMode
<> SPECT_PLAYERS
then
8110 if gSpectPID1
= UID
then
8115 if gSpectViewTwo
and (gSpectPID2
= UID
) then
8122 function g_Game_IsWatchedTeam(Team
: Byte): Boolean;
8127 if (gPlayer1
<> nil) and (gPlayer1
.Team
= Team
) then
8132 if (gPlayer2
<> nil) and (gPlayer2
.Team
= Team
) then
8137 if gSpectMode
<> SPECT_PLAYERS
then
8139 Pl
:= g_Player_Get(gSpectPID1
);
8140 if (Pl
<> nil) and (Pl
.Team
= Team
) then
8145 if gSpectViewTwo
then
8147 Pl
:= g_Player_Get(gSpectPID2
);
8148 if (Pl
<> nil) and (Pl
.Team
= Team
) then
8156 procedure g_Game_Message(Msg
: string; Time
: Word);
8158 MessageLineLength
:= (gScreenWidth
- 204) div e_CharFont_GetMaxWidth(gMenuFont
);
8159 MessageText
:= b_Text_Wrap(b_Text_Format(Msg
), MessageLineLength
);
8160 MessageTime
:= Time
;
8163 {$IFDEF ENABLE_SOUND}
8164 procedure g_Game_ChatSound(Text: String; Taunt
: Boolean = True);
8166 punct
: Array[0..13] of String =
8167 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
8173 function IsPunctuation(S
: String): Boolean;
8178 if Length(S
) <> 1 then
8180 for i
:= Low(punct
) to High(punct
) do
8181 if S
= punct
[i
] then
8187 function FilterPunctuation(S
: String): String;
8191 for i
:= Low(punct
) to High(punct
) do
8192 S
:= StringReplace(S
, punct
[i
], ' ', [rfReplaceAll
]);
8198 if gUseChatSounds
and Taunt
and (gChatSounds
<> nil) and (Pos(': ', Text) > 0) then
8200 // remove player name
8201 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
8202 // for FullWord check
8203 Text := toLowerCase1251(' ' + Text + ' ');
8204 fpText
:= FilterPunctuation(Text);
8206 for i
:= 0 to Length(gChatSounds
) - 1 do
8209 for j
:= 0 to Length(gChatSounds
[i
].Tags
) - 1 do
8211 if gChatSounds
[i
].FullWord
and (not IsPunctuation(gChatSounds
[i
].Tags
[j
])) then
8212 ok
:= Pos(' ' + gChatSounds
[i
].Tags
[j
] + ' ', fpText
) > 0
8214 ok
:= Pos(gChatSounds
[i
].Tags
[j
], Text) > 0;
8220 gChatSounds
[i
].Sound
.Play();
8226 g_Sound_PlayEx('SOUND_GAME_RADIO');
8229 procedure g_Game_Announce_GoodShot(SpawnerUID
: Word);
8238 if not g_Game_IsWatchedPlayer(SpawnerUID
) then
8242 if goodsnd
[a
].IsPlaying() then
8245 goodsnd
[Random(4)].Play();
8248 procedure g_Game_Announce_KillCombo(Param
: Integer);
8255 UID
:= Param
and $FFFF;
8260 Pl
:= g_Player_Get(UID
);
8269 g_Console_Add(Format(_lc
[I_PLAYER_KILL_2X
], [Name
]), True);
8273 g_Console_Add(Format(_lc
[I_PLAYER_KILL_3X
], [Name
]), True);
8277 g_Console_Add(Format(_lc
[I_PLAYER_KILL_4X
], [Name
]), True);
8281 g_Console_Add(Format(_lc
[I_PLAYER_KILL_MX
], [Name
]), True);
8289 if not g_Game_IsWatchedPlayer(UID
) then
8292 if (not g_Game_IsWatchedPlayer(UID
)) and (c
< 4) then
8296 if killsnd
[n
].IsPlaying() then
8301 procedure g_Game_Announce_BodyKill(SpawnerUID
: Word);
8309 if not g_Game_IsWatchedPlayer(SpawnerUID
) then
8313 if hahasnd
[a
].IsPlaying() then
8316 hahasnd
[Random(3)].Play();
8320 procedure g_Game_Effect_Bubbles (fX
, fY
: Integer; count
: Word; devX
, devY
: Byte; Silent
: Boolean);
8322 g_GFX_Bubbles(fX
, fY
, count
, devX
, devY
);
8323 {$IFDEF ENABLE_SOUND}
8324 if not Silent
then if Random(2) = 0
8325 then g_Sound_PlayExAt('SOUND_GAME_BUBBLE1', fX
, fY
)
8326 else g_Sound_PlayExAt('SOUND_GAME_BUBBLE2', fX
, fY
);
8330 procedure g_Game_StartVote(Command
, Initiator
: string);
8334 if not gVotesEnabled
then Exit
;
8335 if gGameSettings
.GameType
<> GT_SERVER
then Exit
;
8336 if gVoteInProgress
or gVotePassed
then
8338 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_INPROGRESS
], [gVoteCommand
]), True);
8339 MH_SEND_VoteEvent(NET_VE_INPROGRESS
, gVoteCommand
);
8342 gVoteInProgress
:= True;
8343 gVotePassed
:= False;
8344 gVoteTimer
:= gTime
+ gVoteTimeout
* 1000;
8347 gVoteCommand
:= Command
;
8349 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
8350 Need
:= Floor((NetClientCount
+1)/2.0)+1
8352 Need
:= Floor(NetClientCount
/2.0)+1;
8353 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_STARTED
], [Initiator
, Command
, Need
]), True);
8354 MH_SEND_VoteEvent(NET_VE_STARTED
, Initiator
, Command
, Need
);
8357 procedure g_Game_CheckVote
;
8361 if gGameSettings
.GameType
<> GT_SERVER
then Exit
;
8362 if not gVoteInProgress
then Exit
;
8364 if (gTime
>= gVoteTimer
) then
8366 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
8367 Need
:= Floor((NetClientCount
+1)/2.0) + 1
8369 Need
:= Floor(NetClientCount
/2.0) + 1;
8370 if gVoteCount
>= Need
then
8372 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_PASSED
], [gVoteCommand
]), True);
8373 MH_SEND_VoteEvent(NET_VE_PASSED
, gVoteCommand
);
8374 gVotePassed
:= True;
8375 gVoteCmdTimer
:= gTime
+ 5000;
8379 g_Console_Add(_lc
[I_MESSAGE_VOTE_FAILED
], True);
8380 MH_SEND_VoteEvent(NET_VE_FAILED
);
8382 if NetClients
<> nil then
8383 for I
:= Low(NetClients
) to High(NetClients
) do
8384 if NetClients
[i
].Used
then
8385 NetClients
[i
].Voted
:= False;
8386 gVoteInProgress
:= False;
8392 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
8393 Need
:= Floor((NetClientCount
+1)/2.0) + 1
8395 Need
:= Floor(NetClientCount
/2.0) + 1;
8396 if gVoteCount
>= Need
then
8398 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_PASSED
], [gVoteCommand
]), True);
8399 MH_SEND_VoteEvent(NET_VE_PASSED
, gVoteCommand
);
8400 gVoteInProgress
:= False;
8401 gVotePassed
:= True;
8402 gVoteCmdTimer
:= gTime
+ 5000;
8405 if NetClients
<> nil then
8406 for I
:= Low(NetClients
) to High(NetClients
) do
8407 if NetClients
[i
].Used
then
8408 NetClients
[i
].Voted
:= False;
8413 procedure g_Game_SetDebugMode();
8416 // ×èòû (äàæå â ñâîåé èãðå):
8420 procedure g_Game_SetLoadingText(Text: String; Max
: Integer; reWrite
: Boolean);
8424 if Length(LoadingStat
.Msgs
) = 0 then
8430 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
8431 if NextMsg
= Length(Msgs
) then
8433 for i
:= 0 to High(Msgs
)-1 do
8434 Msgs
[i
] := Msgs
[i
+1];
8442 Msgs
[NextMsg
-1] := Text;
8446 PBarWasHere
:= false;
8449 g_ActiveWindow
:= nil;
8450 ProcessLoading(True);
8453 procedure g_Game_StepLoading(Value
: Integer = -1);
8465 if (ShowCount
> LOADING_SHOW_STEP
) or (Value
> -1) then
8468 ProcessLoading(False);
8473 procedure g_Game_ClearLoading();
8482 len
:= ((gScreenHeight
div 3)*2 - 50) div LOADING_INTERLINE
;
8483 if len
< 1 then len
:= 1;
8484 SetLength(Msgs
, len
);
8485 for len
:= Low(Msgs
) to High(Msgs
) do
8488 PBarWasHere
:= false;
8492 procedure Parse_Params(var pars
: TParamStrValues
);
8499 while i
<= ParamCount
do
8502 if (Length(s
) > 1) and (s
[1] = '-') then
8504 if (Length(s
) > 2) and (s
[2] = '-') then
8505 begin // Îäèíî÷íûé ïàðàìåòð
8506 SetLength(pars
, Length(pars
) + 1);
8507 with pars
[High(pars
)] do
8509 Name
:= LowerCase(s
);
8514 if (i
< ParamCount
) then
8515 begin // Ïàðàìåòð ñî çíà÷åíèåì
8517 SetLength(pars
, Length(pars
) + 1);
8518 with pars
[High(pars
)] do
8520 Name
:= LowerCase(s
);
8521 Value
:= LowerCase(ParamStr(i
));
8530 function Find_Param_Value(var pars
: TParamStrValues
; aName
: String): String;
8535 for i
:= 0 to High(pars
) do
8536 if pars
[i
].Name
= aName
then
8538 Result
:= pars
[i
].Value
;
8543 procedure g_Game_Process_Params();
8545 pars
: TParamStrValues
;
8548 LimT
, LimS
: Integer;
8559 s
:= Find_Param_Value(pars
, '--debug');
8562 g_Game_SetDebugMode();
8563 s
:= Find_Param_Value(pars
, '--netdump');
8568 // Connect when game loads
8569 ip
:= Find_Param_Value(pars
, '-connect');
8573 s
:= Find_Param_Value(pars
, '-port');
8574 if (s
= '') or not TryStrToInt(s
, Port
) then
8577 s
:= Find_Param_Value(pars
, '-pw');
8579 g_Game_StartClient(ip
, Port
, s
);
8583 s
:= LowerCase(Find_Param_Value(pars
, '-dbg-mainwad'));
8586 gDefaultMegawadStart
:= s
;
8589 if (Find_Param_Value(pars
, '--dbg-mainwad-restore') <> '') or
8590 (Find_Param_Value(pars
, '--dbg-mainwad-default') <> '') then
8592 gDefaultMegawadStart
:= DF_Default_Megawad_Start
;
8595 // Start map when game loads:
8596 map
:= LowerCase(Find_Param_Value(pars
, '-map'));
8597 if isWadPath(map
) then
8600 s
:= Find_Param_Value(pars
, '-gm');
8601 GMode
:= g_Game_TextToMode(s
);
8602 if GMode
= GM_NONE
then GMode
:= GM_DM
;
8603 if GMode
= GM_SINGLE
then GMode
:= GM_COOP
;
8606 s
:= Find_Param_Value(pars
, '-limt');
8607 if (s
= '') or (not TryStrToInt(s
, LimT
)) then
8613 s
:= Find_Param_Value(pars
, '-lims');
8614 if (s
= '') or (not TryStrToInt(s
, LimS
)) then
8620 s
:= Find_Param_Value(pars
, '-lives');
8621 if (s
= '') or (not TryStrToInt(s
, Lives
)) then
8627 s
:= Find_Param_Value(pars
, '-opt');
8629 then Opt
:= gsGameFlags
8630 else Opt
:= TGameOptions(StrToIntDef(s
, 0));
8633 s
:= Find_Param_Value(pars
, '--close');
8637 // Override map to test:
8638 s
:= LowerCase(Find_Param_Value(pars
, '-testmap'));
8641 if e_IsValidResourceName(s
) then
8642 e_FindResource(AllMapDirs
, s
);
8643 gTestMap
:= ExpandFileName(s
);
8646 // Delete test map after play:
8647 s
:= Find_Param_Value(pars
, '--testdelete');
8650 //gMapToDelete := MapsDir + map;
8651 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType
.Fatal
);
8655 // Delete temporary WAD after play:
8656 s
:= Find_Param_Value(pars
, '--tempdelete');
8657 if (s
<> '') and (gTestMap
<> '') then
8659 gMapToDelete
:= gTestMap
;
8660 gTempDelete
:= True;
8663 // Number of players:
8664 s
:= Find_Param_Value(pars
, '-pl');
8666 n
:= DEFAULT_PLAYERS
8668 n
:= StrToIntDef(s
, DEFAULT_PLAYERS
);
8671 s
:= Find_Param_Value(pars
, '-port');
8672 if (s
= '') or not TryStrToInt(s
, Port
)
8673 then g_Game_StartCustom(map
, GMode
, LimT
, LimS
, Lives
, Opt
, n
)
8674 else g_Game_StartServer(map
, GMode
, LimT
, LimS
, Lives
, Opt
, n
, 0, Port
);
8677 // Execute script when game loads:
8678 s
:= Find_Param_Value(pars
, '-exec');
8681 // if not isWadPath(s) then
8682 // s := GameDir + '/' + s;
8687 if IOResult
<> 0 then
8689 e_WriteLog(Format(_lc
[I_SIMPLE_ERROR
], ['Failed to read file: ' + s
]), TMsgType
.Warning
);
8690 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_READ
], [s
]));
8694 e_WriteLog('Executing script: ' + s
, TMsgType
.Notify
);
8695 g_Console_Add(Format(_lc
[I_CONSOLE_EXEC
], [s
]));
8700 if IOResult
<> 0 then
8702 e_WriteLog(Format(_lc
[I_SIMPLE_ERROR
], ['Failed to read file: ' + s
]), TMsgType
.Warning
);
8703 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_READ
], [s
]));
8707 if Pos('#', s
) <> 1 then // script comment
8708 g_Console_Process(s
, True);
8719 conRegVar('pf_draw_frame', @g_profile_frame_draw
, 'draw frame rendering profiles', 'render profiles');
8720 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
8721 conRegVar('pf_coldet', @g_profile_collision
, 'draw collision detection profiles', 'coldet profiles');
8722 conRegVar('pf_los', @g_profile_los
, 'draw monster LOS profiles', 'monster LOS profiles');
8724 conRegVar('r_sq_draw', @gdbg_map_use_accel_render
, 'accelerated spatial queries in rendering', 'accelerated rendering');
8725 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet
, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
8726 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel
, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
8727 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace
, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
8729 conRegVar('pr_enabled', @gpart_dbg_enabled
, 'enable/disable particles', 'particles');
8730 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled
, 'enable/disable particle physics', 'particle physics');
8732 conRegVar('los_enabled', @gmon_dbg_los_enabled
, 'enable/disable monster LOS calculations', 'monster LOS', true);
8733 conRegVar('mon_think', @gmon_debug_think
, 'enable/disable monster thinking', 'monster thinking', true);
8735 {$IFDEF ENABLE_HOLMES}
8736 conRegVar('dbg_holmes', @g_holmes_enabled
, 'enable/disable Holmes', 'Holmes', true);
8739 conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds
, 'ignore level bounds', '', false);
8741 conRegVar('r_scale', @g_dbg_scale
, 0.01, 100.0, 'render scale', '', false);
8742 conRegVar('r_resolution_scale', @r_pixel_scale
, 0.01, 100.0, 'upscale factor', '', false);
8744 conRegVar('light_enabled', @gwin_k8_enable_light_experiments
, 'enable/disable dynamic lighting', 'lighting');
8745 conRegVar('light_player_halo', @g_playerLight
, 'enable/disable player halo', 'player light halo');
8747 conRegVar('r_smallmap_align_h', @r_smallmap_h
, 'halign: 0: left; 1: center; 2: right', 'horizontal aligning of small maps');
8748 conRegVar('r_smallmap_align_v', @r_smallmap_v
, 'valign: 0: top; 1: center; 2: bottom', 'vertial aligning of small maps');
8750 conRegVar('r_showfps', @gShowFPS
, 'draw fps counter', 'draw fps counter');
8751 conRegVar('r_showtime', @gShowTime
, 'show game time', 'show game time');
8752 conRegVar('r_showping', @gShowPing
, 'show ping', 'show ping');
8753 conRegVar('r_showscore', @gShowScore
, 'show score', 'show score');
8754 conRegVar('r_showkillmsg', @gShowKillMsg
, 'show kill log', 'show kill log');
8755 conRegVar('r_showlives', @gShowLives
, 'show lives', 'show lives');
8756 conRegVar('r_showspect', @gSpectHUD
, 'show spectator hud', 'show spectator hud');
8757 conRegVar('r_showstat', @gShowStat
, 'show stats', 'show stats');
8758 conRegVar('r_showpids', @gShowPIDs
, 'show PIDs', 'show PIDs');
8761 profileFrameDraw
.Free();