1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../shared/a_modes.inc}
23 g_basic
, g_player
, e_graphics
, g_res_downloader
,
24 g_sound
, g_gui
, utils
, md5
, mempool
, xprofiler
,
29 //RESERVED = 0, // FIXME: reuse for something
48 TGameOptions
= set of TGameOption
;
50 TGameSettings
= record
57 ItemRespawnTime
: Word;
58 ItemRespawnRandom
: Word;
59 PowerupRespawnTime
: Word;
60 PowerupRespawnRandom
: Word;
62 Options
: TGameOptions
;
71 TDelayedEvent
= record
80 Sound
: TPlayableSound
;
81 Tags
: Array of String;
85 TPlayerSettings
= record
90 // ones below are sent only to the server
92 WeaponPreferences
: Array[WP_FIRST
..WP_LAST
+1] of Byte;
109 function g_Game_IsNet(): Boolean;
110 function g_Game_IsServer(): Boolean;
111 function g_Game_IsClient(): Boolean;
112 procedure g_Game_Init();
113 procedure g_Game_Free (freeTextures
: Boolean=true);
114 procedure g_Game_LoadData();
115 procedure g_Game_FreeData();
116 procedure g_Game_Update();
117 procedure g_Game_PreUpdate();
118 procedure g_Game_Draw();
119 procedure g_Game_Quit();
120 procedure g_Game_SetupScreenSize();
121 procedure g_Game_ChangeResolution(newWidth
, newHeight
: Word; nowFull
, nowMax
: Boolean);
122 function g_Game_ModeToText(Mode
: Byte): string;
123 function g_Game_TextToMode(Mode
: string): Byte;
124 procedure g_Game_ExecuteEvent(Name
: String);
125 function g_Game_DelayEvent(DEType
: Byte; Time
: LongWord
; Num
: Integer = 0; Str
: String = ''): Integer;
126 procedure g_Game_AddPlayer(Team
: Byte = TEAM_NONE
);
127 procedure g_Game_RemovePlayer();
128 procedure g_Game_Spectate();
129 procedure g_Game_SpectateCenterView();
130 procedure g_Game_StartSingle(Map
: String; TwoPlayers
: Boolean; nPlayers
: Byte);
131 procedure g_Game_StartCustom(Map
: String; GameMode
: Byte; TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte; Options
: TGameOptions
; nPlayers
: Byte);
132 procedure g_Game_StartServer(Map
: String; GameMode
: Byte; TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte; Options
: TGameOptions
; nPlayers
: Byte; IPAddr
: LongWord
; Port
: Word);
133 procedure g_Game_StartClient(Addr
: String; Port
: Word; PW
: String);
134 procedure g_Game_Restart();
135 procedure g_Game_RestartLevel();
136 procedure g_Game_RestartRound(NoMapRestart
: Boolean = False);
137 function g_Game_ClientWAD (NewWAD
: String; const WHash
: TMD5Digest
): AnsiString
;
138 function g_Game_StartMap(asMegawad
: Boolean; Map
: String; Force
: Boolean = False; const oldMapPath
: AnsiString
=''): Boolean;
139 procedure g_Game_ChangeMap(const MapPath
: String);
140 procedure g_Game_ExitLevel(const Map
: AnsiString
);
141 function g_Game_GetFirstMap(WAD
: String): String;
142 function g_Game_GetNextMap(): String;
143 procedure g_Game_NextLevel();
144 procedure g_Game_Pause(Enable
: Boolean);
145 procedure g_Game_HolmesPause(Enable
: Boolean);
146 procedure g_Game_InGameMenu(Show
: Boolean);
147 function g_Game_IsWatchedPlayer(UID
: Word): Boolean;
148 function g_Game_IsWatchedTeam(Team
: Byte): Boolean;
149 procedure g_Game_Message(Msg
: String; Time
: Word);
150 procedure g_Game_LoadMapList(FileName
: String);
151 procedure g_Game_PauseAllSounds(Enable
: Boolean);
152 procedure g_Game_StopAllSounds(all
: Boolean);
153 procedure g_Game_UpdateTriggerSounds();
154 function g_Game_GetMegaWADInfo(WAD
: String): TMegaWADInfo
;
155 procedure g_Game_ChatSound(Text: String; Taunt
: Boolean = True);
156 procedure g_Game_Announce_GoodShot(SpawnerUID
: Word);
157 procedure g_Game_Announce_KillCombo(Param
: Integer);
158 procedure g_Game_Announce_BodyKill(SpawnerUID
: Word);
159 procedure g_Game_Effect_Bubbles(fX
, fY
: Integer; count
: Word; devX
, devY
: Byte; Silent
: Boolean = False);
160 procedure g_Game_StartVote(Command
, Initiator
: string);
161 procedure g_Game_CheckVote
;
162 procedure g_TakeScreenShot(Filename
: string = '');
163 procedure g_FatalError(Text: String);
164 procedure g_SimpleError(Text: String);
165 function g_Game_IsTestMap(): Boolean;
166 procedure g_Game_DeleteTestMap();
167 procedure GameCVars(P
: SSArray
);
168 procedure PlayerSettingsCVars(P
: SSArray
);
169 procedure SystemCommands(P
: SSArray
);
170 procedure GameCommands(P
: SSArray
);
171 procedure GameCheats(P
: SSArray
);
172 procedure DebugCommands(P
: SSArray
);
173 procedure g_Game_Process_Params
;
174 procedure g_Game_SetLoadingText(Text: String; Max
: Integer; reWrite
: Boolean);
175 procedure g_Game_StepLoading(Value
: Integer = -1);
176 procedure g_Game_ClearLoading();
177 procedure g_Game_SetDebugMode();
178 procedure DrawLoadingStat();
179 procedure DrawMenuBackground(tex
: AnsiString
);
181 { procedure SetWinPause(Enable: Boolean); }
186 LOADING_SHOW_STEP
= 100;
187 LOADING_INTERLINE
= 20;
202 MESSAGE_DIKEY
= WM_USER
+ 1;
207 EXIT_ENDLEVELSINGLE
= 4;
208 EXIT_ENDLEVELCUSTOM
= 5;
213 STATE_INTERCUSTOM
= 3;
214 STATE_INTERSINGLE
= 4;
220 LMS_RESPAWN_NONE
= 0;
221 LMS_RESPAWN_WARMUP
= 1;
222 LMS_RESPAWN_FINAL
= 2;
239 CONFIG_FILENAME
= 'Doom2DF.cfg';
241 TEST_MAP_NAME
= '$$$_TEST_$$$';
243 STD_PLAYER_MODEL
= 'Doomer';
251 STATFILE_VERSION
= $03;
255 gGameSettings
: TGameSettings
;
256 gPlayer1Settings
: TPlayerSettings
;
257 gPlayer2Settings
: TPlayerSettings
;
259 gPlayerScreenSize
: TDFPoint
;
260 gPlayer1ScreenCoord
: TDFPoint
;
261 gPlayer2ScreenCoord
: TDFPoint
;
262 gPlayer1
: TPlayer
= nil;
263 gPlayer2
: TPlayer
= nil;
264 gPlayerDrawn
: TPlayer
= nil;
266 gLerpFactor
: Single = 1.0;
267 gSwitchGameMode
: Byte = GM_DM
;
268 gHearPoint1
, gHearPoint2
: THearPoint
;
269 gSoundEffectsDF
: Boolean = False;
270 gSoundTriggerTime
: Word = 0;
271 gAnnouncer
: Integer = ANNOUNCE_NONE
;
272 goodsnd
: array[0..3] of TPlayableSound
;
273 killsnd
: array[0..3] of TPlayableSound
;
274 hahasnd
: array[0..2] of TPlayableSound
;
275 sound_get_flag
: array[0..1] of TPlayableSound
;
276 sound_lost_flag
: array[0..1] of TPlayableSound
;
277 sound_ret_flag
: array[0..1] of TPlayableSound
;
278 sound_cap_flag
: array[0..1] of TPlayableSound
;
279 gBodyKillEvent
: Integer = -1;
280 gDefInterTime
: ShortInt
= -1;
281 gInterEndTime
: LongWord
= 0;
282 gInterTime
: LongWord
= 0;
283 gServInterTime
: Byte = 0;
284 gGameStartTime
: LongWord
= 0;
285 gTotalMonsters
: Integer = 0;
286 gPauseMain
: Boolean = false;
287 gPauseHolmes
: Boolean = false;
288 gShowTime
: Boolean = False;
289 gShowFPS
: Boolean = False;
290 gShowScore
: Boolean = True;
291 gShowStat
: Boolean = True;
292 gShowPIDs
: Boolean = False;
293 gShowKillMsg
: Boolean = True;
294 gShowLives
: Boolean = True;
295 gShowPing
: Boolean = False;
296 gShowMap
: Boolean = False;
298 gState
: Byte = STATE_NONE
;
300 sWidth
, sHeight
: Word;
301 gSpectMode
: Byte = SPECT_NONE
;
302 gSpectHUD
: Boolean = True;
303 gSpectKeyPress
: Boolean = False;
304 gSpectX
: Integer = 0;
305 gSpectY
: Integer = 0;
306 gSpectStep
: Byte = 8;
307 gSpectViewTwo
: Boolean = False;
308 gSpectPID1
: Integer = -1;
309 gSpectPID2
: Integer = -1;
310 gSpectAuto
: Boolean = False;
311 gSpectAutoNext
: LongWord
;
312 gSpectAutoStepX
: Integer;
313 gSpectAutoStepY
: Integer;
314 gMusic
: TMusic
= nil;
315 gLoadGameMode
: Boolean;
316 gCheats
: Boolean = False;
317 gMapOnce
: Boolean = False;
318 gMapToDelete
: String;
319 gTempDelete
: Boolean = False;
320 gLastMap
: Boolean = False;
323 gResolutionChange
: Boolean = False;
324 gRC_Width
, gRC_Height
: Integer;
325 gRC_FullScreen
, gRC_Maximized
: Boolean;
326 gLanguageChange
: Boolean = False;
327 gDebugMode
: Boolean = False;
328 g_debug_Sounds
: Boolean = False;
329 g_debug_Frames
: Boolean = False;
330 g_debug_WinMsgs
: Boolean = False;
331 g_debug_MonsterOff
: Boolean = False;
332 g_debug_BotAIOff
: Byte = 0;
333 g_debug_HealthBar
: Boolean = False;
334 g_Debug_Player
: Boolean = False;
335 gCoopMonstersKilled
: Word = 0;
336 gCoopSecretsFound
: Word = 0;
337 gCoopTotalMonstersKilled
: Word = 0;
338 gCoopTotalSecretsFound
: Word = 0;
339 gCoopTotalMonsters
: Word = 0;
340 gCoopTotalSecrets
: Word = 0;
341 gStatsOff
: Boolean = False;
342 gStatsPressed
: Boolean = False;
343 gExitByTrigger
: Boolean = False;
344 gNextMap
: String = '';
345 gLMSRespawn
: Byte = LMS_RESPAWN_NONE
;
346 gLMSRespawnTime
: Cardinal = 0;
347 gLMSSoftSpawn
: Boolean = False;
348 gMissionFailed
: Boolean = False;
349 gVoteInProgress
: Boolean = False;
350 gVotePassed
: Boolean = False;
351 gVoteCommand
: string = '';
352 gVoteTimer
: Cardinal = 0;
353 gVoteCmdTimer
: Cardinal = 0;
354 gVoteCount
: Integer = 0;
355 gVoteTimeout
: Cardinal = 30;
356 gVoted
: Boolean = False;
357 gVotesEnabled
: Boolean = True;
358 gEvents
: Array of TGameEvent
;
359 gDelayedEvents
: Array of TDelayedEvent
;
360 gUseChatSounds
: Boolean = True;
361 gChatSounds
: Array of TChatSound
;
362 gWeaponAction
: Array [0..1, WP_FACT
..WP_LACT
] of Boolean; // [player, weapon_action]
363 gSelectWeapon
: Array [0..1, WP_FIRST
..WP_LAST
] of Boolean; // [player, weapon]
364 gInterReadyCount
: Integer = 0;
365 gMaxBots
: Integer = 127;
367 g_dbg_ignore_bounds
: Boolean = false;
368 r_smallmap_h
: Integer = 0; // 0: left; 1: center; 2: right
369 r_smallmap_v
: Integer = 2; // 0: top; 1: center; 2: bottom
371 // move button values:
372 // bits 0-1: l/r state:
373 // 0: neither left, nor right pressed
376 // bits 4-5: l/r state when strafe was pressed
377 P1MoveButton
: Byte = 0;
378 P2MoveButton
: Byte = 0;
380 g_profile_frame_update
: Boolean = false;
381 g_profile_frame_draw
: Boolean = false;
382 g_profile_collision
: Boolean = false;
383 g_profile_los
: Boolean = false;
384 g_profile_history_size
: Integer = 1000;
386 g_rlayer_back
: Boolean = true;
387 g_rlayer_step
: Boolean = true;
388 g_rlayer_wall
: Boolean = true;
389 g_rlayer_door
: Boolean = true;
390 g_rlayer_acid1
: Boolean = true;
391 g_rlayer_acid2
: Boolean = true;
392 g_rlayer_water
: Boolean = true;
393 g_rlayer_fore
: Boolean = true;
396 procedure g_ResetDynlights ();
397 procedure g_AddDynLight (x
, y
, radius
: Integer; r
, g
, b
, a
: Single);
398 procedure g_DynLightExplosion (x
, y
, radius
: Integer; r
, g
, b
: Single);
400 function conIsCheatsEnabled (): Boolean; inline;
401 function gPause (): Boolean; inline;
407 {$INCLUDE ../nogl/noGLuses.inc}
408 {$IFDEF ENABLE_HOLMES}
411 e_texture
, e_res
, g_textures
, g_window
, g_menu
,
412 e_input
, e_log
, g_console
, g_items
, g_map
, g_panel
,
413 g_playermodel
, g_gfx
, g_options
, Math
,
414 g_triggers
, g_monsters
, e_sound
, CONFIG
,
415 g_language
, g_net
, g_main
, g_phys
,
416 ENet
, e_msg
, g_netmsg
, g_netmaster
,
417 sfs
, wadreader
, g_system
;
421 hasPBarGfx
: Boolean = false;
424 // ////////////////////////////////////////////////////////////////////////// //
425 function gPause (): Boolean; inline; begin result
:= gPauseMain
or gPauseHolmes
; end;
428 // ////////////////////////////////////////////////////////////////////////// //
429 function conIsCheatsEnabled (): Boolean; inline;
432 if g_Game_IsNet
then exit
;
433 if not gDebugMode
then
435 //if not gCheats then exit;
436 if not (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
]) then exit
;
437 if not (gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
]) then exit
;
443 // ////////////////////////////////////////////////////////////////////////// //
445 profileFrameDraw
: TProfiler
= nil;
448 // ////////////////////////////////////////////////////////////////////////// //
451 x
, y
, radius
: Integer;
454 exploRadius
: Integer;
458 g_dynLights
: array of TDynLight
= nil;
459 g_dynLightCount
: Integer = 0;
460 g_playerLight
: Boolean = false;
462 procedure g_ResetDynlights ();
466 if not gwin_has_stencil
then begin g_dynLightCount
:= 0; exit
; end;
468 for idx
:= 0 to g_dynLightCount
-1 do
470 if g_dynLights
[idx
].exploCount
= -666 then
477 Inc(g_dynLights
[idx
].exploCount
);
478 if (g_dynLights
[idx
].exploCount
< 10) then
480 g_dynLights
[idx
].radius
:= g_dynLights
[idx
].exploRadius
+g_dynLights
[idx
].exploCount
*8;
481 g_dynLights
[idx
].a
:= 0.4+g_dynLights
[idx
].exploCount
/10;
482 if (g_dynLights
[idx
].a
> 0.8) then g_dynLights
[idx
].a
:= 0.8;
483 if lnum
<> idx
then g_dynLights
[lnum
] := g_dynLights
[idx
];
488 g_dynLightCount
:= lnum
;
491 procedure g_AddDynLight (x
, y
, radius
: Integer; r
, g
, b
, a
: Single);
493 if not gwin_has_stencil
then exit
;
494 if g_dynLightCount
= length(g_dynLights
) then SetLength(g_dynLights
, g_dynLightCount
+1024);
495 g_dynLights
[g_dynLightCount
].x
:= x
;
496 g_dynLights
[g_dynLightCount
].y
:= y
;
497 g_dynLights
[g_dynLightCount
].radius
:= radius
;
498 g_dynLights
[g_dynLightCount
].r
:= r
;
499 g_dynLights
[g_dynLightCount
].g
:= g
;
500 g_dynLights
[g_dynLightCount
].b
:= b
;
501 g_dynLights
[g_dynLightCount
].a
:= a
;
502 g_dynLights
[g_dynLightCount
].exploCount
:= -666;
503 Inc(g_dynLightCount
);
506 procedure g_DynLightExplosion (x
, y
, radius
: Integer; r
, g
, b
: Single);
508 if not gwin_has_stencil
then exit
;
509 if g_dynLightCount
= length(g_dynLights
) then SetLength(g_dynLights
, g_dynLightCount
+1024);
510 g_dynLights
[g_dynLightCount
].x
:= x
;
511 g_dynLights
[g_dynLightCount
].y
:= y
;
512 g_dynLights
[g_dynLightCount
].radius
:= 0;
513 g_dynLights
[g_dynLightCount
].exploRadius
:= radius
;
514 g_dynLights
[g_dynLightCount
].r
:= r
;
515 g_dynLights
[g_dynLightCount
].g
:= g
;
516 g_dynLights
[g_dynLightCount
].b
:= b
;
517 g_dynLights
[g_dynLightCount
].a
:= 0;
518 g_dynLights
[g_dynLightCount
].exploCount
:= 0;
519 Inc(g_dynLightCount
);
523 // ////////////////////////////////////////////////////////////////////////// //
524 function calcProfilesHeight (prof
: TProfiler
): Integer;
527 if (prof
= nil) then exit
;
528 if (length(prof
.bars
) = 0) then exit
;
529 result
:= length(prof
.bars
)*(16+2);
533 function drawProfiles (x
, y
: Integer; prof
: TProfiler
): Integer;
540 if (prof
= nil) then exit
;
542 if (length(prof
.bars
) = 0) then exit
;
544 hgt
:= calcProfilesHeight(prof
);
545 if (x
< 0) then x
:= gScreenWidth
-(wdt
-1)+x
;
546 if (y
< 0) then y
:= gScreenHeight
-(hgt
-1)+y
;
548 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
549 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
550 e_DarkenQuadWH(x
, y
, wdt
, hgt
, 150);
553 for ii
:= 0 to High(prof
.bars
) do
555 e_TextureFontPrintEx(x
+2+4*prof
.bars
[ii
].level
, yy
, Format('%s: %d', [prof
.bars
[ii
].name
, prof
.bars
[ii
].value
]), gStdFont
, 255, 255, 0, 1, false);
562 // ////////////////////////////////////////////////////////////////////////// //
564 TEndCustomGameStat
= record
565 PlayerStat
: TPlayerStatArray
;
569 Map
, MapName
: String;
572 TEndSingleGameStat
= record
573 PlayerStat
: Array [0..1] of record
579 TotalSecrets
: Integer;
582 TLoadingStat
= record
586 Msgs
: Array of String;
588 PBarWasHere
: Boolean; // did we draw a progress bar for this message?
591 TParamStrValue
= record
596 TParamStrValues
= Array of TParamStrValue
;
599 INTER_ACTION_TEXT
= 1;
600 INTER_ACTION_PIC
= 2;
601 INTER_ACTION_MUSIC
= 3;
605 FPSCounter
, UPSCounter
: Word;
606 FPSTime
, UPSTime
: LongWord
;
607 DataLoaded
: Boolean = False;
608 IsDrawStat
: Boolean = False;
609 CustomStat
: TEndCustomGameStat
;
610 SingleStat
: TEndSingleGameStat
;
611 LoadingStat
: TLoadingStat
;
612 EndingGameCounter
: Byte = 0;
615 MessageLineLength
: Integer = 80;
616 MapList
: SSArray
= nil;
617 MapIndex
: Integer = -1;
618 InterReadyTime
: Integer = -1;
619 StatShotDone
: Boolean = False;
620 StatFilename
: string = ''; // used by stat screenshot to save with the same name as the csv
621 StatDate
: string = '';
627 text: Array of ShortString
;
628 anim
: Array of ShortString
;
629 pic
: Array of ShortString
;
630 mus
: Array of ShortString
;
632 triggers
: Array of record
634 actions
: Array of record
635 action
, p1
, p2
: Integer;
638 cur_trigger
: Integer;
651 function Compare(a
, b
: TPlayerStat
): Integer;
653 if a
.Spectator
then Result
:= 1
654 else if b
.Spectator
then Result
:= -1
655 else if a
.Frags
< b
.Frags
then Result
:= 1
656 else if a
.Frags
> b
.Frags
then Result
:= -1
657 else if a
.Deaths
< b
.Deaths
then Result
:= -1
658 else if a
.Deaths
> b
.Deaths
then Result
:= 1
659 else if a
.Kills
< b
.Kills
then Result
:= -1
663 procedure SortGameStat(var stat
: TPlayerStatArray
);
668 if stat
= nil then Exit
;
670 for I
:= High(stat
) downto Low(stat
) do
671 for J
:= Low(stat
) to High(stat
) - 1 do
672 if Compare(stat
[J
], stat
[J
+ 1]) = 1 then
675 stat
[J
] := stat
[J
+ 1];
680 // saves a shitty CSV containing the game stats passed to it
681 procedure SaveGameStat(Stat
: TEndCustomGameStat
; Path
: string);
684 dir
, fname
, map
, mode
, etime
, flags
, strf
: String;
689 dir
:= e_GetWriteableDir(StatsDirs
);
690 // stats are placed in stats/yy/mm/dd/*.csv
691 fname
:= e_CatPath(dir
, Path
);
692 ForceDirectories(fname
); // ensure yy/mm/dd exists within the stats dir
693 fname
:= e_CatPath(fname
, StatFilename
+ '.csv');
694 AssignFile(s
, fname
);
696 SetTextCodePage(s
, CP_UTF8
);
698 // line 1: stats ver, datetime, server name, map name, game mode, time limit, score limit, dmflags, game time, num players
699 if g_Game_IsNet
then fname
:= NetServerName
else fname
:= '';
700 map
:= g_ExtractWadNameNoPath(gMapInfo
.Map
) + ':/' + g_ExtractFileName(gMapInfo
.Map
);
701 mode
:= g_Game_ModeToText(Stat
.GameMode
);
702 etime
:= Format('%d:%.2d:%.2d', [
703 Stat
.GameTime
div 1000 div 3600,
704 (Stat
.GameTime
div 1000 div 60) mod 60,
705 Stat
.GameTime
div 1000 mod 60
709 for flag
in gGameSettings
.Options
do
712 System
.WriteStr(strf
, flag
); // FIXME: rename our utils.WriteStr()
716 WriteLn(s
, 'stats_ver,datetime,server,map,mode,timelimit,scorelimit,dmflags,time,num_players');
717 WriteLn(s
, Format('%d,%s,%s,%s,%s,%u,%u,"%s",%s,%d', [
723 gGameSettings
.TimeLimit
,
724 gGameSettings
.ScoreLimit
,
727 Length(Stat
.PlayerStat
)
729 // line 2: game specific shit
730 // if it's a team game: red score, blue score
731 // if it's a coop game: monsters killed, monsters total, secrets found, secrets total
733 if Stat
.GameMode
in [GM_TDM
, GM_CTF
] then
735 Format('red_score,blue_score' + LineEnding
+ '%d,%d', [Stat
.TeamStat
[TEAM_RED
].Score
, Stat
.TeamStat
[TEAM_BLUE
].Score
]))
736 else if Stat
.GameMode
in [GM_COOP
, GM_SINGLE
] then
738 Format('mon_killed,mon_total,secrets_found,secrets_total' + LineEnding
+ '%d,%d,%d,%d',[gCoopMonstersKilled
, gTotalMonsters
, gCoopSecretsFound
, gSecretsCount
]));
739 // lines 3-...: team, player name, frags, deaths
740 WriteLn(s
, 'team,name,frags,deaths');
741 for I
:= Low(Stat
.PlayerStat
) to High(Stat
.PlayerStat
) do
742 with Stat
.PlayerStat
[I
] do
743 WriteLn(s
, Format('%d,%s,%d,%d', [Team
, dquoteStr(Name
), Frags
, Deaths
]));
745 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_WRITE
], [fname
]));
748 g_Console_Add('could not create gamestats file "' + fname
+ '"');
753 procedure ClearDebugCvars();
755 g_debug_Sounds
:= False;
756 g_debug_Frames
:= False;
757 g_debug_WinMsgs
:= False;
758 g_debug_MonsterOff
:= False;
759 g_debug_BotAIOff
:= 0;
760 g_debug_HealthBar
:= False;
761 g_Debug_Player
:= False;
764 function g_Game_ModeToText(Mode
: Byte): string;
768 GM_DM
: Result
:= _lc
[I_MENU_GAME_TYPE_DM
];
769 GM_TDM
: Result
:= _lc
[I_MENU_GAME_TYPE_TDM
];
770 GM_CTF
: Result
:= _lc
[I_MENU_GAME_TYPE_CTF
];
771 GM_COOP
: Result
:= _lc
[I_MENU_GAME_TYPE_COOP
];
772 GM_SINGLE
: Result
:= _lc
[I_MENU_GAME_TYPE_SINGLE
];
776 function g_Game_TextToMode(Mode
: string): Byte;
779 Mode
:= UpperCase(Mode
);
780 if Mode
= _lc
[I_MENU_GAME_TYPE_DM
] then
785 if Mode
= _lc
[I_MENU_GAME_TYPE_TDM
] then
790 if Mode
= _lc
[I_MENU_GAME_TYPE_CTF
] then
795 if Mode
= _lc
[I_MENU_GAME_TYPE_COOP
] then
800 if Mode
= _lc
[I_MENU_GAME_TYPE_SINGLE
] then
807 function g_Game_IsNet(): Boolean;
809 Result
:= (gGameSettings
.GameType
in [GT_SERVER
, GT_CLIENT
]);
812 function g_Game_IsServer(): Boolean;
814 Result
:= (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
, GT_SERVER
]);
817 function g_Game_IsClient(): Boolean;
819 Result
:= (gGameSettings
.GameType
= GT_CLIENT
);
822 function g_Game_GetMegaWADInfo(WAD
: String): TMegaWADInfo
;
829 Result
.name
:= ExtractFileName(WAD
);
830 Result
.description
:= '';
833 w
:= TWADFile
.Create();
836 if not w
.GetResource('INTERSCRIPT', p
, len
) then
842 cfg
:= TConfig
.CreateMem(p
, len
);
843 Result
.name
:= cfg
.ReadStr('megawad', 'name', ExtractFileName(WAD
));
844 Result
.description
:= cfg
.ReadStr('megawad', 'description', '');
845 Result
.author
:= cfg
.ReadStr('megawad', 'author', '');
846 Result
.pic
:= cfg
.ReadStr('megawad', 'pic', '');
852 procedure g_Game_FreeWAD();
856 for a
:= 0 to High(MegaWAD
.res
.pic
) do
857 if MegaWAD
.res
.pic
[a
] <> '' then
858 g_Texture_Delete(MegaWAD
.res
.pic
[a
]);
860 for a
:= 0 to High(MegaWAD
.res
.mus
) do
861 if MegaWAD
.res
.mus
[a
] <> '' then
862 g_Sound_Delete(MegaWAD
.res
.mus
[a
]);
864 MegaWAD
.res
.pic
:= nil;
865 MegaWAD
.res
.text := nil;
866 MegaWAD
.res
.anim
:= nil;
867 MegaWAD
.res
.mus
:= nil;
868 MegaWAD
.triggers
:= nil;
870 g_Texture_Delete('TEXTURE_endpic');
871 g_Sound_Delete('MUSIC_endmus');
873 ZeroMemory(@MegaWAD
, SizeOf(MegaWAD
));
874 gGameSettings
.WAD
:= '';
877 procedure g_Game_LoadWAD(WAD
: string);
886 gGameSettings
.WAD
:= WAD
;
887 if not (gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
]) then
890 MegaWAD
.info
:= g_Game_GetMegaWADInfo(WAD
);
892 w
:= TWADFile
.Create();
895 if not w
.GetResource('INTERSCRIPT', p
, len
) then
901 cfg
:= TConfig
.CreateMem(p
, len
);
906 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
907 if s = '' then Break;
910 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
911 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
913 g_Texture_CreateWADEx(s, s);
919 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
920 if s = '' then Break;
923 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
924 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
926 g_Music_CreateWADEx(s, s);
929 MegaWAD
.endpic
:= cfg
.ReadStr('megawad', 'endpic', '');
930 if MegaWAD
.endpic
<> '' then
932 TEXTUREFILTER
:= GL_LINEAR
;
933 s
:= e_GetResourcePath(WadDirs
, MegaWAD
.endpic
, WAD
);
934 g_Texture_CreateWADEx('TEXTURE_endpic', s
);
935 TEXTUREFILTER
:= GL_NEAREST
;
937 MegaWAD
.endmus
:= cfg
.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
938 if MegaWAD
.endmus
<> '' then
940 s
:= e_GetResourcePath(WadDirs
, MegaWAD
.endmus
, WAD
);
941 g_Sound_CreateWADEx('MUSIC_endmus', s
, True);
949 {procedure start_trigger(t: string);
953 function next_trigger(): Boolean;
957 procedure DisableCheats();
963 if gPlayer1
<> nil then gPlayer1
.GodMode
:= False;
964 if gPlayer2
<> nil then gPlayer2
.GodMode
:= False;
965 if gPlayer1
<> nil then gPlayer1
.NoTarget
:= False;
966 if gPlayer2
<> nil then gPlayer2
.NoTarget
:= False;
968 {$IF DEFINED(D2F_DEBUG)}
969 if gPlayer1
<> nil then gPlayer1
.NoTarget
:= True;
970 gAimLine
:= g_dbg_aimline_on
;
974 procedure g_Game_ExecuteEvent(Name
: String);
980 if gEvents
= nil then
982 for a
:= 0 to High(gEvents
) do
983 if gEvents
[a
].Name
= Name
then
985 if gEvents
[a
].Command
<> '' then
986 g_Console_Process(gEvents
[a
].Command
, True);
991 function g_Game_DelayEvent(DEType
: Byte; Time
: LongWord
; Num
: Integer = 0; Str
: String = ''): Integer;
996 if gDelayedEvents
<> nil then
997 for a
:= 0 to High(gDelayedEvents
) do
998 if not gDelayedEvents
[a
].Pending
then
1005 SetLength(gDelayedEvents
, Length(gDelayedEvents
) + 1);
1006 n
:= High(gDelayedEvents
);
1008 gDelayedEvents
[n
].Pending
:= True;
1009 gDelayedEvents
[n
].DEType
:= DEType
;
1010 gDelayedEvents
[n
].DENum
:= Num
;
1011 gDelayedEvents
[n
].DEStr
:= Str
;
1012 if DEType
= DE_GLOBEVENT
then
1013 gDelayedEvents
[n
].Time
:= (sys_GetTicks() {div 1000}) + Time
1015 gDelayedEvents
[n
].Time
:= gTime
+ Time
;
1019 procedure EndGame();
1025 if g_Game_IsNet
and g_Game_IsServer
then
1026 MH_SEND_GameEvent(NET_EV_MAPEND
, Byte(gMissionFailed
));
1029 gPauseMain
:= false;
1030 gPauseHolmes
:= false;
1033 g_Game_StopAllSounds(False);
1038 EndingGameCounter
:= 0;
1039 g_ActiveWindow
:= nil;
1041 gLMSRespawn
:= LMS_RESPAWN_NONE
;
1042 gLMSRespawnTime
:= 0;
1045 EXIT_SIMPLE
: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
1050 begin // Ýòî áûë òåñò
1054 begin // Âûõîä â ãëàâíîå ìåíþ
1055 gMusic
.SetByName('MUSIC_MENU');
1057 if gState
<> STATE_SLIST
then
1059 g_GUI_ShowWindow('MainMenu');
1060 gState
:= STATE_MENU
;
1063 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
1064 slReturnPressed
:= True;
1065 if g_Net_Slist_Fetch(slCurrent
) then
1067 if slCurrent
= nil then
1068 slWaitStr
:= _lc
[I_NET_SLIST_NOSERVERS
];
1071 slWaitStr
:= _lc
[I_NET_SLIST_ERROR
];
1072 g_Serverlist_GenerateTable(slCurrent
, slTable
);
1075 g_Game_ExecuteEvent('ongameend');
1079 EXIT_RESTART
: // Íà÷àòü óðîâåíü ñíà÷àëà
1081 if not g_Game_IsClient
then g_Game_Restart();
1084 EXIT_ENDLEVELCUSTOM
: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
1086 // Ñòàòèñòèêà Ñâîåé èãðû:
1087 FileName
:= g_ExtractWadName(gMapInfo
.Map
);
1089 CustomStat
.GameTime
:= gTime
;
1090 CustomStat
.Map
:= ExtractFileName(FileName
)+':'+g_ExtractFileName(gMapInfo
.Map
); //ResName;
1091 CustomStat
.MapName
:= gMapInfo
.Name
;
1092 CustomStat
.GameMode
:= gGameSettings
.GameMode
;
1093 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1094 CustomStat
.TeamStat
:= gTeamStat
;
1096 CustomStat
.PlayerStat
:= nil;
1098 // Ñòàòèñòèêà èãðîêîâ:
1099 if gPlayers
<> nil then
1101 for a
:= 0 to High(gPlayers
) do
1102 if gPlayers
[a
] <> nil then
1104 SetLength(CustomStat
.PlayerStat
, Length(CustomStat
.PlayerStat
)+1);
1105 with CustomStat
.PlayerStat
[High(CustomStat
.PlayerStat
)] do
1108 Name
:= gPlayers
[a
].Name
;
1109 Frags
:= gPlayers
[a
].Frags
;
1110 Deaths
:= gPlayers
[a
].Death
;
1111 Kills
:= gPlayers
[a
].Kills
;
1112 Team
:= gPlayers
[a
].Team
;
1113 Color
:= gPlayers
[a
].Model
.Color
;
1114 Spectator
:= gPlayers
[a
].FSpectator
;
1118 SortGameStat(CustomStat
.PlayerStat
);
1120 if (gSaveStats
or gScreenshotStats
) and (Length(CustomStat
.PlayerStat
) > 1) then
1123 if g_Game_IsNet
then StatFilename
:= NetServerName
else StatFilename
:= 'local';
1124 StatDate
:= FormatDateTime('yymmdd_hhnnss', t
);
1125 StatFilename
:= StatFilename
+ '_' + CustomStat
.Map
+ '_' + g_Game_ModeToText(CustomStat
.GameMode
);
1126 StatFilename
:= sanitizeFilename(StatFilename
) + '_' + StatDate
;
1128 SaveGameStat(CustomStat
, FormatDateTime('yyyy"/"mm"/"dd', t
));
1131 StatShotDone
:= False;
1134 g_Game_ExecuteEvent('onmapend');
1135 if not g_Game_IsClient
then g_Player_ResetReady
;
1136 gInterReadyCount
:= 0;
1138 // Çàòóõàþùèé ýêðàí:
1139 EndingGameCounter
:= 255;
1140 gState
:= STATE_FOLD
;
1142 if gDefInterTime
< 0 then
1143 gInterEndTime
:= IfThen((gGameSettings
.GameType
= GT_SERVER
) and (gPlayer1
= nil), 15000, 25000)
1145 gInterEndTime
:= gDefInterTime
* 1000;
1148 EXIT_ENDLEVELSINGLE
: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
1150 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
1151 SingleStat
.GameTime
:= gTime
;
1152 SingleStat
.TwoPlayers
:= gPlayer2
<> nil;
1153 SingleStat
.TotalSecrets
:= gSecretsCount
;
1154 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
1155 SingleStat
.PlayerStat
[0].Kills
:= gPlayer1
.MonsterKills
;
1156 SingleStat
.PlayerStat
[0].Secrets
:= gPlayer1
.Secrets
;
1157 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
1158 if SingleStat
.TwoPlayers
then
1160 SingleStat
.PlayerStat
[1].Kills
:= gPlayer2
.MonsterKills
;
1161 SingleStat
.PlayerStat
[1].Secrets
:= gPlayer2
.Secrets
;
1164 g_Game_ExecuteEvent('onmapend');
1167 if gNextMap
<> '' then
1169 gMusic
.SetByName('MUSIC_INTERMUS');
1171 gState
:= STATE_INTERSINGLE
;
1174 g_Game_ExecuteEvent('oninter');
1176 else // Áîëüøå íåò êàðò
1178 // Çàòóõàþùèé ýêðàí:
1179 EndingGameCounter
:= 255;
1180 gState
:= STATE_FOLD
;
1185 // Îêîí÷àíèå îáðàáîòàíî:
1186 if gExit
<> EXIT_QUIT
then
1190 procedure drawTime(X
, Y
: Integer); inline;
1192 e_TextureFontPrint(x
, y
,
1193 Format('%d:%.2d:%.2d', [
1194 gTime
div 1000 div 3600,
1195 (gTime
div 1000 div 60) mod 60,
1196 gTime
div 1000 mod 60
1201 procedure DrawStat();
1203 pc
, x
, y
, w
, h
: Integer;
1204 w1
, w2
, w3
, w4
: Integer;
1206 cw
, ch
, r
, g
, b
, rr
, gg
, bb
: Byte;
1209 stat
: TPlayerStatArray
;
1217 pc
:= g_Player_GetCount
;
1218 e_TextureFontGetSize(gStdFont
, cw
, ch
);
1220 w
:= gScreenWidth
-(gScreenWidth
div 5);
1221 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1224 h
:= 40+ch
*5+(ch
+8)*pc
;
1225 x
:= (gScreenWidth
div 2)-(w
div 2);
1226 y
:= (gScreenHeight
div 2)-(h
div 2);
1228 e_DrawFillQuad(x
, y
, x
+w
-1, y
+h
-1, 64, 64, 64, 32);
1229 e_DrawQuad(x
, y
, x
+w
-1, y
+h
-1, 255, 127, 0);
1231 drawTime(x
+w
-78, y
+8);
1233 wad
:= g_ExtractWadNameNoPath(gMapInfo
.Map
);
1234 map
:= g_ExtractFileName(gMapInfo
.Map
);
1235 mapstr
:= wad
+ ':\' + map
+ ' - ' + gMapInfo
.Name
;
1237 case gGameSettings
.GameMode
of
1240 if gGameSettings
.MaxLives
= 0 then
1241 s1
:= _lc
[I_GAME_DM
]
1243 s1
:= _lc
[I_GAME_LMS
];
1244 s2
:= Format(_lc
[I_GAME_FRAG_LIMIT
], [gGameSettings
.ScoreLimit
]);
1245 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1250 if gGameSettings
.MaxLives
= 0 then
1251 s1
:= _lc
[I_GAME_TDM
]
1253 s1
:= _lc
[I_GAME_TLMS
];
1254 s2
:= Format(_lc
[I_GAME_FRAG_LIMIT
], [gGameSettings
.ScoreLimit
]);
1255 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1260 s1
:= _lc
[I_GAME_CTF
];
1261 s2
:= Format(_lc
[I_GAME_SCORE_LIMIT
], [gGameSettings
.ScoreLimit
]);
1262 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1267 if gGameSettings
.MaxLives
= 0 then
1268 s1
:= _lc
[I_GAME_COOP
]
1270 s1
:= _lc
[I_GAME_SURV
];
1271 s2
:= _lc
[I_GAME_MONSTERS
] + ' ' + IntToStr(gCoopMonstersKilled
) + '/' + IntToStr(gTotalMonsters
);
1272 s3
:= _lc
[I_GAME_SECRETS
] + ' ' + IntToStr(gCoopSecretsFound
) + '/' + IntToStr(gSecretsCount
);
1283 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*cw
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
1285 e_TextureFontPrintEx(x
+(w
div 2)-(Length(mapstr
)*cw
div 2), _y
, mapstr
, gStdFont
, 200, 200, 200, 1);
1287 e_TextureFontPrintEx(x
+16, _y
, s2
, gStdFont
, 200, 200, 200, 1);
1289 e_TextureFontPrintEx(x
+w
-16-(Length(s3
))*cw
, _y
, s3
,
1290 gStdFont
, 200, 200, 200, 1);
1292 if NetMode
= NET_SERVER
then
1293 e_TextureFontPrintEx(x
+8, y
+ 8, _lc
[I_NET_SERVER
], gStdFont
, 255, 255, 255, 1)
1295 if NetMode
= NET_CLIENT
then
1296 e_TextureFontPrintEx(x
+8, y
+ 8,
1297 NetClientIP
+ ':' + IntToStr(NetClientPort
), gStdFont
, 255, 255, 255, 1);
1301 stat
:= g_Player_GetStats();
1304 w2
:= (w
-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1305 w3
:= (w
-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1307 w1
:= w
-16-w2
-w3
-w4
; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1309 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1313 for a
:= TEAM_RED
to TEAM_BLUE
do
1315 if a
= TEAM_RED
then
1317 s1
:= _lc
[I_GAME_TEAM_RED
];
1324 s1
:= _lc
[I_GAME_TEAM_BLUE
];
1330 e_TextureFontPrintEx(x
+16, _y
, s1
, gStdFont
, r
, g
, b
, 1);
1331 e_TextureFontPrintEx(x
+w1
+16, _y
, IntToStr(gTeamStat
[a
].Score
),
1332 gStdFont
, r
, g
, b
, 1);
1334 _y
:= _y
+ch
+(ch
div 4);
1335 e_DrawLine(1, x
+16, _y
, x
+w
-16, _y
, r
, g
, b
);
1336 _y
:= _y
+(ch
div 4);
1338 for aa
:= 0 to High(stat
) do
1339 if stat
[aa
].Team
= a
then
1355 namestr
:= Format('[%5d] %s', [UID
, Name
])
1359 e_TextureFontPrintEx(x
+16, _y
, namestr
, gStdFont
, rr
, gg
, bb
, 1);
1361 e_TextureFontPrintEx(x
+w1
+16, _y
, Format(_lc
[I_GAME_PING_MS
], [Ping
, Loss
]), gStdFont
, rr
, gg
, bb
, 1);
1363 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
, IntToStr(Frags
), gStdFont
, rr
, gg
, bb
, 1);
1365 e_TextureFontPrintEx(x
+w1
+w2
+w3
+16, _y
, IntToStr(Deaths
), gStdFont
, rr
, gg
, bb
, 1);
1372 else if gGameSettings
.GameMode
in [GM_DM
, GM_COOP
] then
1375 e_TextureFontPrintEx(x
+16, _y
, _lc
[I_GAME_PLAYER_NAME
], gStdFont
, 255, 127, 0, 1);
1376 e_TextureFontPrintEx(x
+16+w1
, _y
, _lc
[I_GAME_PING
], gStdFont
, 255, 127, 0, 1);
1377 e_TextureFontPrintEx(x
+16+w1
+w2
, _y
, _lc
[I_GAME_FRAGS
], gStdFont
, 255, 127, 0, 1);
1378 e_TextureFontPrintEx(x
+16+w1
+w2
+w3
, _y
, _lc
[I_GAME_DEATHS
], gStdFont
, 255, 127, 0, 1);
1381 for aa
:= 0 to High(stat
) do
1395 namestr
:= Format('[%5d] %s', [UID
, Name
])
1399 e_DrawFillQuad(x
+16, _y
+4, x
+32-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
1400 e_DrawQuad(x
+16, _y
+4, x
+32-1, _y
+16+4-1, 192, 192, 192);
1402 e_TextureFontPrintEx(x
+16+16+8, _y
+4, namestr
, gStdFont
, r
, g
, 0, 1);
1404 e_TextureFontPrintEx(x
+w1
+16, _y
+4, Format(_lc
[I_GAME_PING_MS
], [Ping
, Loss
]), gStdFont
, r
, g
, 0, 1);
1406 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
+4, IntToStr(Frags
), gStdFont
, r
, g
, 0, 1);
1408 e_TextureFontPrintEx(x
+w1
+w2
+w3
+16, _y
+4, IntToStr(Deaths
), gStdFont
, r
, g
, 0, 1);
1414 procedure g_Game_Init();
1417 knownFiles
: array of AnsiString
= nil;
1419 wext
, s
: AnsiString
;
1424 gTempDelete
:= False;
1426 sfsGCDisable(); // temporary disable removing of temporary volumes
1429 TEXTUREFILTER
:= GL_LINEAR
;
1430 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD
+':TEXTURES\TITLE');
1431 g_Texture_CreateWADEx('INTER', GameWAD
+':TEXTURES\INTER');
1432 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD
+':TEXTURES\ENDGAME_EN');
1433 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD
+':TEXTURES\ENDGAME_RU');
1434 TEXTUREFILTER
:= GL_NEAREST
;
1436 LoadStdFont('STDTXT', 'STDFONT', gStdFont
);
1437 LoadFont('MENUTXT', 'MENUFONT', gMenuFont
);
1438 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont
);
1440 g_Game_ClearLoading();
1441 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION
]), 0, False);
1442 g_Game_SetLoadingText('', 0, False);
1444 g_Game_SetLoadingText(_lc
[I_LOAD_CONSOLE
], 0, False);
1447 g_Game_SetLoadingText(_lc
[I_LOAD_MODELS
], 0, False);
1448 g_PlayerModel_LoadData();
1450 // load models from all possible wad types, in all known directories
1451 // this does a loosy job (linear search, ooph!), but meh
1452 for wext
in wadExtensions
do
1454 for f
:= High(ModelDirs
) downto Low(ModelDirs
) do
1456 if (FindFirst(ModelDirs
[f
]+DirectorySeparator
+'*'+wext
, faAnyFile
, SR
) = 0) then
1460 for s
in knownFiles
do
1462 if (strEquCI1251(forceFilenameExt(SR
.Name
, ''), forceFilenameExt(ExtractFileName(s
), ''))) then
1470 SetLength(knownFiles
, length(knownFiles
)+1);
1471 knownFiles
[High(knownFiles
)] := ModelDirs
[f
]+DirectorySeparator
+SR
.Name
;
1473 until (FindNext(SR
) <> 0);
1479 if (length(knownFiles
) = 0) then raise Exception
.Create('no player models found!');
1481 if (length(knownFiles
) = 1) then e_LogWriteln('1 player model found.', TMsgType
.Notify
) else e_LogWritefln('%d player models found.', [Integer(length(knownFiles
))], TMsgType
.Notify
);
1482 for s
in knownFiles
do
1484 if not g_PlayerModel_Load(s
) then e_LogWritefln('Error loading model "%s"', [s
], TMsgType
.Warning
);
1488 gPauseMain
:= false;
1489 gPauseHolmes
:= false;
1492 {e_MouseInfo.Accel := 1.0;}
1494 g_Game_SetLoadingText(_lc
[I_LOAD_GAME_DATA
], 0, False);
1497 g_Game_SetLoadingText(_lc
[I_LOAD_MUSIC
], 0, False);
1498 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD
+':MUSIC\INTERMUS', True);
1499 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD
+':MUSIC\MENU', True);
1500 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD
+':MUSIC\ROUNDMUS', True, True);
1501 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD
+':MUSIC\ENDMUS', True);
1504 g_Game_SetLoadingText(_lc
[I_LOAD_MENUS
], 0, False);
1508 gMusic
:= TMusic
.Create();
1509 gMusic
.SetByName('MUSIC_MENU');
1512 gGameSettings
.WarmupTime
:= 30;
1514 gState
:= STATE_MENU
;
1516 SetLength(gEvents
, 6);
1517 gEvents
[0].Name
:= 'ongamestart';
1518 gEvents
[1].Name
:= 'ongameend';
1519 gEvents
[2].Name
:= 'onmapstart';
1520 gEvents
[3].Name
:= 'onmapend';
1521 gEvents
[4].Name
:= 'oninter';
1522 gEvents
[5].Name
:= 'onwadend';
1524 sfsGCEnable(); // enable releasing unused volumes
1528 procedure g_Game_Free(freeTextures
: Boolean=true);
1530 e_WriteLog('g_Game_Free: completion of the gameplay', TMsgType
.Notify
);
1531 if NetMode
= NET_CLIENT
then g_Net_Disconnect();
1532 if NetMode
= NET_SERVER
then g_Net_Host_Die();
1534 g_Map_Free(freeTextures
);
1536 g_Player_RemoveAllCorpses();
1538 gGameSettings
.GameType
:= GT_NONE
;
1539 if gGameSettings
.GameMode
= GM_SINGLE
then
1540 gGameSettings
.GameMode
:= GM_DM
;
1541 gSwitchGameMode
:= gGameSettings
.GameMode
;
1544 gExitByTrigger
:= False;
1547 function IsActivePlayer(p
: TPlayer
): Boolean;
1552 Result
:= (not p
.FDummy
) and (not p
.FSpectator
);
1555 function GetActivePlayer_ByID(ID
: Integer): TPlayer
;
1562 if gPlayers
= nil then
1564 for a
:= Low(gPlayers
) to High(gPlayers
) do
1565 if IsActivePlayer(gPlayers
[a
]) then
1567 if gPlayers
[a
].UID
<> ID
then
1569 Result
:= gPlayers
[a
];
1574 function GetActivePlayerID_Next(Skip
: Integer = -1): Integer;
1580 if gPlayers
= nil then
1584 for a
:= Low(gPlayers
) to High(gPlayers
) do
1585 if IsActivePlayer(gPlayers
[a
]) then
1587 SetLength(ids
, Length(ids
) + 1);
1588 ids
[High(ids
)] := gPlayers
[a
].UID
;
1589 if gPlayers
[a
].UID
= Skip
then
1592 if Length(ids
) = 0 then
1597 Result
:= ids
[(idx
+ 1) mod Length(ids
)];
1600 function GetActivePlayerID_Prev(Skip
: Integer = -1): Integer;
1606 if gPlayers
= nil then
1610 for a
:= Low(gPlayers
) to High(gPlayers
) do
1611 if IsActivePlayer(gPlayers
[a
]) then
1613 SetLength(ids
, Length(ids
) + 1);
1614 ids
[High(ids
)] := gPlayers
[a
].UID
;
1615 if gPlayers
[a
].UID
= Skip
then
1618 if Length(ids
) = 0 then
1621 Result
:= ids
[Length(ids
) - 1]
1623 Result
:= ids
[(Length(ids
) - 1 + idx
) mod Length(ids
)];
1626 function GetActivePlayerID_Random(Skip
: Integer = -1): Integer;
1632 if gPlayers
= nil then
1636 for a
:= Low(gPlayers
) to High(gPlayers
) do
1637 if IsActivePlayer(gPlayers
[a
]) then
1639 SetLength(ids
, Length(ids
) + 1);
1640 ids
[High(ids
)] := gPlayers
[a
].UID
;
1641 if gPlayers
[a
].UID
= Skip
then
1644 if Length(ids
) = 0 then
1646 if Length(ids
) = 1 then
1651 Result
:= ids
[Random(Length(ids
))];
1653 while (idx
<> -1) and (Result
= Skip
) and (a
> 0) do
1655 Result
:= ids
[Random(Length(ids
))];
1660 function GetRandomSpectMode(Current
: Byte): Byte;
1667 0: Result
:= SPECT_STATS
;
1668 1: Result
:= SPECT_MAPVIEW
;
1669 2: Result
:= SPECT_MAPVIEW
;
1670 3: Result
:= SPECT_PLAYERS
;
1671 4: Result
:= SPECT_PLAYERS
;
1672 5: Result
:= SPECT_PLAYERS
;
1673 6: Result
:= SPECT_PLAYERS
;
1675 if (Current
in [SPECT_STATS
, SPECT_MAPVIEW
]) and (Current
= Result
) then
1679 procedure ProcessPlayerControls (plr
: TPlayer
; p
: Integer; var MoveButton
: Byte);
1685 if (plr
= nil) then exit
;
1686 if (p
= 2) then time
:= 1000 else time
:= 1;
1687 strafeDir
:= MoveButton
shr 4;
1688 MoveButton
:= MoveButton
and $0F;
1690 if gPlayerAction
[p
, ACTION_MOVELEFT
] and (not gPlayerAction
[p
, ACTION_MOVERIGHT
]) then
1691 MoveButton
:= 1 // Íàæàòà òîëüêî "Âëåâî"
1692 else if (not gPlayerAction
[p
, ACTION_MOVELEFT
]) and gPlayerAction
[p
, ACTION_MOVERIGHT
] then
1693 MoveButton
:= 2 // Íàæàòà òîëüêî "Âïðàâî"
1694 else if (not gPlayerAction
[p
, ACTION_MOVELEFT
]) and (not gPlayerAction
[p
, ACTION_MOVERIGHT
]) then
1695 MoveButton
:= 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1697 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1698 if MoveButton
= 1 then
1699 plr
.PressKey(KEY_LEFT
, time
)
1700 else if MoveButton
= 2 then
1701 plr
.PressKey(KEY_RIGHT
, time
);
1703 // if we have "strafe" key, turn off old strafe mechanics
1704 if gPlayerAction
[p
, ACTION_STRAFE
] then
1706 // new strafe mechanics
1707 if (strafeDir
= 0) then
1708 strafeDir
:= MoveButton
; // start strafing
1709 // now set direction according to strafe (reversed)
1710 if (strafeDir
= 2) then
1711 plr
.SetDirection(TDirection
.D_LEFT
)
1712 else if (strafeDir
= 1) then
1713 plr
.SetDirection(TDirection
.D_RIGHT
)
1717 strafeDir
:= 0; // not strafing anymore
1718 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1719 if (MoveButton
= 2) and gPlayerAction
[p
, ACTION_MOVELEFT
] then
1720 plr
.SetDirection(TDirection
.D_LEFT
)
1721 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1722 else if (MoveButton
= 1) and gPlayerAction
[p
, ACTION_MOVERIGHT
] then
1723 plr
.SetDirection(TDirection
.D_RIGHT
)
1724 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1725 else if MoveButton
<> 0 then
1726 plr
.SetDirection(TDirection(MoveButton
-1))
1729 // fix movebutton state
1730 MoveButton
:= MoveButton
or (strafeDir
shl 4);
1732 // Îñòàëüíûå êëàâèøè:
1733 if gPlayerAction
[p
, ACTION_JUMP
] then plr
.PressKey(KEY_JUMP
, time
);
1734 if gPlayerAction
[p
, ACTION_LOOKUP
] then plr
.PressKey(KEY_UP
, time
);
1735 if gPlayerAction
[p
, ACTION_LOOKDOWN
] then plr
.PressKey(KEY_DOWN
, time
);
1736 if gPlayerAction
[p
, ACTION_ATTACK
] then plr
.PressKey(KEY_FIRE
);
1737 if gPlayerAction
[p
, ACTION_ACTIVATE
] then plr
.PressKey(KEY_OPEN
);
1739 for i
:= WP_FACT
to WP_LACT
do
1741 if gWeaponAction
[p
, i
] then
1743 plr
.ProcessWeaponAction(i
);
1744 gWeaponAction
[p
, i
] := False
1748 for i
:= WP_FIRST
to WP_LAST
do
1750 if gSelectWeapon
[p
, i
] then
1752 plr
.QueueWeaponSwitch(i
); // all choices are passed there, and god will take the best
1753 gSelectWeapon
[p
, i
] := False
1757 // HACK: add dynlight here
1758 if gwin_k8_enable_light_experiments
then
1760 if e_KeyPressed(IK_F8
) and gGameOn
and (not gConsoleShow
) and (g_ActiveWindow
= nil) then
1762 g_playerLight
:= true;
1764 if e_KeyPressed(IK_F9
) and gGameOn
and (not gConsoleShow
) and (g_ActiveWindow
= nil) then
1766 g_playerLight
:= false;
1770 if gwin_has_stencil
and g_playerLight
then g_AddDynLight(plr
.GameX
+32, plr
.GameY
+40, 128, 1, 1, 0, 0.6);
1773 // HACK: don't have a "key was pressed" function
1774 procedure InterReady();
1776 if InterReadyTime
> gTime
then Exit
;
1777 InterReadyTime
:= gTime
+ 3000;
1778 MC_SEND_CheatRequest(NET_CHEAT_READY
);
1781 procedure g_Game_PreUpdate();
1783 // these are in separate PreUpdate functions because they can interact during Update()
1784 // and are synced over the net
1785 // we don't care that much about corpses and gibs
1786 g_Player_PreUpdate();
1787 g_Monsters_PreUpdate();
1788 g_Items_PreUpdate();
1789 g_Weapon_PreUpdate();
1792 procedure g_Game_Update();
1794 Msg
: g_gui
.TMessage
;
1800 function sendMonsPos (mon
: TMonster
): Boolean;
1802 result
:= false; // don't stop
1803 // this will also reset "need-send" flag
1804 if mon
.gncNeedSend
then
1806 MH_SEND_MonsterPos(mon
.UID
);
1808 else if (mon
.MonsterType
= MONSTER_BARREL
) then
1810 if (mon
.GameVelX
<> 0) or (mon
.GameVelY
<> 0) then MH_SEND_MonsterPos(mon
.UID
);
1812 else if (mon
.MonsterState
<> MONSTATE_SLEEP
) then
1814 if (mon
.MonsterState
<> MONSTATE_DEAD
) or (mon
.GameVelX
<> 0) or (mon
.GameVelY
<> 0) then MH_SEND_MonsterPos(mon
.UID
);
1818 function sendMonsPosUnexpected (mon
: TMonster
): Boolean;
1820 result
:= false; // don't stop
1821 // this will also reset "need-send" flag
1822 if mon
.gncNeedSend
then MH_SEND_MonsterPos(mon
.UID
);
1825 function sendItemPos (it
: PItem
): Boolean;
1827 result
:= false; // don't stop
1830 MH_SEND_ItemPos(it
.myId
);
1831 it
.needSend
:= False;
1836 reliableUpdate
: Boolean;
1841 // Ïîðà âûêëþ÷àòü èãðó:
1842 if gExit
= EXIT_QUIT
then
1844 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1848 if gExit
= EXIT_QUIT
then
1852 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî
1853 // no need to, as we'll do it in event handler
1855 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1858 if (NetMode
= NET_NONE
) and (g_Game_IsNet
) and (gGameOn
or (gState
in [STATE_FOLD
, STATE_INTERCUSTOM
])) then
1860 gExit
:= EXIT_SIMPLE
;
1865 // process master server communications
1866 g_Net_Slist_Pulse();
1869 STATE_INTERSINGLE
, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1870 STATE_INTERCUSTOM
, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1871 STATE_INTERTEXT
, // Òåêñò ìåæäó óðîâíÿìè
1872 STATE_INTERPIC
: // Êàðòèíêà ìåæäó óðîâíÿìè
1874 if g_Game_IsNet
and g_Game_IsServer
then
1876 gInterTime
:= gInterTime
+ GAME_TICK
;
1877 a
:= Min((gInterEndTime
- gInterTime
) div 1000 + 1, 255);
1878 if a
<> gServInterTime
then
1880 gServInterTime
:= a
;
1881 MH_SEND_TimeSync(gServInterTime
);
1885 if (not g_Game_IsClient
) and
1889 e_KeyPressed(IK_RETURN
) or e_KeyPressed(IK_KPRETURN
) or e_KeyPressed(IK_SPACE
) or
1890 e_KeyPressed(VK_FIRE
) or e_KeyPressed(VK_OPEN
) or
1891 e_KeyPressed(JOY0_ATTACK
) or e_KeyPressed(JOY1_ATTACK
) or
1892 e_KeyPressed(JOY2_ATTACK
) or e_KeyPressed(JOY3_ATTACK
)
1894 and (not gJustChatted
) and (not gConsoleShow
) and (not gChatShow
)
1895 and (g_ActiveWindow
= nil)
1897 or (g_Game_IsNet
and ((gInterTime
> gInterEndTime
) or ((gInterReadyCount
>= NetClientCount
) and (NetClientCount
> 0))))
1900 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1901 g_Game_StopAllSounds(True);
1903 if gMapOnce
then // Ýòî áûë òåñò
1904 gExit
:= EXIT_SIMPLE
1906 if gNextMap
<> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1907 g_Game_ChangeMap(gNextMap
)
1908 else // Ñëåäóþùåé êàðòû íåò
1910 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
] then
1912 // Âûõîä â ãëàâíîå ìåíþ:
1914 g_GUI_ShowWindow('MainMenu');
1915 gMusic
.SetByName('MUSIC_MENU');
1917 gState
:= STATE_MENU
;
1920 // Ôèíàëüíàÿ êàðòèíêà:
1921 g_Game_ExecuteEvent('onwadend');
1923 if not gMusic
.SetByName('MUSIC_endmus') then
1924 gMusic
.SetByName('MUSIC_STDENDMUS');
1926 gState
:= STATE_ENDPIC
;
1928 g_Game_ExecuteEvent('ongameend');
1933 else if g_Game_IsClient
and
1936 e_KeyPressed(IK_RETURN
) or e_KeyPressed(IK_KPRETURN
) or e_KeyPressed(IK_SPACE
) or
1937 e_KeyPressed(VK_FIRE
) or e_KeyPressed(VK_OPEN
) or
1938 e_KeyPressed(JOY0_ATTACK
) or e_KeyPressed(JOY1_ATTACK
) or
1939 e_KeyPressed(JOY2_ATTACK
) or e_KeyPressed(JOY3_ATTACK
)
1941 and (not gJustChatted
) and (not gConsoleShow
) and (not gChatShow
)
1942 and (g_ActiveWindow
= nil)
1950 if gState
= STATE_INTERTEXT
then
1951 if InterText
.counter
> 0 then
1952 InterText
.counter
:= InterText
.counter
- 1;
1955 STATE_FOLD
: // Çàòóõàíèå ýêðàíà
1957 if EndingGameCounter
= 0 then
1959 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1960 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
] then
1962 gState
:= STATE_INTERCUSTOM
;
1963 InterReadyTime
:= -1;
1964 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then
1966 g_Game_ExecuteEvent('onwadend');
1967 if not gMusic
.SetByName('MUSIC_endmus') then
1968 gMusic
.SetByName('MUSIC_STDENDMUS');
1971 gMusic
.SetByName('MUSIC_ROUNDMUS');
1975 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1977 gMusic
.SetByName('MUSIC_INTERMUS');
1979 gState
:= STATE_INTERSINGLE
;
1982 g_Game_ExecuteEvent('oninter');
1985 DecMin(EndingGameCounter
, 6, 0);
1988 STATE_ENDPIC
: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1990 if gMapOnce
then // Ýòî áûë òåñò
1992 gExit
:= EXIT_SIMPLE
;
1998 g_Serverlist_Control(slCurrent
, slTable
);
2001 // Ñòàòèñòèêà ïî Tab:
2003 IsDrawStat
:= (not gConsoleShow
) and (not gChatShow
) and (gGameSettings
.GameType
<> GT_SINGLE
) and g_Console_Action(ACTION_SCORES
);
2006 if gGameOn
and not gPause
and (gState
<> STATE_FOLD
) then
2008 // Âðåìÿ += 28 ìèëëèñåêóíä:
2009 gTime
:= gTime
+ GAME_TICK
;
2011 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
2012 if MessageTime
= 0 then
2014 if MessageTime
> 0 then
2015 MessageTime
:= MessageTime
- 1;
2017 if (g_Game_IsServer
) then
2019 // Áûë çàäàí ëèìèò âðåìåíè:
2020 if (gGameSettings
.TimeLimit
> 0) then
2021 if (gTime
- gGameStartTime
) div 1000 >= gGameSettings
.TimeLimit
then
2022 begin // Îí ïðîøåë => êîíåö óðîâíÿ
2027 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
2028 if (gLMSRespawn
> LMS_RESPAWN_NONE
) and (gLMSRespawnTime
< gTime
) then
2029 g_Game_RestartRound(gLMSSoftSpawn
);
2031 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
2032 if gVoteInProgress
and (gVoteTimer
< gTime
) then
2034 else if gVotePassed
and (gVoteCmdTimer
< gTime
) then
2036 g_Console_Process(gVoteCommand
);
2038 gVotePassed
:= False;
2041 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
2042 if gFlags
[FLAG_RED
].State
= FLAG_STATE_CAPTURED
then
2043 gFlags
[FLAG_RED
].CaptureTime
:= gFlags
[FLAG_RED
].CaptureTime
+ GAME_TICK
;
2044 if gFlags
[FLAG_BLUE
].State
= FLAG_STATE_CAPTURED
then
2045 gFlags
[FLAG_BLUE
].CaptureTime
:= gFlags
[FLAG_BLUE
].CaptureTime
+ GAME_TICK
;
2047 // Áûë çàäàí ëèìèò ïîáåä:
2048 if (gGameSettings
.ScoreLimit
> 0) then
2052 if gGameSettings
.GameMode
= GM_DM
then
2053 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
2054 for i
:= 0 to High(gPlayers
) do
2055 if gPlayers
[i
] <> nil then
2056 if gPlayers
[i
].Frags
> b
then
2057 b
:= gPlayers
[i
].Frags
;
2060 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
2061 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
2062 b
:= Max(gTeamStat
[TEAM_RED
].Score
, gTeamStat
[TEAM_BLUE
].Score
);
2065 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
2066 if b
>= gGameSettings
.ScoreLimit
then
2073 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
2074 if gPlayer1
<> nil then gPlayer1
.ReleaseKeys();
2075 if gPlayer2
<> nil then gPlayer2
.ReleaseKeys();
2076 if (not gConsoleShow
) and (not gChatShow
) and (g_ActiveWindow
= nil) then
2078 ProcessPlayerControls(gPlayer1
, 0, P1MoveButton
);
2079 ProcessPlayerControls(gPlayer2
, 1, P2MoveButton
);
2080 end // if not console
2083 if g_Game_IsNet
and (gPlayer1
<> nil) then gPlayer1
.PressKey(KEY_CHAT
, 10000);
2085 // process weapon switch queue
2089 if (gPlayer1
= nil) and (gPlayer2
= nil) and
2090 (not gConsoleShow
) and (not gChatShow
) and (g_ActiveWindow
= nil) then
2092 if not gSpectKeyPress
then
2094 if gPlayerAction
[0, ACTION_JUMP
] and (not gSpectAuto
) then
2096 // switch spect mode
2098 SPECT_NONE
: ; // not spectator
2100 SPECT_MAPVIEW
: Inc(gSpectMode
);
2101 SPECT_PLAYERS
: gSpectMode
:= SPECT_STATS
; // reset to 1
2103 gSpectKeyPress
:= True;
2105 if (gSpectMode
= SPECT_MAPVIEW
)
2106 and (not gSpectAuto
) then
2108 if gPlayerAction
[0, ACTION_MOVELEFT
] then
2109 gSpectX
:= Max(gSpectX
- gSpectStep
, 0);
2110 if gPlayerAction
[0, ACTION_MOVERIGHT
] then
2111 gSpectX
:= Min(gSpectX
+ gSpectStep
, gMapInfo
.Width
- gScreenWidth
);
2112 if gPlayerAction
[0, ACTION_LOOKUP
] then
2113 gSpectY
:= Max(gSpectY
- gSpectStep
, 0);
2114 if gPlayerAction
[0, ACTION_LOOKDOWN
] then
2115 gSpectY
:= Min(gSpectY
+ gSpectStep
, gMapInfo
.Height
- gScreenHeight
);
2116 if gWeaponAction
[0, WP_PREV
] then
2119 if gSpectStep
> 4 then gSpectStep
:= gSpectStep
shr 1;
2120 gWeaponAction
[0, WP_PREV
] := False;
2122 if gWeaponAction
[0, WP_NEXT
] then
2125 if gSpectStep
< 64 then gSpectStep
:= gSpectStep
shl 1;
2126 gWeaponAction
[0, WP_NEXT
] := False;
2129 if (gSpectMode
= SPECT_PLAYERS
)
2130 and (not gSpectAuto
) then
2132 if gPlayerAction
[0, ACTION_LOOKUP
] then
2135 gSpectViewTwo
:= True;
2136 gSpectKeyPress
:= True;
2138 if gPlayerAction
[0, ACTION_LOOKDOWN
] then
2140 // remove second view
2141 gSpectViewTwo
:= False;
2142 gSpectKeyPress
:= True;
2144 if gPlayerAction
[0, ACTION_MOVELEFT
] then
2146 // prev player (view 1)
2147 gSpectPID1
:= GetActivePlayerID_Prev(gSpectPID1
);
2148 gSpectKeyPress
:= True;
2150 if gPlayerAction
[0, ACTION_MOVERIGHT
] then
2152 // next player (view 1)
2153 gSpectPID1
:= GetActivePlayerID_Next(gSpectPID1
);
2154 gSpectKeyPress
:= True;
2156 if gWeaponAction
[0, WP_PREV
] then
2158 // prev player (view 2)
2159 gSpectPID2
:= GetActivePlayerID_Prev(gSpectPID2
);
2160 gWeaponAction
[0, WP_PREV
] := False;
2162 if gWeaponAction
[0, WP_NEXT
] then
2164 // next player (view 2)
2165 gSpectPID2
:= GetActivePlayerID_Next(gSpectPID2
);
2166 gWeaponAction
[0, WP_NEXT
] := False;
2169 if gPlayerAction
[0, ACTION_ATTACK
] then
2171 if (gSpectMode
= SPECT_STATS
) and (not gSpectAuto
) then
2174 gSpectAutoNext
:= 0;
2175 gSpectViewTwo
:= False;
2176 gSpectKeyPress
:= True;
2181 gSpectMode
:= SPECT_STATS
;
2182 gSpectAuto
:= False;
2183 gSpectKeyPress
:= True;
2188 if (not gPlayerAction
[0, ACTION_JUMP
]) and
2189 (not gPlayerAction
[0, ACTION_ATTACK
]) and
2190 (not gPlayerAction
[0, ACTION_MOVELEFT
]) and
2191 (not gPlayerAction
[0, ACTION_MOVERIGHT
]) and
2192 (not gPlayerAction
[0, ACTION_LOOKUP
]) and
2193 (not gPlayerAction
[0, ACTION_LOOKDOWN
]) then
2194 gSpectKeyPress
:= False;
2198 if gSpectMode
= SPECT_MAPVIEW
then
2200 i
:= Min(Max(gSpectX
+ gSpectAutoStepX
, 0), gMapInfo
.Width
- gScreenWidth
);
2202 gSpectAutoNext
:= gTime
2205 i
:= Min(Max(gSpectY
+ gSpectAutoStepY
, 0), gMapInfo
.Height
- gScreenHeight
);
2207 gSpectAutoNext
:= gTime
2211 if gSpectAutoNext
<= gTime
then
2213 if gSpectAutoNext
> 0 then
2215 gSpectMode
:= GetRandomSpectMode(gSpectMode
);
2219 gSpectX
:= Random(gMapInfo
.Width
- gScreenWidth
);
2220 gSpectY
:= Random(gMapInfo
.Height
- gScreenHeight
);
2221 gSpectAutoStepX
:= Random(9) - 4;
2222 gSpectAutoStepY
:= Random(9) - 4;
2223 if ((gSpectX
< 800) and (gSpectAutoStepX
< 0)) or
2224 ((gSpectX
> gMapInfo
.Width
- gScreenWidth
- 800) and (gSpectAutoStepX
> 0)) then
2225 gSpectAutoStepX
:= gSpectAutoStepX
* -1;
2226 if ((gSpectY
< 800) and (gSpectAutoStepY
< 0)) or
2227 ((gSpectY
> gMapInfo
.Height
- gScreenHeight
- 800) and (gSpectAutoStepY
> 0)) then
2228 gSpectAutoStepY
:= gSpectAutoStepY
* -1;
2232 gSpectPID1
:= GetActivePlayerID_Random(gSpectPID1
);
2237 SPECT_STATS
: gSpectAutoNext
:= gTime
+ (Random(3) + 5) * 1000;
2238 SPECT_MAPVIEW
: gSpectAutoNext
:= gTime
+ (Random(4) + 7) * 1000;
2239 SPECT_PLAYERS
: gSpectAutoNext
:= gTime
+ (Random(7) + 8) * 1000;
2245 // Îáíîâëÿåì âñå îñòàëüíîå:
2248 g_Triggers_Update();
2250 g_Monsters_Update();
2252 g_Player_UpdateAll();
2253 g_Player_UpdatePhysicalObjects();
2255 // server: send newly spawned monsters unconditionally
2256 if (gGameSettings
.GameType
= GT_SERVER
) then
2258 if (Length(gMonstersSpawned
) > 0) then
2260 for I
:= 0 to High(gMonstersSpawned
) do MH_SEND_MonsterSpawn(gMonstersSpawned
[I
]);
2261 SetLength(gMonstersSpawned
, 0);
2265 if (gSoundTriggerTime
> 8) then
2267 g_Game_UpdateTriggerSounds();
2268 gSoundTriggerTime
:= 0;
2272 Inc(gSoundTriggerTime
);
2275 if (NetMode
= NET_SERVER
) then
2277 Inc(NetTimeToUpdate
);
2278 Inc(NetTimeToReliable
);
2280 // send monster updates
2281 if (NetTimeToReliable
>= NetRelupdRate
) or (NetTimeToUpdate
>= NetUpdateRate
) then
2283 // send all monsters (periodic sync)
2284 reliableUpdate
:= (NetTimeToReliable
>= NetRelupdRate
);
2286 for I
:= 0 to High(gPlayers
) do
2288 if (gPlayers
[I
] <> nil) then MH_SEND_PlayerPos(reliableUpdate
, gPlayers
[I
].UID
);
2291 g_Mons_ForEach(sendMonsPos
);
2293 // update flags that aren't stationary
2294 if gGameSettings
.GameMode
= GM_CTF
then
2295 for I
:= FLAG_RED
to FLAG_BLUE
do
2296 if gFlags
[I
].NeedSend
then
2298 gFlags
[I
].NeedSend
:= False;
2302 // update items that aren't stationary
2303 g_Items_ForEachAlive(sendItemPos
);
2305 if reliableUpdate
then
2307 NetTimeToReliable
:= 0;
2308 NetTimeToUpdate
:= NetUpdateRate
;
2312 NetTimeToUpdate
:= 0;
2317 // send only mosters with some unexpected changes
2318 g_Mons_ForEach(sendMonsPosUnexpected
);
2321 // send unexpected platform changes
2322 g_Map_NetSendInterestingPanels();
2324 g_Net_Slist_ServerUpdate();
2326 if NetUseMaster then
2328 if (gTime >= NetTimeToMaster) or g_Net_Slist_IsConnectionInProgress then
2330 if (not g_Net_Slist_IsConnectionActive) then g_Net_Slist_Connect(false); // non-blocking connection to the master
2332 NetTimeToMaster := gTime + NetMasterRate;
2337 else if (NetMode
= NET_CLIENT
) then
2339 MC_SEND_PlayerPos();
2341 end; // if gameOn ...
2343 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
2344 if g_ActiveWindow
<> nil then
2346 w
:= e_GetFirstKeyPressed();
2348 if (w
<> IK_INVALID
) then
2350 Msg
.Msg
:= MESSAGE_DIKEY
;
2352 g_ActiveWindow
.OnMessage(Msg
);
2355 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
2356 if g_ActiveWindow
<> nil then
2357 g_ActiveWindow
.Update();
2359 // Íóæíî ñìåíèòü ðàçðåøåíèå:
2360 if gResolutionChange
then
2362 e_WriteLog('Changing resolution', TMsgType
.Notify
);
2363 g_Game_ChangeResolution(gRC_Width
, gRC_Height
, gRC_FullScreen
, gRC_Maximized
);
2364 gResolutionChange
:= False;
2365 g_ActiveWindow
:= nil;
2368 // Íóæíî ñìåíèòü ÿçûê:
2369 if gLanguageChange
then
2371 //e_WriteLog('Read language file', MSG_NOTIFY);
2372 //g_Language_Load(DataDir + gLanguage + '.txt');
2373 g_Language_Set(gLanguage
);
2377 gLanguageChange
:= False;
2381 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
2382 if e_KeyPressed(IK_F10
) and
2384 (not gConsoleShow
) and
2385 (g_ActiveWindow
= nil) then
2390 Time
:= sys_GetTicks() {div 1000};
2392 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
2393 if gDelayedEvents
<> nil then
2394 for a
:= 0 to High(gDelayedEvents
) do
2395 if gDelayedEvents
[a
].Pending
and
2397 ((gDelayedEvents
[a
].DEType
= DE_GLOBEVENT
) and (gDelayedEvents
[a
].Time
<= Time
)) or
2398 ((gDelayedEvents
[a
].DEType
> DE_GLOBEVENT
) and (gDelayedEvents
[a
].Time
<= gTime
))
2401 case gDelayedEvents
[a
].DEType
of
2403 g_Game_ExecuteEvent(gDelayedEvents
[a
].DEStr
);
2406 g_Game_Announce_GoodShot(gDelayedEvents
[a
].DENum
);
2410 g_Game_Announce_KillCombo(gDelayedEvents
[a
].DENum
);
2411 if g_Game_IsNet
and g_Game_IsServer
then
2412 MH_SEND_GameEvent(NET_EV_KILLCOMBO
, gDelayedEvents
[a
].DENum
);
2416 g_Game_Announce_BodyKill(gDelayedEvents
[a
].DENum
);
2418 gDelayedEvents
[a
].Pending
:= False;
2421 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2422 UPSCounter
:= UPSCounter
+ 1;
2423 if Time
- UPSTime
>= 1000 then
2432 g_Weapon_AddDynLights();
2433 g_Items_AddDynLights();
2437 procedure g_Game_LoadChatSounds(Resource
: string);
2440 FileName
, Snd
: string;
2442 len
, cnt
, tags
, i
, j
: Integer;
2445 FileName
:= g_ExtractWadName(Resource
);
2447 WAD
:= TWADFile
.Create();
2448 WAD
.ReadFile(FileName
);
2450 if not WAD
.GetResource(g_ExtractFilePathName(Resource
), p
, len
) then
2457 cfg
:= TConfig
.CreateMem(p
, len
);
2458 cnt
:= cfg
.ReadInt('ChatSounds', 'Count', 0);
2460 SetLength(gChatSounds
, cnt
);
2461 for i
:= 0 to Length(gChatSounds
) - 1 do
2463 gChatSounds
[i
].Sound
:= nil;
2464 Snd
:= Trim(cfg
.ReadStr(IntToStr(i
), 'Sound', ''));
2465 tags
:= cfg
.ReadInt(IntToStr(i
), 'Tags', 0);
2466 if (Snd
= '') or (Tags
<= 0) then
2468 g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i
), GameWAD
+':'+Snd
);
2469 gChatSounds
[i
].Sound
:= TPlayableSound
.Create();
2470 gChatSounds
[i
].Sound
.SetByName('SOUND_CHAT_MACRO' + IntToStr(i
));
2471 SetLength(gChatSounds
[i
].Tags
, tags
);
2472 for j
:= 0 to tags
- 1 do
2473 gChatSounds
[i
].Tags
[j
] := toLowerCase1251(cfg
.ReadStr(IntToStr(i
), 'Tag' + IntToStr(j
), ''));
2474 gChatSounds
[i
].FullWord
:= cfg
.ReadBool(IntToStr(i
), 'FullWord', False);
2481 procedure g_Game_FreeChatSounds();
2485 for i
:= 0 to Length(gChatSounds
) - 1 do
2487 gChatSounds
[i
].Sound
.Free();
2488 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i
));
2490 SetLength(gChatSounds
, 0);
2494 procedure g_Game_LoadData();
2501 if DataLoaded
then Exit
;
2503 e_WriteLog('Loading game data...', TMsgType
.Notify
);
2505 g_Texture_CreateWADEx('NOTEXTURE', GameWAD
+':TEXTURES\NOTEXTURE');
2506 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD
+':TEXTURES\HUD');
2507 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD
+':TEXTURES\AIRBAR');
2508 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD
+':TEXTURES\JETBAR');
2509 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD
+':TEXTURES\HUDBG');
2510 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD
+':TEXTURES\ARMORHUD');
2511 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD
+':TEXTURES\FLAGHUD_R_BASE');
2512 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD
+':TEXTURES\FLAGHUD_R_STOLEN');
2513 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD
+':TEXTURES\FLAGHUD_R_DROP');
2514 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD
+':TEXTURES\FLAGHUD_B_BASE');
2515 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD
+':TEXTURES\FLAGHUD_B_STOLEN');
2516 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD
+':TEXTURES\FLAGHUD_B_DROP');
2517 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD
+':TEXTURES\TALKBUBBLE');
2518 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD
+':TEXTURES\PENTA');
2519 g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD
+':TEXTURES\PLRIND');
2522 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD
+':TEXTURES\LLEFT') then hasPBarGfx
:= false;
2523 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD
+':TEXTURES\LMARKER') then hasPBarGfx
:= false;
2524 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD
+':TEXTURES\LMIDDLE') then hasPBarGfx
:= false;
2525 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD
+':TEXTURES\LRIGHT') then hasPBarGfx
:= false;
2529 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl
, hl
);
2530 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr
, hr
);
2531 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb
, hb
);
2532 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm
, hm
);
2533 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
2539 hasPBarGfx
:= false;
2543 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD
+':TEXTURES\TELEPORT', 64, 64, 10, False);
2544 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD
+':WEAPONS\PUNCH', 64, 64, 4, False);
2545 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD
+':WEAPONS\PUNCH_UP', 64, 64, 4, False);
2546 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD
+':WEAPONS\PUNCH_DN', 64, 64, 4, False);
2547 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD
+':WEAPONS\PUNCHB', 64, 64, 4, False);
2548 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD
+':WEAPONS\PUNCHB_UP', 64, 64, 4, False);
2549 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD
+':WEAPONS\PUNCHB_DN', 64, 64, 4, False);
2550 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD
+':SOUNDS\TELEPORT');
2551 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD
+':SOUNDS\NOTELEPORT');
2552 g_Sound_CreateWADEx('SOUND_GAME_SECRET', GameWAD
+':SOUNDS\SECRET');
2553 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD
+':SOUNDS\DOOROPEN');
2554 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD
+':SOUNDS\DOORCLOSE');
2555 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD
+':SOUNDS\BULK1');
2556 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD
+':SOUNDS\BULK2');
2557 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD
+':SOUNDS\BUBBLE1');
2558 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD
+':SOUNDS\BUBBLE2');
2559 g_Sound_CreateWADEx('SOUND_GAME_BURNING', GameWAD
+':SOUNDS\BURNING');
2560 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD
+':SOUNDS\SWITCH1');
2561 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD
+':SOUNDS\SWITCH0');
2562 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD
+':SOUNDS\RADIO');
2563 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD
+':SOUNDS\GOOD1');
2564 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD
+':SOUNDS\GOOD2');
2565 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD
+':SOUNDS\GOOD3');
2566 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD
+':SOUNDS\GOOD4');
2567 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD
+':SOUNDS\KILL2X');
2568 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD
+':SOUNDS\KILL3X');
2569 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD
+':SOUNDS\KILL4X');
2570 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD
+':SOUNDS\KILLMX');
2571 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD
+':SOUNDS\MUHAHA1');
2572 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD
+':SOUNDS\MUHAHA2');
2573 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD
+':SOUNDS\MUHAHA3');
2574 g_Sound_CreateWADEx('SOUND_CTF_GET1', GameWAD
+':SOUNDS\GETFLAG1');
2575 g_Sound_CreateWADEx('SOUND_CTF_GET2', GameWAD
+':SOUNDS\GETFLAG2');
2576 g_Sound_CreateWADEx('SOUND_CTF_LOST1', GameWAD
+':SOUNDS\LOSTFLG1');
2577 g_Sound_CreateWADEx('SOUND_CTF_LOST2', GameWAD
+':SOUNDS\LOSTFLG2');
2578 g_Sound_CreateWADEx('SOUND_CTF_RETURN1', GameWAD
+':SOUNDS\RETFLAG1');
2579 g_Sound_CreateWADEx('SOUND_CTF_RETURN2', GameWAD
+':SOUNDS\RETFLAG2');
2580 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE1', GameWAD
+':SOUNDS\CAPFLAG1');
2581 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE2', GameWAD
+':SOUNDS\CAPFLAG2');
2583 goodsnd
[0] := TPlayableSound
.Create();
2584 goodsnd
[1] := TPlayableSound
.Create();
2585 goodsnd
[2] := TPlayableSound
.Create();
2586 goodsnd
[3] := TPlayableSound
.Create();
2588 goodsnd
[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2589 goodsnd
[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2590 goodsnd
[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2591 goodsnd
[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2593 killsnd
[0] := TPlayableSound
.Create();
2594 killsnd
[1] := TPlayableSound
.Create();
2595 killsnd
[2] := TPlayableSound
.Create();
2596 killsnd
[3] := TPlayableSound
.Create();
2598 killsnd
[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2599 killsnd
[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2600 killsnd
[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2601 killsnd
[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2603 hahasnd
[0] := TPlayableSound
.Create();
2604 hahasnd
[1] := TPlayableSound
.Create();
2605 hahasnd
[2] := TPlayableSound
.Create();
2607 hahasnd
[0].SetByName('SOUND_ANNOUNCER_MUHAHA1');
2608 hahasnd
[1].SetByName('SOUND_ANNOUNCER_MUHAHA2');
2609 hahasnd
[2].SetByName('SOUND_ANNOUNCER_MUHAHA3');
2611 sound_get_flag
[0] := TPlayableSound
.Create();
2612 sound_get_flag
[1] := TPlayableSound
.Create();
2613 sound_lost_flag
[0] := TPlayableSound
.Create();
2614 sound_lost_flag
[1] := TPlayableSound
.Create();
2615 sound_ret_flag
[0] := TPlayableSound
.Create();
2616 sound_ret_flag
[1] := TPlayableSound
.Create();
2617 sound_cap_flag
[0] := TPlayableSound
.Create();
2618 sound_cap_flag
[1] := TPlayableSound
.Create();
2620 sound_get_flag
[0].SetByName('SOUND_CTF_GET1');
2621 sound_get_flag
[1].SetByName('SOUND_CTF_GET2');
2622 sound_lost_flag
[0].SetByName('SOUND_CTF_LOST1');
2623 sound_lost_flag
[1].SetByName('SOUND_CTF_LOST2');
2624 sound_ret_flag
[0].SetByName('SOUND_CTF_RETURN1');
2625 sound_ret_flag
[1].SetByName('SOUND_CTF_RETURN2');
2626 sound_cap_flag
[0].SetByName('SOUND_CTF_CAPTURE1');
2627 sound_cap_flag
[1].SetByName('SOUND_CTF_CAPTURE2');
2629 g_Game_LoadChatSounds(GameWAD
+':CHATSND\SNDCFG');
2631 g_Game_SetLoadingText(_lc
[I_LOAD_ITEMS_DATA
], 0, False);
2634 g_Game_SetLoadingText(_lc
[I_LOAD_WEAPONS_DATA
], 0, False);
2635 g_Weapon_LoadData();
2637 g_Monsters_LoadData();
2642 procedure g_Game_FreeData();
2644 if not DataLoaded
then Exit
;
2647 g_Weapon_FreeData();
2648 g_Monsters_FreeData();
2650 e_WriteLog('Releasing game data...', TMsgType
.Notify
);
2652 g_Texture_Delete('NOTEXTURE');
2653 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2654 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2655 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2656 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2657 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2658 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2659 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2660 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2661 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2662 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2663 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2664 g_Frames_DeleteByName('FRAMES_TELEPORT');
2665 g_Frames_DeleteByName('FRAMES_PUNCH');
2666 g_Frames_DeleteByName('FRAMES_PUNCH_UP');
2667 g_Frames_DeleteByName('FRAMES_PUNCH_DN');
2668 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK');
2669 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP');
2670 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
2671 g_Sound_Delete('SOUND_GAME_TELEPORT');
2672 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2673 g_Sound_Delete('SOUND_GAME_SECRET');
2674 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2675 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2676 g_Sound_Delete('SOUND_GAME_BULK1');
2677 g_Sound_Delete('SOUND_GAME_BULK2');
2678 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2679 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2680 g_Sound_Delete('SOUND_GAME_BURNING');
2681 g_Sound_Delete('SOUND_GAME_SWITCH1');
2682 g_Sound_Delete('SOUND_GAME_SWITCH0');
2689 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2690 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2691 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2692 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2699 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2700 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2701 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2702 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2708 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1');
2709 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2');
2710 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3');
2712 sound_get_flag
[0].Free();
2713 sound_get_flag
[1].Free();
2714 sound_lost_flag
[0].Free();
2715 sound_lost_flag
[1].Free();
2716 sound_ret_flag
[0].Free();
2717 sound_ret_flag
[1].Free();
2718 sound_cap_flag
[0].Free();
2719 sound_cap_flag
[1].Free();
2721 g_Sound_Delete('SOUND_CTF_GET1');
2722 g_Sound_Delete('SOUND_CTF_GET2');
2723 g_Sound_Delete('SOUND_CTF_LOST1');
2724 g_Sound_Delete('SOUND_CTF_LOST2');
2725 g_Sound_Delete('SOUND_CTF_RETURN1');
2726 g_Sound_Delete('SOUND_CTF_RETURN2');
2727 g_Sound_Delete('SOUND_CTF_CAPTURE1');
2728 g_Sound_Delete('SOUND_CTF_CAPTURE2');
2730 g_Game_FreeChatSounds();
2732 DataLoaded
:= False;
2735 procedure DrawCustomStat();
2741 ww2
, hh2
, r
, g
, b
, rr
, gg
, bb
: Byte;
2742 s1
, s2
, topstr
: String;
2744 e_TextureFontGetSize(gStdFont
, ww2
, hh2
);
2748 if g_Console_Action(ACTION_SCORES
) then
2750 if not gStatsPressed
then
2752 gStatsOff
:= not gStatsOff
;
2753 gStatsPressed
:= True;
2757 gStatsPressed
:= False;
2761 s1
:= _lc
[I_MENU_INTER_NOTICE_TAB
];
2762 w
:= (Length(s1
) * ww2
) div 2;
2763 x
:= gScreenWidth
div 2 - w
;
2765 e_TextureFontPrint(x
, y
, s1
, gStdFont
);
2769 if (gGameSettings
.GameMode
= GM_COOP
) then
2771 if gMissionFailed
then
2772 topstr
:= _lc
[I_MENU_INTER_MISSION_FAIL
]
2774 topstr
:= _lc
[I_MENU_INTER_LEVEL_COMPLETE
];
2777 topstr
:= _lc
[I_MENU_INTER_ROUND_OVER
];
2779 e_CharFont_GetSize(gMenuFont
, topstr
, ww1
, hh1
);
2780 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(ww1
div 2), 16, topstr
);
2782 if g_Game_IsNet
then
2784 topstr
:= Format(_lc
[I_MENU_INTER_NOTICE_TIME
], [gServInterTime
]);
2785 if not gChatShow
then
2786 e_TextureFontPrintEx((gScreenWidth
div 2)-(Length(topstr
)*ww2
div 2),
2787 gScreenHeight
-(hh2
+4)*2, topstr
, gStdFont
, 255, 255, 255, 1);
2790 if g_Game_IsClient
then
2791 topstr
:= _lc
[I_MENU_INTER_NOTICE_MAP
]
2793 topstr
:= _lc
[I_MENU_INTER_NOTICE_SPACE
];
2794 if not gChatShow
then
2795 e_TextureFontPrintEx((gScreenWidth
div 2)-(Length(topstr
)*ww2
div 2),
2796 gScreenHeight
-(hh2
+4), topstr
, gStdFont
, 255, 255, 255, 1);
2801 w
:= gScreenWidth
-x
*2;
2807 e_DrawFillQuad(x
, y
, gScreenWidth
-x
-1, gScreenHeight
-y
-1, 64, 64, 64, 32);
2808 e_DrawQuad(x
, y
, gScreenWidth
-x
-1, gScreenHeight
-y
-1, 255, 127, 0);
2810 m
:= Max(Length(_lc
[I_MENU_MAP
])+1, Length(_lc
[I_GAME_GAME_TIME
])+1)*ww2
;
2812 case CustomStat
.GameMode
of
2815 if gGameSettings
.MaxLives
= 0 then
2816 s1
:= _lc
[I_GAME_DM
]
2818 s1
:= _lc
[I_GAME_LMS
];
2822 if gGameSettings
.MaxLives
= 0 then
2823 s1
:= _lc
[I_GAME_TDM
]
2825 s1
:= _lc
[I_GAME_TLMS
];
2827 GM_CTF
: s1
:= _lc
[I_GAME_CTF
];
2830 if gGameSettings
.MaxLives
= 0 then
2831 s1
:= _lc
[I_GAME_COOP
]
2833 s1
:= _lc
[I_GAME_SURV
];
2839 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
2843 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_MENU_MAP
], gStdFont
, 255, 127, 0, 1);
2844 e_TextureFontPrint(x
+8+m
, _y
, Format('%s - %s', [CustomStat
.Map
, CustomStat
.MapName
]), gStdFont
);
2847 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_GAME_TIME
], gStdFont
, 255, 127, 0, 1);
2848 e_TextureFontPrint(x
+8+m
, _y
, Format('%d:%.2d:%.2d', [CustomStat
.GameTime
div 1000 div 3600,
2849 (CustomStat
.GameTime
div 1000 div 60) mod 60,
2850 CustomStat
.GameTime
div 1000 mod 60]), gStdFont
);
2852 pc
:= Length(CustomStat
.PlayerStat
);
2853 if pc
= 0 then Exit
;
2855 if CustomStat
.GameMode
= GM_COOP
then
2857 m
:= Max(Length(_lc
[I_GAME_MONSTERS
])+1, Length(_lc
[I_GAME_SECRETS
])+1)*ww2
;
2859 s2
:= _lc
[I_GAME_MONSTERS
];
2860 e_TextureFontPrintEx(x
+8, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2861 e_TextureFontPrintEx(x
+8+m
, _y
, IntToStr(gCoopMonstersKilled
) + '/' + IntToStr(gTotalMonsters
), gStdFont
, 255, 255, 255, 1);
2863 s2
:= _lc
[I_GAME_SECRETS
];
2864 e_TextureFontPrintEx(x
+8, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2865 e_TextureFontPrintEx(x
+8+m
, _y
, IntToStr(gCoopSecretsFound
) + '/' + IntToStr(gSecretsCount
), gStdFont
, 255, 255, 255, 1);
2868 m
:= Max(Length(_lc
[I_GAME_MONSTERS_TOTAL
])+1, Length(_lc
[I_GAME_SECRETS_TOTAL
])+1)*ww2
;
2870 s2
:= _lc
[I_GAME_MONSTERS_TOTAL
];
2871 e_TextureFontPrintEx(x
+250, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2872 e_TextureFontPrintEx(x
+250+m
, _y
, IntToStr(gCoopTotalMonstersKilled
) + '/' + IntToStr(gCoopTotalMonsters
), gStdFont
, 255, 255, 255, 1);
2874 s2
:= _lc
[I_GAME_SECRETS_TOTAL
];
2875 e_TextureFontPrintEx(x
+250, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2876 e_TextureFontPrintEx(x
+250+m
, _y
, IntToStr(gCoopTotalSecretsFound
) + '/' + IntToStr(gCoopTotalSecrets
), gStdFont
, 255, 255, 255, 1);
2880 if CustomStat
.GameMode
in [GM_TDM
, GM_CTF
] then
2885 if TeamStat
[TEAM_RED
].Score
> TeamStat
[TEAM_BLUE
].Score
then s1
:= _lc
[I_GAME_WIN_RED
]
2886 else if TeamStat
[TEAM_BLUE
].Score
> TeamStat
[TEAM_RED
].Score
then s1
:= _lc
[I_GAME_WIN_BLUE
]
2887 else s1
:= _lc
[I_GAME_WIN_DRAW
];
2889 e_TextureFontPrintEx(x
+8+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
2892 for t
:= TEAM_RED
to TEAM_BLUE
do
2894 if t
= TEAM_RED
then
2896 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_TEAM_RED
],
2897 gStdFont
, 255, 0, 0, 1);
2898 e_TextureFontPrintEx(x
+w1
+8, _y
, IntToStr(CustomStat
.TeamStat
[TEAM_RED
].Score
),
2899 gStdFont
, 255, 0, 0, 1);
2906 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_TEAM_BLUE
],
2907 gStdFont
, 0, 0, 255, 1);
2908 e_TextureFontPrintEx(x
+w1
+8, _y
, IntToStr(CustomStat
.TeamStat
[TEAM_BLUE
].Score
),
2909 gStdFont
, 0, 0, 255, 1);
2915 e_DrawLine(1, x
+8, _y
+20, x
-8+w
, _y
+20, r
, g
, b
);
2918 for p
:= 0 to High(CustomStat
.PlayerStat
) do
2919 if CustomStat
.PlayerStat
[p
].Team
= t
then
2920 with CustomStat
.PlayerStat
[p
] do
2934 if (gPlayers
[Num
] <> nil) and (gPlayers
[Num
].FReady
) then
2935 e_TextureFontPrintEx(x
+16, _y
, Name
+ ' *', gStdFont
, rr
, gg
, bb
, 1)
2937 e_TextureFontPrintEx(x
+16, _y
, Name
, gStdFont
, rr
, gg
, bb
, 1);
2938 e_TextureFontPrintEx(x
+w1
+16, _y
, IntToStr(Frags
), gStdFont
, rr
, gg
, bb
, 1);
2939 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
, IntToStr(Deaths
), gStdFont
, rr
, gg
, bb
, 1);
2946 else if CustomStat
.GameMode
in [GM_DM
, GM_COOP
] then
2949 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_PLAYER_NAME
], gStdFont
, 255, 127, 0, 1);
2950 e_TextureFontPrintEx(x
+8+w1
, _y
, _lc
[I_GAME_FRAGS
], gStdFont
, 255, 127, 0, 1);
2951 e_TextureFontPrintEx(x
+8+w1
+w2
, _y
, _lc
[I_GAME_DEATHS
], gStdFont
, 255, 127, 0, 1);
2954 for p
:= 0 to High(CustomStat
.PlayerStat
) do
2955 with CustomStat
.PlayerStat
[p
] do
2957 e_DrawFillQuad(x
+8, _y
+4, x
+24-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
2964 if (gPlayers
[Num
] <> nil) and (gPlayers
[Num
].FReady
) then
2965 e_TextureFontPrintEx(x
+8+16+8, _y
+4, Name
+ ' *', gStdFont
, r
, r
, r
, 1, True)
2967 e_TextureFontPrintEx(x
+8+16+8, _y
+4, Name
, gStdFont
, r
, r
, r
, 1, True);
2968 e_TextureFontPrintEx(x
+w1
+8+16+8, _y
+4, IntToStr(Frags
), gStdFont
, r
, r
, r
, 1, True);
2969 e_TextureFontPrintEx(x
+w1
+w2
+8+16+8, _y
+4, IntToStr(Deaths
), gStdFont
, r
, r
, r
, 1, True);
2974 // HACK: take stats screenshot immediately after the first frame of the stats showing
2975 if gScreenshotStats
and (not StatShotDone
) and (Length(CustomStat
.PlayerStat
) > 1) then
2977 g_TakeScreenShot('stats/' + StatFilename
);
2978 StatShotDone
:= True;
2982 procedure DrawSingleStat();
2984 tm
, key_x
, val_x
, y
: Integer;
2988 procedure player_stat(n
: Integer);
2994 s1
:= Format(' %d ', [SingleStat
.PlayerStat
[n
].Kills
]);
2995 s2
:= Format(' %d', [gTotalMonsters
]);
2997 e_CharFont_Print(gMenuFont
, key_x
, y
, _lc
[I_MENU_INTER_KILLS
]);
2998 e_CharFont_PrintEx(gMenuFont
, val_x
, y
, s1
, _RGB(255, 0, 0));
2999 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3000 e_CharFont_Print(gMenuFont
, val_x
+w1
, y
, '/');
3002 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3003 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
, s2
, _RGB(255, 0, 0));
3005 // "Kills-per-minute: ##.#":
3006 s1
:= _lc
[I_MENU_INTER_KPM
];
3008 kpm
:= (SingleStat
.PlayerStat
[n
].Kills
/ tm
) * 60
3010 kpm
:= SingleStat
.PlayerStat
[n
].Kills
;
3011 s2
:= Format(' %.1f', [kpm
]);
3013 e_CharFont_Print(gMenuFont
, key_x
, y
+32, s1
);
3014 e_CharFont_PrintEx(gMenuFont
, val_x
, y
+32, s2
, _RGB(255, 0, 0));
3016 // "Secrets found: # / #":
3017 s1
:= Format(' %d ', [SingleStat
.PlayerStat
[n
].Secrets
]);
3018 s2
:= Format(' %d', [SingleStat
.TotalSecrets
]);
3020 e_CharFont_Print(gMenuFont
, key_x
, y
+64, _lc
[I_MENU_INTER_SECRETS
]);
3021 e_CharFont_PrintEx(gMenuFont
, val_x
, y
+64, s1
, _RGB(255, 0, 0));
3022 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3023 e_CharFont_Print(gMenuFont
, val_x
+w1
, y
+64, '/');
3025 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3026 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
+64, s2
, _RGB(255, 0, 0));
3030 // "Level Complete":
3031 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_INTER_LEVEL_COMPLETE
], w1
, h
);
3032 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 32, _lc
[I_MENU_INTER_LEVEL_COMPLETE
]);
3034 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
3035 s1
:= _lc
[I_MENU_INTER_KPM
];
3036 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3039 e_CharFont_GetSize(gMenuFont
, s1
, w2
, h
);
3041 key_x
:= (gScreenWidth
-w1
-w2
) div 2;
3042 val_x
:= key_x
+ w1
;
3045 tm
:= SingleStat
.GameTime
div 1000;
3046 s1
:= _lc
[I_MENU_INTER_TIME
];
3047 s2
:= Format(' %d:%.2d:%.2d', [tm
div (60*60), (tm
mod (60*60)) div 60, tm
mod 60]);
3049 e_CharFont_Print(gMenuFont
, key_x
, 80, s1
);
3050 e_CharFont_PrintEx(gMenuFont
, val_x
, 80, s2
, _RGB(255, 0, 0));
3052 if SingleStat
.TwoPlayers
then
3055 s1
:= _lc
[I_MENU_PLAYER_1
];
3056 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3057 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 128, s1
);
3059 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3064 s1
:= _lc
[I_MENU_PLAYER_2
];
3065 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3066 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 288, s1
);
3068 // Ñòàòèñòèêà âòîðîãî èãðîêà:
3074 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3080 procedure DrawLoadingStat();
3081 procedure drawRect (x
, y
, w
, h
: Integer);
3083 if (w
< 1) or (h
< 1) then exit
;
3085 glVertex2f(x
+0.375, y
+0.375);
3086 glVertex2f(x
+w
+0.375, y
+0.375);
3087 glVertex2f(x
+w
+0.375, y
+h
+0.375);
3088 glVertex2f(x
+0.375, y
+h
+0.375);
3092 function drawPBar (cur
, total
: Integer; washere
: Boolean): Boolean;
3094 rectW
, rectH
: Integer;
3101 idl
, idr
, idb
, idm
: LongWord
;
3105 if (total
< 1) then exit
;
3106 if (cur
< 1) then exit
; // don't blink
3107 if (not washere
) and (cur
>= total
) then exit
; // don't blink
3108 //if (cur < 0) then cur := 0;
3109 //if (cur > total) then cur := total;
3112 if (hasPBarGfx
) then
3114 g_Texture_Get('UI_GFX_PBAR_LEFT', idl
);
3115 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl
, hl
);
3116 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr
);
3117 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr
, hr
);
3118 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb
);
3119 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb
, hb
);
3120 g_Texture_Get('UI_GFX_PBAR_MARKER', idm
);
3121 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm
, hm
);
3123 //rectW := gScreenWidth-360;
3124 rectW
:= trunc(624.0*gScreenWidth
/1024.0);
3127 x0
:= (gScreenWidth
-rectW
) div 2;
3128 y0
:= gScreenHeight
-rectH
-64;
3129 if (y0
< 2) then y0
:= 2;
3131 glEnable(GL_SCISSOR_TEST
);
3134 glScissor(x0
, gScreenHeight
-y0
-rectH
, rectW
, rectH
);
3135 e_DrawSize(idl
, x0
, y0
, 0, true, false, wl
, hl
);
3136 e_DrawSize(idr
, x0
+rectW
-wr
, y0
, 0, true, false, wr
, hr
);
3139 glScissor(x0
+wl
, gScreenHeight
-y0
-rectH
, rectW
-wl
-wr
, rectH
);
3141 while (f
< x0
+rectW
) do
3143 e_DrawSize(idb
, f
, y0
, 0, true, false, wb
, hb
);
3148 wdt
:= (rectW
-wl
-wr
)*cur
div total
;
3149 if (wdt
> rectW
-wl
-wr
) then wdt
:= rectW
-wr
-wr
;
3152 my
:= y0
; // don't be so smart, ketmar: +(rectH-wm) div 2;
3153 glScissor(x0
+wl
, gScreenHeight
-my
-rectH
, wdt
, hm
);
3157 e_DrawSize(idm
, f
, y0
, 0, true, false, wm
, hm
);
3163 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
3167 rectW
:= gScreenWidth
-64;
3170 x0
:= (gScreenWidth
-rectW
) div 2;
3171 y0
:= gScreenHeight
-rectH
-64;
3172 if (y0
< 2) then y0
:= 2;
3174 glDisable(GL_BLEND
);
3175 glDisable(GL_TEXTURE_2D
);
3177 //glClearColor(0, 0, 0, 0);
3178 //glClear(GL_COLOR_BUFFER_BIT);
3180 glColor4ub(127, 127, 127, 255);
3181 drawRect(x0
-2, y0
-2, rectW
+4, rectH
+4);
3183 glColor4ub(0, 0, 0, 255);
3184 drawRect(x0
-1, y0
-1, rectW
+2, rectH
+2);
3186 glColor4ub(127, 127, 127, 255);
3187 wdt
:= rectW
*cur
div total
;
3188 if (wdt
> rectW
) then wdt
:= rectW
;
3189 drawRect(x0
, y0
, wdt
, rectH
);
3198 if (Length(LoadingStat
.Msgs
) = 0) then exit
;
3200 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_LOADING
], ww
, hh
);
3201 yy
:= (gScreenHeight
div 3);
3202 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(ww
div 2), yy
-2*hh
, _lc
[I_MENU_LOADING
]);
3203 xx
:= (gScreenWidth
div 3);
3207 for i
:= 0 to NextMsg
-1 do
3209 if (i
= (NextMsg
-1)) and (MaxValue
> 0) then
3210 s
:= Format('%s: %d/%d', [Msgs
[i
], CurValue
, MaxValue
])
3214 e_CharFont_PrintEx(gMenuSmallFont
, xx
, yy
, s
, _RGB(255, 0, 0));
3215 yy
:= yy
+ LOADING_INTERLINE
;
3216 PBarWasHere
:= drawPBar(CurValue
, MaxValue
, PBarWasHere
);
3221 procedure DrawMenuBackground(tex
: AnsiString
);
3227 if g_Texture_Get(tex
, ID
) then
3229 e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
3230 e_GetTextureSize(ID
, @w
, @h
);
3232 w
:= round(w
* 1.333 * (gScreenHeight
/ h
))
3234 w
:= trunc(w
* (gScreenHeight
/ h
));
3235 e_DrawSize(ID
, (gScreenWidth
- w
) div 2, 0, 0, False, False, w
, gScreenHeight
);
3237 else e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
3240 procedure DrawMinimap(p
: TPlayer
; RenderRect
: e_graphics
.TRect
);
3242 a
, aX
, aY
, aX2
, aY2
, Scale
, ScaleSz
: Integer;
3244 function monDraw (mon
: TMonster
): Boolean;
3246 result
:= false; // don't stop
3251 // Ëåâûé âåðõíèé óãîë
3252 aX
:= Obj
.X
div ScaleSz
+ 1;
3253 aY
:= Obj
.Y
div ScaleSz
+ 1;
3255 aX2
:= max(Obj
.Rect
.Width
div ScaleSz
, 1);
3256 aY2
:= max(Obj
.Rect
.Height
div ScaleSz
, 1);
3257 // Ïðàâûé íèæíèé óãîë
3258 aX2
:= aX
+ aX2
- 1;
3259 aY2
:= aY
+ aY2
- 1;
3260 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 255, 0, 0);
3266 if (gMapInfo
.Width
> RenderRect
.Right
- RenderRect
.Left
) or
3267 (gMapInfo
.Height
> RenderRect
.Bottom
- RenderRect
.Top
) then
3270 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
3271 ScaleSz
:= 16 div Scale
;
3272 // Ðàçìåðû ìèíè-êàðòû:
3273 aX
:= max(gMapInfo
.Width
div ScaleSz
, 1);
3274 aY
:= max(gMapInfo
.Height
div ScaleSz
, 1);
3276 e_DrawFillQuad(0, 0, aX
-1, aY
-1, 0, 0, 0, 0);
3278 if gWalls
<> nil then
3281 for a
:= 0 to High(gWalls
) do
3283 if PanelType
<> 0 then
3285 // Ëåâûé âåðõíèé óãîë:
3286 aX
:= X
div ScaleSz
;
3287 aY
:= Y
div ScaleSz
;
3289 aX2
:= max(Width
div ScaleSz
, 1);
3290 aY2
:= max(Height
div ScaleSz
, 1);
3291 // Ïðàâûé íèæíèé óãîë:
3292 aX2
:= aX
+ aX2
- 1;
3293 aY2
:= aY
+ aY2
- 1;
3296 PANEL_WALL
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 208, 208, 208, 0);
3297 PANEL_OPENDOOR
, PANEL_CLOSEDOOR
:
3298 if Enabled
then e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 160, 160, 160, 0);
3302 if gSteps
<> nil then
3305 for a
:= 0 to High(gSteps
) do
3307 if PanelType
<> 0 then
3309 // Ëåâûé âåðõíèé óãîë:
3310 aX
:= X
div ScaleSz
;
3311 aY
:= Y
div ScaleSz
;
3313 aX2
:= max(Width
div ScaleSz
, 1);
3314 aY2
:= max(Height
div ScaleSz
, 1);
3315 // Ïðàâûé íèæíèé óãîë:
3316 aX2
:= aX
+ aX2
- 1;
3317 aY2
:= aY
+ aY2
- 1;
3319 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 128, 128, 128, 0);
3322 if gLifts
<> nil then
3325 for a
:= 0 to High(gLifts
) do
3327 if PanelType
<> 0 then
3329 // Ëåâûé âåðõíèé óãîë:
3330 aX
:= X
div ScaleSz
;
3331 aY
:= Y
div ScaleSz
;
3333 aX2
:= max(Width
div ScaleSz
, 1);
3334 aY2
:= max(Height
div ScaleSz
, 1);
3335 // Ïðàâûé íèæíèé óãîë:
3336 aX2
:= aX
+ aX2
- 1;
3337 aY2
:= aY
+ aY2
- 1;
3340 LIFTTYPE_UP
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 116, 72, 36, 0);
3341 LIFTTYPE_DOWN
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 116, 124, 96, 0);
3342 LIFTTYPE_LEFT
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 200, 80, 4, 0);
3343 LIFTTYPE_RIGHT
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 252, 140, 56, 0);
3347 if gWater
<> nil then
3350 for a
:= 0 to High(gWater
) do
3352 if PanelType
<> 0 then
3354 // Ëåâûé âåðõíèé óãîë:
3355 aX
:= X
div ScaleSz
;
3356 aY
:= Y
div ScaleSz
;
3358 aX2
:= max(Width
div ScaleSz
, 1);
3359 aY2
:= max(Height
div ScaleSz
, 1);
3360 // Ïðàâûé íèæíèé óãîë:
3361 aX2
:= aX
+ aX2
- 1;
3362 aY2
:= aY
+ aY2
- 1;
3364 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 0, 192, 0);
3367 if gAcid1
<> nil then
3369 // Ðèñóåì êèñëîòó 1:
3370 for a
:= 0 to High(gAcid1
) do
3372 if PanelType
<> 0 then
3374 // Ëåâûé âåðõíèé óãîë:
3375 aX
:= X
div ScaleSz
;
3376 aY
:= Y
div ScaleSz
;
3378 aX2
:= max(Width
div ScaleSz
, 1);
3379 aY2
:= max(Height
div ScaleSz
, 1);
3380 // Ïðàâûé íèæíèé óãîë:
3381 aX2
:= aX
+ aX2
- 1;
3382 aY2
:= aY
+ aY2
- 1;
3384 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 176, 0, 0);
3387 if gAcid2
<> nil then
3389 // Ðèñóåì êèñëîòó 2:
3390 for a
:= 0 to High(gAcid2
) do
3392 if PanelType
<> 0 then
3394 // Ëåâûé âåðõíèé óãîë:
3395 aX
:= X
div ScaleSz
;
3396 aY
:= Y
div ScaleSz
;
3398 aX2
:= max(Width
div ScaleSz
, 1);
3399 aY2
:= max(Height
div ScaleSz
, 1);
3400 // Ïðàâûé íèæíèé óãîë:
3401 aX2
:= aX
+ aX2
- 1;
3402 aY2
:= aY
+ aY2
- 1;
3404 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 176, 0, 0, 0);
3407 if gPlayers
<> nil then
3410 for a
:= 0 to High(gPlayers
) do
3411 if gPlayers
[a
] <> nil then with gPlayers
[a
] do
3413 // Ëåâûé âåðõíèé óãîë:
3414 aX
:= Obj
.X
div ScaleSz
+ 1;
3415 aY
:= Obj
.Y
div ScaleSz
+ 1;
3417 aX2
:= max(Obj
.Rect
.Width
div ScaleSz
, 1);
3418 aY2
:= max(Obj
.Rect
.Height
div ScaleSz
, 1);
3419 // Ïðàâûé íèæíèé óãîë:
3420 aX2
:= aX
+ aX2
- 1;
3421 aY2
:= aY
+ aY2
- 1;
3423 if gPlayers
[a
] = p
then
3424 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 255, 0, 0)
3427 TEAM_RED
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 0, 0, 0);
3428 TEAM_BLUE
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 0, 255, 0);
3429 else e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 128, 0, 0);
3434 g_Mons_ForEach(monDraw
);
3439 procedure renderAmbientQuad (hasAmbient
: Boolean; constref ambColor
: TDFColor
);
3441 if not hasAmbient
then exit
;
3442 e_AmbientQuad(sX
, sY
, sWidth
, sHeight
, ambColor
.r
, ambColor
.g
, ambColor
.b
, ambColor
.a
);
3446 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3447 //FIXME: broken for splitscreen mode
3448 procedure renderDynLightsInternal ();
3450 //hasAmbient: Boolean;
3451 //ambColor: TDFColor;
3453 lx
, ly
, lrad
: Integer;
3454 scxywh
: array[0..3] of GLint
;
3457 if e_NoGraphics
then exit
;
3459 //TODO: lights should be in separate grid, i think
3460 // but on the other side: grid may be slower for dynlights, as their lifetime is short
3461 if (not gwin_k8_enable_light_experiments
) or (not gwin_has_stencil
) or (g_dynLightCount
< 1) then exit
;
3464 //ambColor := gCurrentMap['light_ambient'].rgba;
3465 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3467 { // this will multiply incoming color to alpha from framebuffer
3469 glBlendFunc(GL_DST_ALPHA, GL_ONE);
3473 * light rendering: (INVALID!)
3474 * glStencilFunc(GL_EQUAL, 0, $ff);
3476 * glClear(GL_STENCIL_BUFFER_BIT);
3477 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3478 * draw shadow volume into stencil buffer
3479 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3480 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
3482 * draw color-less quad with light alpha (WARNING! don't touch color!)
3483 * glEnable(GL_BLEND);
3484 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
3485 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
3487 wassc
:= (glIsEnabled(GL_SCISSOR_TEST
) <> 0);
3488 if wassc
then glGetIntegerv(GL_SCISSOR_BOX
, @scxywh
[0]) else glGetIntegerv(GL_VIEWPORT
, @scxywh
[0]);
3490 // setup OpenGL parameters
3491 glStencilMask($FFFFFFFF);
3492 glStencilFunc(GL_ALWAYS
, 0, $FFFFFFFF);
3493 glEnable(GL_STENCIL_TEST
);
3494 glEnable(GL_SCISSOR_TEST
);
3495 glClear(GL_STENCIL_BUFFER_BIT
);
3496 glStencilFunc(GL_EQUAL
, 0, $ff);
3498 for lln
:= 0 to g_dynLightCount
-1 do
3500 lx
:= g_dynLights
[lln
].x
;
3501 ly
:= g_dynLights
[lln
].y
;
3502 lrad
:= g_dynLights
[lln
].radius
;
3503 if (lrad
< 3) then continue
;
3505 if (lx
-sX
+lrad
< 0) then continue
;
3506 if (ly
-sY
+lrad
< 0) then continue
;
3507 if (lx
-sX
-lrad
>= gPlayerScreenSize
.X
) then continue
;
3508 if (ly
-sY
-lrad
>= gPlayerScreenSize
.Y
) then continue
;
3510 // set scissor to optimize drawing
3511 if (g_dbg_scale
= 1.0) then
3513 glScissor((lx
-sX
)-lrad
+2, gPlayerScreenSize
.Y
-(ly
-sY
)-lrad
-1+2, lrad
*2-4, lrad
*2-4);
3517 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
3519 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3520 if (g_dbg_scale
<> 1.0) then glClear(GL_STENCIL_BUFFER_BIT
);
3521 glStencilOp(GL_KEEP
, GL_KEEP
, GL_INCR
);
3522 // draw extruded panels
3523 glDisable(GL_TEXTURE_2D
);
3524 glDisable(GL_BLEND
);
3525 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
); // no need to modify color buffer
3526 if (lrad
> 4) then g_Map_DrawPanelShadowVolumes(lx
, ly
, lrad
);
3527 // render light texture
3528 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
); // modify color buffer
3529 glStencilOp(GL_ZERO
, GL_ZERO
, GL_ZERO
); // draw light, and clear stencil buffer
3532 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
3533 glEnable(GL_TEXTURE_2D
);
3534 // color and opacity
3535 glColor4f(g_dynLights
[lln
].r
, g_dynLights
[lln
].g
, g_dynLights
[lln
].b
, g_dynLights
[lln
].a
);
3536 glBindTexture(GL_TEXTURE_2D
, g_Texture_Light());
3538 glTexCoord2f(0.0, 0.0); glVertex2i(lx
-lrad
, ly
-lrad
); // top-left
3539 glTexCoord2f(1.0, 0.0); glVertex2i(lx
+lrad
, ly
-lrad
); // top-right
3540 glTexCoord2f(1.0, 1.0); glVertex2i(lx
+lrad
, ly
+lrad
); // bottom-right
3541 glTexCoord2f(0.0, 1.0); glVertex2i(lx
-lrad
, ly
+lrad
); // bottom-left
3546 glDisable(GL_STENCIL_TEST
);
3547 glDisable(GL_BLEND
);
3548 glDisable(GL_SCISSOR_TEST
);
3549 //glScissor(0, 0, sWidth, sHeight);
3551 glScissor(scxywh
[0], scxywh
[1], scxywh
[2], scxywh
[3]);
3552 if wassc
then glEnable(GL_SCISSOR_TEST
) else glDisable(GL_SCISSOR_TEST
);
3556 function fixViewportForScale (): Boolean;
3558 nx0
, ny0
, nw
, nh
: Integer;
3561 if (g_dbg_scale
<> 1.0) then
3564 nx0
:= round(sX
-(gPlayerScreenSize
.X
-(sWidth
*g_dbg_scale
))/2/g_dbg_scale
);
3565 ny0
:= round(sY
-(gPlayerScreenSize
.Y
-(sHeight
*g_dbg_scale
))/2/g_dbg_scale
);
3566 nw
:= round(sWidth
/g_dbg_scale
);
3567 nh
:= round(sHeight
/g_dbg_scale
);
3576 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3577 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3578 procedure renderMapInternal (backXOfs
, backYOfs
: Integer; setTransMatrix
: Boolean);
3580 TDrawCB
= procedure ();
3583 hasAmbient
: Boolean;
3585 doAmbient
: Boolean = false;
3587 procedure drawPanelType (profname
: AnsiString
; panType
: DWord
; doDraw
: Boolean);
3592 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
3593 if gdbg_map_use_accel_render
then
3595 tagmask
:= panelTypeToTag(panType
);
3596 while (gDrawPanelList
.count
> 0) do
3598 pan
:= TPanel(gDrawPanelList
.front());
3599 if ((pan
.tag
and tagmask
) = 0) then break
;
3600 if doDraw
then pan
.Draw(doAmbient
, ambColor
);
3601 gDrawPanelList
.popFront();
3606 if doDraw
then g_Map_DrawPanels(panType
, hasAmbient
, ambColor
);
3608 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3611 procedure drawOther (profname
: AnsiString
; cb
: TDrawCB
);
3613 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
3614 if assigned(cb
) then cb();
3615 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3619 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('total');
3621 // our accelerated renderer will collect all panels to gDrawPanelList
3622 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3623 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('collect');
3624 if gdbg_map_use_accel_render
then
3626 g_Map_CollectDrawPanels(sX
, sY
, sWidth
, sHeight
);
3628 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3630 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('skyback');
3631 g_Map_DrawBack(backXOfs
, backYOfs
);
3632 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3634 if setTransMatrix
then
3636 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3637 glScalef(g_dbg_scale
, g_dbg_scale
, 1.0);
3638 glTranslatef(-sX
, -sY
, 0);
3642 ambColor
:= gCurrentMap
['light_ambient'].rgba
;
3643 hasAmbient
:= (not ambColor
.isOpaque
) or (not ambColor
.isBlack
);
3648 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3649 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3650 glClear(GL_COLOR_BUFFER_BIT);
3653 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3656 drawPanelType('*back', PANEL_BACK
, g_rlayer_back
);
3657 drawPanelType('*step', PANEL_STEP
, g_rlayer_step
);
3658 drawOther('items', @g_Items_Draw
);
3659 drawOther('weapons', @g_Weapon_Draw
);
3660 drawOther('shells', @g_Player_DrawShells
);
3661 drawOther('drawall', @g_Player_DrawAll
);
3662 drawOther('corpses', @g_Player_DrawCorpses
);
3663 drawPanelType('*wall', PANEL_WALL
, g_rlayer_wall
);
3664 drawOther('monsters', @g_Monsters_Draw
);
3665 drawOther('itemdrop', @g_Items_DrawDrop
);
3666 drawPanelType('*door', PANEL_CLOSEDOOR
, g_rlayer_door
);
3667 drawOther('gfx', @g_GFX_Draw
);
3668 drawOther('flags', @g_Map_DrawFlags
);
3669 drawPanelType('*acid1', PANEL_ACID1
, g_rlayer_acid1
);
3670 drawPanelType('*acid2', PANEL_ACID2
, g_rlayer_acid2
);
3671 drawPanelType('*water', PANEL_WATER
, g_rlayer_water
);
3672 drawOther('dynlights', @renderDynLightsInternal
);
3674 if hasAmbient
{and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3676 renderAmbientQuad(hasAmbient
, ambColor
);
3680 drawPanelType('*fore', PANEL_FORE
, g_rlayer_fore
);
3683 if g_debug_HealthBar
then
3685 g_Monsters_DrawHealth();
3686 g_Player_DrawHealth();
3689 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainEnd(); // map rendering
3693 procedure DrawMapView(x
, y
, w
, h
: Integer);
3700 bx
:= Round(x
/(gMapInfo
.Width
- w
)*(gBackSize
.X
- w
));
3701 by
:= Round(y
/(gMapInfo
.Height
- h
)*(gBackSize
.Y
- h
));
3708 fixViewportForScale();
3709 renderMapInternal(-bx
, -by
, true);
3715 procedure DrawPlayer(p
: TPlayer
);
3717 px
, py
, a
, b
, c
, d
, i
, fX
, fY
: Integer;
3721 if (p
= nil) or (p
.FDummy
) then
3724 g_Map_DrawBack(0, 0);
3729 if (profileFrameDraw
= nil) then profileFrameDraw
:= TProfiler
.Create('RENDER', g_profile_history_size
);
3730 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainBegin(g_profile_frame_draw
);
3736 camObj
:= p
.getCameraObj();
3737 camObj
.lerp(gLerpFactor
, fX
, fY
);
3738 px
:= fX
+ PLAYER_RECT_CX
;
3739 py
:= fY
+ PLAYER_RECT_CY
+nlerp(p
.SlopeOld
, camObj
.slopeUpLeft
, gLerpFactor
);
3741 if (g_dbg_scale
= 1.0) and (not g_dbg_ignore_bounds
) then
3743 if (px
> (gPlayerScreenSize
.X
div 2)) then a
:= -px
+(gPlayerScreenSize
.X
div 2) else a
:= 0;
3744 if (py
> (gPlayerScreenSize
.Y
div 2)) then b
:= -py
+(gPlayerScreenSize
.Y
div 2) else b
:= 0;
3746 if (px
> gMapInfo
.Width
-(gPlayerScreenSize
.X
div 2)) then a
:= -gMapInfo
.Width
+gPlayerScreenSize
.X
;
3747 if (py
> gMapInfo
.Height
-(gPlayerScreenSize
.Y
div 2)) then b
:= -gMapInfo
.Height
+gPlayerScreenSize
.Y
;
3749 if (gMapInfo
.Width
= gPlayerScreenSize
.X
) then a
:= 0
3750 else if (gMapInfo
.Width
< gPlayerScreenSize
.X
) then
3753 a
:= (gPlayerScreenSize
.X
-gMapInfo
.Width
) div 2;
3756 if (gMapInfo
.Height
= gPlayerScreenSize
.Y
) then b
:= 0
3757 else if (gMapInfo
.Height
< gPlayerScreenSize
.Y
) then
3760 b
:= (gPlayerScreenSize
.Y
-gMapInfo
.Height
) div 2;
3765 // scaled, ignore level bounds
3766 a
:= -px
+(gPlayerScreenSize
.X
div 2);
3767 b
:= -py
+(gPlayerScreenSize
.Y
div 2);
3772 sWidth
:= gPlayerScreenSize
.X
;
3773 sHeight
:= gPlayerScreenSize
.Y
;
3774 fixViewportForScale();
3776 i
:= py
- (sY
+ sHeight
div 2);
3777 if (p
.IncCam
> 0) then
3779 // clamp to level bounds
3780 if (sY
- p
.IncCam
< 0) then
3781 p
.IncCam
:= nclamp(sY
, 0, 120);
3782 // clamp around player position
3784 p
.IncCam
:= nclamp(p
.IncCam
, 0, max(0, 120 - i
));
3786 else if (p
.IncCam
< 0) then
3788 // clamp to level bounds
3789 if (sY
+ sHeight
- p
.IncCam
> gMapInfo
.Height
) then
3790 p
.IncCam
:= nclamp(sY
+ sHeight
- gMapInfo
.Height
, -120, 0);
3791 // clamp around player position
3793 p
.IncCam
:= nclamp(p
.IncCam
, min(0, -120 - i
), 0);
3796 sY
:= sY
- nlerp(p
.IncCamOld
, p
.IncCam
, gLerpFactor
);
3798 if (not g_dbg_ignore_bounds
) then
3800 if (sX
+sWidth
> gMapInfo
.Width
) then sX
:= gMapInfo
.Width
-sWidth
;
3801 if (sY
+sHeight
> gMapInfo
.Height
) then sY
:= gMapInfo
.Height
-sHeight
;
3802 if (sX
< 0) then sX
:= 0;
3803 if (sY
< 0) then sY
:= 0;
3806 if (gBackSize
.X
<= gPlayerScreenSize
.X
) or (gMapInfo
.Width
<= sWidth
) then c
:= 0 else c
:= trunc((gBackSize
.X
-gPlayerScreenSize
.X
)*sX
/(gMapInfo
.Width
-sWidth
));
3807 if (gBackSize
.Y
<= gPlayerScreenSize
.Y
) or (gMapInfo
.Height
<= sHeight
) then d
:= 0 else d
:= trunc((gBackSize
.Y
-gPlayerScreenSize
.Y
)*sY
/(gMapInfo
.Height
-sHeight
));
3809 //r_smallmap_h: 0: left; 1: center; 2: right
3810 //r_smallmap_v: 0: top; 1: center; 2: bottom
3812 if (gMapInfo
.Width
= sWidth
) then
3816 else if (gMapInfo
.Width
< sWidth
) then
3818 case r_smallmap_h
of
3819 1: sX
:= -((sWidth
-gMapInfo
.Width
) div 2); // center
3820 2: sX
:= -(sWidth
-gMapInfo
.Width
); // right
3821 else sX
:= 0; // left
3825 if (gMapInfo
.Height
= sHeight
) then
3829 else if (gMapInfo
.Height
< sHeight
) then
3831 case r_smallmap_v
of
3832 1: sY
:= -((sHeight
-gMapInfo
.Height
) div 2); // center
3833 2: sY
:= -(sHeight
-gMapInfo
.Height
); // bottom
3834 else sY
:= 0; // top
3840 p
.viewPortW
:= sWidth
;
3841 p
.viewPortH
:= sHeight
;
3843 {$IFDEF ENABLE_HOLMES}
3844 if (p
= gPlayer1
) then
3846 g_Holmes_plrViewPos(sX
, sY
);
3847 g_Holmes_plrViewSize(sWidth
, sHeight
);
3851 renderMapInternal(-c
, -d
, true);
3853 if (gGameSettings
.GameMode
<> GM_SINGLE
) and (gPlayerIndicator
> 0) then
3854 case gPlayerIndicator
of
3856 p
.DrawIndicator(_RGB(255, 255, 255));
3859 for i
:= 0 to High(gPlayers
) do
3860 if gPlayers
[i
] <> nil then
3861 if gPlayers
[i
] = p
then p
.DrawIndicator(_RGB(255, 255, 255))
3862 else if (gPlayers
[i
].Team
= p
.Team
) and (gPlayers
[i
].Team
<> TEAM_NONE
) then
3863 if gPlayerIndicatorStyle
= 1 then
3864 gPlayers
[i
].DrawIndicator(_RGB(192, 192, 192))
3865 else gPlayers
[i
].DrawIndicator(gPlayers
[i
].GetColor
);
3869 for a := 0 to High(gCollideMap) do
3870 for b := 0 to High(gCollideMap[a]) do
3873 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3875 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3879 1: e_DrawPoint(1, b, a, 200, 200, 200);
3880 2: e_DrawPoint(1, b, a, 64, 64, 255);
3881 3: e_DrawPoint(1, b, a, 255, 0, 255);
3891 if gShowMap
then DrawMinimap(p
, _TRect(0, 0, 128, 128));
3892 if g_Debug_Player
then
3893 g_Player_DrawDebug(p
);
3897 procedure drawProfilers ();
3902 if g_profile_frame_draw
and (profileFrameDraw
<> nil) then
3903 px
-= drawProfiles(px
, py
, profileFrameDraw
);
3905 if g_profile_collision
and (profMapCollision
<> nil) then
3907 px
-= drawProfiles(px
, py
, profMapCollision
);
3908 py
-= calcProfilesHeight(profMonsLOS
);
3911 if g_profile_los
and (profMonsLOS
<> nil) then
3913 px
-= drawProfiles(px
, py
, profMonsLOS
);
3914 py
-= calcProfilesHeight(profMonsLOS
);
3918 procedure g_Game_Draw();
3925 plView1
, plView2
: TPlayer
;
3928 if gExit
= EXIT_QUIT
then Exit
;
3930 Time
:= sys_GetTicks() {div 1000};
3931 FPSCounter
:= FPSCounter
+1;
3932 if Time
- FPSTime
>= 1000 then
3939 e_SetRendertarget(True);
3940 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
3942 if gGameOn
or (gState
= STATE_FOLD
) then
3944 if (gPlayer1
<> nil) and (gPlayer2
<> nil) then
3946 gSpectMode
:= SPECT_NONE
;
3947 if not gRevertPlayers
then
3949 plView1
:= gPlayer1
;
3950 plView2
:= gPlayer2
;
3954 plView1
:= gPlayer2
;
3955 plView2
:= gPlayer1
;
3959 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
3961 gSpectMode
:= SPECT_NONE
;
3962 if gPlayer2
= nil then
3965 plView1
:= gPlayer2
;
3974 if (plView1
= nil) and (plView2
= nil) and (gSpectMode
= SPECT_NONE
) then
3975 gSpectMode
:= SPECT_STATS
;
3977 if gSpectMode
= SPECT_PLAYERS
then
3978 if gPlayers
<> nil then
3980 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
3981 if plView1
= nil then
3983 gSpectPID1
:= GetActivePlayerID_Next();
3984 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
3986 if gSpectViewTwo
then
3988 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
3989 if plView2
= nil then
3991 gSpectPID2
:= GetActivePlayerID_Next();
3992 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
3997 if gSpectMode
= SPECT_MAPVIEW
then
3999 // Ðåæèì ïðîñìîòðà êàðòû
4001 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
4002 DrawMapView(gSpectX
, gSpectY
, gScreenWidth
, gScreenHeight
);
4003 gHearPoint1
.Active
:= True;
4004 gHearPoint1
.Coords
.X
:= gScreenWidth
div 2 + gSpectX
;
4005 gHearPoint1
.Coords
.Y
:= gScreenHeight
div 2 + gSpectY
;
4006 gHearPoint2
.Active
:= False;
4010 Split
:= (plView1
<> nil) and (plView2
<> nil);
4012 // Òî÷êè ñëóõà èãðîêîâ
4013 if plView1
<> nil then
4015 gHearPoint1
.Active
:= True;
4016 gHearPoint1
.Coords
.X
:= plView1
.GameX
+ PLAYER_RECT
.Width
;
4017 gHearPoint1
.Coords
.Y
:= plView1
.GameY
+ PLAYER_RECT
.Height
DIV 2;
4019 gHearPoint1
.Active
:= False;
4020 if plView2
<> nil then
4022 gHearPoint2
.Active
:= True;
4023 gHearPoint2
.Coords
.X
:= plView2
.GameX
+ PLAYER_RECT
.Width
;
4024 gHearPoint2
.Coords
.Y
:= plView2
.GameY
+ PLAYER_RECT
.Height
DIV 2;
4026 gHearPoint2
.Active
:= False;
4028 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4029 gPlayerScreenSize
.X
:= gScreenWidth
-196;
4032 gPlayerScreenSize
.Y
:= gScreenHeight
div 2;
4033 if gScreenHeight
mod 2 = 0 then
4034 Dec(gPlayerScreenSize
.Y
);
4037 gPlayerScreenSize
.Y
:= gScreenHeight
;
4040 if gScreenHeight
mod 2 = 0 then
4041 e_SetViewPort(0, gPlayerScreenSize
.Y
+2, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
)
4043 e_SetViewPort(0, gPlayerScreenSize
.Y
+1, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
4045 DrawPlayer(plView1
);
4046 gPlayer1ScreenCoord
.X
:= sX
;
4047 gPlayer1ScreenCoord
.Y
:= sY
;
4051 e_SetViewPort(0, 0, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
4053 DrawPlayer(plView2
);
4054 gPlayer2ScreenCoord
.X
:= sX
;
4055 gPlayer2ScreenCoord
.Y
:= sY
;
4058 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
4061 e_DrawLine(2, 0, gScreenHeight
div 2, gScreenWidth
, gScreenHeight
div 2, 0, 0, 0);
4064 {$IFDEF ENABLE_HOLMES}
4066 if (g_holmes_enabled
) then g_Holmes_Draw();
4069 if MessageText
<> '' then
4073 e_CharFont_GetSizeFmt(gMenuFont
, MessageText
, w
, h
);
4075 e_CharFont_PrintFmt(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
4076 (gScreenHeight
div 2)-(h
div 2), MessageText
)
4078 e_CharFont_PrintFmt(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
4079 Round(gScreenHeight
/ 2.75)-(h
div 2), MessageText
);
4082 if IsDrawStat
or (gSpectMode
= SPECT_STATS
) then
4085 if gSpectHUD
and (not gChatShow
) and (gSpectMode
<> SPECT_NONE
) and (not gSpectAuto
) then
4087 // Draw spectator GUI
4090 e_TextureFontGetSize(gStdFont
, ww
, hh
);
4093 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Stats', gStdFont
, 255, 255, 255, 1);
4095 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Observe Map', gStdFont
, 255, 255, 255, 1);
4097 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Watch Players', gStdFont
, 255, 255, 255, 1);
4099 e_TextureFontPrintEx(2*ww
, gScreenHeight
- (hh
+2), '< jump >', gStdFont
, 255, 255, 255, 1);
4100 if gSpectMode
= SPECT_STATS
then
4102 e_TextureFontPrintEx(16*ww
, gScreenHeight
- (hh
+2)*2, 'Autoview', gStdFont
, 255, 255, 255, 1);
4103 e_TextureFontPrintEx(16*ww
, gScreenHeight
- (hh
+2), '< fire >', gStdFont
, 255, 255, 255, 1);
4105 if gSpectMode
= SPECT_MAPVIEW
then
4107 e_TextureFontPrintEx(22*ww
, gScreenHeight
- (hh
+2)*2, '[-]', gStdFont
, 255, 255, 255, 1);
4108 e_TextureFontPrintEx(26*ww
, gScreenHeight
- (hh
+2)*2, 'Step ' + IntToStr(gSpectStep
), gStdFont
, 255, 255, 255, 1);
4109 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2)*2, '[+]', gStdFont
, 255, 255, 255, 1);
4110 e_TextureFontPrintEx(18*ww
, gScreenHeight
- (hh
+2), '<prev weap>', gStdFont
, 255, 255, 255, 1);
4111 e_TextureFontPrintEx(30*ww
, gScreenHeight
- (hh
+2), '<next weap>', gStdFont
, 255, 255, 255, 1);
4113 if gSpectMode
= SPECT_PLAYERS
then
4115 e_TextureFontPrintEx(22*ww
, gScreenHeight
- (hh
+2)*2, 'Player 1', gStdFont
, 255, 255, 255, 1);
4116 e_TextureFontPrintEx(20*ww
, gScreenHeight
- (hh
+2), '<left/right>', gStdFont
, 255, 255, 255, 1);
4117 if gSpectViewTwo
then
4119 e_TextureFontPrintEx(37*ww
, gScreenHeight
- (hh
+2)*2, 'Player 2', gStdFont
, 255, 255, 255, 1);
4120 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2), '<prev w/next w>', gStdFont
, 255, 255, 255, 1);
4121 e_TextureFontPrintEx(52*ww
, gScreenHeight
- (hh
+2)*2, '2x View', gStdFont
, 255, 255, 255, 1);
4122 e_TextureFontPrintEx(51*ww
, gScreenHeight
- (hh
+2), '<up/down>', gStdFont
, 255, 255, 255, 1);
4126 e_TextureFontPrintEx(35*ww
, gScreenHeight
- (hh
+2)*2, '2x View', gStdFont
, 255, 255, 255, 1);
4127 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2), '<up/down>', gStdFont
, 255, 255, 255, 1);
4133 if gPauseMain
and gGameOn
and (g_ActiveWindow
= nil) then
4135 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4136 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4138 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_PAUSE
], w
, h
);
4139 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
4140 (gScreenHeight
div 2)-(h
div 2), _lc
[I_MENU_PAUSE
]);
4145 if (gState
= STATE_MENU
) then
4147 if (g_ActiveWindow
= nil) or (g_ActiveWindow
.BackTexture
= '') then DrawMenuBackground('MENU_BACKGROUND');
4148 // F3 at menu will show game loading dialog
4149 if e_KeyPressed(IK_F3
) then g_Menu_Show_LoadMenu(true);
4150 if (g_ActiveWindow
<> nil) then
4152 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4153 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4157 // F3 at titlepic will show game loading dialog
4158 if e_KeyPressed(IK_F3
) then
4160 g_Menu_Show_LoadMenu(true);
4161 if (g_ActiveWindow
<> nil) then e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4166 if gState
= STATE_FOLD
then
4168 e_DrawFillQuad(0, 0, gScreenWidth
-1, gScreenHeight
-1, 0, 0, 0, EndingGameCounter
);
4171 if gState
= STATE_INTERCUSTOM
then
4173 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then
4175 back
:= 'TEXTURE_endpic';
4176 if not g_Texture_Get(back
, ID
) then
4177 back
:= _lc
[I_TEXTURE_ENDPIC
];
4182 DrawMenuBackground(back
);
4186 if g_ActiveWindow
<> nil then
4188 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4189 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4193 if gState
= STATE_INTERSINGLE
then
4195 if EndingGameCounter
> 0 then
4197 e_DrawFillQuad(0, 0, gScreenWidth
-1, gScreenHeight
-1, 0, 0, 0, EndingGameCounter
);
4203 DrawMenuBackground(back
);
4207 if g_ActiveWindow
<> nil then
4209 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4210 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4215 if gState
= STATE_ENDPIC
then
4218 if g_Texture_Get('TEXTURE_endpic', ID
) then DrawMenuBackground('TEXTURE_endpic')
4219 else DrawMenuBackground(_lc
[I_TEXTURE_ENDPIC
]);
4221 if g_ActiveWindow
<> nil then
4223 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4224 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4228 if gState
= STATE_SLIST
then
4230 // if g_Texture_Get('MENU_BACKGROUND', ID) then
4232 // e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
4233 // //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4235 DrawMenuBackground('MENU_BACKGROUND');
4236 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4237 g_Serverlist_Draw(slCurrent
, slTable
);
4241 if g_ActiveWindow
<> nil then
4245 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4246 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4248 g_ActiveWindow
.Draw();
4255 if g_debug_Sounds
and gGameOn
then
4257 for w
:= 0 to High(e_SoundsArray
) do
4258 for h
:= 0 to e_SoundsArray
[w
].nRefs
do
4259 e_DrawPoint(1, w
+100, h
+100, 255, 0, 0);
4264 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS
]), gStdFont
);
4265 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS
]), gStdFont
);
4268 if gGameOn
and gShowTime
then
4269 drawTime(gScreenWidth
-72, gScreenHeight
-16);
4271 if gGameOn
then drawProfilers();
4273 // TODO: draw this after the FBO and remap mouse click coordinates
4275 {$IFDEF ENABLE_HOLMES}
4279 // blit framebuffer to screen
4281 e_SetRendertarget(False);
4282 e_SetViewPort(0, 0, gWinSizeX
, gWinSizeY
);
4283 e_BlitFramebuffer(gWinSizeX
, gWinSizeY
);
4285 // draw the overlay stuff on top of it
4290 // FIXME: This cannot be called from anywhere other than ProcessMessages(), because otherwise
4291 // remaining events in the system queue may cause use-after-free! Do 'gExit := EXIT_QUIT' instead.
4292 procedure g_Game_Quit();
4294 e_WriteLog('g_Game_Quit: cleanup assets before shutting down', TMsgType
.Notify
);
4295 g_Game_StopAllSounds(True);
4298 g_PlayerModel_FreeData();
4299 g_Texture_DeleteAll();
4300 g_Frames_DeleteAll();
4305 if NetInitDone
then g_Net_Free
;
4307 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
4308 if gMapToDelete
<> '' then
4309 g_Game_DeleteTestMap();
4311 sys_RequestQuit(); // FIXME: this posts an event that have no sense anymore at this moment.
4314 procedure g_FatalError(Text: String);
4316 g_Console_Add(Format(_lc
[I_FATAL_ERROR
], [Text]), True);
4317 e_WriteLog(Format(_lc
[I_FATAL_ERROR
], [Text]), TMsgType
.Warning
);
4319 gExit
:= EXIT_SIMPLE
;
4320 if gGameOn
then EndGame
;
4323 procedure g_SimpleError(Text: String);
4325 g_Console_Add(Format(_lc
[I_SIMPLE_ERROR
], [Text]), True);
4326 e_WriteLog(Format(_lc
[I_SIMPLE_ERROR
], [Text]), TMsgType
.Warning
);
4329 procedure g_Game_SetupScreenSize();
4331 RES_FACTOR
= 4.0 / 3.0;
4337 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4338 gPlayerScreenSize
.X
:= gScreenWidth
-196;
4339 if (gPlayer1
<> nil) and (gPlayer2
<> nil) then
4340 gPlayerScreenSize
.Y
:= gScreenHeight
div 2
4342 gPlayerScreenSize
.Y
:= gScreenHeight
;
4344 // Ðàçìåð çàäíåãî ïëàíà:
4345 if BackID
<> DWORD(-1) then
4348 if (gScreenWidth
*s
> gMapInfo
.Width
) or
4349 (gScreenHeight
*s
> gMapInfo
.Height
) then
4351 gBackSize
.X
:= gScreenWidth
;
4352 gBackSize
.Y
:= gScreenHeight
;
4356 e_GetTextureSize(BackID
, @bw
, @bh
);
4357 rf
:= Single(bw
) / Single(bh
);
4358 if (rf
> RES_FACTOR
) then bw
:= Round(Single(bh
) * RES_FACTOR
)
4359 else if (rf
< RES_FACTOR
) then bh
:= Round(Single(bw
) / RES_FACTOR
);
4360 s
:= Max(gScreenWidth
/ bw
, gScreenHeight
/ bh
);
4361 if (s
< 1.0) then s
:= 1.0;
4362 gBackSize
.X
:= Round(bw
*s
);
4363 gBackSize
.Y
:= Round(bh
*s
);
4368 procedure g_Game_ChangeResolution(newWidth
, newHeight
: Word; nowFull
, nowMax
: Boolean);
4370 sys_SetDisplayMode(newWidth
, newHeight
, gBPP
, nowFull
, nowMax
);
4373 procedure g_Game_AddPlayer(Team
: Byte = TEAM_NONE
);
4375 if ((not gGameOn
) and (gState
<> STATE_INTERCUSTOM
))
4376 or (not (gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
])) then
4379 if (gGameSettings
.MaxLives
> 0) and (gLMSRespawn
= LMS_RESPAWN_NONE
) then
4382 if gPlayer1
= nil then
4384 if g_Game_IsClient
then
4386 if NetPlrUID1
> -1 then
4387 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE
);
4391 if not (Team
in [TEAM_RED
, TEAM_BLUE
]) then
4392 Team
:= gPlayer1Settings
.Team
;
4394 // Ñîçäàíèå ïåðâîãî èãðîêà:
4395 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4396 gPlayer1Settings
.Color
,
4398 if gPlayer1
= nil then
4399 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]))
4402 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4403 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4404 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4405 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4406 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4407 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [gPlayer1
.Name
]), True);
4408 if g_Game_IsServer
and g_Game_IsNet
then
4409 MH_SEND_PlayerCreate(gPlayer1
.UID
);
4410 gPlayer1
.Respawn(False, True);
4411 g_Net_Slist_ServerPlayerComes();
4416 if gPlayer2
= nil then
4418 if g_Game_IsClient
then
4420 if NetPlrUID2
> -1 then
4421 gPlayer2
:= g_Player_Get(NetPlrUID2
);
4425 if not (Team
in [TEAM_RED
, TEAM_BLUE
]) then
4426 Team
:= gPlayer2Settings
.Team
;
4428 // Ñîçäàíèå âòîðîãî èãðîêà:
4429 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4430 gPlayer2Settings
.Color
,
4432 if gPlayer2
= nil then
4433 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]))
4436 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4437 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4438 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4439 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4440 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4441 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [gPlayer2
.Name
]), True);
4442 if g_Game_IsServer
and g_Game_IsNet
then
4443 MH_SEND_PlayerCreate(gPlayer2
.UID
);
4444 gPlayer2
.Respawn(False, True);
4445 g_Net_Slist_ServerPlayerComes();
4452 procedure g_Game_RemovePlayer();
4456 if ((not gGameOn
) and (gState
<> STATE_INTERCUSTOM
))
4457 or (not (gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
])) then
4462 if g_Game_IsServer
then
4465 Pl
.Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
4466 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [Pl
.Name
]), True);
4467 g_Player_Remove(Pl
.UID
);
4468 g_Net_Slist_ServerPlayerLeaves();
4472 gSpectLatchPID2
:= Pl
.UID
;
4480 if g_Game_IsServer
then
4483 Pl
.Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
4484 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [Pl
.Name
]), True);
4485 g_Player_Remove(Pl
.UID
);
4486 g_Net_Slist_ServerPlayerLeaves();
4489 gSpectLatchPID1
:= Pl
.UID
;
4491 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE
);
4495 g_Net_Slist_ServerPlayerLeaves();
4498 procedure g_Game_Spectate();
4500 g_Game_RemovePlayer();
4501 if gPlayer1
<> nil then
4502 g_Game_RemovePlayer();
4505 procedure g_Game_SpectateCenterView();
4507 gSpectX
:= Max(gMapInfo
.Width
div 2 - gScreenWidth
div 2, 0);
4508 gSpectY
:= Max(gMapInfo
.Height
div 2 - gScreenHeight
div 2, 0);
4511 procedure g_Game_StartSingle(Map
: String; TwoPlayers
: Boolean; nPlayers
: Byte);
4518 e_WriteLog('Starting singleplayer game...', TMsgType
.Notify
);
4520 g_Game_ClearLoading();
4523 gGameSettings
:= Default(TGameSettings
);
4526 gGameSettings
.GameType
:= GT_SINGLE
;
4527 gGameSettings
.MaxLives
:= 0;
4528 gGameSettings
.Options
:= [TGameOption
.ALLOW_EXIT
, TGameOption
.MONSTERS
,
4529 TGameOption
.BOTS_VS_MONSTERS
, TGameOption
.TEAM_HIT_PROJECTILE
, TGameOption
.TEAM_HIT_TRACE
,
4530 TGameOption
.POWERUP_RANDOM
, TGameOption
.ITEM_ALL_RANDOM
, TGameOption
.ITEM_LIFE_RANDOM
,
4531 TGameOption
.ITEM_AMMO_RANDOM
, TGameOption
.ITEM_WEAPON_RANDOM
];
4532 gSwitchGameMode
:= GM_SINGLE
;
4534 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4535 gLMSRespawnTime
:= 0;
4536 gSpectLatchPID1
:= 0;
4537 gSpectLatchPID2
:= 0;
4539 g_Game_ExecuteEvent('ongamestart');
4541 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4542 g_Game_SetupScreenSize();
4544 // Ñîçäàíèå ïåðâîãî èãðîêà:
4545 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4546 gPlayer1Settings
.Color
,
4547 gPlayer1Settings
.Team
, False));
4548 if gPlayer1
= nil then
4550 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4554 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4555 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4556 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4557 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4558 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4561 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
4564 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4565 gPlayer2Settings
.Color
,
4566 gPlayer2Settings
.Team
, False));
4567 if gPlayer2
= nil then
4569 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]));
4573 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4574 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4575 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4576 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4577 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4581 // Çàãðóçêà è çàïóñê êàðòû:
4582 if not g_Game_StartMap(false{asMegawad}, MAP
, True) then
4584 if (Pos(':\', Map
) > 0) or (Pos(':/', Map
) > 0) then tmps
:= Map
else tmps
:= gGameSettings
.WAD
+ ':\' + MAP
;
4585 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [tmps
]));
4589 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4593 for i
:= nPl
+1 to nPlayers
do
4594 g_Player_Create(STD_PLAYER_MODEL
, _RGB(0, 0, 0), 0, True);
4597 procedure g_Game_StartCustom(Map
: String; GameMode
: Byte;
4598 TimeLimit
, ScoreLimit
: Word;
4600 Options
: TGameOptions
; nPlayers
: Byte);
4606 e_WriteLog('Starting custom game...', TMsgType
.Notify
);
4608 g_Game_ClearLoading();
4611 gGameSettings
.GameType
:= GT_CUSTOM
;
4612 gGameSettings
.GameMode
:= GameMode
;
4613 gSwitchGameMode
:= GameMode
;
4614 gGameSettings
.TimeLimit
:= TimeLimit
;
4615 gGameSettings
.ScoreLimit
:= ScoreLimit
;
4616 gGameSettings
.MaxLives
:= IfThen(GameMode
= GM_CTF
, 0, MaxLives
);
4617 gGameSettings
.Options
:= Options
;
4619 gCoopTotalMonstersKilled
:= 0;
4620 gCoopTotalSecretsFound
:= 0;
4621 gCoopTotalMonsters
:= 0;
4622 gCoopTotalSecrets
:= 0;
4626 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4627 gLMSRespawnTime
:= 0;
4628 gSpectLatchPID1
:= 0;
4629 gSpectLatchPID2
:= 0;
4631 g_Game_ExecuteEvent('ongamestart');
4633 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4634 g_Game_SetupScreenSize();
4636 // Ðåæèì íàáëþäàòåëÿ:
4637 if nPlayers
= 0 then
4644 if nPlayers
>= 1 then
4646 // Ñîçäàíèå ïåðâîãî èãðîêà:
4647 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4648 gPlayer1Settings
.Color
,
4649 gPlayer1Settings
.Team
, False));
4650 if gPlayer1
= nil then
4652 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4656 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4657 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4658 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4659 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4660 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4664 if nPlayers
>= 2 then
4666 // Ñîçäàíèå âòîðîãî èãðîêà:
4667 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4668 gPlayer2Settings
.Color
,
4669 gPlayer2Settings
.Team
, False));
4670 if gPlayer2
= nil then
4672 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]));
4676 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4677 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4678 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4679 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4680 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4684 // Çàãðóçêà è çàïóñê êàðòû:
4685 if not g_Game_StartMap(true{asMegawad}, Map
, True) then
4687 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [Map
]));
4691 // Íåò òî÷åê ïîÿâëåíèÿ:
4692 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1
) +
4693 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2
) +
4694 g_Map_GetPointCount(RESPAWNPOINT_DM
) +
4695 g_Map_GetPointCount(RESPAWNPOINT_RED
)+
4696 g_Map_GetPointCount(RESPAWNPOINT_BLUE
)) < 1 then
4698 g_FatalError(_lc
[I_GAME_ERROR_GET_SPAWN
]);
4702 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4706 for i
:= nPl
+1 to nPlayers
do
4707 g_Player_Create(STD_PLAYER_MODEL
, _RGB(0, 0, 0), 0, True);
4710 procedure g_Game_StartServer(Map
: String; GameMode
: Byte;
4711 TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte;
4712 Options
: TGameOptions
; nPlayers
: Byte;
4713 IPAddr
: LongWord
; Port
: Word);
4716 g_Net_Slist_ServerClosed();
4718 e_WriteLog('Starting net game (server)...', TMsgType
.Notify
);
4720 g_Game_ClearLoading();
4725 gGameSettings
.GameType
:= GT_SERVER
;
4726 gGameSettings
.GameMode
:= GameMode
;
4727 gSwitchGameMode
:= GameMode
;
4728 gGameSettings
.TimeLimit
:= TimeLimit
;
4729 gGameSettings
.ScoreLimit
:= ScoreLimit
;
4730 gGameSettings
.MaxLives
:= IfThen(GameMode
= GM_CTF
, 0, MaxLives
);
4731 gGameSettings
.Options
:= Options
;
4733 gCoopTotalMonstersKilled
:= 0;
4734 gCoopTotalSecretsFound
:= 0;
4735 gCoopTotalMonsters
:= 0;
4736 gCoopTotalSecrets
:= 0;
4740 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4741 gLMSRespawnTime
:= 0;
4742 gSpectLatchPID1
:= 0;
4743 gSpectLatchPID2
:= 0;
4745 g_Game_ExecuteEvent('ongamestart');
4747 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
4748 g_Game_SetupScreenSize();
4750 // Ðåæèì íàáëþäàòåëÿ:
4751 if nPlayers
= 0 then
4757 if nPlayers
>= 1 then
4759 // Ñîçäàíèå ïåðâîãî èãðîêà:
4760 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4761 gPlayer1Settings
.Color
,
4762 gPlayer1Settings
.Team
, False));
4763 if gPlayer1
= nil then
4765 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4769 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4770 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4771 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4772 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4773 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4776 if nPlayers
>= 2 then
4778 // Ñîçäàíèå âòîðîãî èãðîêà:
4779 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4780 gPlayer2Settings
.Color
,
4781 gPlayer2Settings
.Team
, False));
4782 if gPlayer2
= nil then
4784 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]));
4788 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4789 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4790 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4791 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4792 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4795 g_Game_SetLoadingText(_lc
[I_LOAD_HOST
], 0, False);
4796 if NetForwardPorts
then
4797 g_Game_SetLoadingText(_lc
[I_LOAD_PORTS
], 0, False);
4800 if not g_Net_Host(IPAddr
, Port
, NetMaxClients
) then
4802 g_FatalError(_lc
[I_NET_MSG
] + Format(_lc
[I_NET_ERR_HOST
], [Port
]));
4806 g_Net_Slist_Set(NetMasterList
);
4808 g_Net_Slist_ServerStarted();
4810 // Çàãðóçêà è çàïóñê êàðòû:
4811 if not g_Game_StartMap(false{asMegawad}, Map
, True) then
4813 g_Net_Slist_ServerClosed();
4814 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [Map
]));
4818 // Íåò òî÷åê ïîÿâëåíèÿ:
4819 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1
) +
4820 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2
) +
4821 g_Map_GetPointCount(RESPAWNPOINT_DM
) +
4822 g_Map_GetPointCount(RESPAWNPOINT_RED
)+
4823 g_Map_GetPointCount(RESPAWNPOINT_BLUE
)) < 1 then
4825 g_Net_Slist_ServerClosed();
4826 g_FatalError(_lc
[I_GAME_ERROR_GET_SPAWN
]);
4830 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4833 g_Net_Slist_ServerMapStarted();
4834 NetState
:= NET_STATE_GAME
;
4837 procedure g_Game_StartClient(Addr
: String; Port
: Word; PW
: String);
4852 e_WriteLog('Starting net game (client)...', TMsgType
.Notify
);
4853 e_WriteLog('NET: Trying to connect to ' + Addr
+ ':' + IntToStr(Port
) + '...', TMsgType
.Notify
);
4855 g_Game_ClearLoading();
4860 gGameSettings
.GameType
:= GT_CLIENT
;
4862 gCoopTotalMonstersKilled
:= 0;
4863 gCoopTotalSecretsFound
:= 0;
4864 gCoopTotalMonsters
:= 0;
4865 gCoopTotalSecrets
:= 0;
4869 g_Game_ExecuteEvent('ongamestart');
4871 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4872 g_Game_SetupScreenSize();
4874 NetState
:= NET_STATE_AUTH
;
4876 g_Game_SetLoadingText(_lc
[I_LOAD_CONNECT
], 0, False);
4878 // create (or update) map/resource databases
4879 g_Res_CreateDatabases(true);
4881 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4882 gLMSRespawnTime
:= 0;
4883 gSpectLatchPID1
:= 0;
4884 gSpectLatchPID2
:= 0;
4887 if not g_Net_Connect(Addr
, Port
) then
4889 g_FatalError(_lc
[I_NET_MSG
] + _lc
[I_NET_ERR_CONN
]);
4890 NetState
:= NET_STATE_NONE
;
4894 g_Game_SetLoadingText(_lc
[I_LOAD_SEND_INFO
], 0, False);
4896 g_Game_SetLoadingText(_lc
[I_LOAD_WAIT_INFO
], 0, False);
4901 // fuck! https://www.mail-archive.com/enet-discuss@cubik.org/msg00852.html
4902 // tl;dr: on shitdows, we can get -1 sometimes, and it is *NOT* a failure.
4903 // thank you, enet. let's ignore failures altogether then.
4904 while (enet_host_service(NetHost
, @NetEvent
, 50) > 0) do
4906 if (NetEvent
.kind
= ENET_EVENT_TYPE_RECEIVE
) then
4908 Ptr
:= NetEvent
.packet
^.data
;
4909 if not InMsg
.Init(Ptr
, NetEvent
.packet
^.dataLength
, True) then
4911 enet_packet_destroy(NetEvent
.packet
);
4915 InMsg
.ReadLongWord(); // skip size
4916 MID
:= InMsg
.ReadByte();
4918 if (MID
= NET_MSG_INFO
) and (State
= 0) then
4920 NetMyID
:= InMsg
.ReadByte();
4921 NetPlrUID1
:= InMsg
.ReadWord();
4923 WadName
:= InMsg
.ReadString();
4924 Map
:= InMsg
.ReadString();
4926 gWADHash
:= InMsg
.ReadMD5();
4928 gGameSettings
.GameMode
:= InMsg
.ReadByte();
4929 gSwitchGameMode
:= gGameSettings
.GameMode
;
4930 gGameSettings
.ScoreLimit
:= InMsg
.ReadWord();
4931 gGameSettings
.TimeLimit
:= InMsg
.ReadWord();
4932 gGameSettings
.MaxLives
:= InMsg
.ReadByte();
4933 gGameSettings
.Options
:= TGameOptions(InMsg
.ReadLongWord());
4934 T
:= InMsg
.ReadLongWord();
4936 //newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4937 //if newResPath = '' then
4939 //g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4940 newResPath
:= g_Res_DownloadMapWAD(ExtractFileName(WadName
), gWADHash
);
4941 if newResPath
= '' then
4943 g_FatalError(_lc
[I_NET_ERR_HASH
]);
4944 enet_packet_destroy(NetEvent
.packet
);
4945 NetState
:= NET_STATE_NONE
;
4948 e_LogWritefln('using downloaded map wad [%s] for [%s]`', [newResPath
, WadName
], TMsgType
.Notify
);
4950 //newResPath := ExtractRelativePath(MapsDir, newResPath);
4953 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4954 gPlayer1Settings
.Color
,
4955 gPlayer1Settings
.Team
, False));
4957 if gPlayer1
= nil then
4959 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4961 enet_packet_destroy(NetEvent
.packet
);
4962 NetState
:= NET_STATE_NONE
;
4966 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4967 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4968 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4969 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4970 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4971 gPlayer1
.UID
:= NetPlrUID1
;
4972 gPlayer1
.Reset(True);
4974 if not g_Game_StartMap(false{asMegawad}, newResPath
+ ':\' + Map
, True) then
4976 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [WadName
+ ':\' + Map
]));
4978 enet_packet_destroy(NetEvent
.packet
);
4979 NetState
:= NET_STATE_NONE
;
4987 enet_packet_destroy(NetEvent
.packet
);
4991 enet_packet_destroy(NetEvent
.packet
);
4995 if (NetEvent
.kind
= ENET_EVENT_TYPE_DISCONNECT
) then
4998 if (NetEvent
.data
<= NET_DISC_MAX
) then
4999 g_Console_Add(_lc
[I_NET_MSG_ERROR
] + _lc
[I_NET_ERR_CONN
] + ' ' +
5000 _lc
[TStrings_Locale(Cardinal(I_NET_DISC_NONE
) + NetEvent
.data
)], True);
5007 ProcessLoading(True);
5008 if g_Net_UserRequestExit() then
5017 g_FatalError(_lc
[I_NET_MSG
] + _lc
[I_NET_ERR_CONN
]);
5018 NetState
:= NET_STATE_NONE
;
5023 NetState
:= NET_STATE_GAME
;
5024 MC_SEND_FullStateRequest
;
5025 e_WriteLog('NET: Connection successful.', TMsgType
.Notify
);
5029 lastAsMegaWad
: Boolean = false;
5031 procedure g_Game_ChangeMap(const MapPath
: String);
5035 g_Game_ClearLoading();
5037 Force
:= gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
];
5038 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
5039 if gExitByTrigger
then
5042 gExitByTrigger
:= False;
5044 if not g_Game_StartMap(lastAsMegaWad
, MapPath
, Force
) then
5045 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [MapPath
]));
5048 procedure g_Game_Restart();
5052 if g_Game_IsClient
then
5054 map
:= g_ExtractFileName(gMapInfo
.Map
);
5055 e_LogWritefln('g_Game_Restart: map = "%s" gCurrentMapFileName = "%s"', [map
, gCurrentMapFileName
]);
5059 g_Game_ClearLoading();
5060 g_Game_StartMap(lastAsMegaWad
, Map
, True, gCurrentMapFileName
);
5063 function g_Game_StartMap (asMegawad
: Boolean; Map
: String; Force
: Boolean = False; const oldMapPath
: AnsiString
=''): Boolean;
5065 NewWAD
, ResName
: String;
5069 g_Map_Free((Map
<> gCurrentMapFileName
) and (oldMapPath
<> gCurrentMapFileName
));
5070 g_Player_RemoveAllCorpses();
5072 if (not g_Game_IsClient
) and
5073 (gSwitchGameMode
<> gGameSettings
.GameMode
) and
5074 (gGameSettings
.GameMode
<> GM_SINGLE
) then
5076 if gSwitchGameMode
= GM_CTF
then
5077 gGameSettings
.MaxLives
:= 0;
5078 gGameSettings
.GameMode
:= gSwitchGameMode
;
5081 gSwitchGameMode
:= gGameSettings
.GameMode
;
5083 g_Player_ResetTeams();
5085 lastAsMegaWad
:= asMegawad
;
5086 if isWadPath(Map
) then
5088 NewWAD
:= g_ExtractWadName(Map
);
5089 ResName
:= g_ExtractFileName(Map
);
5090 if g_Game_IsServer
then
5092 nws
:= findDiskWad(NewWAD
);
5093 //writeln('000: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']');
5096 if (length(nws
) = 0) then nws
:= e_FindWad(MegawadDirs
, NewWAD
);
5097 if (length(nws
) = 0) then nws
:= e_FindWad(MapDirs
, NewWAD
);
5101 if (length(nws
) = 0) then nws
:= e_FindWad(MapDirs
, NewWAD
);
5102 if (length(nws
) = 0) then nws
:= e_FindWad(MegawadDirs
, NewWAD
);
5104 //if (length(nws) = 0) then nws := e_FindWad(MapDownloadDirs, NewWAD);
5105 //writeln('001: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']');
5107 if (length(nws
) = 0) then
5109 ResName
:= ''; // failed
5114 if (g_Game_IsNet
) then gWADHash
:= MD5File(nws
);
5115 //writeln('********: nws=', nws, ' : Map=', Map, ' : nw=', NewWAD, ' : resname=', ResName);
5116 g_Game_LoadWAD(NewWAD
);
5121 // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART
5122 NewWAD
:= g_Game_ClientWAD(NewWAD
, gWADHash
);
5127 NewWAD
:= gGameSettings
.WAD
;
5133 //writeln('********: gsw=', gGameSettings.WAD, '; rn=', ResName);
5135 if (ResName
<> '') and (NewWAD
<> '') then
5137 //result := g_Map_Load(gGameSettings.WAD + ':\' + ResName);
5138 result
:= g_Map_Load(NewWAD
+':\'+ResName
);
5142 g_Player_ResetAll(Force
or gLastMap
, gGameSettings
.GameType
= GT_SINGLE
);
5144 gState
:= STATE_NONE
;
5145 g_ActiveWindow
:= nil;
5151 if gGameSettings
.GameMode
= GM_CTF
then
5153 g_Map_ResetFlag(FLAG_RED
);
5154 g_Map_ResetFlag(FLAG_BLUE
);
5155 // CTF, à ôëàãîâ íåò:
5156 if not g_Map_HaveFlagPoints() then
5157 g_SimpleError(_lc
[I_GAME_ERROR_CTF
]);
5162 gState
:= STATE_MENU
;
5167 gPauseMain
:= false;
5168 gPauseHolmes
:= false;
5169 NetTimeToUpdate
:= 1;
5170 NetTimeToReliable
:= 0;
5171 NetTimeToMaster
:= NetMasterRate
;
5172 gSpectLatchPID1
:= 0;
5173 gSpectLatchPID2
:= 0;
5174 gMissionFailed
:= False;
5177 gCoopMonstersKilled
:= 0;
5178 gCoopSecretsFound
:= 0;
5180 gVoteInProgress
:= False;
5181 gVotePassed
:= False;
5187 if not gGameOn
then Exit
;
5189 g_Game_SpectateCenterView();
5191 if g_Game_IsServer
then
5193 if (gGameSettings
.MaxLives
> 0) and (gGameSettings
.WarmupTime
> 0) then
5195 gLMSRespawn
:= LMS_RESPAWN_WARMUP
;
5196 gLMSRespawnTime
:= gTime
+ gGameSettings
.WarmupTime
*1000;
5197 gLMSSoftSpawn
:= True;
5198 if g_Game_IsNet
then
5199 MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, gLMSRespawnTime
- gTime
);
5203 gLMSRespawn
:= LMS_RESPAWN_NONE
;
5204 gLMSRespawnTime
:= 0;
5208 if NetMode
= NET_SERVER
then
5210 // reset full state flags
5211 if NetClients
<> nil then
5212 for I
:= 0 to High(NetClients
) do
5213 NetClients
[I
].FullUpdateSent
:= False;
5215 MH_SEND_GameEvent(NET_EV_MAPSTART
, gGameSettings
.GameMode
, Map
);
5218 g_Net_Slist_ServerMapStarted();
5220 if NetClients
<> nil then
5221 for I
:= 0 to High(NetClients
) do
5222 if NetClients
[I
].Used
then
5224 NetClients
[I
].Voted
:= False;
5225 if NetClients
[I
].RequestedFullUpdate
then
5227 MH_SEND_Everything((NetClients
[I
].State
= NET_STATE_AUTH
), I
);
5228 NetClients
[I
].RequestedFullUpdate
:= False;
5232 g_Net_UnbanNonPerm();
5237 gCoopTotalMonstersKilled
:= 0;
5238 gCoopTotalSecretsFound
:= 0;
5239 gCoopTotalMonsters
:= 0;
5240 gCoopTotalSecrets
:= 0;
5244 g_Game_ExecuteEvent('onmapstart');
5247 procedure SetFirstLevel
;
5251 MapList
:= g_Map_GetMapsList(gGameSettings
.WAD
);
5252 if MapList
= nil then
5255 SortSArray(MapList
);
5256 gNextMap
:= MapList
[Low(MapList
)];
5261 procedure g_Game_ExitLevel(const Map
: AnsiString
);
5265 gCoopTotalMonstersKilled
:= gCoopTotalMonstersKilled
+ gCoopMonstersKilled
;
5266 gCoopTotalSecretsFound
:= gCoopTotalSecretsFound
+ gCoopSecretsFound
;
5267 gCoopTotalMonsters
:= gCoopTotalMonsters
+ gTotalMonsters
;
5268 gCoopTotalSecrets
:= gCoopTotalSecrets
+ gSecretsCount
;
5270 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
5271 if gGameSettings
.GameType
= GT_SINGLE
then
5272 gExit
:= EXIT_ENDLEVELSINGLE
5273 else // Âûøëè â âûõîä â Ñâîåé èãðå
5275 gExit
:= EXIT_ENDLEVELCUSTOM
;
5276 if gGameSettings
.GameMode
= GM_COOP
then
5277 g_Player_RememberAll
;
5279 if not g_Map_Exist(gGameSettings
.WAD
+ ':\' + gNextMap
) then
5282 if gGameSettings
.GameMode
= GM_COOP
then
5285 gStatsPressed
:= True;
5286 gNextMap
:= 'MAP01';
5288 if not g_Map_Exist(gGameSettings
.WAD
+ ':\' + gNextMap
) then
5291 if g_Game_IsNet
then
5293 MH_SEND_GameStats();
5294 MH_SEND_CoopStats();
5300 procedure g_Game_RestartLevel();
5304 if gGameSettings
.GameMode
= GM_SINGLE
then
5309 gExit
:= EXIT_ENDLEVELCUSTOM
;
5310 Map
:= g_ExtractFileName(gMapInfo
.Map
);
5314 function g_Game_ClientWAD (NewWAD
: String; const WHash
: TMD5Digest
): AnsiString
;
5316 gWAD
{, xwad}: String;
5319 if not g_Game_IsClient
then Exit
;
5320 //e_LogWritefln('*** g_Game_ClientWAD: `%s`', [NewWAD]);
5322 gWAD
:= g_Res_DownloadMapWAD(ExtractFileName(NewWAD
), WHash
);
5327 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_WAD
], [ExtractFileName(NewWAD
)]));
5331 e_LogWritefln('using downloaded client map wad [%s] for [%s]', [gWAD
, NewWAD
], TMsgType
.Notify
);
5334 g_Game_LoadWAD(NewWAD
);
5338 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then Exit;
5339 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
5342 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
5343 gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash);
5347 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
5351 NewWAD := ExtractRelativePath(MapsDir, gWAD);
5352 g_Game_LoadWAD(NewWAD);
5356 procedure g_Game_RestartRound(NoMapRestart
: Boolean = False);
5358 i
, n
, nb
, nr
: Integer;
5360 if not g_Game_IsServer
then Exit
;
5361 if gLMSRespawn
= LMS_RESPAWN_NONE
then Exit
;
5362 gLMSRespawn
:= LMS_RESPAWN_NONE
;
5363 gLMSRespawnTime
:= 0;
5366 if (gGameSettings
.GameMode
= GM_COOP
) and not NoMapRestart
then
5368 gMissionFailed
:= True;
5369 g_Game_RestartLevel
;
5373 n
:= 0; nb
:= 0; nr
:= 0;
5374 for i
:= Low(gPlayers
) to High(gPlayers
) do
5375 if (gPlayers
[i
] <> nil) and
5376 ((not gPlayers
[i
].FSpectator
) or gPlayers
[i
].FWantsInGame
or
5377 (gPlayers
[i
] is TBot
)) then
5380 if gPlayers
[i
].Team
= TEAM_RED
then Inc(nr
)
5381 else if gPlayers
[i
].Team
= TEAM_BLUE
then Inc(nb
)
5384 if (n
< 1) or ((gGameSettings
.GameMode
= GM_TDM
) and ((nr
= 0) or (nb
= 0))) then
5386 // wait a second until the fuckers finally decide to join
5387 gLMSRespawn
:= LMS_RESPAWN_WARMUP
;
5388 gLMSRespawnTime
:= gTime
+ gGameSettings
.WarmupTime
*1000;
5389 gLMSSoftSpawn
:= NoMapRestart
;
5390 if g_Game_IsNet
then
5391 MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, gLMSRespawnTime
- gTime
);
5395 g_Player_RemoveAllCorpses
;
5396 g_Game_Message(_lc
[I_MESSAGE_LMS_START
], 144);
5397 if g_Game_IsNet
then
5398 MH_SEND_GameEvent(NET_EV_LMS_START
);
5400 for i
:= Low(gPlayers
) to High(gPlayers
) do
5402 if gPlayers
[i
] = nil then continue
;
5403 if gPlayers
[i
] is TBot
then gPlayers
[i
].FWantsInGame
:= True;
5404 // don't touch normal spectators
5405 if gPlayers
[i
].FSpectator
and not gPlayers
[i
].FWantsInGame
then
5407 gPlayers
[i
].FNoRespawn
:= True;
5408 gPlayers
[i
].Lives
:= 0;
5409 if g_Game_IsNet
then
5410 MH_SEND_PlayerStats(gPlayers
[I
].UID
);
5413 gPlayers
[i
].FNoRespawn
:= False;
5414 gPlayers
[i
].Lives
:= gGameSettings
.MaxLives
;
5415 gPlayers
[i
].Respawn(False, True);
5416 if gGameSettings
.GameMode
= GM_COOP
then
5418 gPlayers
[i
].Frags
:= 0;
5419 gPlayers
[i
].RestoreState
;
5421 if (gPlayer1
= nil) and (gSpectLatchPID1
> 0) then
5422 gPlayer1
:= g_Player_Get(gSpectLatchPID1
);
5423 if (gPlayer2
= nil) and (gSpectLatchPID2
> 0) then
5424 gPlayer2
:= g_Player_Get(gSpectLatchPID2
);
5427 g_Items_RestartRound();
5429 gLMSSoftSpawn
:= False;
5432 function g_Game_GetFirstMap(WAD
: String): String;
5436 MapList
:= g_Map_GetMapsList(WAD
);
5437 if MapList
= nil then
5440 SortSArray(MapList
);
5441 Result
:= MapList
[Low(MapList
)];
5443 if not g_Map_Exist(WAD
+ ':\' + Result
) then
5449 function g_Game_GetNextMap(): String;
5456 MapList
:= g_Map_GetMapsList(gGameSettings
.WAD
);
5457 if MapList
= nil then
5460 Map
:= g_ExtractFileName(gMapInfo
.Map
);
5462 SortSArray(MapList
);
5464 for I
:= Low(MapList
) to High(MapList
) do
5465 if Map
= MapList
[I
] then
5471 if MapIndex
<> -255 then
5473 if MapIndex
= High(MapList
) then
5474 Result
:= MapList
[Low(MapList
)]
5476 Result
:= MapList
[MapIndex
+ 1];
5478 if not g_Map_Exist(gGameSettings
.WAD
+ ':\' + Result
) then Result
:= Map
;
5484 procedure g_Game_NextLevel();
5486 if gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
, GM_COOP
] then
5487 gExit
:= EXIT_ENDLEVELCUSTOM
5490 gExit
:= EXIT_ENDLEVELSINGLE
;
5494 if gNextMap
<> '' then Exit
;
5495 gNextMap
:= g_Game_GetNextMap();
5498 function g_Game_IsTestMap(): Boolean;
5500 result
:= StrEquCI1251(TEST_MAP_NAME
, g_ExtractFileName(gMapInfo
.Map
));
5503 procedure g_Game_DeleteTestMap();
5506 //MapName: AnsiString;
5514 a
:= Pos('.wad:\', toLowerCase1251(gMapToDelete
));
5515 if (a
= 0) then a
:= Pos('.wad:/', toLowerCase1251(gMapToDelete
));
5516 if (a
= 0) then exit
;
5518 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
5519 WadName
:= Copy(gMapToDelete
, 1, a
+3);
5520 Delete(gMapToDelete
, 1, a
+5);
5521 gMapToDelete
:= UpperCase(gMapToDelete
);
5523 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
5526 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
5527 if MapName <> TEST_MAP_NAME then
5530 if not gTempDelete then
5532 time := g_GetFileTime(WadName);
5533 WAD := TWADFile.Create();
5536 if not WAD.ReadFile(WadName) then
5537 begin // Íåò òàêîãî WAD-ôàéëà
5542 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
5544 MapList := WAD.GetResourcesList('');
5546 if MapList <> nil then
5547 for a := 0 to High(MapList) do
5548 if MapList[a] = MapName then
5550 // Óäàëÿåì è ñîõðàíÿåì:
5551 WAD.RemoveResource('', MapName);
5552 WAD.SaveTo(WadName);
5557 g_SetFileTime(WadName, time);
5560 if gTempDelete
then DeleteFile(WadName
);
5563 procedure GameCVars(P
: SSArray
);
5566 stat
: TPlayerStatArray
;
5570 procedure ParseGameFlag(Flag
: TGameOption
; OffMsg
, OnMsg
: TStrings_Locale
; OnMapChange
: Boolean = False);
5574 if Length(P
) <= 1 then
5575 x
:= Flag
in gsGameFlags
5581 then gsGameFlags
+= [Flag
]
5582 else gsGameFlags
-= [Flag
];
5584 if g_Game_IsServer
then
5587 then gGameSettings
.Options
+= [Flag
]
5588 else gGameSettings
.Options
-= [Flag
];
5589 if g_Game_IsNet
then MH_SEND_GameSettings
;
5594 then g_Console_Add(_lc
[OnMsg
])
5595 else g_Console_Add(_lc
[OffMsg
]);
5597 if OnMapChange
and g_Game_IsServer
then
5598 g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5603 cmd
:= LowerCase(P
[0]);
5607 if (Length(P
) > 1) then
5609 a
:= g_Game_TextToMode(P
[1]);
5610 if a
= GM_SINGLE
then a
:= GM_COOP
;
5611 gsGameMode
:= g_Game_ModeToText(a
);
5612 if g_Game_IsServer
then
5614 gSwitchGameMode
:= a
;
5615 if (gGameOn
and (gGameSettings
.GameMode
= GM_SINGLE
)) or
5616 (gState
= STATE_INTERSINGLE
) then
5617 gSwitchGameMode
:= GM_SINGLE
;
5619 gGameSettings
.GameMode
:= gSwitchGameMode
;
5623 if gSwitchGameMode
= gGameSettings
.GameMode
then
5624 g_Console_Add(Format(_lc
[I_MSG_GAMEMODE_CURRENT
],
5625 [g_Game_ModeToText(gGameSettings
.GameMode
)]))
5627 g_Console_Add(Format(_lc
[I_MSG_GAMEMODE_CHANGE
],
5628 [g_Game_ModeToText(gGameSettings
.GameMode
),
5629 g_Game_ModeToText(gSwitchGameMode
)]));
5633 ParseGameFlag(TGameOption
.TEAM_DAMAGE
, I_MSG_FRIENDLY_FIRE_OFF
, I_MSG_FRIENDLY_FIRE_ON
);
5634 'g_friendly_absorb_damage':
5635 ParseGameFlag(TGameOption
.TEAM_ABSORB_DAMAGE
, I_MSG_FRIENDLY_ABSORB_DAMAGE_OFF
, I_MSG_FRIENDLY_ABSORB_DAMAGE_ON
);
5636 'g_friendly_hit_trace':
5637 ParseGameFlag(TGameOption
.TEAM_HIT_TRACE
, I_MSG_FRIENDLY_HIT_TRACE_OFF
, I_MSG_FRIENDLY_HIT_TRACE_ON
);
5638 'g_friendly_hit_projectile':
5639 ParseGameFlag(TGameOption
.TEAM_HIT_PROJECTILE
, I_MSG_FRIENDLY_PROJECT_TRACE_OFF
, I_MSG_FRIENDLY_PROJECT_TRACE_ON
);
5640 'g_items_all_respawn_random':
5641 ParseGameFlag(TGameOption
.ITEM_ALL_RANDOM
, I_MSG_ITEM_ALL_RANDOM_OFF
, I_MSG_ITEM_ALL_RANDOM_ON
, False);
5642 'g_items_help_respawn_random':
5643 ParseGameFlag(TGameOption
.ITEM_LIFE_RANDOM
, I_MSG_ITEM_LIFE_RANDOM_OFF
, I_MSG_ITEM_LIFE_RANDOM_ON
, False);
5644 'g_items_ammo_respawn_random':
5645 ParseGameFlag(TGameOption
.ITEM_AMMO_RANDOM
, I_MSG_ITEM_AMMO_RANDOM_OFF
, I_MSG_ITEM_AMMO_RANDOM_ON
, False);
5646 'g_items_weapon_respawn_random':
5647 ParseGameFlag(TGameOption
.ITEM_WEAPON_RANDOM
, I_MSG_ITEM_WEAPON_RANDOM_OFF
, I_MSG_ITEM_WEAPON_RANDOM_ON
);
5648 'g_powerup_randomize_respawn':
5649 ParseGameFlag(TGameOption
.POWERUP_RANDOM
, I_MSG_POWERUP_RANDOM_OFF
, I_MSG_POWERUP_RANDOM_ON
, False);
5651 ParseGameFlag(TGameOption
.WEAPONS_STAY
, I_MSG_WEAPONSTAY_OFF
, I_MSG_WEAPONSTAY_ON
);
5653 ParseGameFlag(TGameOption
.ALLOW_EXIT
, I_MSG_ALLOWEXIT_OFF
, I_MSG_ALLOWEXIT_ON
, True);
5655 ParseGameFlag(TGameOption
.MONSTERS
, I_MSG_ALLOWMON_OFF
, I_MSG_ALLOWMON_ON
, True);
5657 ParseGameFlag(TGameOption
.ALLOW_DROP_FLAG
, I_MSG_ALLOWDROPFLAG_OFF
, I_MSG_ALLOWDROPFLAG_ON
);
5659 ParseGameFlag(TGameOption
.THROW_FLAG
, I_MSG_THROWFLAG_OFF
, I_MSG_THROWFLAG_ON
);
5661 ParseGameFlag(TGameOption
.BOTS_VS_PLAYERS
, I_MSG_BOTSVSPLAYERS_OFF
, I_MSG_BOTSVSPLAYERS_ON
);
5663 ParseGameFlag(TGameOption
.BOTS_VS_MONSTERS
, I_MSG_BOTSVSMONSTERS_OFF
, I_MSG_BOTSVSMONSTERS_ON
);
5665 ParseGameFlag(TGameOption
.DM_KEYS
, I_MSG_DMKEYS_OFF
, I_MSG_DMKEYS_ON
, True);
5667 'g_gameflags': begin
5668 if Length(P
) > 1 then
5670 gsGameFlags
:= TGameOptions(StrToDWordDef(P
[1], LongWord(gsGameFlags
)));
5671 if g_Game_IsServer
then
5673 gGameSettings
.Options
:= gsGameFlags
;
5674 if g_Game_IsNet
then MH_SEND_GameSettings
;
5678 g_Console_Add(Format('%s %u', [cmd
, LongWord(gsGameFlags
)]));
5681 'g_warmup_time': begin
5682 if Length(P
) > 1 then
5684 gsWarmupTime
:= nclamp(StrToIntDef(P
[1], gsWarmupTime
), 0, $FFFF);
5685 if g_Game_IsServer
then
5687 gGameSettings
.WarmupTime
:= gsWarmupTime
;
5688 // extend warmup if it's already going
5689 if gLMSRespawn
= LMS_RESPAWN_WARMUP
then
5691 gLMSRespawnTime
:= gTime
+ gsWarmupTime
* 1000;
5692 if g_Game_IsNet
then MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, gLMSRespawnTime
- gTime
);
5694 if g_Game_IsNet
then MH_SEND_GameSettings
;
5698 g_Console_Add(Format(_lc
[I_MSG_WARMUP
], [Integer(gsWarmupTime
)]));
5699 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5702 'g_spawn_invul': begin
5703 if Length(P
) > 1 then
5705 gsSpawnInvul
:= nclamp(StrToIntDef(P
[1], gsSpawnInvul
), 0, $FFFF);
5706 if g_Game_IsServer
then
5708 gGameSettings
.SpawnInvul
:= gsSpawnInvul
;
5709 if g_Game_IsNet
then MH_SEND_GameSettings
;
5713 g_Console_Add(Format('%s %d', [cmd
, Integer(gsSpawnInvul
)]));
5716 'g_item_respawn_time': begin
5717 if Length(P
) > 1 then
5719 gsItemRespawnTime
:= nclamp(StrToIntDef(P
[1], gsItemRespawnTime
), 0, $FFFF);
5720 if g_Game_IsServer
then
5722 gGameSettings
.ItemRespawnTime
:= gsItemRespawnTime
;
5723 if g_Game_IsNet
then MH_SEND_GameSettings
;
5727 g_Console_Add(Format('%s %d', [cmd
, Integer(gsItemRespawnTime
)]));
5728 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5731 'g_item_time_random': begin
5732 if Length(P
) > 1 then
5734 gsItemRespawnRandom
:= nclamp(StrToIntDef(P
[1], gsItemRespawnRandom
), 0, $FFFF);
5735 if g_Game_IsServer
then
5737 gGameSettings
.ItemRespawnRandom
:= gsItemRespawnRandom
;
5738 if g_Game_IsNet
then MH_SEND_GameSettings
;
5742 g_Console_Add(Format('%s %d', [cmd
, Integer(gsItemRespawnRandom
)]));
5743 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5746 'g_powerup_respawn_time': begin
5747 if Length(P
) > 1 then
5749 gsPowerupRespawnTime
:= nclamp(StrToIntDef(P
[1], gsPowerupRespawnTime
), 0, $FFFF);
5750 if g_Game_IsServer
then
5752 gGameSettings
.PowerupRespawnTime
:= gsPowerupRespawnTime
;
5753 if g_Game_IsNet
then MH_SEND_GameSettings
;
5757 g_Console_Add(Format('%s %d', [cmd
, Integer(gsPowerupRespawnTime
)]));
5758 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5761 'g_powerup_time_random': begin
5762 if Length(P
) > 1 then
5764 gsPowerupRespawnRandom
:= nclamp(StrToIntDef(P
[1], gsPowerupRespawnRandom
), 0, $FFFF);
5765 if g_Game_IsServer
then
5767 gGameSettings
.PowerupRespawnRandom
:= gsPowerupRespawnRandom
;
5768 if g_Game_IsNet
then MH_SEND_GameSettings
;
5772 g_Console_Add(Format('%s %d', [cmd
, Integer(gsPowerupRespawnRandom
)]));
5773 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5776 'sv_intertime': begin
5777 if (Length(P
) > 1) then
5778 gDefInterTime
:= Min(Max(StrToIntDef(P
[1], gDefInterTime
), -1), 120);
5780 g_Console_Add(cmd
+ ' = ' + IntToStr(gDefInterTime
));
5783 'g_max_particles': begin
5784 if Length(p
) = 2 then
5786 a
:= Max(0, StrToIntDef(p
[1], 0));
5789 else if Length(p
) = 1 then
5791 e_LogWritefln('%s', [g_GFX_GetMax()])
5795 e_LogWritefln('usage: %s <n>', [cmd
])
5799 'g_max_shells': begin
5800 if Length(p
) = 2 then
5802 a
:= Max(0, StrToIntDef(p
[1], 0));
5805 else if Length(p
) = 1 then
5807 e_LogWritefln('%s', [g_Shells_GetMax()])
5811 e_LogWritefln('usage: %s <n>', [cmd
])
5816 if Length(p
) = 2 then
5818 a
:= Max(0, StrToIntDef(p
[1], 0));
5821 else if Length(p
) = 1 then
5823 e_LogWritefln('%s', [g_Gibs_GetMax()])
5827 e_LogWritefln('usage: %s <n>', [cmd
])
5831 'g_max_corpses': begin
5832 if Length(p
) = 2 then
5834 a
:= Max(0, StrToIntDef(p
[1], 0));
5837 else if Length(p
) = 1 then
5839 e_LogWritefln('%s', [g_Corpses_GetMax()])
5843 e_LogWritefln('usage: %s <n>', [cmd
])
5847 'g_force_model': begin
5848 if Length(p
) = 2 then
5850 a
:= StrToIntDef(p
[1], 0);
5851 g_Force_Model_Set(a
);
5852 if (g_Force_Model_Get() <> 0) and (gPlayers
<> nil) then
5854 for a
:= Low(gPlayers
) to High(gPlayers
) do
5856 if (gPlayers
[a
] <> nil) then
5858 if (gPlayers
[a
].UID
= gPlayer1
.UID
) then
5860 else if (gPlayer2
<> nil) and (gPlayers
[a
].UID
= gPlayer2
.UID
) then
5862 gPlayers
[a
].setModel(g_Forced_Model_GetName());
5866 else if (g_Force_Model_Get() = 0) and (gPlayers
<> nil) then
5868 for a
:= Low(gPlayers
) to High(gPlayers
) do
5870 if (gPlayers
[a
] <> nil) then
5872 if (gPlayers
[a
].UID
= gPlayer1
.UID
) then
5874 else if (gPlayer2
<> nil) and (gPlayers
[a
].UID
= gPlayer2
.UID
) then
5876 gPlayers
[a
].setModel(gPlayers
[a
].FActualModelName
);
5883 'g_force_model_name': begin
5884 if (Length(P
) > 1) then
5886 cmd
:= b_Text_Unformat(P
[1]);
5887 g_Forced_Model_SetName(cmd
);
5888 if (g_Force_Model_Get() <> 0) and (gPlayers
<> nil) then
5890 for a
:= Low(gPlayers
) to High(gPlayers
) do
5892 if (gPlayers
[a
] <> nil) then
5894 if (gPlayers
[a
].UID
= gPlayer1
.UID
) then
5896 else if (gPlayer2
<> nil) and (gPlayers
[a
].UID
= gPlayer2
.UID
) then
5898 gPlayers
[a
].setModel(g_Forced_Model_GetName());
5905 'g_scorelimit': begin
5906 if Length(P
) > 1 then
5908 gsScoreLimit
:= nclamp(StrToIntDef(P
[1], gsScoreLimit
), 0, $FFFF);
5910 if g_Game_IsServer
then
5913 if gGameSettings
.GameMode
= GM_DM
then
5915 stat
:= g_Player_GetStats();
5917 for a
:= 0 to High(stat
) do
5918 if stat
[a
].Frags
> b
then
5922 b
:= Max(gTeamStat
[TEAM_RED
].Score
, gTeamStat
[TEAM_BLUE
].Score
);
5924 // if someone has a higher score, set it to that instead
5925 gsScoreLimit
:= max(gsScoreLimit
, b
);
5926 gGameSettings
.ScoreLimit
:= gsScoreLimit
;
5928 if g_Game_IsNet
then MH_SEND_GameSettings
;
5932 g_Console_Add(Format(_lc
[I_MSG_SCORE_LIMIT
], [Integer(gsScoreLimit
)]));
5935 'g_timelimit': begin
5936 if Length(P
) > 1 then
5938 gsTimeLimit
:= nclamp(StrToIntDef(P
[1], gsTimeLimit
), 0, $FFFF);
5939 if g_Game_IsServer
then
5941 gGameSettings
.TimeLimit
:= gsTimeLimit
;
5942 if g_Game_IsNet
then MH_SEND_GameSettings
;
5945 g_Console_Add(Format(_lc
[I_MSG_TIME_LIMIT
],
5946 [gsTimeLimit
div 3600,
5947 (gsTimeLimit
div 60) mod 60,
5948 gsTimeLimit
mod 60]));
5952 if Length(P
) > 1 then
5953 gMaxBots
:= nclamp(StrToIntDef(P
[1], gMaxBots
), 0, 127);
5954 g_Console_Add('g_max_bots = ' + IntToStr(gMaxBots
));
5958 if Length(P
) > 1 then
5960 gsMaxLives
:= nclamp(StrToIntDef(P
[1], gsMaxLives
), 0, $FFFF);
5961 if g_Game_IsServer
then
5963 gGameSettings
.MaxLives
:= gsMaxLives
;
5964 if g_Game_IsNet
then MH_SEND_GameSettings
;
5968 g_Console_Add(Format(_lc
[I_MSG_LIVES
], [Integer(gsMaxLives
)]));
5973 procedure PlayerSettingsCVars(P
: SSArray
);
5978 function ParseTeam(s
: string): Byte;
5982 'red', '1': result
:= TEAM_RED
;
5983 'blue', '2': result
:= TEAM_BLUE
;
5984 else result
:= TEAM_NONE
;
5988 cmd
:= LowerCase(P
[0]);
5992 if (Length(P
) > 1) then
5994 gPlayer1Settings
.Name
:= b_Text_Unformat(P
[1]);
5995 if g_Game_IsClient
then
5996 MC_SEND_PlayerSettings
5997 else if gGameOn
and (gPlayer1
<> nil) then
5999 gPlayer1
.Name
:= b_Text_Unformat(P
[1]);
6000 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer1
.UID
);
6006 if (Length(P
) > 1) then
6008 gPlayer2Settings
.Name
:= b_Text_Unformat(P
[1]);
6009 if g_Game_IsClient
then
6010 MC_SEND_PlayerSettings
6011 else if gGameOn
and (gPlayer2
<> nil) then
6013 gPlayer2
.Name
:= b_Text_Unformat(P
[1]);
6014 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer2
.UID
);
6020 if Length(P
) > 3 then
6022 gPlayer1Settings
.Color
:= _RGB(EnsureRange(StrToIntDef(P
[1], 0), 0, 255),
6023 EnsureRange(StrToIntDef(P
[2], 0), 0, 255),
6024 EnsureRange(StrToIntDef(P
[3], 0), 0, 255));
6025 if g_Game_IsClient
then
6026 MC_SEND_PlayerSettings
6027 else if gGameOn
and (gPlayer1
<> nil) then
6029 gPlayer1
.SetColor(gPlayer1Settings
.Color
);
6030 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer1
.UID
);
6036 if Length(P
) > 3 then
6038 gPlayer2Settings
.Color
:= _RGB(EnsureRange(StrToIntDef(P
[1], 0), 0, 255),
6039 EnsureRange(StrToIntDef(P
[2], 0), 0, 255),
6040 EnsureRange(StrToIntDef(P
[3], 0), 0, 255));
6041 if g_Game_IsClient
then
6042 MC_SEND_PlayerSettings
6043 else if gGameOn
and (gPlayer2
<> nil) then
6045 gPlayer2
.SetColor(gPlayer2Settings
.Color
);
6046 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer2
.UID
);
6052 if (Length(P
) > 1) then
6054 gPlayer1Settings
.Model
:= P
[1];
6055 if g_Game_IsClient
then
6056 MC_SEND_PlayerSettings
6057 else if gGameOn
and (gPlayer1
<> nil) then
6059 gPlayer1
.FActualModelName
:= gPlayer1Settings
.Model
;
6060 gPlayer1
.SetModel(gPlayer1Settings
.Model
);
6061 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer1
.UID
);
6067 if (Length(P
) > 1) then
6069 gPlayer2Settings
.Model
:= P
[1];
6070 if g_Game_IsClient
then
6071 MC_SEND_PlayerSettings
6072 else if gGameOn
and (gPlayer2
<> nil) then
6074 gPlayer2
.FActualModelName
:= gPlayer2Settings
.Model
;
6075 gPlayer2
.SetModel(gPlayer2Settings
.Model
);
6076 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer2
.UID
);
6082 // TODO: switch teams if in game or store this separately
6083 if (Length(P
) > 1) then
6085 team
:= ParseTeam(P
[1]);
6086 if team
= TEAM_NONE
then
6087 g_Console_Add('expected ''red'', ''blue'', 1 or 2')
6088 else if not gGameOn
and not g_Game_IsNet
then
6089 gPlayer1Settings
.Team
:= team
6091 g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
6096 // TODO: switch teams if in game or store this separately
6097 if (Length(P
) > 1) then
6099 team
:= ParseTeam(P
[1]);
6100 if team
= TEAM_NONE
then
6101 g_Console_Add('expected ''red'', ''blue'', 1 or 2')
6102 else if not gGameOn
and not g_Game_IsNet
then
6103 gPlayer2Settings
.Team
:= team
6105 g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
6110 if (Length(P
) = 2) then
6111 gPlayer1Settings
.WeaponSwitch
:= EnsureRange(StrTointDef(P
[1], 0), 0, 2);
6115 if (Length(P
) = 2) then
6116 gPlayer2Settings
.WeaponSwitch
:= EnsureRange(StrTointDef(P
[1], 0), 0, 2);
6120 if (Length(P
) = 2) then
6121 gPlayer1Settings
.SwitchToEmpty
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6125 if (Length(P
) = 2) then
6126 gPlayer2Settings
.SwitchToEmpty
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6130 if (Length(P
) = 2) then
6131 gPlayer1Settings
.SkipIronFist
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6135 if (Length(P
) = 2) then
6136 gPlayer2Settings
.SkipIronFist
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6138 'p1_priority_ironfist':
6140 if (Length(P
) = 2) then
6141 gPlayer1Settings
.WeaponPreferences
[WEAPON_IRONFIST
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6143 'p2_priority_ironfist':
6145 if (Length(P
) = 2) then
6146 gPlayer2Settings
.WeaponPreferences
[WEAPON_IRONFIST
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6150 if (Length(P
) = 2) then
6151 gPlayer1Settings
.WeaponPreferences
[WEAPON_SAW
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6155 if (Length(P
) = 2) then
6156 gPlayer2Settings
.WeaponPreferences
[WEAPON_SAW
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6158 'p1_priority_pistol':
6160 if (Length(P
) = 2) then
6161 gPlayer1Settings
.WeaponPreferences
[WEAPON_PISTOL
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6163 'p2_priority_pistol':
6165 if (Length(P
) = 2) then
6166 gPlayer2Settings
.WeaponPreferences
[WEAPON_PISTOL
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6168 'p1_priority_shotgun1':
6170 if (Length(P
) = 2) then
6171 gPlayer1Settings
.WeaponPreferences
[WEAPON_SHOTGUN1
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6173 'p2_priority_shotgun1':
6175 if (Length(P
) = 2) then
6176 gPlayer2Settings
.WeaponPreferences
[WEAPON_SHOTGUN1
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6178 'p1_priority_shotgun2':
6180 if (Length(P
) = 2) then
6181 gPlayer1Settings
.WeaponPreferences
[WEAPON_SHOTGUN2
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6183 'p2_priority_shotgun2':
6185 if (Length(P
) = 2) then
6186 gPlayer2Settings
.WeaponPreferences
[WEAPON_SHOTGUN2
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6188 'p1_priority_chaingun':
6190 if (Length(P
) = 2) then
6191 gPlayer1Settings
.WeaponPreferences
[WEAPON_CHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6193 'p2_priority_chaingun':
6195 if (Length(P
) = 2) then
6196 gPlayer2Settings
.WeaponPreferences
[WEAPON_CHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6198 'p1_priority_rocketlauncher':
6200 if (Length(P
) = 2) then
6201 gPlayer1Settings
.WeaponPreferences
[WEAPON_ROCKETLAUNCHER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6203 'p2_priority_rocketlauncher':
6205 if (Length(P
) = 2) then
6206 gPlayer2Settings
.WeaponPreferences
[WEAPON_ROCKETLAUNCHER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6208 'p1_priority_plasma':
6210 if (Length(P
) = 2) then
6211 gPlayer1Settings
.WeaponPreferences
[WEAPON_PLASMA
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6213 'p2_priority_plasma':
6215 if (Length(P
) = 2) then
6216 gPlayer2Settings
.WeaponPreferences
[WEAPON_PLASMA
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6220 if (Length(P
) = 2) then
6221 gPlayer1Settings
.WeaponPreferences
[WEAPON_BFG
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6225 if (Length(P
) = 2) then
6226 gPlayer2Settings
.WeaponPreferences
[WEAPON_BFG
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6228 'p1_priority_superchaingun':
6230 if (Length(P
) = 2) then
6231 gPlayer1Settings
.WeaponPreferences
[WEAPON_SUPERCHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6233 'p2_priority_superchaingun':
6235 if (Length(P
) = 2) then
6236 gPlayer2Settings
.WeaponPreferences
[WEAPON_SUPERCHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6238 'p1_priority_flamethrower':
6240 if (Length(P
) = 2) then
6241 gPlayer1Settings
.WeaponPreferences
[WEAPON_FLAMETHROWER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6243 'p2_priority_flamethrower':
6245 if (Length(P
) = 2) then
6246 gPlayer2Settings
.WeaponPreferences
[WEAPON_FLAMETHROWER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6248 'p1_priority_berserk':
6250 if (Length(P
) = 2) then
6251 gPlayer1Settings
.WeaponPreferences
[WP_LAST
+1] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6253 'p2_priority_berserk':
6255 if (Length(P
) = 2) then
6256 gPlayer2Settings
.WeaponPreferences
[WP_LAST
+1] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6261 procedure PrintHeapStats();
6265 hs
:= GetFPCHeapStatus();
6266 e_LogWriteLn ('v===== heap status =====v');
6267 e_LogWriteFln('max heap size = %d k', [hs
.MaxHeapSize
div 1024]);
6268 e_LogWriteFln('max heap used = %d k', [hs
.MaxHeapUsed
div 1024]);
6269 e_LogWriteFln('cur heap size = %d k', [hs
.CurrHeapSize
div 1024]);
6270 e_LogWriteFln('cur heap used = %d k', [hs
.CurrHeapUsed
div 1024]);
6271 e_LogWriteFln('cur heap free = %d k', [hs
.CurrHeapFree
div 1024]);
6272 e_LogWriteLn ('^=======================^');
6275 procedure DebugCommands(P
: SSArray
);
6282 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
6283 if {gDebugMode}conIsCheatsEnabled
then
6285 cmd
:= LowerCase(P
[0]);
6286 if cmd
= 'd_window' then
6288 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth
, gScreenHeight
]));
6289 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX
, gWinSizeY
]));
6291 else if cmd
= 'd_sounds' then
6293 if (Length(P
) > 1) and
6294 ((P
[1] = '1') or (P
[1] = '0')) then
6295 g_Debug_Sounds
:= (P
[1][1] = '1');
6297 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds
)]));
6299 else if cmd
= 'd_frames' then
6301 if (Length(P
) > 1) and
6302 ((P
[1] = '1') or (P
[1] = '0')) then
6303 g_Debug_Frames
:= (P
[1][1] = '1');
6305 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames
)]));
6307 else if cmd
= 'd_winmsg' then
6309 if (Length(P
) > 1) and
6310 ((P
[1] = '1') or (P
[1] = '0')) then
6311 g_Debug_WinMsgs
:= (P
[1][1] = '1');
6313 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs
)]));
6315 else if (cmd
= 'd_monoff') and not g_Game_IsNet
then
6317 if (Length(P
) > 1) and
6318 ((P
[1] = '1') or (P
[1] = '0')) then
6319 g_Debug_MonsterOff
:= (P
[1][1] = '1');
6321 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff
)]));
6323 else if (cmd
= 'd_botoff') and not g_Game_IsNet
then
6325 if Length(P
) > 1 then
6327 '0': g_debug_BotAIOff
:= 0;
6328 '1': g_debug_BotAIOff
:= 1;
6329 '2': g_debug_BotAIOff
:= 2;
6330 '3': g_debug_BotAIOff
:= 3;
6333 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff
]));
6335 else if cmd
= 'd_monster' then
6337 if gGameOn
and (gPlayer1
<> nil) and (gPlayer1
.alive
) and (not g_Game_IsNet
) then
6338 if Length(P
) < 2 then
6340 g_Console_Add(cmd
+ ' [ID | Name] [behaviour]');
6341 g_Console_Add('ID | Name');
6342 for b
:= MONSTER_DEMON
to MONSTER_MAN
do
6343 g_Console_Add(Format('%2d | %s', [b
, g_Mons_NameByTypeId(b
)]));
6344 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
6347 a
:= StrToIntDef(P
[1], 0);
6348 if (a
< MONSTER_DEMON
) or (a
> MONSTER_MAN
) then
6349 a
:= g_Mons_TypeIdByName(P
[1]);
6351 if (a
< MONSTER_DEMON
) or (a
> MONSTER_MAN
) then
6352 g_Console_Add(Format(_lc
[I_MSG_NO_MONSTER
], [P
[1]]))
6355 with gPlayer1
.Obj
do
6357 mon
:= g_Monsters_Create(a
,
6358 X
+ Rect
.X
+ (Rect
.Width
div 2),
6359 Y
+ Rect
.Y
+ Rect
.Height
,
6360 gPlayer1
.Direction
, True);
6362 if (Length(P
) > 2) and (mon
<> nil) then
6364 if (CompareText(P
[2], 'normal') = 0) then mon
.MonsterBehaviour
:= BH_NORMAL
6365 else if (CompareText(P
[2], 'killer') = 0) then mon
.MonsterBehaviour
:= BH_KILLER
6366 else if (CompareText(P
[2], 'maniac') = 0) then mon
.MonsterBehaviour
:= BH_MANIAC
6367 else if (CompareText(P
[2], 'insane') = 0) then mon
.MonsterBehaviour
:= BH_INSANE
6368 else if (CompareText(P
[2], 'cannibal') = 0) then mon
.MonsterBehaviour
:= BH_CANNIBAL
6369 else if (CompareText(P
[2], 'good') = 0) then mon
.MonsterBehaviour
:= BH_GOOD
6370 else if (CompareText(P
[2], 'friend') = 0) then mon
.MonsterBehaviour
:= BH_GOOD
6371 else if (CompareText(P
[2], 'friendly') = 0) then mon
.MonsterBehaviour
:= BH_GOOD
6372 else mon
.MonsterBehaviour
:= Min(Max(StrToIntDef(P
[2], BH_NORMAL
), BH_NORMAL
), BH_GOOD
);
6377 else if (cmd
= 'd_health') then
6379 if (Length(P
) > 1) and
6380 ((P
[1] = '1') or (P
[1] = '0')) then
6381 g_debug_HealthBar
:= (P
[1][1] = '1');
6383 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar
)]));
6385 else if (cmd
= 'd_player') then
6387 if (Length(P
) > 1) and
6388 ((P
[1] = '1') or (P
[1] = '0')) then
6389 g_debug_Player
:= (P
[1][1] = '1');
6391 g_Console_Add(Format(cmd
+ ' is %d', [Byte(g_Debug_Player
)]));
6393 else if (cmd
= 'd_mem') then
6399 g_Console_Add(_lc
[I_MSG_NOT_DEBUG
]);
6403 procedure GameCheats(P
: SSArray
);
6409 if (not gGameOn
) or (not conIsCheatsEnabled
) then
6411 g_Console_Add('not available');
6417 g_Console_Add('where is the player?!');
6420 cmd
:= LowerCase(P
[0]);
6424 plr
.GodMode
:= not plr
.GodMode
;
6425 if plr
.GodMode
then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
6428 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
6429 if cmd
= 'give' then
6431 if length(P
) < 2 then begin g_Console_Add('give what?!'); exit
; end;
6432 for f
:= 1 to High(P
) do
6434 cmd
:= LowerCase(P
[f
]);
6435 if cmd
= 'health' then begin plr
.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue
; end;
6436 if (cmd
= 'all') {or (cmd = 'weapons')} then begin plr
.TankRamboCheats(False); g_Console_Add('player got the gifts'); continue
; end;
6437 if cmd
= 'exit' then
6439 if gTriggers
<> nil then
6441 for a
:= 0 to High(gTriggers
) do
6443 if gTriggers
[a
].TriggerType
= TRIGGER_EXIT
then
6445 g_Console_Add('player left the map');
6446 gExitByTrigger
:= True;
6447 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
6448 g_Game_ExitLevel(gTriggers
[a
].tgcMap
);
6456 if cmd
= 'air' then begin plr
.GiveItem(ITEM_OXYGEN
); g_Console_Add('player got some air'); continue
; end;
6457 if cmd
= 'jetpack' then begin plr
.GiveItem(ITEM_JETPACK
); g_Console_Add('player got a jetpack'); continue
; end;
6458 if cmd
= 'suit' then begin plr
.GiveItem(ITEM_SUIT
); g_Console_Add('player got an envirosuit'); continue
; end;
6459 if cmd
= 'berserk' then begin plr
.GiveItem(ITEM_MEDKIT_BLACK
); g_Console_Add('player got a berserk pack'); continue
; end;
6460 if cmd
= 'backpack' then begin plr
.GiveItem(ITEM_AMMO_BACKPACK
); g_Console_Add('player got a backpack'); continue
; end;
6462 if cmd
= 'helmet' then begin plr
.GiveItem(ITEM_HELMET
); g_Console_Add('player got a helmet'); continue
; end;
6463 if cmd
= 'bottle' then begin plr
.GiveItem(ITEM_BOTTLE
); g_Console_Add('player got a bottle of health'); continue
; end;
6465 if cmd
= 'stimpack' then begin plr
.GiveItem(ITEM_MEDKIT_SMALL
); g_Console_Add('player got a stimpack'); continue
; end;
6466 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;
6468 if cmd
= 'greenarmor' then begin plr
.GiveItem(ITEM_ARMOR_GREEN
); g_Console_Add('player got a security armor'); continue
; end;
6469 if cmd
= 'bluearmor' then begin plr
.GiveItem(ITEM_ARMOR_BLUE
); g_Console_Add('player got a combat armor'); continue
; end;
6471 if (cmd
= 'soulsphere') or (cmd
= 'soul') then begin plr
.GiveItem(ITEM_SPHERE_BLUE
); g_Console_Add('player got a soul sphere'); continue
; end;
6472 if (cmd
= 'megasphere') or (cmd
= 'mega') then begin plr
.GiveItem(ITEM_SPHERE_WHITE
); g_Console_Add('player got a megasphere'); continue
; end;
6474 if (cmd
= 'invul') or (cmd
= 'invulnerability') then begin plr
.GiveItem(ITEM_INVUL
); g_Console_Add('player got invulnerability'); continue
; end;
6475 if (cmd
= 'invis') or (cmd
= 'invisibility') then begin plr
.GiveItem(ITEM_INVIS
); g_Console_Add('player got invisibility'); continue
; end;
6477 if cmd
= 'redkey' then begin plr
.GiveItem(ITEM_KEY_RED
); g_Console_Add('player got the red key'); continue
; end;
6478 if cmd
= 'greenkey' then begin plr
.GiveItem(ITEM_KEY_GREEN
); g_Console_Add('player got the green key'); continue
; end;
6479 if cmd
= 'bluekey' then begin plr
.GiveItem(ITEM_KEY_BLUE
); g_Console_Add('player got the blue key'); continue
; end;
6481 if (cmd
= 'shotgun') or (cmd
= 'sg') then begin plr
.GiveItem(ITEM_WEAPON_SHOTGUN1
); g_Console_Add('player got a shotgun'); continue
; end;
6482 if (cmd
= 'supershotgun') or (cmd
= 'ssg') then begin plr
.GiveItem(ITEM_WEAPON_SHOTGUN2
); g_Console_Add('player got a supershotgun'); continue
; end;
6483 if cmd
= 'chaingun' then begin plr
.GiveItem(ITEM_WEAPON_CHAINGUN
); g_Console_Add('player got a chaingun'); continue
; end;
6484 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;
6485 if cmd
= 'plasmagun' then begin plr
.GiveItem(ITEM_WEAPON_PLASMA
); g_Console_Add('player got a plasma gun'); continue
; end;
6486 if cmd
= 'bfg' then begin plr
.GiveItem(ITEM_WEAPON_BFG
); g_Console_Add('player got a BFG-9000'); continue
; end;
6488 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;
6489 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;
6490 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;
6491 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;
6492 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;
6493 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;
6495 if cmd
= 'superchaingun' then begin plr
.GiveItem(ITEM_WEAPON_SUPERCHAINGUN
); g_Console_Add('player got a superchaingun'); continue
; end;
6496 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;
6498 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;
6499 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;
6501 if cmd
= 'chainsaw' then begin plr
.GiveItem(ITEM_WEAPON_SAW
); g_Console_Add('player got a chainsaw'); continue
; end;
6503 if cmd
= 'ammo' then
6505 plr
.GiveItem(ITEM_AMMO_SHELLS_BOX
);
6506 plr
.GiveItem(ITEM_AMMO_BULLETS_BOX
);
6507 plr
.GiveItem(ITEM_AMMO_CELL_BIG
);
6508 plr
.GiveItem(ITEM_AMMO_ROCKET_BOX
);
6509 plr
.GiveItem(ITEM_AMMO_FUELCAN
);
6510 g_Console_Add('player got some ammo');
6514 if cmd
= 'clip' then begin plr
.GiveItem(ITEM_AMMO_BULLETS
); g_Console_Add('player got some bullets'); continue
; end;
6515 if cmd
= 'bullets' then begin plr
.GiveItem(ITEM_AMMO_BULLETS_BOX
); g_Console_Add('player got a box of bullets'); continue
; end;
6517 if cmd
= 'shells' then begin plr
.GiveItem(ITEM_AMMO_SHELLS
); g_Console_Add('player got some shells'); continue
; end;
6518 if cmd
= 'shellbox' then begin plr
.GiveItem(ITEM_AMMO_SHELLS_BOX
); g_Console_Add('player got a box of shells'); continue
; end;
6520 if cmd
= 'cells' then begin plr
.GiveItem(ITEM_AMMO_CELL
); g_Console_Add('player got some cells'); continue
; end;
6521 if cmd
= 'battery' then begin plr
.GiveItem(ITEM_AMMO_CELL_BIG
); g_Console_Add('player got cell battery'); continue
; end;
6523 if cmd
= 'rocket' then begin plr
.GiveItem(ITEM_AMMO_ROCKET
); g_Console_Add('player got a rocket'); continue
; end;
6524 if cmd
= 'rocketbox' then begin plr
.GiveItem(ITEM_AMMO_ROCKET_BOX
); g_Console_Add('player got some rockets'); continue
; end;
6526 if (cmd
= 'fuel') or (cmd
= 'fuelcan') then begin plr
.GiveItem(ITEM_AMMO_FUELCAN
); g_Console_Add('player got fuel canister'); continue
; end;
6528 if cmd
= 'weapons' then
6530 plr
.GiveItem(ITEM_WEAPON_SHOTGUN1
);
6531 plr
.GiveItem(ITEM_WEAPON_SHOTGUN2
);
6532 plr
.GiveItem(ITEM_WEAPON_CHAINGUN
);
6533 plr
.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER
);
6534 plr
.GiveItem(ITEM_WEAPON_PLASMA
);
6535 plr
.GiveItem(ITEM_WEAPON_BFG
);
6536 g_Console_Add('player got weapons');
6540 if cmd
= 'keys' then
6542 plr
.GiveItem(ITEM_KEY_RED
);
6543 plr
.GiveItem(ITEM_KEY_GREEN
);
6544 plr
.GiveItem(ITEM_KEY_BLUE
);
6545 g_Console_Add('player got all keys');
6549 g_Console_Add('i don''t know how to give '''+cmd
+'''!');
6554 if cmd
= 'open' then
6556 g_Console_Add('player activated sesame');
6557 g_Triggers_OpenAll();
6564 if gFly
then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
6568 if cmd
= 'noclip' then
6571 g_Console_Add('wall hardeness adjusted');
6575 if cmd
= 'notarget' then
6577 plr
.NoTarget
:= not plr
.NoTarget
;
6578 if plr
.NoTarget
then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
6582 if cmd
= 'noreload' then
6584 plr
.NoReload
:= not plr
.NoReload
;
6585 if plr
.NoReload
then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
6589 if cmd
= 'speedy' then
6591 MAX_RUNVEL
:= 32-MAX_RUNVEL
;
6592 g_Console_Add('speed adjusted');
6596 if cmd
= 'jumpy' then
6598 VEL_JUMP
:= 30-VEL_JUMP
;
6599 g_Console_Add('jump height adjusted');
6603 if cmd
= 'automap' then
6605 gShowMap
:= not gShowMap
;
6606 if gShowMap
then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
6610 if cmd
= 'aimline' then
6612 gAimLine
:= not gAimLine
;
6613 if gAimLine
then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
6618 procedure GameCommands(P
: SSArray
);
6624 pl
: pTNetClient
= nil;
6633 cmd
:= LowerCase(P
[0]);
6635 if cmd
= 'pause' then
6637 if (g_ActiveWindow
= nil) then
6638 g_Game_Pause(not gPauseMain
);
6640 else if cmd
= 'endgame' then
6641 gExit
:= EXIT_SIMPLE
6642 else if cmd
= 'restart' then
6644 if gGameOn
or (gState
in [STATE_INTERSINGLE
, STATE_INTERCUSTOM
]) then
6646 if g_Game_IsClient
then
6648 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6653 g_Console_Add(_lc
[I_MSG_NOT_GAME
]);
6655 else if cmd
= 'kick' then
6657 if g_Game_IsServer
then
6659 if Length(P
) < 2 then
6661 g_Console_Add('kick <name>');
6666 g_Console_Add('kick <name>');
6670 if g_Game_IsNet
then
6671 pl
:= g_Net_Client_ByName(P
[1]);
6674 s
:= g_Net_ClientName_ByID(pl
^.ID
);
6675 g_Net_Host_Kick(pl
^.ID
, NET_DISC_KICK
);
6676 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [s
]));
6677 MH_SEND_GameEvent(NET_EV_PLAYER_KICK
, 0, s
);
6678 g_Net_Slist_ServerPlayerLeaves();
6679 end else if gPlayers
<> nil then
6680 for a
:= Low(gPlayers
) to High(gPlayers
) do
6681 if gPlayers
[a
] <> nil then
6682 if Copy(LowerCase(gPlayers
[a
].Name
), 1, Length(P
[1])) = LowerCase(P
[1]) then
6684 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
6685 if not(gPlayers
[a
] is TBot
) and (gGameSettings
.GameType
= GT_SINGLE
) then
6687 gPlayers
[a
].Lives
:= 0;
6688 gPlayers
[a
].Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
6689 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [gPlayers
[a
].Name
]), True);
6690 g_Player_Remove(gPlayers
[a
].UID
);
6691 g_Net_Slist_ServerPlayerLeaves();
6692 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
6696 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
6698 else if cmd
= 'kick_id' then
6700 if g_Game_IsServer
and g_Game_IsNet
then
6702 if Length(P
) < 2 then
6704 g_Console_Add('kick_id <client ID>');
6709 g_Console_Add('kick_id <client ID>');
6713 a
:= StrToIntDef(P
[1], 0);
6714 if (NetClients
<> nil) and (a
<= High(NetClients
)) then
6716 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6718 s
:= g_Net_ClientName_ByID(NetClients
[a
].ID
);
6719 g_Net_Host_Kick(NetClients
[a
].ID
, NET_DISC_KICK
);
6720 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [s
]));
6721 MH_SEND_GameEvent(NET_EV_PLAYER_KICK
, 0, s
);
6722 g_Net_Slist_ServerPlayerLeaves();
6726 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6728 else if cmd
= 'kick_pid' then
6730 if g_Game_IsServer
and g_Game_IsNet
then
6732 if Length(P
) < 2 then
6734 g_Console_Add('kick_pid <player ID>');
6739 g_Console_Add('kick_pid <player ID>');
6743 a
:= StrToIntDef(P
[1], 0);
6744 pl
:= g_Net_Client_ByPlayer(a
);
6745 if (pl
<> nil) and pl
^.Used
and (pl
^.Peer
<> nil) then
6747 s
:= g_Net_ClientName_ByID(pl
^.ID
);
6748 g_Net_Host_Kick(pl
^.ID
, NET_DISC_KICK
);
6749 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [s
]));
6750 MH_SEND_GameEvent(NET_EV_PLAYER_KICK
, 0, s
);
6751 g_Net_Slist_ServerPlayerLeaves();
6754 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6756 else if cmd
= 'ban' then
6758 if g_Game_IsServer
and g_Game_IsNet
then
6760 if Length(P
) < 2 then
6762 g_Console_Add('ban <name>');
6767 g_Console_Add('ban <name>');
6771 pl
:= g_Net_Client_ByName(P
[1]);
6773 g_Net_Host_Ban(pl
, False)
6775 g_Console_Add(Format(_lc
[I_NET_ERR_NAME404
], [P
[1]]));
6777 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6779 else if cmd
= 'ban_id' then
6781 if g_Game_IsServer
and g_Game_IsNet
then
6783 if Length(P
) < 2 then
6785 g_Console_Add('ban_id <client ID>');
6790 g_Console_Add('ban_id <client ID>');
6794 a
:= StrToIntDef(P
[1], 0);
6795 if (NetClients
<> nil) and (a
<= High(NetClients
)) then
6796 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6797 g_Net_Host_Ban(pl
, False);
6799 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6801 else if cmd
= 'ban_pid' then
6803 if g_Game_IsServer
and g_Game_IsNet
then
6805 if Length(P
) < 2 then
6807 g_Console_Add('ban_pid <player ID>');
6812 g_Console_Add('ban_pid <player ID>');
6816 a
:= StrToIntDef(P
[1], 0);
6817 pl
:= g_Net_Client_ByPlayer(a
);
6818 if (pl
<> nil) and pl
^.Used
and (pl
^.Peer
<> nil) then
6819 g_Net_Host_Ban(pl
, False);
6821 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6823 else if cmd
= 'permban' then
6825 if g_Game_IsServer
and g_Game_IsNet
then
6827 if Length(P
) < 2 then
6829 g_Console_Add('permban <name>');
6834 g_Console_Add('permban <name>');
6838 pl
:= g_Net_Client_ByName(P
[1]);
6840 g_Net_Host_Ban(pl
, True)
6842 g_Console_Add(Format(_lc
[I_NET_ERR_NAME404
], [P
[1]]));
6844 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6846 else if cmd
= 'permban_id' then
6848 if g_Game_IsServer
and g_Game_IsNet
then
6850 if Length(P
) < 2 then
6852 g_Console_Add('permban_id <client ID>');
6857 g_Console_Add('permban_id <client ID>');
6861 a
:= StrToIntDef(P
[1], 0);
6862 if (NetClients
<> nil) and (a
<= High(NetClients
)) then
6863 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6864 g_Net_Host_Ban(@NetClients
[a
], True);
6866 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6868 else if cmd
= 'permban_pid' then
6870 if g_Game_IsServer
and g_Game_IsNet
then
6872 if Length(P
) < 2 then
6874 g_Console_Add('permban_pid <player ID>');
6879 g_Console_Add('permban_pid <player ID>');
6883 a
:= StrToIntDef(P
[1], 0);
6884 pl
:= g_Net_Client_ByPlayer(a
);
6885 if (pl
<> nil) and pl
^.Used
and (pl
^.Peer
<> nil) then
6886 g_Net_Host_Ban(pl
, True);
6888 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6890 else if cmd
= 'permban_ip' then
6892 if g_Game_IsServer
and g_Game_IsNet
then
6894 if Length(P
) < 2 then
6896 g_Console_Add('permban_ip <IP address>');
6901 g_Console_Add('permban_ip <IP address>');
6905 g_Net_BanAddress(P
[1]);
6906 g_Net_SaveBanList();
6907 g_Console_Add(Format(_lc
[I_PLAYER_BAN
], [P
[1]]));
6909 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6911 else if cmd
= 'unban' then
6913 if g_Game_IsServer
and g_Game_IsNet
then
6915 if Length(P
) < 2 then
6917 g_Console_Add('unban <IP Address>');
6922 g_Console_Add('unban <IP Address>');
6926 if g_Net_UnbanAddress(P
[1]) then
6928 g_Console_Add(Format(_lc
[I_MSG_UNBAN_OK
], [P
[1]]));
6929 g_Net_SaveBanList();
6931 g_Console_Add(Format(_lc
[I_MSG_UNBAN_FAIL
], [P
[1]]));
6933 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6935 else if cmd
= 'clientlist' then
6937 if g_Game_IsServer
and g_Game_IsNet
then
6940 if NetClients
<> nil then
6941 for a
:= Low(NetClients
) to High(NetClients
) do
6942 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6944 plr
:= g_Player_Get(NetClients
[a
].Player
);
6945 if plr
= nil then continue
;
6947 g_Console_Add(Format('#%2d: %-15s | %s', [a
,
6948 IpToStr(NetClients
[a
].Peer
^.address
.host
), plr
.Name
]));
6951 g_Console_Add(_lc
[I_MSG_NOCLIENTS
]);
6953 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6955 else if cmd
= 'connect' then
6957 if (NetMode
= NET_NONE
) then
6959 if Length(P
) < 2 then
6961 g_Console_Add('connect <IP> [port] [password]');
6966 g_Console_Add('connect <IP> [port] [password]');
6970 if Length(P
) > 2 then
6971 prt
:= StrToIntDef(P
[2], 25666)
6975 if Length(P
) > 3 then
6980 g_Game_StartClient(P
[1], prt
, pw
);
6983 else if cmd
= 'disconnect' then
6985 if (NetMode
= NET_CLIENT
) then
6988 else if cmd
= 'reconnect' then
6990 if (NetMode
= NET_SERVER
) then
6993 if (NetMode
= NET_CLIENT
) then
6996 gExit
:= EXIT_SIMPLE
;
7000 //TODO: Use last successful password to reconnect, instead of ''
7001 g_Game_StartClient(NetClientIP
, NetClientPort
, '');
7003 else if (cmd
= 'addbot') or
7004 (cmd
= 'bot_add') then
7007 1: g_Bot_Add(TEAM_NONE
, 2);
7008 2: g_Bot_Add(TEAM_NONE
, StrToIntDef(P
[1], 2));
7010 g_Bot_Add(TEAM_NONE
, StrToIntDef(P
[1], 2), StrToIntDef(P
[2], 100));
7013 else if cmd
= 'bot_addlist' then
7016 1: g_Bot_AddList(TEAM_NONE
, '');
7017 2: g_Bot_AddList(TEAM_NONE
, P
[1], StrToIntDef(P
[1], -1));
7019 if P
[2] = 'red' then
7021 else if P
[2] = 'blue' then
7027 then g_Bot_AddList(t
, P
[1], StrToIntDef(P
[1], -1))
7028 else g_Bot_AddList(t
, P
[1], StrToIntDef(P
[1], -1), StrToIntDef(P
[3], 100));
7031 else if cmd
= 'bot_removeall' then
7033 else if cmd
= 'chat' then
7035 if g_Game_IsNet
then
7037 if Length(P
) > 1 then
7039 for a
:= 1 to High(P
) do
7040 chstr
:= chstr
+ P
[a
] + ' ';
7042 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7044 if Length(chstr
) < 1 then
7046 g_Console_Add('chat <text>');
7050 chstr
:= b_Text_Format(chstr
);
7051 if g_Game_IsClient
then
7052 MC_SEND_Chat(chstr
, NET_CHAT_PLAYER
)
7054 MH_SEND_Chat(gPlayer1Settings
.Name
+ ': ' + chstr
, NET_CHAT_PLAYER
);
7057 g_Console_Add('chat <text>');
7059 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7061 else if cmd
= 'teamchat' then
7063 if g_Game_IsNet
and (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then
7065 if Length(P
) > 1 then
7067 for a
:= 1 to High(P
) do
7068 chstr
:= chstr
+ P
[a
] + ' ';
7070 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7072 if Length(chstr
) < 1 then
7074 g_Console_Add('teamchat <text>');
7078 chstr
:= b_Text_Format(chstr
);
7079 if g_Game_IsClient
then
7080 MC_SEND_Chat(chstr
, NET_CHAT_TEAM
)
7082 MH_SEND_Chat(gPlayer1Settings
.Name
+ ': ' + chstr
, NET_CHAT_TEAM
,
7083 gPlayer1Settings
.Team
);
7086 g_Console_Add('teamchat <text>');
7088 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7090 else if (cmd
= 'an') or (cmd
= 'announce') then
7092 if g_Game_IsNet
then
7094 if Length(P
) > 1 then
7096 for a
:= 1 to High(P
) do
7097 chstr
:= chstr
+ P
[a
] + ' ';
7099 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7101 if Length(chstr
) < 1 then
7103 g_Console_Add('announce <text>');
7107 chstr
:= 'centerprint 100 ' + b_Text_Format(chstr
);
7108 if g_Game_IsClient
then
7109 MC_SEND_RCONCommand(chstr
)
7111 g_Console_Process(chstr
, True);
7114 g_Console_Add('announce <text>');
7116 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7118 else if cmd
= 'game' then
7120 if gGameSettings
.GameType
<> GT_NONE
then
7122 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7125 if Length(P
) = 1 then
7127 g_Console_Add(cmd
+ ' <WAD> [MAP] [# players]');
7130 // game not started yet, load first map from some wad
7132 s
:= addWadExtension(P
[1]);
7133 found
:= e_FindResource(AllMapDirs
, s
);
7137 P
[1] := ExpandFileName(P
[1]);
7138 // if map not choosed then set first map
7139 if Length(P
) < 3 then
7142 P
[2] := g_Game_GetFirstMap(P
[1]);
7145 s
:= P
[1] + ':\' + UpperCase(P
[2]);
7147 if g_Map_Exist(s
) then
7151 with gGameSettings
do
7153 Options
:= gsGameFlags
;
7154 GameMode
:= g_Game_TextToMode(gsGameMode
);
7155 if gSwitchGameMode
<> GM_NONE
then
7156 GameMode
:= gSwitchGameMode
;
7157 if GameMode
= GM_NONE
then GameMode
:= GM_DM
;
7158 if GameMode
= GM_SINGLE
then GameMode
:= GM_COOP
;
7160 if Length(P
) >= 4 then
7161 b
:= StrToIntDef(P
[3], 1);
7162 g_Game_StartCustom(s
, GameMode
, TimeLimit
,
7163 ScoreLimit
, MaxLives
, Options
, b
);
7168 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[1]]))
7170 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [UpperCase(P
[2]), P
[1]]));
7172 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]));
7174 else if cmd
= 'host' then
7176 if gGameSettings
.GameType
<> GT_NONE
then
7178 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7181 if Length(P
) < 4 then
7183 g_Console_Add(cmd
+ ' <listen IP> <port> <WAD> [MAP] [# players]');
7186 if not StrToIp(P
[1], listen
) then
7188 prt
:= StrToIntDef(P
[2], 25666);
7190 s
:= addWadExtension(P
[3]);
7191 found
:= e_FindResource(AllMapDirs
, s
);
7195 // get first map in wad, if not specified
7196 if Length(P
) < 5 then
7199 P
[4] := g_Game_GetFirstMap(P
[1]);
7201 s
:= P
[3] + ':\' + UpperCase(P
[4]);
7202 if g_Map_Exist(s
) then
7206 with gGameSettings
do
7208 Options
:= gsGameFlags
;
7209 GameMode
:= g_Game_TextToMode(gsGameMode
);
7210 if gSwitchGameMode
<> GM_NONE
then GameMode
:= gSwitchGameMode
;
7211 if GameMode
= GM_NONE
then GameMode
:= GM_DM
;
7212 if GameMode
= GM_SINGLE
then GameMode
:= GM_COOP
;
7214 if Length(P
) >= 6 then
7215 b
:= StrToIntDef(P
[5], 0);
7216 g_Game_StartServer(s
, GameMode
, TimeLimit
, ScoreLimit
, MaxLives
, Options
, b
, listen
, prt
)
7222 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[3]]))
7224 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [UpperCase(P
[4]), P
[3]]))
7229 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[3]]))
7232 else if cmd
= 'map' then
7234 if Length(P
) = 1 then
7236 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7238 g_Console_Add(cmd
+ ' <MAP>');
7239 g_Console_Add(cmd
+ ' <WAD> [MAP]')
7243 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7246 else if not e_IsValidResourceName(P
[1]) then
7248 g_Console_Add('wad name must not be absolute or relative');
7252 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7254 if Length(P
) < 3 then
7256 // first param is map or wad
7257 s
:= UpperCase(P
[1]);
7258 if g_Map_Exist(gGameSettings
.WAD
+ ':\' + s
) then
7260 gExitByTrigger
:= False;
7263 // already in game, finish current map
7265 gExit
:= EXIT_ENDLEVELCUSTOM
;
7269 // intermission, so change map immediately
7276 found
:= e_FindResource(AllMapDirs
, s
);
7278 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [s
, 'WAD ' + P
[1]]));
7281 // no such map, found wad
7284 P
[1] := ExpandFileName(pw
);
7285 P
[2] := g_Game_GetFirstMap(P
[1]);
7286 s
:= P
[1] + ':\' + P
[2];
7287 if g_Map_Exist(s
) then
7289 gExitByTrigger
:= False;
7292 // already in game, finish current map
7294 gExit
:= EXIT_ENDLEVELCUSTOM
7298 // intermission, so change map immediately
7305 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[1]]))
7307 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7312 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7318 s
:= addWadExtension(P
[1]);
7319 found
:= e_FindResource(AllMapDirs
, s
);
7323 P
[2] := UpperCase(P
[2]);
7324 s
:= P
[1] + ':\' + P
[2];
7325 if g_Map_Exist(s
) then
7327 gExitByTrigger
:= False;
7331 gExit
:= EXIT_ENDLEVELCUSTOM
7340 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7345 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7351 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7355 else if cmd
= 'nextmap' then
7357 if not(gGameOn
or (gState
= STATE_INTERCUSTOM
)) then
7359 g_Console_Add(_lc
[I_MSG_NOT_GAME
])
7364 if Length(P
) = 1 then
7366 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7368 g_Console_Add(cmd
+ ' <MAP>');
7369 g_Console_Add(cmd
+ ' <WAD> [MAP]');
7374 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7377 else if not e_IsValidResourceName(P
[1]) then
7379 g_Console_Add('wad name must not be absolute or relative');
7384 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7386 if Length(P
) < 3 then
7388 // first param is map or wad
7389 s
:= UpperCase(P
[1]);
7390 if g_Map_Exist(gGameSettings
.WAD
+ ':\' + s
) then
7393 gExitByTrigger
:= False;
7399 // no such map, found wad
7400 pw
:= addWadExtension(P
[1]);
7401 found
:= e_FindResource(MapDirs
, pw
);
7403 found
:= e_FindResource(WadDirs
, pw
);
7405 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [s
, P
[1]]));
7408 // map not specified, select first map
7410 P
[2] := g_Game_GetFirstMap(P
[1]);
7411 s
:= P
[1] + ':\' + P
[2];
7412 if g_Map_Exist(s
) then
7414 gExitByTrigger
:= False;
7421 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[1]]))
7423 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7428 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7434 // specified two params wad + map
7435 pw
:= addWadExtension(P
[1]);
7436 found
:= e_FindResource(MapDirs
, pw
);
7438 found
:= e_FindResource(MapDirs
, pw
);
7442 P
[2] := UpperCase(P
[2]);
7443 s
:= P
[1] + ':\' + P
[2];
7444 if g_Map_Exist(s
) then
7446 gExitByTrigger
:= False;
7452 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7457 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7463 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7468 if gNextMap
= '' then
7469 g_Console_Add(_lc
[I_MSG_NEXTMAP_UNSET
])
7471 g_Console_Add(Format(_lc
[I_MSG_NEXTMAP_SET
], [gNextMap
]))
7475 else if (cmd
= 'endmap') or (cmd
= 'goodbye') then
7479 g_Console_Add(_lc
[I_MSG_NOT_GAME
])
7483 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7485 gExitByTrigger
:= False;
7486 // next map not specified, try to find trigger EXIT
7487 if (gNextMap
= '') and (gTriggers
<> nil) then
7489 for a
:= 0 to High(gTriggers
) do
7491 if gTriggers
[a
].TriggerType
= TRIGGER_EXIT
then
7493 gExitByTrigger
:= True;
7494 //gNextMap := gTriggers[a].Data.MapName;
7495 gNextMap
:= gTriggers
[a
].tgcMap
;
7500 if gNextMap
= '' then
7501 gNextMap
:= g_Game_GetNextMap();
7502 if not isWadPath(gNextMap
) then
7503 s
:= gGameSettings
.WAD
+ ':\' + gNextMap
7506 if g_Map_Exist(s
) then
7507 gExit
:= EXIT_ENDLEVELCUSTOM
7509 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [gNextMap
]))
7513 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7517 else if (cmd
= 'event') then
7519 if (Length(P
) <= 1) then
7521 for a
:= 0 to High(gEvents
) do
7522 if gEvents
[a
].Command
= '' then
7523 g_Console_Add(gEvents
[a
].Name
+ ' <none>')
7525 g_Console_Add(gEvents
[a
].Name
+ ' "' + gEvents
[a
].Command
+ '"');
7528 if (Length(P
) = 2) then
7530 for a
:= 0 to High(gEvents
) do
7531 if gEvents
[a
].Name
= P
[1] then
7532 if gEvents
[a
].Command
= '' then
7533 g_Console_Add(gEvents
[a
].Name
+ ' <none>')
7535 g_Console_Add(gEvents
[a
].Name
+ ' "' + gEvents
[a
].Command
+ '"');
7538 for a
:= 0 to High(gEvents
) do
7539 if gEvents
[a
].Name
= P
[1] then
7541 gEvents
[a
].Command
:= '';
7542 for b
:= 2 to High(P
) do
7543 if Pos(' ', P
[b
]) = 0 then
7544 gEvents
[a
].Command
:= gEvents
[a
].Command
+ ' ' + P
[b
]
7546 gEvents
[a
].Command
:= gEvents
[a
].Command
+ ' "' + P
[b
] + '"';
7547 gEvents
[a
].Command
:= Trim(gEvents
[a
].Command
);
7551 else if cmd
= 'suicide' then
7555 if g_Game_IsClient
then
7556 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE
)
7559 if gPlayer1
<> nil then
7560 gPlayer1
.Damage(SUICIDE_DAMAGE
, gPlayer1
.UID
, 0, 0, HIT_SELF
);
7561 if gPlayer2
<> nil then
7562 gPlayer2
.Damage(SUICIDE_DAMAGE
, gPlayer2
.UID
, 0, 0, HIT_SELF
);
7566 else if cmd
= 'screenshot' then
7570 else if (cmd
= 'weapnext') or (cmd
= 'weapprev') then
7572 a
:= 1 - (ord(cmd
[5]) - ord('n'));
7574 gWeaponAction
[0, WP_PREV
] := True;
7576 gWeaponAction
[0, WP_NEXT
] := True;
7578 else if cmd
= 'weapon' then
7580 if Length(p
) = 2 then
7582 a
:= WP_FIRST
+ StrToIntDef(p
[1], 0) - 1;
7583 if (a
>= WP_FIRST
) and (a
<= WP_LAST
) then
7584 gSelectWeapon
[0, a
] := True
7587 else if (cmd
= 'p1_weapnext') or (cmd
= 'p1_weapprev')
7588 or (cmd
= 'p2_weapnext') or (cmd
= 'p2_weapprev') then
7590 a
:= 1 - (ord(cmd
[8]) - ord('n'));
7591 b
:= ord(cmd
[2]) - ord('1');
7593 gWeaponAction
[b
, WP_PREV
] := True;
7595 gWeaponAction
[b
, WP_NEXT
] := True;
7597 else if (cmd
= 'p1_weapon') or (cmd
= 'p2_weapon') then
7599 if Length(p
) = 2 then
7601 a
:= WP_FIRST
+ StrToIntDef(p
[1], 0) - 1;
7602 b
:= ord(cmd
[2]) - ord('1');
7603 if (a
>= WP_FIRST
) and (a
<= WP_LAST
) then
7604 gSelectWeapon
[b
, a
] := True
7607 else if (cmd
= 'p1_weapbest') or (cmd
= 'p2_weapbest') then
7609 b
:= ord(cmd
[2]) - ord('1');
7611 gSelectWeapon
[b
, gPlayer1
.GetMorePrefered()] := True
7613 gSelectWeapon
[b
, gPlayer2
.GetMorePrefered()] := True;
7615 else if (cmd
= 'dropflag') then
7617 if g_Game_IsServer
then
7619 if gPlayer2
<> nil then gPlayer2
.TryDropFlag();
7620 if gPlayer1
<> nil then gPlayer1
.TryDropFlag();
7623 MC_SEND_CheatRequest(NET_CHEAT_DROPFLAG
);
7625 else if (cmd
= 'p1_dropflag') or (cmd
= 'p2_dropflag') then
7627 b
:= ord(cmd
[2]) - ord('1');
7628 if g_Game_IsServer
then
7630 if (b
= 1) and (gPlayer2
<> nil) then gPlayer2
.TryDropFlag()
7631 else if (b
= 0) and (gPlayer1
<> nil) then gPlayer1
.TryDropFlag();
7634 MC_SEND_CheatRequest(NET_CHEAT_DROPFLAG
);
7636 // Êîìàíäû Ñâîåé èãðû:
7637 else if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
] then
7639 if cmd
= 'bot_addred' then
7641 if Length(P
) > 1 then
7642 g_Bot_Add(TEAM_RED
, StrToIntDef(P
[1], 2))
7644 g_Bot_Add(TEAM_RED
, 2);
7646 else if cmd
= 'bot_addblue' then
7648 if Length(P
) > 1 then
7649 g_Bot_Add(TEAM_BLUE
, StrToIntDef(P
[1], 2))
7651 g_Bot_Add(TEAM_BLUE
, 2);
7653 else if cmd
= 'spectate' then
7659 else if cmd
= 'say' then
7661 if g_Game_IsServer
and g_Game_IsNet
then
7663 if Length(P
) > 1 then
7666 for a
:= 1 to High(P
) do
7667 chstr
:= chstr
+ P
[a
] + ' ';
7669 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7671 if Length(chstr
) < 1 then
7673 g_Console_Add('say <text>');
7677 chstr
:= b_Text_Format(chstr
);
7678 MH_SEND_Chat(chstr
, NET_CHAT_PLAYER
);
7680 else g_Console_Add('say <text>');
7682 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
7684 else if cmd
= 'tell' then
7686 if g_Game_IsServer
and g_Game_IsNet
then
7688 if (Length(P
) > 2) and (P
[1] <> '') then
7691 for a
:= 2 to High(P
) do
7692 chstr
:= chstr
+ P
[a
] + ' ';
7694 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7696 if Length(chstr
) < 1 then
7698 g_Console_Add('tell <playername> <text>');
7702 pl
:= g_Net_Client_ByName(P
[1]);
7704 MH_SEND_Chat(b_Text_Format(chstr
), NET_CHAT_PLAYER
, pl
^.ID
)
7706 g_Console_Add(Format(_lc
[I_NET_ERR_NAME404
], [P
[1]]));
7708 else g_Console_Add('tell <playername> <text>');
7710 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
7712 else if cmd
= 'centerprint' then
7714 if (Length(P
) > 2) and (P
[1] <> '') then
7717 for a
:= 2 to High(P
) do
7718 chstr
:= chstr
+ P
[a
] + ' ';
7720 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7722 if Length(chstr
) < 1 then
7724 g_Console_Add('centerprint <timeout> <text>');
7728 a
:= StrToIntDef(P
[1], 100);
7729 chstr
:= b_Text_Format(chstr
);
7730 g_Game_Message(chstr
, a
);
7731 if g_Game_IsNet
and g_Game_IsServer
then
7732 MH_SEND_GameEvent(NET_EV_BIGTEXT
, a
, chstr
);
7734 else g_Console_Add('centerprint <timeout> <text>');
7736 else if (cmd
= 'overtime') and not g_Game_IsClient
then
7738 if (Length(P
) = 1) or (StrToIntDef(P
[1], -1) <= 0) then
7740 // Äîïîëíèòåëüíîå âðåìÿ:
7741 gGameSettings
.TimeLimit
:= (gTime
- gGameStartTime
) div 1000 + Word(StrToIntDef(P
[1], 0));
7743 g_Console_Add(Format(_lc
[I_MSG_TIME_LIMIT
],
7744 [gGameSettings
.TimeLimit
div 3600,
7745 (gGameSettings
.TimeLimit
div 60) mod 60,
7746 gGameSettings
.TimeLimit
mod 60]));
7747 if g_Game_IsNet
then MH_SEND_GameSettings
;
7749 else if (cmd
= 'rcon_password') and g_Game_IsClient
then
7751 if (Length(P
) <= 1) then
7752 g_Console_Add('rcon_password <password>')
7754 MC_SEND_RCONPassword(P
[1]);
7756 else if cmd
= 'rcon' then
7758 if g_Game_IsClient
then
7760 if Length(P
) > 1 then
7763 for a
:= 1 to High(P
) do
7764 chstr
:= chstr
+ P
[a
] + ' ';
7766 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7768 if Length(chstr
) < 1 then
7770 g_Console_Add('rcon <command>');
7774 MC_SEND_RCONCommand(chstr
);
7776 else g_Console_Add('rcon <command>');
7779 else if cmd
= 'ready' then
7781 if g_Game_IsServer
and (gLMSRespawn
= LMS_RESPAWN_WARMUP
) then
7782 gLMSRespawnTime
:= gTime
+ 100;
7784 else if (cmd
= 'callvote') and g_Game_IsNet
then
7786 if Length(P
) > 1 then
7789 for a
:= 1 to High(P
) do begin
7790 if a
> 1 then chstr
:= chstr
+ ' ';
7791 chstr
:= chstr
+ P
[a
];
7794 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7796 if Length(chstr
) < 1 then
7798 g_Console_Add('callvote <command>');
7802 if g_Game_IsClient
then
7803 MC_SEND_Vote(True, chstr
)
7805 g_Game_StartVote(chstr
, gPlayer1Settings
.Name
);
7806 g_Console_Process('vote', True);
7809 g_Console_Add('callvote <command>');
7811 else if (cmd
= 'vote') and g_Game_IsNet
then
7813 if g_Game_IsClient
then
7815 else if gVoteInProgress
then
7817 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
7818 a
:= Floor((NetClientCount
+1)/2.0) + 1
7820 a
:= Floor(NetClientCount
/2.0) + 1;
7825 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_REVOKED
], [gPlayer1Settings
.Name
, gVoteCount
, a
]), True);
7826 MH_SEND_VoteEvent(NET_VE_REVOKE
, gPlayer1Settings
.Name
, 'a', gVoteCount
, a
);
7832 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_VOTE
], [gPlayer1Settings
.Name
, gVoteCount
, a
]), True);
7833 MH_SEND_VoteEvent(NET_VE_VOTE
, gPlayer1Settings
.Name
, 'a', gVoteCount
, a
);
7841 procedure SystemCommands(P
: SSArray
);
7845 cmd
:= LowerCase(P
[0]);
7854 gRC_Width
:= Max(1, gRC_Width
);
7855 gRC_Height
:= Max(1, gRC_Height
);
7856 gBPP
:= Max(1, gBPP
);
7857 if sys_SetDisplayMode(gRC_Width
, gRC_Height
, gBPP
, gRC_FullScreen
, gRC_Maximized
) = True then
7858 e_LogWriteln('resolution changed')
7860 e_LogWriteln('resolution not changed');
7861 sys_EnableVSync(gVSync
);
7865 if Length(p
) = 2 then
7867 gMaxFPS
:= StrToIntDef(p
[1], gMaxFPS
);
7869 gFrameTime
:= 1000 div gMaxFPS
7873 e_LogWritefln('r_maxfps %d', [gMaxFPS
]);
7877 if Length(p
) = 2 then
7879 gAskLanguage
:= true;
7880 gLanguage
:= LANGUAGE_ENGLISH
;
7881 case LowerCase(p
[1]) of
7884 gAskLanguage
:= false;
7885 gLanguage
:= LANGUAGE_ENGLISH
;
7889 gAskLanguage
:= false;
7890 gLanguage
:= LANGUAGE_RUSSIAN
;
7894 gAskLanguage
:= true;
7895 gLanguage
:= LANGUAGE_ENGLISH
;
7898 g_Language_Set(gLanguage
);
7902 e_LogWritefln('usage: %s <English|Russian|Ask>', [cmd
]);
7908 procedure g_TakeScreenShot(Filename
: string = '');
7909 var s
: TStream
; t
: TDateTime
; dir
, date
, name
: String;
7911 if e_NoGraphics
then Exit
;
7913 dir
:= e_GetWriteableDir(ScreenshotDirs
);
7915 if Filename
= '' then
7918 DateTimeToString(date
, 'yyyy-mm-dd-hh-nn-ss', t
);
7919 Filename
:= 'screenshot-' + date
;
7922 name
:= e_CatPath(dir
, Filename
+ '.png');
7923 s
:= createDiskFile(name
);
7925 e_MakeScreenshot(s
, gWinSizeX
, gWinSizeY
);
7927 g_Console_Add(Format(_lc
[I_CONSOLE_SCREENSHOT
], [name
]))
7929 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_WRITE
], [name
]));
7934 g_Console_Add('oh shit, i can''t create screenshot!')
7938 procedure g_Game_InGameMenu(Show
: Boolean);
7940 if (g_ActiveWindow
= nil) and Show
then
7942 if gGameSettings
.GameType
= GT_SINGLE
then
7943 g_GUI_ShowWindow('GameSingleMenu')
7946 if g_Game_IsClient
then
7947 g_GUI_ShowWindow('GameClientMenu')
7949 if g_Game_IsNet
then
7950 g_GUI_ShowWindow('GameServerMenu')
7952 g_GUI_ShowWindow('GameCustomMenu');
7954 g_Sound_PlayEx('MENU_OPEN');
7956 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7957 if (not g_Game_IsNet
) then
7961 if (g_ActiveWindow
<> nil) and (not Show
) then
7963 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7964 if (not g_Game_IsNet
) then
7965 g_Game_Pause(False);
7969 procedure g_Game_Pause (Enable
: Boolean);
7973 if not gGameOn
then exit
;
7975 if not (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
]) then exit
;
7978 gPauseMain
:= Enable
;
7980 if (gPause
<> oldPause
) then g_Game_PauseAllSounds(gPause
);
7983 procedure g_Game_HolmesPause (Enable
: Boolean);
7987 if not gGameOn
then exit
;
7988 if not (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
]) then exit
;
7991 gPauseHolmes
:= Enable
;
7993 if (gPause
<> oldPause
) then g_Game_PauseAllSounds(gPause
);
7996 procedure g_Game_PauseAllSounds(Enable
: Boolean);
8001 if gTriggers
<> nil then
8002 for i
:= 0 to High(gTriggers
) do
8003 with gTriggers
[i
] do
8004 if (TriggerType
= TRIGGER_SOUND
) and
8006 Sound
.IsPlaying() then
8008 Sound
.Pause(Enable
);
8012 if gPlayers
<> nil then
8013 for i
:= 0 to High(gPlayers
) do
8014 if gPlayers
[i
] <> nil then
8015 gPlayers
[i
].PauseSounds(Enable
);
8018 if gMusic
<> nil then
8019 gMusic
.Pause(Enable
);
8022 procedure g_Game_StopAllSounds(all
: Boolean);
8026 if gTriggers
<> nil then
8027 for i
:= 0 to High(gTriggers
) do
8028 with gTriggers
[i
] do
8029 if (TriggerType
= TRIGGER_SOUND
) and
8033 if gMusic
<> nil then
8040 procedure g_Game_UpdateTriggerSounds
;
8043 if gTriggers
<> nil then
8044 for i
:= 0 to High(gTriggers
) do
8045 with gTriggers
[i
] do
8046 if (TriggerType
= TRIGGER_SOUND
) and (Sound
<> nil) and tgcLocal
and Sound
.IsPlaying() then
8047 Sound
.SetCoordsRect(X
, Y
, Width
, Height
, tgcVolume
/ 255.0)
8050 function g_Game_IsWatchedPlayer(UID
: Word): Boolean;
8053 if (gPlayer1
<> nil) and (gPlayer1
.UID
= UID
) then
8058 if (gPlayer2
<> nil) and (gPlayer2
.UID
= UID
) then
8063 if gSpectMode
<> SPECT_PLAYERS
then
8065 if gSpectPID1
= UID
then
8070 if gSpectViewTwo
and (gSpectPID2
= UID
) then
8077 function g_Game_IsWatchedTeam(Team
: Byte): Boolean;
8082 if (gPlayer1
<> nil) and (gPlayer1
.Team
= Team
) then
8087 if (gPlayer2
<> nil) and (gPlayer2
.Team
= Team
) then
8092 if gSpectMode
<> SPECT_PLAYERS
then
8094 Pl
:= g_Player_Get(gSpectPID1
);
8095 if (Pl
<> nil) and (Pl
.Team
= Team
) then
8100 if gSpectViewTwo
then
8102 Pl
:= g_Player_Get(gSpectPID2
);
8103 if (Pl
<> nil) and (Pl
.Team
= Team
) then
8111 procedure g_Game_Message(Msg
: string; Time
: Word);
8113 MessageLineLength
:= (gScreenWidth
- 204) div e_CharFont_GetMaxWidth(gMenuFont
);
8114 MessageText
:= b_Text_Wrap(b_Text_Format(Msg
), MessageLineLength
);
8115 MessageTime
:= Time
;
8118 procedure g_Game_ChatSound(Text: String; Taunt
: Boolean = True);
8120 punct
: Array[0..13] of String =
8121 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
8127 function IsPunctuation(S
: String): Boolean;
8132 if Length(S
) <> 1 then
8134 for i
:= Low(punct
) to High(punct
) do
8135 if S
= punct
[i
] then
8141 function FilterPunctuation(S
: String): String;
8145 for i
:= Low(punct
) to High(punct
) do
8146 S
:= StringReplace(S
, punct
[i
], ' ', [rfReplaceAll
]);
8152 if gUseChatSounds
and Taunt
and (gChatSounds
<> nil) and (Pos(': ', Text) > 0) then
8154 // remove player name
8155 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
8156 // for FullWord check
8157 Text := toLowerCase1251(' ' + Text + ' ');
8158 fpText
:= FilterPunctuation(Text);
8160 for i
:= 0 to Length(gChatSounds
) - 1 do
8163 for j
:= 0 to Length(gChatSounds
[i
].Tags
) - 1 do
8165 if gChatSounds
[i
].FullWord
and (not IsPunctuation(gChatSounds
[i
].Tags
[j
])) then
8166 ok
:= Pos(' ' + gChatSounds
[i
].Tags
[j
] + ' ', fpText
) > 0
8168 ok
:= Pos(gChatSounds
[i
].Tags
[j
], Text) > 0;
8174 gChatSounds
[i
].Sound
.Play();
8180 g_Sound_PlayEx('SOUND_GAME_RADIO');
8183 procedure g_Game_Announce_GoodShot(SpawnerUID
: Word);
8192 if not g_Game_IsWatchedPlayer(SpawnerUID
) then
8196 if goodsnd
[a
].IsPlaying() then
8199 goodsnd
[Random(4)].Play();
8202 procedure g_Game_Announce_KillCombo(Param
: Integer);
8209 UID
:= Param
and $FFFF;
8214 Pl
:= g_Player_Get(UID
);
8223 g_Console_Add(Format(_lc
[I_PLAYER_KILL_2X
], [Name
]), True);
8227 g_Console_Add(Format(_lc
[I_PLAYER_KILL_3X
], [Name
]), True);
8231 g_Console_Add(Format(_lc
[I_PLAYER_KILL_4X
], [Name
]), True);
8235 g_Console_Add(Format(_lc
[I_PLAYER_KILL_MX
], [Name
]), True);
8243 if not g_Game_IsWatchedPlayer(UID
) then
8246 if (not g_Game_IsWatchedPlayer(UID
)) and (c
< 4) then
8250 if killsnd
[n
].IsPlaying() then
8255 procedure g_Game_Announce_BodyKill(SpawnerUID
: Word);
8263 if not g_Game_IsWatchedPlayer(SpawnerUID
) then
8267 if hahasnd
[a
].IsPlaying() then
8270 hahasnd
[Random(3)].Play();
8273 procedure g_Game_Effect_Bubbles (fX
, fY
: Integer; count
: Word; devX
, devY
: Byte; Silent
: Boolean);
8275 g_GFX_Bubbles(fX
, fY
, count
, devX
, devY
);
8276 if not Silent
then if Random(2) = 0
8277 then g_Sound_PlayExAt('SOUND_GAME_BUBBLE1', fX
, fY
)
8278 else g_Sound_PlayExAt('SOUND_GAME_BUBBLE2', fX
, fY
);
8281 procedure g_Game_StartVote(Command
, Initiator
: string);
8285 if not gVotesEnabled
then Exit
;
8286 if gGameSettings
.GameType
<> GT_SERVER
then Exit
;
8287 if gVoteInProgress
or gVotePassed
then
8289 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_INPROGRESS
], [gVoteCommand
]), True);
8290 MH_SEND_VoteEvent(NET_VE_INPROGRESS
, gVoteCommand
);
8293 gVoteInProgress
:= True;
8294 gVotePassed
:= False;
8295 gVoteTimer
:= gTime
+ gVoteTimeout
* 1000;
8298 gVoteCommand
:= Command
;
8300 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
8301 Need
:= Floor((NetClientCount
+1)/2.0)+1
8303 Need
:= Floor(NetClientCount
/2.0)+1;
8304 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_STARTED
], [Initiator
, Command
, Need
]), True);
8305 MH_SEND_VoteEvent(NET_VE_STARTED
, Initiator
, Command
, Need
);
8308 procedure g_Game_CheckVote
;
8312 if gGameSettings
.GameType
<> GT_SERVER
then Exit
;
8313 if not gVoteInProgress
then Exit
;
8315 if (gTime
>= gVoteTimer
) then
8317 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
8318 Need
:= Floor((NetClientCount
+1)/2.0) + 1
8320 Need
:= Floor(NetClientCount
/2.0) + 1;
8321 if gVoteCount
>= Need
then
8323 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_PASSED
], [gVoteCommand
]), True);
8324 MH_SEND_VoteEvent(NET_VE_PASSED
, gVoteCommand
);
8325 gVotePassed
:= True;
8326 gVoteCmdTimer
:= gTime
+ 5000;
8330 g_Console_Add(_lc
[I_MESSAGE_VOTE_FAILED
], True);
8331 MH_SEND_VoteEvent(NET_VE_FAILED
);
8333 if NetClients
<> nil then
8334 for I
:= Low(NetClients
) to High(NetClients
) do
8335 if NetClients
[i
].Used
then
8336 NetClients
[i
].Voted
:= False;
8337 gVoteInProgress
:= False;
8343 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
8344 Need
:= Floor((NetClientCount
+1)/2.0) + 1
8346 Need
:= Floor(NetClientCount
/2.0) + 1;
8347 if gVoteCount
>= Need
then
8349 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_PASSED
], [gVoteCommand
]), True);
8350 MH_SEND_VoteEvent(NET_VE_PASSED
, gVoteCommand
);
8351 gVoteInProgress
:= False;
8352 gVotePassed
:= True;
8353 gVoteCmdTimer
:= gTime
+ 5000;
8356 if NetClients
<> nil then
8357 for I
:= Low(NetClients
) to High(NetClients
) do
8358 if NetClients
[i
].Used
then
8359 NetClients
[i
].Voted
:= False;
8364 procedure g_Game_LoadMapList(FileName
: string);
8372 if not FileExists(FileName
) then Exit
;
8374 AssignFile(ListFile
, FileName
);
8376 while not EOF(ListFile
) do
8378 ReadLn(ListFile
, s
);
8381 if s
= '' then Continue
;
8383 SetLength(MapList
, Length(MapList
)+1);
8384 MapList
[High(MapList
)] := s
;
8386 CloseFile(ListFile
);
8389 procedure g_Game_SetDebugMode();
8392 // ×èòû (äàæå â ñâîåé èãðå):
8396 procedure g_Game_SetLoadingText(Text: String; Max
: Integer; reWrite
: Boolean);
8400 if Length(LoadingStat
.Msgs
) = 0 then
8406 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
8407 if NextMsg
= Length(Msgs
) then
8409 for i
:= 0 to High(Msgs
)-1 do
8410 Msgs
[i
] := Msgs
[i
+1];
8418 Msgs
[NextMsg
-1] := Text;
8422 PBarWasHere
:= false;
8425 g_ActiveWindow
:= nil;
8426 ProcessLoading(True);
8429 procedure g_Game_StepLoading(Value
: Integer = -1);
8441 if (ShowCount
> LOADING_SHOW_STEP
) or (Value
> -1) then
8444 ProcessLoading(False);
8449 procedure g_Game_ClearLoading();
8458 len
:= ((gScreenHeight
div 3)*2 - 50) div LOADING_INTERLINE
;
8459 if len
< 1 then len
:= 1;
8460 SetLength(Msgs
, len
);
8461 for len
:= Low(Msgs
) to High(Msgs
) do
8464 PBarWasHere
:= false;
8468 procedure Parse_Params(var pars
: TParamStrValues
);
8475 while i
<= ParamCount
do
8478 if (Length(s
) > 1) and (s
[1] = '-') then
8480 if (Length(s
) > 2) and (s
[2] = '-') then
8481 begin // Îäèíî÷íûé ïàðàìåòð
8482 SetLength(pars
, Length(pars
) + 1);
8483 with pars
[High(pars
)] do
8485 Name
:= LowerCase(s
);
8490 if (i
< ParamCount
) then
8491 begin // Ïàðàìåòð ñî çíà÷åíèåì
8493 SetLength(pars
, Length(pars
) + 1);
8494 with pars
[High(pars
)] do
8496 Name
:= LowerCase(s
);
8497 Value
:= LowerCase(ParamStr(i
));
8506 function Find_Param_Value(var pars
: TParamStrValues
; aName
: String): String;
8511 for i
:= 0 to High(pars
) do
8512 if pars
[i
].Name
= aName
then
8514 Result
:= pars
[i
].Value
;
8519 procedure g_Game_Process_Params();
8521 pars
: TParamStrValues
;
8524 LimT
, LimS
: Integer;
8535 s
:= Find_Param_Value(pars
, '--debug');
8538 g_Game_SetDebugMode();
8539 s
:= Find_Param_Value(pars
, '--netdump');
8544 // Connect when game loads
8545 ip
:= Find_Param_Value(pars
, '-connect');
8549 s
:= Find_Param_Value(pars
, '-port');
8550 if (s
= '') or not TryStrToInt(s
, Port
) then
8553 s
:= Find_Param_Value(pars
, '-pw');
8555 g_Game_StartClient(ip
, Port
, s
);
8559 s
:= LowerCase(Find_Param_Value(pars
, '-dbg-mainwad'));
8562 gDefaultMegawadStart
:= s
;
8565 if (Find_Param_Value(pars
, '--dbg-mainwad-restore') <> '') or
8566 (Find_Param_Value(pars
, '--dbg-mainwad-default') <> '') then
8568 gDefaultMegawadStart
:= DF_Default_Megawad_Start
;
8571 // Start map when game loads:
8572 map
:= LowerCase(Find_Param_Value(pars
, '-map'));
8573 if isWadPath(map
) then
8576 s
:= Find_Param_Value(pars
, '-gm');
8577 GMode
:= g_Game_TextToMode(s
);
8578 if GMode
= GM_NONE
then GMode
:= GM_DM
;
8579 if GMode
= GM_SINGLE
then GMode
:= GM_COOP
;
8582 s
:= Find_Param_Value(pars
, '-limt');
8583 if (s
= '') or (not TryStrToInt(s
, LimT
)) then
8589 s
:= Find_Param_Value(pars
, '-lims');
8590 if (s
= '') or (not TryStrToInt(s
, LimS
)) then
8596 s
:= Find_Param_Value(pars
, '-lives');
8597 if (s
= '') or (not TryStrToInt(s
, Lives
)) then
8603 s
:= Find_Param_Value(pars
, '-opt');
8605 then Opt
:= gsGameFlags
8606 else Opt
:= TGameOptions(StrToIntDef(s
, 0));
8609 s
:= Find_Param_Value(pars
, '--close');
8613 // Override map to test:
8614 s
:= LowerCase(Find_Param_Value(pars
, '-testmap'));
8617 if e_IsValidResourceName(s
) then
8618 e_FindResource(AllMapDirs
, s
);
8619 gTestMap
:= ExpandFileName(s
);
8622 // Delete test map after play:
8623 s
:= Find_Param_Value(pars
, '--testdelete');
8626 //gMapToDelete := MapsDir + map;
8627 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType
.Fatal
);
8631 // Delete temporary WAD after play:
8632 s
:= Find_Param_Value(pars
, '--tempdelete');
8633 if (s
<> '') and (gTestMap
<> '') then
8635 gMapToDelete
:= gTestMap
;
8636 gTempDelete
:= True;
8639 // Number of players:
8640 s
:= Find_Param_Value(pars
, '-pl');
8642 n
:= DEFAULT_PLAYERS
8644 n
:= StrToIntDef(s
, DEFAULT_PLAYERS
);
8647 s
:= Find_Param_Value(pars
, '-port');
8648 if (s
= '') or not TryStrToInt(s
, Port
)
8649 then g_Game_StartCustom(map
, GMode
, LimT
, LimS
, Lives
, Opt
, n
)
8650 else g_Game_StartServer(map
, GMode
, LimT
, LimS
, Lives
, Opt
, n
, 0, Port
);
8653 // Execute script when game loads:
8654 s
:= Find_Param_Value(pars
, '-exec');
8657 // if not isWadPath(s) then
8658 // s := GameDir + '/' + s;
8663 if IOResult
<> 0 then
8665 e_WriteLog(Format(_lc
[I_SIMPLE_ERROR
], ['Failed to read file: ' + s
]), TMsgType
.Warning
);
8666 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_READ
], [s
]));
8670 e_WriteLog('Executing script: ' + s
, TMsgType
.Notify
);
8671 g_Console_Add(Format(_lc
[I_CONSOLE_EXEC
], [s
]));
8676 if IOResult
<> 0 then
8678 e_WriteLog(Format(_lc
[I_SIMPLE_ERROR
], ['Failed to read file: ' + s
]), TMsgType
.Warning
);
8679 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_READ
], [s
]));
8683 if Pos('#', s
) <> 1 then // script comment
8684 g_Console_Process(s
, True);
8695 conRegVar('pf_draw_frame', @g_profile_frame_draw
, 'draw frame rendering profiles', 'render profiles');
8696 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
8697 conRegVar('pf_coldet', @g_profile_collision
, 'draw collision detection profiles', 'coldet profiles');
8698 conRegVar('pf_los', @g_profile_los
, 'draw monster LOS profiles', 'monster LOS profiles');
8700 conRegVar('r_sq_draw', @gdbg_map_use_accel_render
, 'accelerated spatial queries in rendering', 'accelerated rendering');
8701 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet
, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
8702 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel
, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
8703 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace
, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
8705 conRegVar('pr_enabled', @gpart_dbg_enabled
, 'enable/disable particles', 'particles');
8706 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled
, 'enable/disable particle physics', 'particle physics');
8708 conRegVar('los_enabled', @gmon_dbg_los_enabled
, 'enable/disable monster LOS calculations', 'monster LOS', true);
8709 conRegVar('mon_think', @gmon_debug_think
, 'enable/disable monster thinking', 'monster thinking', true);
8711 {$IFDEF ENABLE_HOLMES}
8712 conRegVar('dbg_holmes', @g_holmes_enabled
, 'enable/disable Holmes', 'Holmes', true);
8715 conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds
, 'ignore level bounds', '', false);
8717 conRegVar('r_scale', @g_dbg_scale
, 0.01, 100.0, 'render scale', '', false);
8718 conRegVar('r_resolution_scale', @r_pixel_scale
, 0.01, 100.0, 'upscale factor', '', false);
8720 conRegVar('light_enabled', @gwin_k8_enable_light_experiments
, 'enable/disable dynamic lighting', 'lighting');
8721 conRegVar('light_player_halo', @g_playerLight
, 'enable/disable player halo', 'player light halo');
8723 conRegVar('r_smallmap_align_h', @r_smallmap_h
, 'halign: 0: left; 1: center; 2: right', 'horizontal aligning of small maps');
8724 conRegVar('r_smallmap_align_v', @r_smallmap_v
, 'valign: 0: top; 1: center; 2: bottom', 'vertial aligning of small maps');
8726 conRegVar('r_showfps', @gShowFPS
, 'draw fps counter', 'draw fps counter');
8727 conRegVar('r_showtime', @gShowTime
, 'show game time', 'show game time');
8728 conRegVar('r_showping', @gShowPing
, 'show ping', 'show ping');
8729 conRegVar('r_showscore', @gShowScore
, 'show score', 'show score');
8730 conRegVar('r_showkillmsg', @gShowKillMsg
, 'show kill log', 'show kill log');
8731 conRegVar('r_showlives', @gShowLives
, 'show lives', 'show lives');
8732 conRegVar('r_showspect', @gSpectHUD
, 'show spectator hud', 'show spectator hud');
8733 conRegVar('r_showstat', @gShowStat
, 'show stats', 'show stats');
8734 conRegVar('r_showpids', @gShowPIDs
, 'show PIDs', 'show PIDs');