1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../shared/a_modes.inc}
23 g_basic
, g_player
, e_graphics
, g_res_downloader
,
24 g_sound
, g_gui
, utils
, md5
, mempool
, xprofiler
,
29 //RESERVED = 0, // FIXME: reuse for something
48 TGameOptions
= set of TGameOption
;
50 TGameSettings
= record
57 ItemRespawnTime
: Word;
58 ItemRespawnRandom
: Word;
59 PowerupRespawnTime
: Word;
60 PowerupRespawnRandom
: Word;
62 Options
: TGameOptions
;
71 TDelayedEvent
= record
80 Sound
: TPlayableSound
;
81 Tags
: Array of String;
85 TPlayerSettings
= record
90 // ones below are sent only to the server
92 WeaponPreferences
: Array[WP_FIRST
..WP_LAST
+1] of Byte;
109 function g_Game_IsNet(): Boolean;
110 function g_Game_IsServer(): Boolean;
111 function g_Game_IsClient(): Boolean;
112 procedure g_Game_Init();
113 procedure g_Game_Free (freeTextures
: Boolean=true);
114 procedure g_Game_LoadData();
115 procedure g_Game_FreeData();
116 procedure g_Game_Update();
117 procedure g_Game_PreUpdate();
118 procedure g_Game_Draw();
119 procedure g_Game_Quit();
120 procedure g_Game_SetupScreenSize();
121 procedure g_Game_ChangeResolution(newWidth
, newHeight
: Word; nowFull
, nowMax
: Boolean);
122 function g_Game_ModeToText(Mode
: Byte): string;
123 function g_Game_TextToMode(Mode
: string): Byte;
124 procedure g_Game_ExecuteEvent(Name
: String);
125 function g_Game_DelayEvent(DEType
: Byte; Time
: LongWord; Num
: Integer = 0; Str
: String = ''): Integer;
126 procedure g_Game_AddPlayer(Team
: Byte = TEAM_NONE
);
127 procedure g_Game_RemovePlayer();
128 procedure g_Game_Spectate();
129 procedure g_Game_SpectateCenterView();
130 procedure g_Game_StartSingle(Map
: String; TwoPlayers
: Boolean; nPlayers
: Byte);
131 procedure g_Game_StartCustom(Map
: String; GameMode
: Byte; TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte; Options
: TGameOptions
; nPlayers
: Byte);
132 procedure g_Game_StartServer(Map
: String; GameMode
: Byte; TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte; Options
: TGameOptions
; nPlayers
: Byte; IPAddr
: LongWord; Port
: Word);
133 procedure g_Game_StartClient(Addr
: String; Port
: Word; PW
: String);
134 procedure g_Game_Restart();
135 procedure g_Game_RestartLevel();
136 procedure g_Game_RestartRound(NoMapRestart
: Boolean = False);
137 function g_Game_ClientWAD (NewWAD
: String; const WHash
: TMD5Digest
): AnsiString;
138 function g_Game_StartMap(asMegawad
: Boolean; Map
: String; Force
: Boolean = False; const oldMapPath
: AnsiString=''): Boolean;
139 procedure g_Game_ChangeMap(const MapPath
: String);
140 procedure g_Game_ExitLevel(const Map
: AnsiString);
141 function g_Game_GetFirstMap(WAD
: String): String;
142 function g_Game_GetNextMap(): String;
143 procedure g_Game_NextLevel();
144 procedure g_Game_Pause(Enable
: Boolean);
145 procedure g_Game_HolmesPause(Enable
: Boolean);
146 procedure g_Game_InGameMenu(Show
: Boolean);
147 function g_Game_IsWatchedPlayer(UID
: Word): Boolean;
148 function g_Game_IsWatchedTeam(Team
: Byte): Boolean;
149 procedure g_Game_Message(Msg
: String; Time
: Word);
150 procedure g_Game_LoadMapList(FileName
: String);
151 procedure g_Game_PauseAllSounds(Enable
: Boolean);
152 procedure g_Game_StopAllSounds(all
: Boolean);
153 procedure g_Game_UpdateTriggerSounds();
154 function g_Game_GetMegaWADInfo(WAD
: String): TMegaWADInfo
;
155 procedure g_Game_ChatSound(Text: String; Taunt
: Boolean = True);
156 procedure g_Game_Announce_GoodShot(SpawnerUID
: Word);
157 procedure g_Game_Announce_KillCombo(Param
: Integer);
158 procedure g_Game_Announce_BodyKill(SpawnerUID
: Word);
159 procedure g_Game_Effect_Bubbles(fX
, fY
: Integer; count
: Word; devX
, devY
: Byte; Silent
: Boolean = False);
160 procedure g_Game_StartVote(Command
, Initiator
: string);
161 procedure g_Game_CheckVote
;
162 procedure g_TakeScreenShot(Filename
: string = '');
163 procedure g_FatalError(Text: String);
164 procedure g_SimpleError(Text: String);
165 function g_Game_IsTestMap(): Boolean;
166 procedure g_Game_DeleteTestMap();
167 procedure GameCVars(P
: SSArray
);
168 procedure PlayerSettingsCVars(P
: SSArray
);
169 procedure SystemCommands(P
: SSArray
);
170 procedure GameCommands(P
: SSArray
);
171 procedure GameCheats(P
: SSArray
);
172 procedure DebugCommands(P
: SSArray
);
173 procedure g_Game_Process_Params
;
174 procedure g_Game_SetLoadingText(Text: String; Max
: Integer; reWrite
: Boolean);
175 procedure g_Game_StepLoading(Value
: Integer = -1);
176 procedure g_Game_ClearLoading();
177 procedure g_Game_SetDebugMode();
178 procedure DrawLoadingStat();
179 procedure DrawMenuBackground(tex
: AnsiString);
181 { procedure SetWinPause(Enable: Boolean); }
186 LOADING_SHOW_STEP
= 100;
187 LOADING_INTERLINE
= 20;
202 MESSAGE_DIKEY
= WM_USER
+ 1;
207 EXIT_ENDLEVELSINGLE
= 4;
208 EXIT_ENDLEVELCUSTOM
= 5;
213 STATE_INTERCUSTOM
= 3;
214 STATE_INTERSINGLE
= 4;
220 LMS_RESPAWN_NONE
= 0;
221 LMS_RESPAWN_WARMUP
= 1;
222 LMS_RESPAWN_FINAL
= 2;
239 CONFIG_FILENAME
= 'Doom2DF.cfg';
241 TEST_MAP_NAME
= '$$$_TEST_$$$';
243 STD_PLAYER_MODEL
= 'Doomer';
251 STATFILE_VERSION
= $03;
255 gGameSettings
: TGameSettings
;
256 gPlayer1Settings
: TPlayerSettings
;
257 gPlayer2Settings
: TPlayerSettings
;
259 gPlayerScreenSize
: TDFPoint
;
260 gPlayer1ScreenCoord
: TDFPoint
;
261 gPlayer2ScreenCoord
: TDFPoint
;
262 gPlayer1
: TPlayer
= nil;
263 gPlayer2
: TPlayer
= nil;
264 gPlayerDrawn
: TPlayer
= nil;
266 gLerpFactor
: Single = 1.0;
267 gSwitchGameMode
: Byte = GM_DM
;
268 gHearPoint1
, gHearPoint2
: THearPoint
;
269 gSoundEffectsDF
: Boolean = False;
270 gSoundTriggerTime
: Word = 0;
271 gAnnouncer
: Integer = ANNOUNCE_NONE
;
272 goodsnd
: array[0..3] of TPlayableSound
;
273 killsnd
: array[0..3] of TPlayableSound
;
274 hahasnd
: array[0..2] of TPlayableSound
;
275 sound_get_flag
: array[0..1] of TPlayableSound
;
276 sound_lost_flag
: array[0..1] of TPlayableSound
;
277 sound_ret_flag
: array[0..1] of TPlayableSound
;
278 sound_cap_flag
: array[0..1] of TPlayableSound
;
279 gBodyKillEvent
: Integer = -1;
280 gDefInterTime
: ShortInt = -1;
281 gInterEndTime
: LongWord = 0;
282 gInterTime
: LongWord = 0;
283 gServInterTime
: Byte = 0;
284 gGameStartTime
: LongWord = 0;
285 gTotalMonsters
: Integer = 0;
286 gPauseMain
: Boolean = false;
287 gPauseHolmes
: Boolean = false;
288 gShowTime
: Boolean = False;
289 gShowFPS
: Boolean = False;
290 gShowScore
: Boolean = True;
291 gShowStat
: Boolean = True;
292 gShowPIDs
: Boolean = False;
293 gShowKillMsg
: Boolean = True;
294 gShowLives
: Boolean = True;
295 gShowPing
: Boolean = False;
296 gShowMap
: Boolean = False;
298 gState
: Byte = STATE_NONE
;
300 sWidth
, sHeight
: Word;
301 gSpectMode
: Byte = SPECT_NONE
;
302 gSpectHUD
: Boolean = True;
303 gSpectKeyPress
: Boolean = False;
304 gSpectX
: Integer = 0;
305 gSpectY
: Integer = 0;
306 gSpectStep
: Byte = 8;
307 gSpectViewTwo
: Boolean = False;
308 gSpectPID1
: Integer = -1;
309 gSpectPID2
: Integer = -1;
310 gSpectAuto
: Boolean = False;
311 gSpectAutoNext
: LongWord;
312 gSpectAutoStepX
: Integer;
313 gSpectAutoStepY
: Integer;
314 gMusic
: TMusic
= nil;
315 gLoadGameMode
: Boolean;
316 gCheats
: Boolean = False;
317 gMapOnce
: Boolean = False;
318 gMapToDelete
: String;
319 gTempDelete
: Boolean = False;
320 gLastMap
: Boolean = False;
323 gResolutionChange
: Boolean = False;
324 gRC_Width
, gRC_Height
: Integer;
325 gRC_FullScreen
, gRC_Maximized
: Boolean;
326 gLanguageChange
: Boolean = False;
327 gDebugMode
: Boolean = False;
328 g_debug_Sounds
: Boolean = False;
329 g_debug_Frames
: Boolean = False;
330 g_debug_WinMsgs
: Boolean = False;
331 g_debug_MonsterOff
: Boolean = False;
332 g_debug_BotAIOff
: Byte = 0;
333 g_debug_HealthBar
: Boolean = False;
334 g_Debug_Player
: Boolean = False;
335 gCoopMonstersKilled
: Word = 0;
336 gCoopSecretsFound
: Word = 0;
337 gCoopTotalMonstersKilled
: Word = 0;
338 gCoopTotalSecretsFound
: Word = 0;
339 gCoopTotalMonsters
: Word = 0;
340 gCoopTotalSecrets
: Word = 0;
341 gStatsOff
: Boolean = False;
342 gStatsPressed
: Boolean = False;
343 gExitByTrigger
: Boolean = False;
344 gNextMap
: String = '';
345 gLMSRespawn
: Byte = LMS_RESPAWN_NONE
;
346 gLMSRespawnTime
: Cardinal = 0;
347 gLMSSoftSpawn
: Boolean = False;
348 gMissionFailed
: Boolean = False;
349 gVoteInProgress
: Boolean = False;
350 gVotePassed
: Boolean = False;
351 gVoteCommand
: string = '';
352 gVoteTimer
: Cardinal = 0;
353 gVoteCmdTimer
: Cardinal = 0;
354 gVoteCount
: Integer = 0;
355 gVoteTimeout
: Cardinal = 30;
356 gVoted
: Boolean = False;
357 gVotesEnabled
: Boolean = True;
358 gEvents
: Array of TGameEvent
;
359 gDelayedEvents
: Array of TDelayedEvent
;
360 gUseChatSounds
: Boolean = True;
361 gChatSounds
: Array of TChatSound
;
362 gWeaponAction
: Array [0..1, WP_FACT
..WP_LACT
] of Boolean; // [player, weapon_action]
363 gSelectWeapon
: Array [0..1, WP_FIRST
..WP_LAST
] of Boolean; // [player, weapon]
364 gInterReadyCount
: Integer = 0;
365 gMaxBots
: Integer = 127;
367 g_dbg_ignore_bounds
: Boolean = false;
368 r_smallmap_h
: Integer = 0; // 0: left; 1: center; 2: right
369 r_smallmap_v
: Integer = 2; // 0: top; 1: center; 2: bottom
371 // move button values:
372 // bits 0-1: l/r state:
373 // 0: neither left, nor right pressed
376 // bits 4-5: l/r state when strafe was pressed
377 P1MoveButton
: Byte = 0;
378 P2MoveButton
: Byte = 0;
380 g_profile_frame_update
: Boolean = false;
381 g_profile_frame_draw
: Boolean = false;
382 g_profile_collision
: Boolean = false;
383 g_profile_los
: Boolean = false;
384 g_profile_history_size
: Integer = 1000;
386 g_rlayer_back
: Boolean = true;
387 g_rlayer_step
: Boolean = true;
388 g_rlayer_wall
: Boolean = true;
389 g_rlayer_door
: Boolean = true;
390 g_rlayer_acid1
: Boolean = true;
391 g_rlayer_acid2
: Boolean = true;
392 g_rlayer_water
: Boolean = true;
393 g_rlayer_fore
: Boolean = true;
396 procedure g_ResetDynlights ();
397 procedure g_AddDynLight (x
, y
, radius
: Integer; r
, g
, b
, a
: Single);
398 procedure g_DynLightExplosion (x
, y
, radius
: Integer; r
, g
, b
: Single);
400 function conIsCheatsEnabled (): Boolean; inline;
401 function gPause (): Boolean; inline;
407 {$INCLUDE ../nogl/noGLuses.inc}
408 {$IFDEF ENABLE_HOLMES}
411 e_texture
, e_res
, g_textures
, g_window
, g_menu
,
412 e_input
, e_log
, g_console
, g_items
, g_map
, g_panel
,
413 g_playermodel
, g_gfx
, g_options
, Math
,
414 g_triggers
, g_monsters
, e_sound
, CONFIG
,
415 g_language
, g_net
, g_main
, g_phys
,
416 ENet
, e_msg
, g_netmsg
, g_netmaster
,
417 sfs
, wadreader
, g_system
;
421 hasPBarGfx
: Boolean = false;
424 // ////////////////////////////////////////////////////////////////////////// //
425 function gPause (): Boolean; inline; begin result
:= gPauseMain
or gPauseHolmes
; end;
428 // ////////////////////////////////////////////////////////////////////////// //
429 function conIsCheatsEnabled (): Boolean; inline;
432 if g_Game_IsNet
then exit
;
433 if not gDebugMode
then
435 //if not gCheats then exit;
436 if not (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
]) then exit
;
437 if not (gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
]) then exit
;
443 // ////////////////////////////////////////////////////////////////////////// //
445 profileFrameDraw
: TProfiler
= nil;
448 // ////////////////////////////////////////////////////////////////////////// //
451 x
, y
, radius
: Integer;
454 exploRadius
: Integer;
458 g_dynLights
: array of TDynLight
= nil;
459 g_dynLightCount
: Integer = 0;
460 g_playerLight
: Boolean = false;
462 procedure g_ResetDynlights ();
466 if not gwin_has_stencil
then begin g_dynLightCount
:= 0; exit
; end;
468 for idx
:= 0 to g_dynLightCount
-1 do
470 if g_dynLights
[idx
].exploCount
= -666 then
477 Inc(g_dynLights
[idx
].exploCount
);
478 if (g_dynLights
[idx
].exploCount
< 10) then
480 g_dynLights
[idx
].radius
:= g_dynLights
[idx
].exploRadius
+g_dynLights
[idx
].exploCount
*8;
481 g_dynLights
[idx
].a
:= 0.4+g_dynLights
[idx
].exploCount
/10;
482 if (g_dynLights
[idx
].a
> 0.8) then g_dynLights
[idx
].a
:= 0.8;
483 if lnum
<> idx
then g_dynLights
[lnum
] := g_dynLights
[idx
];
488 g_dynLightCount
:= lnum
;
491 procedure g_AddDynLight (x
, y
, radius
: Integer; r
, g
, b
, a
: Single);
493 if not gwin_has_stencil
then exit
;
494 if g_dynLightCount
= length(g_dynLights
) then SetLength(g_dynLights
, g_dynLightCount
+1024);
495 g_dynLights
[g_dynLightCount
].x
:= x
;
496 g_dynLights
[g_dynLightCount
].y
:= y
;
497 g_dynLights
[g_dynLightCount
].radius
:= radius
;
498 g_dynLights
[g_dynLightCount
].r
:= r
;
499 g_dynLights
[g_dynLightCount
].g
:= g
;
500 g_dynLights
[g_dynLightCount
].b
:= b
;
501 g_dynLights
[g_dynLightCount
].a
:= a
;
502 g_dynLights
[g_dynLightCount
].exploCount
:= -666;
503 Inc(g_dynLightCount
);
506 procedure g_DynLightExplosion (x
, y
, radius
: Integer; r
, g
, b
: Single);
508 if not gwin_has_stencil
then exit
;
509 if g_dynLightCount
= length(g_dynLights
) then SetLength(g_dynLights
, g_dynLightCount
+1024);
510 g_dynLights
[g_dynLightCount
].x
:= x
;
511 g_dynLights
[g_dynLightCount
].y
:= y
;
512 g_dynLights
[g_dynLightCount
].radius
:= 0;
513 g_dynLights
[g_dynLightCount
].exploRadius
:= radius
;
514 g_dynLights
[g_dynLightCount
].r
:= r
;
515 g_dynLights
[g_dynLightCount
].g
:= g
;
516 g_dynLights
[g_dynLightCount
].b
:= b
;
517 g_dynLights
[g_dynLightCount
].a
:= 0;
518 g_dynLights
[g_dynLightCount
].exploCount
:= 0;
519 Inc(g_dynLightCount
);
523 // ////////////////////////////////////////////////////////////////////////// //
524 function calcProfilesHeight (prof
: TProfiler
): Integer;
527 if (prof
= nil) then exit
;
528 if (length(prof
.bars
) = 0) then exit
;
529 result
:= length(prof
.bars
)*(16+2);
533 function drawProfiles (x
, y
: Integer; prof
: TProfiler
): Integer;
540 if (prof
= nil) then exit
;
542 if (length(prof
.bars
) = 0) then exit
;
544 hgt
:= calcProfilesHeight(prof
);
545 if (x
< 0) then x
:= gScreenWidth
-(wdt
-1)+x
;
546 if (y
< 0) then y
:= gScreenHeight
-(hgt
-1)+y
;
548 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
549 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
550 e_DarkenQuadWH(x
, y
, wdt
, hgt
, 150);
553 for ii
:= 0 to High(prof
.bars
) do
555 e_TextureFontPrintEx(x
+2+4*prof
.bars
[ii
].level
, yy
, Format('%s: %d', [prof
.bars
[ii
].name
, prof
.bars
[ii
].value
]), gStdFont
, 255, 255, 0, 1, false);
562 // ////////////////////////////////////////////////////////////////////////// //
564 TEndCustomGameStat
= record
565 PlayerStat
: TPlayerStatArray
;
569 Map
, MapName
: String;
572 TEndSingleGameStat
= record
573 PlayerStat
: Array [0..1] of record
579 TotalSecrets
: Integer;
582 TLoadingStat
= record
586 Msgs
: Array of String;
588 PBarWasHere
: Boolean; // did we draw a progress bar for this message?
591 TParamStrValue
= record
596 TParamStrValues
= Array of TParamStrValue
;
599 INTER_ACTION_TEXT
= 1;
600 INTER_ACTION_PIC
= 2;
601 INTER_ACTION_MUSIC
= 3;
605 FPSCounter
, UPSCounter
: Word;
606 FPSTime
, UPSTime
: LongWord;
607 DataLoaded
: Boolean = False;
608 IsDrawStat
: Boolean = False;
609 CustomStat
: TEndCustomGameStat
;
610 SingleStat
: TEndSingleGameStat
;
611 LoadingStat
: TLoadingStat
;
612 EndingGameCounter
: Byte = 0;
615 MessageLineLength
: Integer = 80;
616 MapList
: SSArray
= nil;
617 MapIndex
: Integer = -1;
618 InterReadyTime
: Integer = -1;
619 StatShotDone
: Boolean = False;
620 StatFilename
: string = ''; // used by stat screenshot to save with the same name as the csv
621 StatDate
: string = '';
627 text: Array of ShortString;
628 anim
: Array of ShortString;
629 pic
: Array of ShortString;
630 mus
: Array of ShortString;
632 triggers
: Array of record
634 actions
: Array of record
635 action
, p1
, p2
: Integer;
638 cur_trigger
: Integer;
651 function Compare(a
, b
: TPlayerStat
): Integer;
653 if a
.Spectator
then Result
:= 1
654 else if b
.Spectator
then Result
:= -1
655 else if a
.Frags
< b
.Frags
then Result
:= 1
656 else if a
.Frags
> b
.Frags
then Result
:= -1
657 else if a
.Deaths
< b
.Deaths
then Result
:= -1
658 else if a
.Deaths
> b
.Deaths
then Result
:= 1
659 else if a
.Kills
< b
.Kills
then Result
:= -1
663 procedure SortGameStat(var stat
: TPlayerStatArray
);
668 if stat
= nil then Exit
;
670 for I
:= High(stat
) downto Low(stat
) do
671 for J
:= Low(stat
) to High(stat
) - 1 do
672 if Compare(stat
[J
], stat
[J
+ 1]) = 1 then
675 stat
[J
] := stat
[J
+ 1];
680 // saves a shitty CSV containing the game stats passed to it
681 procedure SaveGameStat(Stat
: TEndCustomGameStat
; Path
: string);
684 dir
, fname
, map
, mode
, etime
, flags
, strf
: String;
689 dir
:= e_GetWriteableDir(StatsDirs
);
690 // stats are placed in stats/yy/mm/dd/*.csv
691 fname
:= e_CatPath(dir
, Path
);
692 ForceDirectories(fname
); // ensure yy/mm/dd exists within the stats dir
693 fname
:= e_CatPath(fname
, StatFilename
+ '.csv');
694 AssignFile(s
, fname
);
696 SetTextCodePage(s
, CP_UTF8
);
698 // line 1: stats ver, datetime, server name, map name, game mode, time limit, score limit, dmflags, game time, num players
699 if g_Game_IsNet
then fname
:= NetServerName
else fname
:= '';
700 map
:= g_ExtractWadNameNoPath(gMapInfo
.Map
) + ':/' + g_ExtractFileName(gMapInfo
.Map
);
701 mode
:= g_Game_ModeToText(Stat
.GameMode
);
702 etime
:= Format('%d:%.2d:%.2d', [
703 Stat
.GameTime
div 1000 div 3600,
704 (Stat
.GameTime
div 1000 div 60) mod 60,
705 Stat
.GameTime
div 1000 mod 60
709 for flag
in gGameSettings
.Options
do
712 System
.WriteStr(strf
, flag
); // FIXME: rename our utils.WriteStr()
716 WriteLn(s
, 'stats_ver,datetime,server,map,mode,timelimit,scorelimit,dmflags,time,num_players');
717 WriteLn(s
, Format('%d,%s,%s,%s,%s,%u,%u,"%s",%s,%d', [
723 gGameSettings
.TimeLimit
,
724 gGameSettings
.ScoreLimit
,
727 Length(Stat
.PlayerStat
)
729 // line 2: game specific shit
730 // if it's a team game: red score, blue score
731 // if it's a coop game: monsters killed, monsters total, secrets found, secrets total
733 if Stat
.GameMode
in [GM_TDM
, GM_CTF
] then
735 Format('red_score,blue_score' + LineEnding
+ '%d,%d', [Stat
.TeamStat
[TEAM_RED
].Score
, Stat
.TeamStat
[TEAM_BLUE
].Score
]))
736 else if Stat
.GameMode
in [GM_COOP
, GM_SINGLE
] then
738 Format('mon_killed,mon_total,secrets_found,secrets_total' + LineEnding
+ '%d,%d,%d,%d',[gCoopMonstersKilled
, gTotalMonsters
, gCoopSecretsFound
, gSecretsCount
]));
739 // lines 3-...: team, player name, frags, deaths
740 WriteLn(s
, 'team,name,frags,deaths');
741 for I
:= Low(Stat
.PlayerStat
) to High(Stat
.PlayerStat
) do
742 with Stat
.PlayerStat
[I
] do
743 WriteLn(s
, Format('%d,%s,%d,%d', [Team
, dquoteStr(Name
), Frags
, Deaths
]));
745 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_WRITE
], [fname
]));
748 g_Console_Add('could not create gamestats file "' + fname
+ '"');
753 procedure ClearDebugCvars();
755 g_debug_Sounds
:= False;
756 g_debug_Frames
:= False;
757 g_debug_WinMsgs
:= False;
758 g_debug_MonsterOff
:= False;
759 g_debug_BotAIOff
:= 0;
760 g_debug_HealthBar
:= False;
761 g_Debug_Player
:= False;
764 function g_Game_ModeToText(Mode
: Byte): string;
768 GM_DM
: Result
:= _lc
[I_MENU_GAME_TYPE_DM
];
769 GM_TDM
: Result
:= _lc
[I_MENU_GAME_TYPE_TDM
];
770 GM_CTF
: Result
:= _lc
[I_MENU_GAME_TYPE_CTF
];
771 GM_COOP
: Result
:= _lc
[I_MENU_GAME_TYPE_COOP
];
772 GM_SINGLE
: Result
:= _lc
[I_MENU_GAME_TYPE_SINGLE
];
776 function g_Game_TextToMode(Mode
: string): Byte;
779 Mode
:= UpperCase(Mode
);
780 if Mode
= _lc
[I_MENU_GAME_TYPE_DM
] then
785 if Mode
= _lc
[I_MENU_GAME_TYPE_TDM
] then
790 if Mode
= _lc
[I_MENU_GAME_TYPE_CTF
] then
795 if Mode
= _lc
[I_MENU_GAME_TYPE_COOP
] then
800 if Mode
= _lc
[I_MENU_GAME_TYPE_SINGLE
] then
807 function g_Game_IsNet(): Boolean;
809 Result
:= (gGameSettings
.GameType
in [GT_SERVER
, GT_CLIENT
]);
812 function g_Game_IsServer(): Boolean;
814 Result
:= (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
, GT_SERVER
]);
817 function g_Game_IsClient(): Boolean;
819 Result
:= (gGameSettings
.GameType
= GT_CLIENT
);
822 function g_Game_GetMegaWADInfo(WAD
: String): TMegaWADInfo
;
829 Result
.name
:= ExtractFileName(WAD
);
830 Result
.description
:= '';
833 w
:= TWADFile
.Create();
836 if not w
.GetResource('INTERSCRIPT', p
, len
) then
842 cfg
:= TConfig
.CreateMem(p
, len
);
843 Result
.name
:= cfg
.ReadStr('megawad', 'name', ExtractFileName(WAD
));
844 Result
.description
:= cfg
.ReadStr('megawad', 'description', '');
845 Result
.author
:= cfg
.ReadStr('megawad', 'author', '');
846 Result
.pic
:= cfg
.ReadStr('megawad', 'pic', '');
852 procedure g_Game_FreeWAD();
856 for a
:= 0 to High(MegaWAD
.res
.pic
) do
857 if MegaWAD
.res
.pic
[a
] <> '' then
858 g_Texture_Delete(MegaWAD
.res
.pic
[a
]);
860 for a
:= 0 to High(MegaWAD
.res
.mus
) do
861 if MegaWAD
.res
.mus
[a
] <> '' then
862 g_Sound_Delete(MegaWAD
.res
.mus
[a
]);
864 MegaWAD
.res
.pic
:= nil;
865 MegaWAD
.res
.text := nil;
866 MegaWAD
.res
.anim
:= nil;
867 MegaWAD
.res
.mus
:= nil;
868 MegaWAD
.triggers
:= nil;
870 g_Texture_Delete('TEXTURE_endpic');
871 g_Sound_Delete('MUSIC_endmus');
873 ZeroMemory(@MegaWAD
, SizeOf(MegaWAD
));
874 gGameSettings
.WAD
:= '';
877 procedure g_Game_LoadWAD(WAD
: string);
886 gGameSettings
.WAD
:= WAD
;
887 if not (gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
]) then
890 MegaWAD
.info
:= g_Game_GetMegaWADInfo(WAD
);
892 w
:= TWADFile
.Create();
895 if not w
.GetResource('INTERSCRIPT', p
, len
) then
901 cfg
:= TConfig
.CreateMem(p
, len
);
906 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
907 if s = '' then Break;
910 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
911 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
913 g_Texture_CreateWADEx(s, s);
919 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
920 if s = '' then Break;
923 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
924 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
926 g_Music_CreateWADEx(s, s);
929 MegaWAD
.endpic
:= cfg
.ReadStr('megawad', 'endpic', '');
930 if MegaWAD
.endpic
<> '' then
932 TEXTUREFILTER
:= GL_LINEAR
;
933 s
:= e_GetResourcePath(WadDirs
, MegaWAD
.endpic
, WAD
);
934 g_Texture_CreateWADEx('TEXTURE_endpic', s
);
935 TEXTUREFILTER
:= GL_NEAREST
;
937 MegaWAD
.endmus
:= cfg
.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
938 if MegaWAD
.endmus
<> '' then
940 s
:= e_GetResourcePath(WadDirs
, MegaWAD
.endmus
, WAD
);
941 g_Sound_CreateWADEx('MUSIC_endmus', s
, True);
949 {procedure start_trigger(t: string);
953 function next_trigger(): Boolean;
957 procedure DisableCheats();
963 if gPlayer1
<> nil then gPlayer1
.GodMode
:= False;
964 if gPlayer2
<> nil then gPlayer2
.GodMode
:= False;
965 if gPlayer1
<> nil then gPlayer1
.NoTarget
:= False;
966 if gPlayer2
<> nil then gPlayer2
.NoTarget
:= False;
968 {$IF DEFINED(D2F_DEBUG)}
969 if gPlayer1
<> nil then gPlayer1
.NoTarget
:= True;
970 gAimLine
:= g_dbg_aimline_on
;
974 procedure g_Game_ExecuteEvent(Name
: String);
980 if gEvents
= nil then
982 for a
:= 0 to High(gEvents
) do
983 if gEvents
[a
].Name
= Name
then
985 if gEvents
[a
].Command
<> '' then
986 g_Console_Process(gEvents
[a
].Command
, True);
991 function g_Game_DelayEvent(DEType
: Byte; Time
: LongWord; Num
: Integer = 0; Str
: String = ''): Integer;
996 if gDelayedEvents
<> nil then
997 for a
:= 0 to High(gDelayedEvents
) do
998 if not gDelayedEvents
[a
].Pending
then
1005 SetLength(gDelayedEvents
, Length(gDelayedEvents
) + 1);
1006 n
:= High(gDelayedEvents
);
1008 gDelayedEvents
[n
].Pending
:= True;
1009 gDelayedEvents
[n
].DEType
:= DEType
;
1010 gDelayedEvents
[n
].DENum
:= Num
;
1011 gDelayedEvents
[n
].DEStr
:= Str
;
1012 if DEType
= DE_GLOBEVENT
then
1013 gDelayedEvents
[n
].Time
:= (sys_GetTicks() {div 1000}) + Time
1015 gDelayedEvents
[n
].Time
:= gTime
+ Time
;
1019 procedure EndGame();
1025 if g_Game_IsNet
and g_Game_IsServer
then
1026 MH_SEND_GameEvent(NET_EV_MAPEND
, Byte(gMissionFailed
));
1029 gPauseMain
:= false;
1030 gPauseHolmes
:= false;
1033 g_Game_StopAllSounds(False);
1038 EndingGameCounter
:= 0;
1039 g_ActiveWindow
:= nil;
1041 gLMSRespawn
:= LMS_RESPAWN_NONE
;
1042 gLMSRespawnTime
:= 0;
1045 EXIT_SIMPLE
: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
1050 begin // Ýòî áûë òåñò
1054 begin // Âûõîä â ãëàâíîå ìåíþ
1055 gMusic
.SetByName('MUSIC_MENU');
1057 if gState
<> STATE_SLIST
then
1059 g_GUI_ShowWindow('MainMenu');
1060 gState
:= STATE_MENU
;
1063 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
1064 slReturnPressed
:= True;
1065 if g_Net_Slist_Fetch(slCurrent
) then
1067 if slCurrent
= nil then
1068 slWaitStr
:= _lc
[I_NET_SLIST_NOSERVERS
];
1071 slWaitStr
:= _lc
[I_NET_SLIST_ERROR
];
1072 g_Serverlist_GenerateTable(slCurrent
, slTable
);
1075 g_Game_ExecuteEvent('ongameend');
1079 EXIT_RESTART
: // Íà÷àòü óðîâåíü ñíà÷àëà
1081 if not g_Game_IsClient
then g_Game_Restart();
1084 EXIT_ENDLEVELCUSTOM
: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
1086 // Ñòàòèñòèêà Ñâîåé èãðû:
1087 FileName
:= g_ExtractWadName(gMapInfo
.Map
);
1089 CustomStat
.GameTime
:= gTime
;
1090 CustomStat
.Map
:= ExtractFileName(FileName
)+':'+g_ExtractFileName(gMapInfo
.Map
); //ResName;
1091 CustomStat
.MapName
:= gMapInfo
.Name
;
1092 CustomStat
.GameMode
:= gGameSettings
.GameMode
;
1093 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1094 CustomStat
.TeamStat
:= gTeamStat
;
1096 CustomStat
.PlayerStat
:= nil;
1098 // Ñòàòèñòèêà èãðîêîâ:
1099 if gPlayers
<> nil then
1101 for a
:= 0 to High(gPlayers
) do
1102 if gPlayers
[a
] <> nil then
1104 SetLength(CustomStat
.PlayerStat
, Length(CustomStat
.PlayerStat
)+1);
1105 with CustomStat
.PlayerStat
[High(CustomStat
.PlayerStat
)] do
1108 Name
:= gPlayers
[a
].Name
;
1109 Frags
:= gPlayers
[a
].Frags
;
1110 Deaths
:= gPlayers
[a
].Death
;
1111 Kills
:= gPlayers
[a
].Kills
;
1112 Team
:= gPlayers
[a
].Team
;
1113 Color
:= gPlayers
[a
].Model
.Color
;
1114 Spectator
:= gPlayers
[a
].FSpectator
;
1118 SortGameStat(CustomStat
.PlayerStat
);
1120 if (gSaveStats
or gScreenshotStats
) and (Length(CustomStat
.PlayerStat
) > 1) then
1123 if g_Game_IsNet
then StatFilename
:= NetServerName
else StatFilename
:= 'local';
1124 StatDate
:= FormatDateTime('yymmdd_hhnnss', t
);
1125 StatFilename
:= StatFilename
+ '_' + CustomStat
.Map
+ '_' + g_Game_ModeToText(CustomStat
.GameMode
);
1126 StatFilename
:= sanitizeFilename(StatFilename
) + '_' + StatDate
;
1128 SaveGameStat(CustomStat
, FormatDateTime('yyyy"/"mm"/"dd', t
));
1131 StatShotDone
:= False;
1134 g_Game_ExecuteEvent('onmapend');
1135 if not g_Game_IsClient
then g_Player_ResetReady
;
1136 gInterReadyCount
:= 0;
1138 // Çàòóõàþùèé ýêðàí:
1139 EndingGameCounter
:= 255;
1140 gState
:= STATE_FOLD
;
1142 if gDefInterTime
< 0 then
1143 gInterEndTime
:= IfThen((gGameSettings
.GameType
= GT_SERVER
) and (gPlayer1
= nil), 15000, 25000)
1145 gInterEndTime
:= gDefInterTime
* 1000;
1148 EXIT_ENDLEVELSINGLE
: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
1150 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
1151 SingleStat
.GameTime
:= gTime
;
1152 SingleStat
.TwoPlayers
:= gPlayer2
<> nil;
1153 SingleStat
.TotalSecrets
:= gSecretsCount
;
1154 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
1155 SingleStat
.PlayerStat
[0].Kills
:= gPlayer1
.MonsterKills
;
1156 SingleStat
.PlayerStat
[0].Secrets
:= gPlayer1
.Secrets
;
1157 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
1158 if SingleStat
.TwoPlayers
then
1160 SingleStat
.PlayerStat
[1].Kills
:= gPlayer2
.MonsterKills
;
1161 SingleStat
.PlayerStat
[1].Secrets
:= gPlayer2
.Secrets
;
1164 g_Game_ExecuteEvent('onmapend');
1167 if gNextMap
<> '' then
1169 gMusic
.SetByName('MUSIC_INTERMUS');
1171 gState
:= STATE_INTERSINGLE
;
1174 g_Game_ExecuteEvent('oninter');
1176 else // Áîëüøå íåò êàðò
1178 // Çàòóõàþùèé ýêðàí:
1179 EndingGameCounter
:= 255;
1180 gState
:= STATE_FOLD
;
1185 // Îêîí÷àíèå îáðàáîòàíî:
1186 if gExit
<> EXIT_QUIT
then
1190 procedure drawTime(X
, Y
: Integer); inline;
1192 e_TextureFontPrint(x
, y
,
1193 Format('%d:%.2d:%.2d', [
1194 gTime
div 1000 div 3600,
1195 (gTime
div 1000 div 60) mod 60,
1196 gTime
div 1000 mod 60
1201 procedure DrawStat();
1203 pc
, x
, y
, w
, h
: Integer;
1204 w1
, w2
, w3
, w4
: Integer;
1206 cw
, ch
, r
, g
, b
, rr
, gg
, bb
: Byte;
1209 stat
: TPlayerStatArray
;
1217 pc
:= g_Player_GetCount
;
1218 e_TextureFontGetSize(gStdFont
, cw
, ch
);
1220 w
:= gScreenWidth
-(gScreenWidth
div 5);
1221 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1224 h
:= 40+ch
*5+(ch
+8)*pc
;
1225 x
:= (gScreenWidth
div 2)-(w
div 2);
1226 y
:= (gScreenHeight
div 2)-(h
div 2);
1228 e_DrawFillQuad(x
, y
, x
+w
-1, y
+h
-1, 64, 64, 64, 32);
1229 e_DrawQuad(x
, y
, x
+w
-1, y
+h
-1, 255, 127, 0);
1231 drawTime(x
+w
-78, y
+8);
1233 wad
:= g_ExtractWadNameNoPath(gMapInfo
.Map
);
1234 map
:= g_ExtractFileName(gMapInfo
.Map
);
1235 mapstr
:= wad
+ ':\' + map
+ ' - ' + gMapInfo
.Name
;
1237 case gGameSettings
.GameMode
of
1240 if gGameSettings
.MaxLives
= 0 then
1241 s1
:= _lc
[I_GAME_DM
]
1243 s1
:= _lc
[I_GAME_LMS
];
1244 s2
:= Format(_lc
[I_GAME_FRAG_LIMIT
], [gGameSettings
.ScoreLimit
]);
1245 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1250 if gGameSettings
.MaxLives
= 0 then
1251 s1
:= _lc
[I_GAME_TDM
]
1253 s1
:= _lc
[I_GAME_TLMS
];
1254 s2
:= Format(_lc
[I_GAME_FRAG_LIMIT
], [gGameSettings
.ScoreLimit
]);
1255 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1260 s1
:= _lc
[I_GAME_CTF
];
1261 s2
:= Format(_lc
[I_GAME_SCORE_LIMIT
], [gGameSettings
.ScoreLimit
]);
1262 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
1267 if gGameSettings
.MaxLives
= 0 then
1268 s1
:= _lc
[I_GAME_COOP
]
1270 s1
:= _lc
[I_GAME_SURV
];
1271 s2
:= _lc
[I_GAME_MONSTERS
] + ' ' + IntToStr(gCoopMonstersKilled
) + '/' + IntToStr(gTotalMonsters
);
1272 s3
:= _lc
[I_GAME_SECRETS
] + ' ' + IntToStr(gCoopSecretsFound
) + '/' + IntToStr(gSecretsCount
);
1283 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*cw
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
1285 e_TextureFontPrintEx(x
+(w
div 2)-(Length(mapstr
)*cw
div 2), _y
, mapstr
, gStdFont
, 200, 200, 200, 1);
1287 e_TextureFontPrintEx(x
+16, _y
, s2
, gStdFont
, 200, 200, 200, 1);
1289 e_TextureFontPrintEx(x
+w
-16-(Length(s3
))*cw
, _y
, s3
,
1290 gStdFont
, 200, 200, 200, 1);
1292 if NetMode
= NET_SERVER
then
1293 e_TextureFontPrintEx(x
+8, y
+ 8, _lc
[I_NET_SERVER
], gStdFont
, 255, 255, 255, 1)
1295 if NetMode
= NET_CLIENT
then
1296 e_TextureFontPrintEx(x
+8, y
+ 8,
1297 NetClientIP
+ ':' + IntToStr(NetClientPort
), gStdFont
, 255, 255, 255, 1);
1301 stat
:= g_Player_GetStats();
1304 w2
:= (w
-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1305 w3
:= (w
-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1307 w1
:= w
-16-w2
-w3
-w4
; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1309 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1313 for a
:= TEAM_RED
to TEAM_BLUE
do
1315 if a
= TEAM_RED
then
1317 s1
:= _lc
[I_GAME_TEAM_RED
];
1324 s1
:= _lc
[I_GAME_TEAM_BLUE
];
1330 e_TextureFontPrintEx(x
+16, _y
, s1
, gStdFont
, r
, g
, b
, 1);
1331 e_TextureFontPrintEx(x
+w1
+16, _y
, IntToStr(gTeamStat
[a
].Score
),
1332 gStdFont
, r
, g
, b
, 1);
1334 _y
:= _y
+ch
+(ch
div 4);
1335 e_DrawLine(1, x
+16, _y
, x
+w
-16, _y
, r
, g
, b
);
1336 _y
:= _y
+(ch
div 4);
1338 for aa
:= 0 to High(stat
) do
1339 if stat
[aa
].Team
= a
then
1355 namestr
:= Format('[%5d] %s', [UID
, Name
])
1359 e_TextureFontPrintEx(x
+16, _y
, namestr
, gStdFont
, rr
, gg
, bb
, 1);
1361 e_TextureFontPrintEx(x
+w1
+16, _y
, Format(_lc
[I_GAME_PING_MS
], [Ping
, Loss
]), gStdFont
, rr
, gg
, bb
, 1);
1363 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
, IntToStr(Frags
), gStdFont
, rr
, gg
, bb
, 1);
1365 e_TextureFontPrintEx(x
+w1
+w2
+w3
+16, _y
, IntToStr(Deaths
), gStdFont
, rr
, gg
, bb
, 1);
1372 else if gGameSettings
.GameMode
in [GM_DM
, GM_COOP
] then
1375 e_TextureFontPrintEx(x
+16, _y
, _lc
[I_GAME_PLAYER_NAME
], gStdFont
, 255, 127, 0, 1);
1376 e_TextureFontPrintEx(x
+16+w1
, _y
, _lc
[I_GAME_PING
], gStdFont
, 255, 127, 0, 1);
1377 e_TextureFontPrintEx(x
+16+w1
+w2
, _y
, _lc
[I_GAME_FRAGS
], gStdFont
, 255, 127, 0, 1);
1378 e_TextureFontPrintEx(x
+16+w1
+w2
+w3
, _y
, _lc
[I_GAME_DEATHS
], gStdFont
, 255, 127, 0, 1);
1381 for aa
:= 0 to High(stat
) do
1395 namestr
:= Format('[%5d] %s', [UID
, Name
])
1399 e_DrawFillQuad(x
+16, _y
+4, x
+32-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
1400 e_DrawQuad(x
+16, _y
+4, x
+32-1, _y
+16+4-1, 192, 192, 192);
1402 e_TextureFontPrintEx(x
+16+16+8, _y
+4, namestr
, gStdFont
, r
, g
, 0, 1);
1404 e_TextureFontPrintEx(x
+w1
+16, _y
+4, Format(_lc
[I_GAME_PING_MS
], [Ping
, Loss
]), gStdFont
, r
, g
, 0, 1);
1406 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
+4, IntToStr(Frags
), gStdFont
, r
, g
, 0, 1);
1408 e_TextureFontPrintEx(x
+w1
+w2
+w3
+16, _y
+4, IntToStr(Deaths
), gStdFont
, r
, g
, 0, 1);
1414 procedure g_Game_Init();
1417 knownFiles
: array of AnsiString = nil;
1419 wext
, s
: AnsiString;
1424 gTempDelete
:= False;
1426 sfsGCDisable(); // temporary disable removing of temporary volumes
1429 TEXTUREFILTER
:= GL_LINEAR
;
1430 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD
+':TEXTURES\TITLE');
1431 g_Texture_CreateWADEx('INTER', GameWAD
+':TEXTURES\INTER');
1432 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD
+':TEXTURES\ENDGAME_EN');
1433 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD
+':TEXTURES\ENDGAME_RU');
1434 TEXTUREFILTER
:= GL_NEAREST
;
1436 LoadStdFont('STDTXT', 'STDFONT', gStdFont
);
1437 LoadFont('MENUTXT', 'MENUFONT', gMenuFont
);
1438 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont
);
1440 g_Game_ClearLoading();
1441 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION
]), 0, False);
1442 g_Game_SetLoadingText('', 0, False);
1444 g_Game_SetLoadingText(_lc
[I_LOAD_CONSOLE
], 0, False);
1447 g_Game_SetLoadingText(_lc
[I_LOAD_MODELS
], 0, False);
1448 g_PlayerModel_LoadData();
1450 // load models from all possible wad types, in all known directories
1451 // this does a loosy job (linear search, ooph!), but meh
1452 for wext
in wadExtensions
do
1454 for f
:= High(ModelDirs
) downto Low(ModelDirs
) do
1456 if (FindFirst(ModelDirs
[f
]+DirectorySeparator
+'*'+wext
, faAnyFile
, SR
) = 0) then
1460 for s
in knownFiles
do
1462 if (strEquCI1251(forceFilenameExt(SR
.Name
, ''), forceFilenameExt(ExtractFileName(s
), ''))) then
1470 SetLength(knownFiles
, length(knownFiles
)+1);
1471 knownFiles
[High(knownFiles
)] := ModelDirs
[f
]+DirectorySeparator
+SR
.Name
;
1473 until (FindNext(SR
) <> 0);
1479 if (length(knownFiles
) = 0) then raise Exception
.Create('no player models found!');
1481 if (length(knownFiles
) = 1) then e_LogWriteln('1 player model found.', TMsgType
.Notify
) else e_LogWritefln('%d player models found.', [Integer(length(knownFiles
))], TMsgType
.Notify
);
1482 for s
in knownFiles
do
1484 if not g_PlayerModel_Load(s
) then e_LogWritefln('Error loading model "%s"', [s
], TMsgType
.Warning
);
1488 gPauseMain
:= false;
1489 gPauseHolmes
:= false;
1492 {e_MouseInfo.Accel := 1.0;}
1494 g_Game_SetLoadingText(_lc
[I_LOAD_GAME_DATA
], 0, False);
1497 g_Game_SetLoadingText(_lc
[I_LOAD_MUSIC
], 0, False);
1498 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD
+':MUSIC\INTERMUS', True);
1499 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD
+':MUSIC\MENU', True);
1500 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD
+':MUSIC\ROUNDMUS', True, True);
1501 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD
+':MUSIC\ENDMUS', True);
1504 g_Game_SetLoadingText(_lc
[I_LOAD_MENUS
], 0, False);
1508 gMusic
:= TMusic
.Create();
1509 gMusic
.SetByName('MUSIC_MENU');
1512 gGameSettings
.WarmupTime
:= 30;
1514 gState
:= STATE_MENU
;
1516 SetLength(gEvents
, 6);
1517 gEvents
[0].Name
:= 'ongamestart';
1518 gEvents
[1].Name
:= 'ongameend';
1519 gEvents
[2].Name
:= 'onmapstart';
1520 gEvents
[3].Name
:= 'onmapend';
1521 gEvents
[4].Name
:= 'oninter';
1522 gEvents
[5].Name
:= 'onwadend';
1524 sfsGCEnable(); // enable releasing unused volumes
1528 procedure g_Game_Free(freeTextures
: Boolean=true);
1530 if NetMode
= NET_CLIENT
then g_Net_Disconnect();
1531 if NetMode
= NET_SERVER
then g_Net_Host_Die();
1533 g_Map_Free(freeTextures
);
1535 g_Player_RemoveAllCorpses();
1537 gGameSettings
.GameType
:= GT_NONE
;
1538 if gGameSettings
.GameMode
= GM_SINGLE
then
1539 gGameSettings
.GameMode
:= GM_DM
;
1540 gSwitchGameMode
:= gGameSettings
.GameMode
;
1543 gExitByTrigger
:= False;
1546 function IsActivePlayer(p
: TPlayer
): Boolean;
1551 Result
:= (not p
.FDummy
) and (not p
.FSpectator
);
1554 function GetActivePlayer_ByID(ID
: Integer): TPlayer
;
1561 if gPlayers
= nil then
1563 for a
:= Low(gPlayers
) to High(gPlayers
) do
1564 if IsActivePlayer(gPlayers
[a
]) then
1566 if gPlayers
[a
].UID
<> ID
then
1568 Result
:= gPlayers
[a
];
1573 function GetActivePlayerID_Next(Skip
: Integer = -1): Integer;
1579 if gPlayers
= nil then
1583 for a
:= Low(gPlayers
) to High(gPlayers
) do
1584 if IsActivePlayer(gPlayers
[a
]) then
1586 SetLength(ids
, Length(ids
) + 1);
1587 ids
[High(ids
)] := gPlayers
[a
].UID
;
1588 if gPlayers
[a
].UID
= Skip
then
1591 if Length(ids
) = 0 then
1596 Result
:= ids
[(idx
+ 1) mod Length(ids
)];
1599 function GetActivePlayerID_Prev(Skip
: Integer = -1): Integer;
1605 if gPlayers
= nil then
1609 for a
:= Low(gPlayers
) to High(gPlayers
) do
1610 if IsActivePlayer(gPlayers
[a
]) then
1612 SetLength(ids
, Length(ids
) + 1);
1613 ids
[High(ids
)] := gPlayers
[a
].UID
;
1614 if gPlayers
[a
].UID
= Skip
then
1617 if Length(ids
) = 0 then
1620 Result
:= ids
[Length(ids
) - 1]
1622 Result
:= ids
[(Length(ids
) - 1 + idx
) mod Length(ids
)];
1625 function GetActivePlayerID_Random(Skip
: Integer = -1): Integer;
1631 if gPlayers
= nil then
1635 for a
:= Low(gPlayers
) to High(gPlayers
) do
1636 if IsActivePlayer(gPlayers
[a
]) then
1638 SetLength(ids
, Length(ids
) + 1);
1639 ids
[High(ids
)] := gPlayers
[a
].UID
;
1640 if gPlayers
[a
].UID
= Skip
then
1643 if Length(ids
) = 0 then
1645 if Length(ids
) = 1 then
1650 Result
:= ids
[Random(Length(ids
))];
1652 while (idx
<> -1) and (Result
= Skip
) and (a
> 0) do
1654 Result
:= ids
[Random(Length(ids
))];
1659 function GetRandomSpectMode(Current
: Byte): Byte;
1666 0: Result
:= SPECT_STATS
;
1667 1: Result
:= SPECT_MAPVIEW
;
1668 2: Result
:= SPECT_MAPVIEW
;
1669 3: Result
:= SPECT_PLAYERS
;
1670 4: Result
:= SPECT_PLAYERS
;
1671 5: Result
:= SPECT_PLAYERS
;
1672 6: Result
:= SPECT_PLAYERS
;
1674 if (Current
in [SPECT_STATS
, SPECT_MAPVIEW
]) and (Current
= Result
) then
1678 procedure ProcessPlayerControls (plr
: TPlayer
; p
: Integer; var MoveButton
: Byte);
1684 if (plr
= nil) then exit
;
1685 if (p
= 2) then time
:= 1000 else time
:= 1;
1686 strafeDir
:= MoveButton
shr 4;
1687 MoveButton
:= MoveButton
and $0F;
1689 if gPlayerAction
[p
, ACTION_MOVELEFT
] and (not gPlayerAction
[p
, ACTION_MOVERIGHT
]) then
1690 MoveButton
:= 1 // Íàæàòà òîëüêî "Âëåâî"
1691 else if (not gPlayerAction
[p
, ACTION_MOVELEFT
]) and gPlayerAction
[p
, ACTION_MOVERIGHT
] then
1692 MoveButton
:= 2 // Íàæàòà òîëüêî "Âïðàâî"
1693 else if (not gPlayerAction
[p
, ACTION_MOVELEFT
]) and (not gPlayerAction
[p
, ACTION_MOVERIGHT
]) then
1694 MoveButton
:= 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1696 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1697 if MoveButton
= 1 then
1698 plr
.PressKey(KEY_LEFT
, time
)
1699 else if MoveButton
= 2 then
1700 plr
.PressKey(KEY_RIGHT
, time
);
1702 // if we have "strafe" key, turn off old strafe mechanics
1703 if gPlayerAction
[p
, ACTION_STRAFE
] then
1705 // new strafe mechanics
1706 if (strafeDir
= 0) then
1707 strafeDir
:= MoveButton
; // start strafing
1708 // now set direction according to strafe (reversed)
1709 if (strafeDir
= 2) then
1710 plr
.SetDirection(TDirection
.D_LEFT
)
1711 else if (strafeDir
= 1) then
1712 plr
.SetDirection(TDirection
.D_RIGHT
)
1716 strafeDir
:= 0; // not strafing anymore
1717 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1718 if (MoveButton
= 2) and gPlayerAction
[p
, ACTION_MOVELEFT
] then
1719 plr
.SetDirection(TDirection
.D_LEFT
)
1720 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1721 else if (MoveButton
= 1) and gPlayerAction
[p
, ACTION_MOVERIGHT
] then
1722 plr
.SetDirection(TDirection
.D_RIGHT
)
1723 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1724 else if MoveButton
<> 0 then
1725 plr
.SetDirection(TDirection(MoveButton
-1))
1728 // fix movebutton state
1729 MoveButton
:= MoveButton
or (strafeDir
shl 4);
1731 // Îñòàëüíûå êëàâèøè:
1732 if gPlayerAction
[p
, ACTION_JUMP
] then plr
.PressKey(KEY_JUMP
, time
);
1733 if gPlayerAction
[p
, ACTION_LOOKUP
] then plr
.PressKey(KEY_UP
, time
);
1734 if gPlayerAction
[p
, ACTION_LOOKDOWN
] then plr
.PressKey(KEY_DOWN
, time
);
1735 if gPlayerAction
[p
, ACTION_ATTACK
] then plr
.PressKey(KEY_FIRE
);
1736 if gPlayerAction
[p
, ACTION_ACTIVATE
] then plr
.PressKey(KEY_OPEN
);
1738 for i
:= WP_FACT
to WP_LACT
do
1740 if gWeaponAction
[p
, i
] then
1742 plr
.ProcessWeaponAction(i
);
1743 gWeaponAction
[p
, i
] := False
1747 for i
:= WP_FIRST
to WP_LAST
do
1749 if gSelectWeapon
[p
, i
] then
1751 plr
.QueueWeaponSwitch(i
); // all choices are passed there, and god will take the best
1752 gSelectWeapon
[p
, i
] := False
1756 // HACK: add dynlight here
1757 if gwin_k8_enable_light_experiments
then
1759 if e_KeyPressed(IK_F8
) and gGameOn
and (not gConsoleShow
) and (g_ActiveWindow
= nil) then
1761 g_playerLight
:= true;
1763 if e_KeyPressed(IK_F9
) and gGameOn
and (not gConsoleShow
) and (g_ActiveWindow
= nil) then
1765 g_playerLight
:= false;
1769 if gwin_has_stencil
and g_playerLight
then g_AddDynLight(plr
.GameX
+32, plr
.GameY
+40, 128, 1, 1, 0, 0.6);
1772 // HACK: don't have a "key was pressed" function
1773 procedure InterReady();
1775 if InterReadyTime
> gTime
then Exit
;
1776 InterReadyTime
:= gTime
+ 3000;
1777 MC_SEND_CheatRequest(NET_CHEAT_READY
);
1780 procedure g_Game_PreUpdate();
1782 // these are in separate PreUpdate functions because they can interact during Update()
1783 // and are synced over the net
1784 // we don't care that much about corpses and gibs
1785 g_Player_PreUpdate();
1786 g_Monsters_PreUpdate();
1787 g_Items_PreUpdate();
1788 g_Weapon_PreUpdate();
1791 procedure g_Game_Update();
1793 Msg
: g_gui
.TMessage
;
1799 function sendMonsPos (mon
: TMonster
): Boolean;
1801 result
:= false; // don't stop
1802 // this will also reset "need-send" flag
1803 if mon
.gncNeedSend
then
1805 MH_SEND_MonsterPos(mon
.UID
);
1807 else if (mon
.MonsterType
= MONSTER_BARREL
) then
1809 if (mon
.GameVelX
<> 0) or (mon
.GameVelY
<> 0) then MH_SEND_MonsterPos(mon
.UID
);
1811 else if (mon
.MonsterState
<> MONSTATE_SLEEP
) then
1813 if (mon
.MonsterState
<> MONSTATE_DEAD
) or (mon
.GameVelX
<> 0) or (mon
.GameVelY
<> 0) then MH_SEND_MonsterPos(mon
.UID
);
1817 function sendMonsPosUnexpected (mon
: TMonster
): Boolean;
1819 result
:= false; // don't stop
1820 // this will also reset "need-send" flag
1821 if mon
.gncNeedSend
then MH_SEND_MonsterPos(mon
.UID
);
1824 function sendItemPos (it
: PItem
): Boolean;
1826 result
:= false; // don't stop
1829 MH_SEND_ItemPos(it
.myId
);
1830 it
.needSend
:= False;
1835 reliableUpdate
: Boolean;
1840 // Ïîðà âûêëþ÷àòü èãðó:
1841 if gExit
= EXIT_QUIT
then
1843 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1847 if gExit
= EXIT_QUIT
then
1851 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî
1852 // no need to, as we'll do it in event handler
1854 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1857 if (NetMode
= NET_NONE
) and (g_Game_IsNet
) and (gGameOn
or (gState
in [STATE_FOLD
, STATE_INTERCUSTOM
])) then
1859 gExit
:= EXIT_SIMPLE
;
1864 // process master server communications
1865 g_Net_Slist_Pulse();
1868 STATE_INTERSINGLE
, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1869 STATE_INTERCUSTOM
, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1870 STATE_INTERTEXT
, // Òåêñò ìåæäó óðîâíÿìè
1871 STATE_INTERPIC
: // Êàðòèíêà ìåæäó óðîâíÿìè
1873 if g_Game_IsNet
and g_Game_IsServer
then
1875 gInterTime
:= gInterTime
+ GAME_TICK
;
1876 a
:= Min((gInterEndTime
- gInterTime
) div 1000 + 1, 255);
1877 if a
<> gServInterTime
then
1879 gServInterTime
:= a
;
1880 MH_SEND_TimeSync(gServInterTime
);
1884 if (not g_Game_IsClient
) and
1888 e_KeyPressed(IK_RETURN
) or e_KeyPressed(IK_KPRETURN
) or e_KeyPressed(IK_SPACE
) or
1889 e_KeyPressed(VK_FIRE
) or e_KeyPressed(VK_OPEN
) or
1890 e_KeyPressed(JOY0_ATTACK
) or e_KeyPressed(JOY1_ATTACK
) or
1891 e_KeyPressed(JOY2_ATTACK
) or e_KeyPressed(JOY3_ATTACK
)
1893 and (not gJustChatted
) and (not gConsoleShow
) and (not gChatShow
)
1894 and (g_ActiveWindow
= nil)
1896 or (g_Game_IsNet
and ((gInterTime
> gInterEndTime
) or ((gInterReadyCount
>= NetClientCount
) and (NetClientCount
> 0))))
1899 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1900 g_Game_StopAllSounds(True);
1902 if gMapOnce
then // Ýòî áûë òåñò
1903 gExit
:= EXIT_SIMPLE
1905 if gNextMap
<> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1906 g_Game_ChangeMap(gNextMap
)
1907 else // Ñëåäóþùåé êàðòû íåò
1909 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
] then
1911 // Âûõîä â ãëàâíîå ìåíþ:
1913 g_GUI_ShowWindow('MainMenu');
1914 gMusic
.SetByName('MUSIC_MENU');
1916 gState
:= STATE_MENU
;
1919 // Ôèíàëüíàÿ êàðòèíêà:
1920 g_Game_ExecuteEvent('onwadend');
1922 if not gMusic
.SetByName('MUSIC_endmus') then
1923 gMusic
.SetByName('MUSIC_STDENDMUS');
1925 gState
:= STATE_ENDPIC
;
1927 g_Game_ExecuteEvent('ongameend');
1932 else if g_Game_IsClient
and
1935 e_KeyPressed(IK_RETURN
) or e_KeyPressed(IK_KPRETURN
) or e_KeyPressed(IK_SPACE
) or
1936 e_KeyPressed(VK_FIRE
) or e_KeyPressed(VK_OPEN
) or
1937 e_KeyPressed(JOY0_ATTACK
) or e_KeyPressed(JOY1_ATTACK
) or
1938 e_KeyPressed(JOY2_ATTACK
) or e_KeyPressed(JOY3_ATTACK
)
1940 and (not gJustChatted
) and (not gConsoleShow
) and (not gChatShow
)
1941 and (g_ActiveWindow
= nil)
1949 if gState
= STATE_INTERTEXT
then
1950 if InterText
.counter
> 0 then
1951 InterText
.counter
:= InterText
.counter
- 1;
1954 STATE_FOLD
: // Çàòóõàíèå ýêðàíà
1956 if EndingGameCounter
= 0 then
1958 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1959 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
] then
1961 gState
:= STATE_INTERCUSTOM
;
1962 InterReadyTime
:= -1;
1963 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then
1965 g_Game_ExecuteEvent('onwadend');
1966 if not gMusic
.SetByName('MUSIC_endmus') then
1967 gMusic
.SetByName('MUSIC_STDENDMUS');
1970 gMusic
.SetByName('MUSIC_ROUNDMUS');
1974 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1976 gMusic
.SetByName('MUSIC_INTERMUS');
1978 gState
:= STATE_INTERSINGLE
;
1981 g_Game_ExecuteEvent('oninter');
1984 DecMin(EndingGameCounter
, 6, 0);
1987 STATE_ENDPIC
: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1989 if gMapOnce
then // Ýòî áûë òåñò
1991 gExit
:= EXIT_SIMPLE
;
1997 g_Serverlist_Control(slCurrent
, slTable
);
2000 // Ñòàòèñòèêà ïî Tab:
2002 IsDrawStat
:= (not gConsoleShow
) and (not gChatShow
) and (gGameSettings
.GameType
<> GT_SINGLE
) and g_Console_Action(ACTION_SCORES
);
2005 if gGameOn
and not gPause
and (gState
<> STATE_FOLD
) then
2007 // Âðåìÿ += 28 ìèëëèñåêóíä:
2008 gTime
:= gTime
+ GAME_TICK
;
2010 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
2011 if MessageTime
= 0 then
2013 if MessageTime
> 0 then
2014 MessageTime
:= MessageTime
- 1;
2016 if (g_Game_IsServer
) then
2018 // Áûë çàäàí ëèìèò âðåìåíè:
2019 if (gGameSettings
.TimeLimit
> 0) then
2020 if (gTime
- gGameStartTime
) div 1000 >= gGameSettings
.TimeLimit
then
2021 begin // Îí ïðîøåë => êîíåö óðîâíÿ
2026 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
2027 if (gLMSRespawn
> LMS_RESPAWN_NONE
) and (gLMSRespawnTime
< gTime
) then
2028 g_Game_RestartRound(gLMSSoftSpawn
);
2030 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
2031 if gVoteInProgress
and (gVoteTimer
< gTime
) then
2033 else if gVotePassed
and (gVoteCmdTimer
< gTime
) then
2035 g_Console_Process(gVoteCommand
);
2037 gVotePassed
:= False;
2040 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
2041 if gFlags
[FLAG_RED
].State
= FLAG_STATE_CAPTURED
then
2042 gFlags
[FLAG_RED
].CaptureTime
:= gFlags
[FLAG_RED
].CaptureTime
+ GAME_TICK
;
2043 if gFlags
[FLAG_BLUE
].State
= FLAG_STATE_CAPTURED
then
2044 gFlags
[FLAG_BLUE
].CaptureTime
:= gFlags
[FLAG_BLUE
].CaptureTime
+ GAME_TICK
;
2046 // Áûë çàäàí ëèìèò ïîáåä:
2047 if (gGameSettings
.ScoreLimit
> 0) then
2051 if gGameSettings
.GameMode
= GM_DM
then
2052 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
2053 for i
:= 0 to High(gPlayers
) do
2054 if gPlayers
[i
] <> nil then
2055 if gPlayers
[i
].Frags
> b
then
2056 b
:= gPlayers
[i
].Frags
;
2059 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
2060 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
2061 b
:= Max(gTeamStat
[TEAM_RED
].Score
, gTeamStat
[TEAM_BLUE
].Score
);
2064 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
2065 if b
>= gGameSettings
.ScoreLimit
then
2072 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
2073 if gPlayer1
<> nil then gPlayer1
.ReleaseKeys();
2074 if gPlayer2
<> nil then gPlayer2
.ReleaseKeys();
2075 if (not gConsoleShow
) and (not gChatShow
) and (g_ActiveWindow
= nil) then
2077 ProcessPlayerControls(gPlayer1
, 0, P1MoveButton
);
2078 ProcessPlayerControls(gPlayer2
, 1, P2MoveButton
);
2079 end // if not console
2082 if g_Game_IsNet
and (gPlayer1
<> nil) then gPlayer1
.PressKey(KEY_CHAT
, 10000);
2084 // process weapon switch queue
2088 if (gPlayer1
= nil) and (gPlayer2
= nil) and
2089 (not gConsoleShow
) and (not gChatShow
) and (g_ActiveWindow
= nil) then
2091 if not gSpectKeyPress
then
2093 if gPlayerAction
[0, ACTION_JUMP
] and (not gSpectAuto
) then
2095 // switch spect mode
2097 SPECT_NONE
: ; // not spectator
2099 SPECT_MAPVIEW
: Inc(gSpectMode
);
2100 SPECT_PLAYERS
: gSpectMode
:= SPECT_STATS
; // reset to 1
2102 gSpectKeyPress
:= True;
2104 if (gSpectMode
= SPECT_MAPVIEW
)
2105 and (not gSpectAuto
) then
2107 if gPlayerAction
[0, ACTION_MOVELEFT
] then
2108 gSpectX
:= Max(gSpectX
- gSpectStep
, 0);
2109 if gPlayerAction
[0, ACTION_MOVERIGHT
] then
2110 gSpectX
:= Min(gSpectX
+ gSpectStep
, gMapInfo
.Width
- gScreenWidth
);
2111 if gPlayerAction
[0, ACTION_LOOKUP
] then
2112 gSpectY
:= Max(gSpectY
- gSpectStep
, 0);
2113 if gPlayerAction
[0, ACTION_LOOKDOWN
] then
2114 gSpectY
:= Min(gSpectY
+ gSpectStep
, gMapInfo
.Height
- gScreenHeight
);
2115 if gWeaponAction
[0, WP_PREV
] then
2118 if gSpectStep
> 4 then gSpectStep
:= gSpectStep
shr 1;
2119 gWeaponAction
[0, WP_PREV
] := False;
2121 if gWeaponAction
[0, WP_NEXT
] then
2124 if gSpectStep
< 64 then gSpectStep
:= gSpectStep
shl 1;
2125 gWeaponAction
[0, WP_NEXT
] := False;
2128 if (gSpectMode
= SPECT_PLAYERS
)
2129 and (not gSpectAuto
) then
2131 if gPlayerAction
[0, ACTION_LOOKUP
] then
2134 gSpectViewTwo
:= True;
2135 gSpectKeyPress
:= True;
2137 if gPlayerAction
[0, ACTION_LOOKDOWN
] then
2139 // remove second view
2140 gSpectViewTwo
:= False;
2141 gSpectKeyPress
:= True;
2143 if gPlayerAction
[0, ACTION_MOVELEFT
] then
2145 // prev player (view 1)
2146 gSpectPID1
:= GetActivePlayerID_Prev(gSpectPID1
);
2147 gSpectKeyPress
:= True;
2149 if gPlayerAction
[0, ACTION_MOVERIGHT
] then
2151 // next player (view 1)
2152 gSpectPID1
:= GetActivePlayerID_Next(gSpectPID1
);
2153 gSpectKeyPress
:= True;
2155 if gWeaponAction
[0, WP_PREV
] then
2157 // prev player (view 2)
2158 gSpectPID2
:= GetActivePlayerID_Prev(gSpectPID2
);
2159 gWeaponAction
[0, WP_PREV
] := False;
2161 if gWeaponAction
[0, WP_NEXT
] then
2163 // next player (view 2)
2164 gSpectPID2
:= GetActivePlayerID_Next(gSpectPID2
);
2165 gWeaponAction
[0, WP_NEXT
] := False;
2168 if gPlayerAction
[0, ACTION_ATTACK
] then
2170 if (gSpectMode
= SPECT_STATS
) and (not gSpectAuto
) then
2173 gSpectAutoNext
:= 0;
2174 gSpectViewTwo
:= False;
2175 gSpectKeyPress
:= True;
2180 gSpectMode
:= SPECT_STATS
;
2181 gSpectAuto
:= False;
2182 gSpectKeyPress
:= True;
2187 if (not gPlayerAction
[0, ACTION_JUMP
]) and
2188 (not gPlayerAction
[0, ACTION_ATTACK
]) and
2189 (not gPlayerAction
[0, ACTION_MOVELEFT
]) and
2190 (not gPlayerAction
[0, ACTION_MOVERIGHT
]) and
2191 (not gPlayerAction
[0, ACTION_LOOKUP
]) and
2192 (not gPlayerAction
[0, ACTION_LOOKDOWN
]) then
2193 gSpectKeyPress
:= False;
2197 if gSpectMode
= SPECT_MAPVIEW
then
2199 i
:= Min(Max(gSpectX
+ gSpectAutoStepX
, 0), gMapInfo
.Width
- gScreenWidth
);
2201 gSpectAutoNext
:= gTime
2204 i
:= Min(Max(gSpectY
+ gSpectAutoStepY
, 0), gMapInfo
.Height
- gScreenHeight
);
2206 gSpectAutoNext
:= gTime
2210 if gSpectAutoNext
<= gTime
then
2212 if gSpectAutoNext
> 0 then
2214 gSpectMode
:= GetRandomSpectMode(gSpectMode
);
2218 gSpectX
:= Random(gMapInfo
.Width
- gScreenWidth
);
2219 gSpectY
:= Random(gMapInfo
.Height
- gScreenHeight
);
2220 gSpectAutoStepX
:= Random(9) - 4;
2221 gSpectAutoStepY
:= Random(9) - 4;
2222 if ((gSpectX
< 800) and (gSpectAutoStepX
< 0)) or
2223 ((gSpectX
> gMapInfo
.Width
- gScreenWidth
- 800) and (gSpectAutoStepX
> 0)) then
2224 gSpectAutoStepX
:= gSpectAutoStepX
* -1;
2225 if ((gSpectY
< 800) and (gSpectAutoStepY
< 0)) or
2226 ((gSpectY
> gMapInfo
.Height
- gScreenHeight
- 800) and (gSpectAutoStepY
> 0)) then
2227 gSpectAutoStepY
:= gSpectAutoStepY
* -1;
2231 gSpectPID1
:= GetActivePlayerID_Random(gSpectPID1
);
2236 SPECT_STATS
: gSpectAutoNext
:= gTime
+ (Random(3) + 5) * 1000;
2237 SPECT_MAPVIEW
: gSpectAutoNext
:= gTime
+ (Random(4) + 7) * 1000;
2238 SPECT_PLAYERS
: gSpectAutoNext
:= gTime
+ (Random(7) + 8) * 1000;
2244 // Îáíîâëÿåì âñå îñòàëüíîå:
2247 g_Triggers_Update();
2249 g_Monsters_Update();
2251 g_Player_UpdateAll();
2252 g_Player_UpdatePhysicalObjects();
2254 // server: send newly spawned monsters unconditionally
2255 if (gGameSettings
.GameType
= GT_SERVER
) then
2257 if (Length(gMonstersSpawned
) > 0) then
2259 for I
:= 0 to High(gMonstersSpawned
) do MH_SEND_MonsterSpawn(gMonstersSpawned
[I
]);
2260 SetLength(gMonstersSpawned
, 0);
2264 if (gSoundTriggerTime
> 8) then
2266 g_Game_UpdateTriggerSounds();
2267 gSoundTriggerTime
:= 0;
2271 Inc(gSoundTriggerTime
);
2274 if (NetMode
= NET_SERVER
) then
2276 Inc(NetTimeToUpdate
);
2277 Inc(NetTimeToReliable
);
2279 // send monster updates
2280 if (NetTimeToReliable
>= NetRelupdRate
) or (NetTimeToUpdate
>= NetUpdateRate
) then
2282 // send all monsters (periodic sync)
2283 reliableUpdate
:= (NetTimeToReliable
>= NetRelupdRate
);
2285 for I
:= 0 to High(gPlayers
) do
2287 if (gPlayers
[I
] <> nil) then MH_SEND_PlayerPos(reliableUpdate
, gPlayers
[I
].UID
);
2290 g_Mons_ForEach(sendMonsPos
);
2292 // update flags that aren't stationary
2293 if gGameSettings
.GameMode
= GM_CTF
then
2294 for I
:= FLAG_RED
to FLAG_BLUE
do
2295 if gFlags
[I
].NeedSend
then
2297 gFlags
[I
].NeedSend
:= False;
2301 // update items that aren't stationary
2302 g_Items_ForEachAlive(sendItemPos
);
2304 if reliableUpdate
then
2306 NetTimeToReliable
:= 0;
2307 NetTimeToUpdate
:= NetUpdateRate
;
2311 NetTimeToUpdate
:= 0;
2316 // send only mosters with some unexpected changes
2317 g_Mons_ForEach(sendMonsPosUnexpected
);
2320 // send unexpected platform changes
2321 g_Map_NetSendInterestingPanels();
2323 g_Net_Slist_ServerUpdate();
2325 if NetUseMaster then
2327 if (gTime >= NetTimeToMaster) or g_Net_Slist_IsConnectionInProgress then
2329 if (not g_Net_Slist_IsConnectionActive) then g_Net_Slist_Connect(false); // non-blocking connection to the master
2331 NetTimeToMaster := gTime + NetMasterRate;
2336 else if (NetMode
= NET_CLIENT
) then
2338 MC_SEND_PlayerPos();
2340 end; // if gameOn ...
2342 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
2343 if g_ActiveWindow
<> nil then
2345 w
:= e_GetFirstKeyPressed();
2347 if (w
<> IK_INVALID
) then
2349 Msg
.Msg
:= MESSAGE_DIKEY
;
2351 g_ActiveWindow
.OnMessage(Msg
);
2354 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
2355 if g_ActiveWindow
<> nil then
2356 g_ActiveWindow
.Update();
2358 // Íóæíî ñìåíèòü ðàçðåøåíèå:
2359 if gResolutionChange
then
2361 e_WriteLog('Changing resolution', TMsgType
.Notify
);
2362 g_Game_ChangeResolution(gRC_Width
, gRC_Height
, gRC_FullScreen
, gRC_Maximized
);
2363 gResolutionChange
:= False;
2364 g_ActiveWindow
:= nil;
2367 // Íóæíî ñìåíèòü ÿçûê:
2368 if gLanguageChange
then
2370 //e_WriteLog('Read language file', MSG_NOTIFY);
2371 //g_Language_Load(DataDir + gLanguage + '.txt');
2372 g_Language_Set(gLanguage
);
2376 gLanguageChange
:= False;
2380 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
2381 if e_KeyPressed(IK_F10
) and
2383 (not gConsoleShow
) and
2384 (g_ActiveWindow
= nil) then
2389 Time
:= sys_GetTicks() {div 1000};
2391 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
2392 if gDelayedEvents
<> nil then
2393 for a
:= 0 to High(gDelayedEvents
) do
2394 if gDelayedEvents
[a
].Pending
and
2396 ((gDelayedEvents
[a
].DEType
= DE_GLOBEVENT
) and (gDelayedEvents
[a
].Time
<= Time
)) or
2397 ((gDelayedEvents
[a
].DEType
> DE_GLOBEVENT
) and (gDelayedEvents
[a
].Time
<= gTime
))
2400 case gDelayedEvents
[a
].DEType
of
2402 g_Game_ExecuteEvent(gDelayedEvents
[a
].DEStr
);
2405 g_Game_Announce_GoodShot(gDelayedEvents
[a
].DENum
);
2409 g_Game_Announce_KillCombo(gDelayedEvents
[a
].DENum
);
2410 if g_Game_IsNet
and g_Game_IsServer
then
2411 MH_SEND_GameEvent(NET_EV_KILLCOMBO
, gDelayedEvents
[a
].DENum
);
2415 g_Game_Announce_BodyKill(gDelayedEvents
[a
].DENum
);
2417 gDelayedEvents
[a
].Pending
:= False;
2420 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2421 UPSCounter
:= UPSCounter
+ 1;
2422 if Time
- UPSTime
>= 1000 then
2431 g_Weapon_AddDynLights();
2432 g_Items_AddDynLights();
2436 procedure g_Game_LoadChatSounds(Resource
: string);
2439 FileName
, Snd
: string;
2441 len
, cnt
, tags
, i
, j
: Integer;
2444 FileName
:= g_ExtractWadName(Resource
);
2446 WAD
:= TWADFile
.Create();
2447 WAD
.ReadFile(FileName
);
2449 if not WAD
.GetResource(g_ExtractFilePathName(Resource
), p
, len
) then
2456 cfg
:= TConfig
.CreateMem(p
, len
);
2457 cnt
:= cfg
.ReadInt('ChatSounds', 'Count', 0);
2459 SetLength(gChatSounds
, cnt
);
2460 for i
:= 0 to Length(gChatSounds
) - 1 do
2462 gChatSounds
[i
].Sound
:= nil;
2463 Snd
:= Trim(cfg
.ReadStr(IntToStr(i
), 'Sound', ''));
2464 tags
:= cfg
.ReadInt(IntToStr(i
), 'Tags', 0);
2465 if (Snd
= '') or (Tags
<= 0) then
2467 g_Sound_CreateWADEx('SOUND_CHAT_MACRO' + IntToStr(i
), GameWAD
+':'+Snd
);
2468 gChatSounds
[i
].Sound
:= TPlayableSound
.Create();
2469 gChatSounds
[i
].Sound
.SetByName('SOUND_CHAT_MACRO' + IntToStr(i
));
2470 SetLength(gChatSounds
[i
].Tags
, tags
);
2471 for j
:= 0 to tags
- 1 do
2472 gChatSounds
[i
].Tags
[j
] := toLowerCase1251(cfg
.ReadStr(IntToStr(i
), 'Tag' + IntToStr(j
), ''));
2473 gChatSounds
[i
].FullWord
:= cfg
.ReadBool(IntToStr(i
), 'FullWord', False);
2480 procedure g_Game_FreeChatSounds();
2484 for i
:= 0 to Length(gChatSounds
) - 1 do
2486 gChatSounds
[i
].Sound
.Free();
2487 g_Sound_Delete('SOUND_CHAT_MACRO' + IntToStr(i
));
2489 SetLength(gChatSounds
, 0);
2493 procedure g_Game_LoadData();
2500 if DataLoaded
then Exit
;
2502 e_WriteLog('Loading game data...', TMsgType
.Notify
);
2504 g_Texture_CreateWADEx('NOTEXTURE', GameWAD
+':TEXTURES\NOTEXTURE');
2505 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD
+':TEXTURES\HUD');
2506 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD
+':TEXTURES\AIRBAR');
2507 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD
+':TEXTURES\JETBAR');
2508 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD
+':TEXTURES\HUDBG');
2509 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD
+':TEXTURES\ARMORHUD');
2510 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD
+':TEXTURES\FLAGHUD_R_BASE');
2511 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD
+':TEXTURES\FLAGHUD_R_STOLEN');
2512 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD
+':TEXTURES\FLAGHUD_R_DROP');
2513 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD
+':TEXTURES\FLAGHUD_B_BASE');
2514 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD
+':TEXTURES\FLAGHUD_B_STOLEN');
2515 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD
+':TEXTURES\FLAGHUD_B_DROP');
2516 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD
+':TEXTURES\TALKBUBBLE');
2517 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD
+':TEXTURES\PENTA');
2518 g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD
+':TEXTURES\PLRIND');
2521 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD
+':TEXTURES\LLEFT') then hasPBarGfx
:= false;
2522 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD
+':TEXTURES\LMARKER') then hasPBarGfx
:= false;
2523 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD
+':TEXTURES\LMIDDLE') then hasPBarGfx
:= false;
2524 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD
+':TEXTURES\LRIGHT') then hasPBarGfx
:= false;
2528 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl
, hl
);
2529 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr
, hr
);
2530 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb
, hb
);
2531 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm
, hm
);
2532 if (wl
> 0) and (hl
> 0) and (wr
> 0) and (hr
= hl
) and (wb
> 0) and (hb
= hl
) and (wm
> 0) and (hm
> 0) and (hm
<= hl
) then
2538 hasPBarGfx
:= false;
2542 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD
+':TEXTURES\TELEPORT', 64, 64, 10, False);
2543 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD
+':WEAPONS\PUNCH', 64, 64, 4, False);
2544 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD
+':WEAPONS\PUNCH_UP', 64, 64, 4, False);
2545 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD
+':WEAPONS\PUNCH_DN', 64, 64, 4, False);
2546 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD
+':WEAPONS\PUNCHB', 64, 64, 4, False);
2547 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD
+':WEAPONS\PUNCHB_UP', 64, 64, 4, False);
2548 g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD
+':WEAPONS\PUNCHB_DN', 64, 64, 4, False);
2549 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD
+':SOUNDS\TELEPORT');
2550 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD
+':SOUNDS\NOTELEPORT');
2551 g_Sound_CreateWADEx('SOUND_GAME_SECRET', GameWAD
+':SOUNDS\SECRET');
2552 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD
+':SOUNDS\DOOROPEN');
2553 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD
+':SOUNDS\DOORCLOSE');
2554 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD
+':SOUNDS\BULK1');
2555 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD
+':SOUNDS\BULK2');
2556 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD
+':SOUNDS\BUBBLE1');
2557 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD
+':SOUNDS\BUBBLE2');
2558 g_Sound_CreateWADEx('SOUND_GAME_BURNING', GameWAD
+':SOUNDS\BURNING');
2559 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD
+':SOUNDS\SWITCH1');
2560 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD
+':SOUNDS\SWITCH0');
2561 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD
+':SOUNDS\RADIO');
2562 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD
+':SOUNDS\GOOD1');
2563 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD
+':SOUNDS\GOOD2');
2564 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD
+':SOUNDS\GOOD3');
2565 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD
+':SOUNDS\GOOD4');
2566 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD
+':SOUNDS\KILL2X');
2567 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD
+':SOUNDS\KILL3X');
2568 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD
+':SOUNDS\KILL4X');
2569 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD
+':SOUNDS\KILLMX');
2570 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD
+':SOUNDS\MUHAHA1');
2571 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD
+':SOUNDS\MUHAHA2');
2572 g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD
+':SOUNDS\MUHAHA3');
2573 g_Sound_CreateWADEx('SOUND_CTF_GET1', GameWAD
+':SOUNDS\GETFLAG1');
2574 g_Sound_CreateWADEx('SOUND_CTF_GET2', GameWAD
+':SOUNDS\GETFLAG2');
2575 g_Sound_CreateWADEx('SOUND_CTF_LOST1', GameWAD
+':SOUNDS\LOSTFLG1');
2576 g_Sound_CreateWADEx('SOUND_CTF_LOST2', GameWAD
+':SOUNDS\LOSTFLG2');
2577 g_Sound_CreateWADEx('SOUND_CTF_RETURN1', GameWAD
+':SOUNDS\RETFLAG1');
2578 g_Sound_CreateWADEx('SOUND_CTF_RETURN2', GameWAD
+':SOUNDS\RETFLAG2');
2579 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE1', GameWAD
+':SOUNDS\CAPFLAG1');
2580 g_Sound_CreateWADEx('SOUND_CTF_CAPTURE2', GameWAD
+':SOUNDS\CAPFLAG2');
2582 goodsnd
[0] := TPlayableSound
.Create();
2583 goodsnd
[1] := TPlayableSound
.Create();
2584 goodsnd
[2] := TPlayableSound
.Create();
2585 goodsnd
[3] := TPlayableSound
.Create();
2587 goodsnd
[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2588 goodsnd
[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2589 goodsnd
[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2590 goodsnd
[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2592 killsnd
[0] := TPlayableSound
.Create();
2593 killsnd
[1] := TPlayableSound
.Create();
2594 killsnd
[2] := TPlayableSound
.Create();
2595 killsnd
[3] := TPlayableSound
.Create();
2597 killsnd
[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2598 killsnd
[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2599 killsnd
[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2600 killsnd
[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2602 hahasnd
[0] := TPlayableSound
.Create();
2603 hahasnd
[1] := TPlayableSound
.Create();
2604 hahasnd
[2] := TPlayableSound
.Create();
2606 hahasnd
[0].SetByName('SOUND_ANNOUNCER_MUHAHA1');
2607 hahasnd
[1].SetByName('SOUND_ANNOUNCER_MUHAHA2');
2608 hahasnd
[2].SetByName('SOUND_ANNOUNCER_MUHAHA3');
2610 sound_get_flag
[0] := TPlayableSound
.Create();
2611 sound_get_flag
[1] := TPlayableSound
.Create();
2612 sound_lost_flag
[0] := TPlayableSound
.Create();
2613 sound_lost_flag
[1] := TPlayableSound
.Create();
2614 sound_ret_flag
[0] := TPlayableSound
.Create();
2615 sound_ret_flag
[1] := TPlayableSound
.Create();
2616 sound_cap_flag
[0] := TPlayableSound
.Create();
2617 sound_cap_flag
[1] := TPlayableSound
.Create();
2619 sound_get_flag
[0].SetByName('SOUND_CTF_GET1');
2620 sound_get_flag
[1].SetByName('SOUND_CTF_GET2');
2621 sound_lost_flag
[0].SetByName('SOUND_CTF_LOST1');
2622 sound_lost_flag
[1].SetByName('SOUND_CTF_LOST2');
2623 sound_ret_flag
[0].SetByName('SOUND_CTF_RETURN1');
2624 sound_ret_flag
[1].SetByName('SOUND_CTF_RETURN2');
2625 sound_cap_flag
[0].SetByName('SOUND_CTF_CAPTURE1');
2626 sound_cap_flag
[1].SetByName('SOUND_CTF_CAPTURE2');
2628 g_Game_LoadChatSounds(GameWAD
+':CHATSND\SNDCFG');
2630 g_Game_SetLoadingText(_lc
[I_LOAD_ITEMS_DATA
], 0, False);
2633 g_Game_SetLoadingText(_lc
[I_LOAD_WEAPONS_DATA
], 0, False);
2634 g_Weapon_LoadData();
2636 g_Monsters_LoadData();
2641 procedure g_Game_FreeData();
2643 if not DataLoaded
then Exit
;
2646 g_Weapon_FreeData();
2647 g_Monsters_FreeData();
2649 e_WriteLog('Releasing game data...', TMsgType
.Notify
);
2651 g_Texture_Delete('NOTEXTURE');
2652 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2653 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2654 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2655 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2656 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2657 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2658 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2659 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2660 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2661 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2662 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2663 g_Frames_DeleteByName('FRAMES_TELEPORT');
2664 g_Frames_DeleteByName('FRAMES_PUNCH');
2665 g_Frames_DeleteByName('FRAMES_PUNCH_UP');
2666 g_Frames_DeleteByName('FRAMES_PUNCH_DN');
2667 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK');
2668 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP');
2669 g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN');
2670 g_Sound_Delete('SOUND_GAME_TELEPORT');
2671 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2672 g_Sound_Delete('SOUND_GAME_SECRET');
2673 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2674 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2675 g_Sound_Delete('SOUND_GAME_BULK1');
2676 g_Sound_Delete('SOUND_GAME_BULK2');
2677 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2678 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2679 g_Sound_Delete('SOUND_GAME_BURNING');
2680 g_Sound_Delete('SOUND_GAME_SWITCH1');
2681 g_Sound_Delete('SOUND_GAME_SWITCH0');
2688 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2689 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2690 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2691 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2698 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2699 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2700 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2701 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2707 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1');
2708 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2');
2709 g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3');
2711 sound_get_flag
[0].Free();
2712 sound_get_flag
[1].Free();
2713 sound_lost_flag
[0].Free();
2714 sound_lost_flag
[1].Free();
2715 sound_ret_flag
[0].Free();
2716 sound_ret_flag
[1].Free();
2717 sound_cap_flag
[0].Free();
2718 sound_cap_flag
[1].Free();
2720 g_Sound_Delete('SOUND_CTF_GET1');
2721 g_Sound_Delete('SOUND_CTF_GET2');
2722 g_Sound_Delete('SOUND_CTF_LOST1');
2723 g_Sound_Delete('SOUND_CTF_LOST2');
2724 g_Sound_Delete('SOUND_CTF_RETURN1');
2725 g_Sound_Delete('SOUND_CTF_RETURN2');
2726 g_Sound_Delete('SOUND_CTF_CAPTURE1');
2727 g_Sound_Delete('SOUND_CTF_CAPTURE2');
2729 g_Game_FreeChatSounds();
2731 DataLoaded
:= False;
2734 procedure DrawCustomStat();
2740 ww2
, hh2
, r
, g
, b
, rr
, gg
, bb
: Byte;
2741 s1
, s2
, topstr
: String;
2743 e_TextureFontGetSize(gStdFont
, ww2
, hh2
);
2747 if g_Console_Action(ACTION_SCORES
) then
2749 if not gStatsPressed
then
2751 gStatsOff
:= not gStatsOff
;
2752 gStatsPressed
:= True;
2756 gStatsPressed
:= False;
2760 s1
:= _lc
[I_MENU_INTER_NOTICE_TAB
];
2761 w
:= (Length(s1
) * ww2
) div 2;
2762 x
:= gScreenWidth
div 2 - w
;
2764 e_TextureFontPrint(x
, y
, s1
, gStdFont
);
2768 if (gGameSettings
.GameMode
= GM_COOP
) then
2770 if gMissionFailed
then
2771 topstr
:= _lc
[I_MENU_INTER_MISSION_FAIL
]
2773 topstr
:= _lc
[I_MENU_INTER_LEVEL_COMPLETE
];
2776 topstr
:= _lc
[I_MENU_INTER_ROUND_OVER
];
2778 e_CharFont_GetSize(gMenuFont
, topstr
, ww1
, hh1
);
2779 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(ww1
div 2), 16, topstr
);
2781 if g_Game_IsNet
then
2783 topstr
:= Format(_lc
[I_MENU_INTER_NOTICE_TIME
], [gServInterTime
]);
2784 if not gChatShow
then
2785 e_TextureFontPrintEx((gScreenWidth
div 2)-(Length(topstr
)*ww2
div 2),
2786 gScreenHeight
-(hh2
+4)*2, topstr
, gStdFont
, 255, 255, 255, 1);
2789 if g_Game_IsClient
then
2790 topstr
:= _lc
[I_MENU_INTER_NOTICE_MAP
]
2792 topstr
:= _lc
[I_MENU_INTER_NOTICE_SPACE
];
2793 if not gChatShow
then
2794 e_TextureFontPrintEx((gScreenWidth
div 2)-(Length(topstr
)*ww2
div 2),
2795 gScreenHeight
-(hh2
+4), topstr
, gStdFont
, 255, 255, 255, 1);
2800 w
:= gScreenWidth
-x
*2;
2806 e_DrawFillQuad(x
, y
, gScreenWidth
-x
-1, gScreenHeight
-y
-1, 64, 64, 64, 32);
2807 e_DrawQuad(x
, y
, gScreenWidth
-x
-1, gScreenHeight
-y
-1, 255, 127, 0);
2809 m
:= Max(Length(_lc
[I_MENU_MAP
])+1, Length(_lc
[I_GAME_GAME_TIME
])+1)*ww2
;
2811 case CustomStat
.GameMode
of
2814 if gGameSettings
.MaxLives
= 0 then
2815 s1
:= _lc
[I_GAME_DM
]
2817 s1
:= _lc
[I_GAME_LMS
];
2821 if gGameSettings
.MaxLives
= 0 then
2822 s1
:= _lc
[I_GAME_TDM
]
2824 s1
:= _lc
[I_GAME_TLMS
];
2826 GM_CTF
: s1
:= _lc
[I_GAME_CTF
];
2829 if gGameSettings
.MaxLives
= 0 then
2830 s1
:= _lc
[I_GAME_COOP
]
2832 s1
:= _lc
[I_GAME_SURV
];
2838 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
2842 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_MENU_MAP
], gStdFont
, 255, 127, 0, 1);
2843 e_TextureFontPrint(x
+8+m
, _y
, Format('%s - %s', [CustomStat
.Map
, CustomStat
.MapName
]), gStdFont
);
2846 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_GAME_TIME
], gStdFont
, 255, 127, 0, 1);
2847 e_TextureFontPrint(x
+8+m
, _y
, Format('%d:%.2d:%.2d', [CustomStat
.GameTime
div 1000 div 3600,
2848 (CustomStat
.GameTime
div 1000 div 60) mod 60,
2849 CustomStat
.GameTime
div 1000 mod 60]), gStdFont
);
2851 pc
:= Length(CustomStat
.PlayerStat
);
2852 if pc
= 0 then Exit
;
2854 if CustomStat
.GameMode
= GM_COOP
then
2856 m
:= Max(Length(_lc
[I_GAME_MONSTERS
])+1, Length(_lc
[I_GAME_SECRETS
])+1)*ww2
;
2858 s2
:= _lc
[I_GAME_MONSTERS
];
2859 e_TextureFontPrintEx(x
+8, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2860 e_TextureFontPrintEx(x
+8+m
, _y
, IntToStr(gCoopMonstersKilled
) + '/' + IntToStr(gTotalMonsters
), gStdFont
, 255, 255, 255, 1);
2862 s2
:= _lc
[I_GAME_SECRETS
];
2863 e_TextureFontPrintEx(x
+8, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2864 e_TextureFontPrintEx(x
+8+m
, _y
, IntToStr(gCoopSecretsFound
) + '/' + IntToStr(gSecretsCount
), gStdFont
, 255, 255, 255, 1);
2867 m
:= Max(Length(_lc
[I_GAME_MONSTERS_TOTAL
])+1, Length(_lc
[I_GAME_SECRETS_TOTAL
])+1)*ww2
;
2869 s2
:= _lc
[I_GAME_MONSTERS_TOTAL
];
2870 e_TextureFontPrintEx(x
+250, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2871 e_TextureFontPrintEx(x
+250+m
, _y
, IntToStr(gCoopTotalMonstersKilled
) + '/' + IntToStr(gCoopTotalMonsters
), gStdFont
, 255, 255, 255, 1);
2873 s2
:= _lc
[I_GAME_SECRETS_TOTAL
];
2874 e_TextureFontPrintEx(x
+250, _y
, s2
, gStdFont
, 255, 127, 0, 1);
2875 e_TextureFontPrintEx(x
+250+m
, _y
, IntToStr(gCoopTotalSecretsFound
) + '/' + IntToStr(gCoopTotalSecrets
), gStdFont
, 255, 255, 255, 1);
2879 if CustomStat
.GameMode
in [GM_TDM
, GM_CTF
] then
2884 if TeamStat
[TEAM_RED
].Score
> TeamStat
[TEAM_BLUE
].Score
then s1
:= _lc
[I_GAME_WIN_RED
]
2885 else if TeamStat
[TEAM_BLUE
].Score
> TeamStat
[TEAM_RED
].Score
then s1
:= _lc
[I_GAME_WIN_BLUE
]
2886 else s1
:= _lc
[I_GAME_WIN_DRAW
];
2888 e_TextureFontPrintEx(x
+8+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
2891 for t
:= TEAM_RED
to TEAM_BLUE
do
2893 if t
= TEAM_RED
then
2895 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_TEAM_RED
],
2896 gStdFont
, 255, 0, 0, 1);
2897 e_TextureFontPrintEx(x
+w1
+8, _y
, IntToStr(CustomStat
.TeamStat
[TEAM_RED
].Score
),
2898 gStdFont
, 255, 0, 0, 1);
2905 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_TEAM_BLUE
],
2906 gStdFont
, 0, 0, 255, 1);
2907 e_TextureFontPrintEx(x
+w1
+8, _y
, IntToStr(CustomStat
.TeamStat
[TEAM_BLUE
].Score
),
2908 gStdFont
, 0, 0, 255, 1);
2914 e_DrawLine(1, x
+8, _y
+20, x
-8+w
, _y
+20, r
, g
, b
);
2917 for p
:= 0 to High(CustomStat
.PlayerStat
) do
2918 if CustomStat
.PlayerStat
[p
].Team
= t
then
2919 with CustomStat
.PlayerStat
[p
] do
2933 if (gPlayers
[Num
] <> nil) and (gPlayers
[Num
].FReady
) then
2934 e_TextureFontPrintEx(x
+16, _y
, Name
+ ' *', gStdFont
, rr
, gg
, bb
, 1)
2936 e_TextureFontPrintEx(x
+16, _y
, Name
, gStdFont
, rr
, gg
, bb
, 1);
2937 e_TextureFontPrintEx(x
+w1
+16, _y
, IntToStr(Frags
), gStdFont
, rr
, gg
, bb
, 1);
2938 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
, IntToStr(Deaths
), gStdFont
, rr
, gg
, bb
, 1);
2945 else if CustomStat
.GameMode
in [GM_DM
, GM_COOP
] then
2948 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_PLAYER_NAME
], gStdFont
, 255, 127, 0, 1);
2949 e_TextureFontPrintEx(x
+8+w1
, _y
, _lc
[I_GAME_FRAGS
], gStdFont
, 255, 127, 0, 1);
2950 e_TextureFontPrintEx(x
+8+w1
+w2
, _y
, _lc
[I_GAME_DEATHS
], gStdFont
, 255, 127, 0, 1);
2953 for p
:= 0 to High(CustomStat
.PlayerStat
) do
2954 with CustomStat
.PlayerStat
[p
] do
2956 e_DrawFillQuad(x
+8, _y
+4, x
+24-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
2963 if (gPlayers
[Num
] <> nil) and (gPlayers
[Num
].FReady
) then
2964 e_TextureFontPrintEx(x
+8+16+8, _y
+4, Name
+ ' *', gStdFont
, r
, r
, r
, 1, True)
2966 e_TextureFontPrintEx(x
+8+16+8, _y
+4, Name
, gStdFont
, r
, r
, r
, 1, True);
2967 e_TextureFontPrintEx(x
+w1
+8+16+8, _y
+4, IntToStr(Frags
), gStdFont
, r
, r
, r
, 1, True);
2968 e_TextureFontPrintEx(x
+w1
+w2
+8+16+8, _y
+4, IntToStr(Deaths
), gStdFont
, r
, r
, r
, 1, True);
2973 // HACK: take stats screenshot immediately after the first frame of the stats showing
2974 if gScreenshotStats
and (not StatShotDone
) and (Length(CustomStat
.PlayerStat
) > 1) then
2976 g_TakeScreenShot('stats/' + StatFilename
);
2977 StatShotDone
:= True;
2981 procedure DrawSingleStat();
2983 tm
, key_x
, val_x
, y
: Integer;
2987 procedure player_stat(n
: Integer);
2993 s1
:= Format(' %d ', [SingleStat
.PlayerStat
[n
].Kills
]);
2994 s2
:= Format(' %d', [gTotalMonsters
]);
2996 e_CharFont_Print(gMenuFont
, key_x
, y
, _lc
[I_MENU_INTER_KILLS
]);
2997 e_CharFont_PrintEx(gMenuFont
, val_x
, y
, s1
, _RGB(255, 0, 0));
2998 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
2999 e_CharFont_Print(gMenuFont
, val_x
+w1
, y
, '/');
3001 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3002 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
, s2
, _RGB(255, 0, 0));
3004 // "Kills-per-minute: ##.#":
3005 s1
:= _lc
[I_MENU_INTER_KPM
];
3007 kpm
:= (SingleStat
.PlayerStat
[n
].Kills
/ tm
) * 60
3009 kpm
:= SingleStat
.PlayerStat
[n
].Kills
;
3010 s2
:= Format(' %.1f', [kpm
]);
3012 e_CharFont_Print(gMenuFont
, key_x
, y
+32, s1
);
3013 e_CharFont_PrintEx(gMenuFont
, val_x
, y
+32, s2
, _RGB(255, 0, 0));
3015 // "Secrets found: # / #":
3016 s1
:= Format(' %d ', [SingleStat
.PlayerStat
[n
].Secrets
]);
3017 s2
:= Format(' %d', [SingleStat
.TotalSecrets
]);
3019 e_CharFont_Print(gMenuFont
, key_x
, y
+64, _lc
[I_MENU_INTER_SECRETS
]);
3020 e_CharFont_PrintEx(gMenuFont
, val_x
, y
+64, s1
, _RGB(255, 0, 0));
3021 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3022 e_CharFont_Print(gMenuFont
, val_x
+w1
, y
+64, '/');
3024 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3025 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
+64, s2
, _RGB(255, 0, 0));
3029 // "Level Complete":
3030 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_INTER_LEVEL_COMPLETE
], w1
, h
);
3031 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 32, _lc
[I_MENU_INTER_LEVEL_COMPLETE
]);
3033 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
3034 s1
:= _lc
[I_MENU_INTER_KPM
];
3035 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3038 e_CharFont_GetSize(gMenuFont
, s1
, w2
, h
);
3040 key_x
:= (gScreenWidth
-w1
-w2
) div 2;
3041 val_x
:= key_x
+ w1
;
3044 tm
:= SingleStat
.GameTime
div 1000;
3045 s1
:= _lc
[I_MENU_INTER_TIME
];
3046 s2
:= Format(' %d:%.2d:%.2d', [tm
div (60*60), (tm
mod (60*60)) div 60, tm
mod 60]);
3048 e_CharFont_Print(gMenuFont
, key_x
, 80, s1
);
3049 e_CharFont_PrintEx(gMenuFont
, val_x
, 80, s2
, _RGB(255, 0, 0));
3051 if SingleStat
.TwoPlayers
then
3054 s1
:= _lc
[I_MENU_PLAYER_1
];
3055 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3056 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 128, s1
);
3058 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3063 s1
:= _lc
[I_MENU_PLAYER_2
];
3064 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
3065 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 288, s1
);
3067 // Ñòàòèñòèêà âòîðîãî èãðîêà:
3073 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
3079 procedure DrawLoadingStat();
3080 procedure drawRect (x
, y
, w
, h
: Integer);
3082 if (w
< 1) or (h
< 1) then exit
;
3084 glVertex2f(x
+0.375, y
+0.375);
3085 glVertex2f(x
+w
+0.375, y
+0.375);
3086 glVertex2f(x
+w
+0.375, y
+h
+0.375);
3087 glVertex2f(x
+0.375, y
+h
+0.375);
3091 function drawPBar (cur
, total
: Integer; washere
: Boolean): Boolean;
3093 rectW
, rectH
: Integer;
3100 idl
, idr
, idb
, idm
: LongWord;
3104 if (total
< 1) then exit
;
3105 if (cur
< 1) then exit
; // don't blink
3106 if (not washere
) and (cur
>= total
) then exit
; // don't blink
3107 //if (cur < 0) then cur := 0;
3108 //if (cur > total) then cur := total;
3111 if (hasPBarGfx
) then
3113 g_Texture_Get('UI_GFX_PBAR_LEFT', idl
);
3114 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl
, hl
);
3115 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr
);
3116 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr
, hr
);
3117 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb
);
3118 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb
, hb
);
3119 g_Texture_Get('UI_GFX_PBAR_MARKER', idm
);
3120 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm
, hm
);
3122 //rectW := gScreenWidth-360;
3123 rectW
:= trunc(624.0*gScreenWidth
/1024.0);
3126 x0
:= (gScreenWidth
-rectW
) div 2;
3127 y0
:= gScreenHeight
-rectH
-64;
3128 if (y0
< 2) then y0
:= 2;
3130 glEnable(GL_SCISSOR_TEST
);
3133 glScissor(x0
, gScreenHeight
-y0
-rectH
, rectW
, rectH
);
3134 e_DrawSize(idl
, x0
, y0
, 0, true, false, wl
, hl
);
3135 e_DrawSize(idr
, x0
+rectW
-wr
, y0
, 0, true, false, wr
, hr
);
3138 glScissor(x0
+wl
, gScreenHeight
-y0
-rectH
, rectW
-wl
-wr
, rectH
);
3140 while (f
< x0
+rectW
) do
3142 e_DrawSize(idb
, f
, y0
, 0, true, false, wb
, hb
);
3147 wdt
:= (rectW
-wl
-wr
)*cur
div total
;
3148 if (wdt
> rectW
-wl
-wr
) then wdt
:= rectW
-wr
-wr
;
3151 my
:= y0
; // don't be so smart, ketmar: +(rectH-wm) div 2;
3152 glScissor(x0
+wl
, gScreenHeight
-my
-rectH
, wdt
, hm
);
3156 e_DrawSize(idm
, f
, y0
, 0, true, false, wm
, hm
);
3162 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
3166 rectW
:= gScreenWidth
-64;
3169 x0
:= (gScreenWidth
-rectW
) div 2;
3170 y0
:= gScreenHeight
-rectH
-64;
3171 if (y0
< 2) then y0
:= 2;
3173 glDisable(GL_BLEND
);
3174 glDisable(GL_TEXTURE_2D
);
3176 //glClearColor(0, 0, 0, 0);
3177 //glClear(GL_COLOR_BUFFER_BIT);
3179 glColor4ub(127, 127, 127, 255);
3180 drawRect(x0
-2, y0
-2, rectW
+4, rectH
+4);
3182 glColor4ub(0, 0, 0, 255);
3183 drawRect(x0
-1, y0
-1, rectW
+2, rectH
+2);
3185 glColor4ub(127, 127, 127, 255);
3186 wdt
:= rectW
*cur
div total
;
3187 if (wdt
> rectW
) then wdt
:= rectW
;
3188 drawRect(x0
, y0
, wdt
, rectH
);
3197 if (Length(LoadingStat
.Msgs
) = 0) then exit
;
3199 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_LOADING
], ww
, hh
);
3200 yy
:= (gScreenHeight
div 3);
3201 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(ww
div 2), yy
-2*hh
, _lc
[I_MENU_LOADING
]);
3202 xx
:= (gScreenWidth
div 3);
3206 for i
:= 0 to NextMsg
-1 do
3208 if (i
= (NextMsg
-1)) and (MaxValue
> 0) then
3209 s
:= Format('%s: %d/%d', [Msgs
[i
], CurValue
, MaxValue
])
3213 e_CharFont_PrintEx(gMenuSmallFont
, xx
, yy
, s
, _RGB(255, 0, 0));
3214 yy
:= yy
+ LOADING_INTERLINE
;
3215 PBarWasHere
:= drawPBar(CurValue
, MaxValue
, PBarWasHere
);
3220 procedure DrawMenuBackground(tex
: AnsiString);
3226 if g_Texture_Get(tex
, ID
) then
3228 e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
3229 e_GetTextureSize(ID
, @w
, @h
);
3231 w
:= round(w
* 1.333 * (gScreenHeight
/ h
))
3233 w
:= trunc(w
* (gScreenHeight
/ h
));
3234 e_DrawSize(ID
, (gScreenWidth
- w
) div 2, 0, 0, False, False, w
, gScreenHeight
);
3236 else e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
3239 procedure DrawMinimap(p
: TPlayer
; RenderRect
: e_graphics
.TRect
);
3241 a
, aX
, aY
, aX2
, aY2
, Scale
, ScaleSz
: Integer;
3243 function monDraw (mon
: TMonster
): Boolean;
3245 result
:= false; // don't stop
3250 // Ëåâûé âåðõíèé óãîë
3251 aX
:= Obj
.X
div ScaleSz
+ 1;
3252 aY
:= Obj
.Y
div ScaleSz
+ 1;
3254 aX2
:= max(Obj
.Rect
.Width
div ScaleSz
, 1);
3255 aY2
:= max(Obj
.Rect
.Height
div ScaleSz
, 1);
3256 // Ïðàâûé íèæíèé óãîë
3257 aX2
:= aX
+ aX2
- 1;
3258 aY2
:= aY
+ aY2
- 1;
3259 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 255, 0, 0);
3265 if (gMapInfo
.Width
> RenderRect
.Right
- RenderRect
.Left
) or
3266 (gMapInfo
.Height
> RenderRect
.Bottom
- RenderRect
.Top
) then
3269 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
3270 ScaleSz
:= 16 div Scale
;
3271 // Ðàçìåðû ìèíè-êàðòû:
3272 aX
:= max(gMapInfo
.Width
div ScaleSz
, 1);
3273 aY
:= max(gMapInfo
.Height
div ScaleSz
, 1);
3275 e_DrawFillQuad(0, 0, aX
-1, aY
-1, 0, 0, 0, 0);
3277 if gWalls
<> nil then
3280 for a
:= 0 to High(gWalls
) do
3282 if PanelType
<> 0 then
3284 // Ëåâûé âåðõíèé óãîë:
3285 aX
:= X
div ScaleSz
;
3286 aY
:= Y
div ScaleSz
;
3288 aX2
:= max(Width
div ScaleSz
, 1);
3289 aY2
:= max(Height
div ScaleSz
, 1);
3290 // Ïðàâûé íèæíèé óãîë:
3291 aX2
:= aX
+ aX2
- 1;
3292 aY2
:= aY
+ aY2
- 1;
3295 PANEL_WALL
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 208, 208, 208, 0);
3296 PANEL_OPENDOOR
, PANEL_CLOSEDOOR
:
3297 if Enabled
then e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 160, 160, 160, 0);
3301 if gSteps
<> nil then
3304 for a
:= 0 to High(gSteps
) do
3306 if PanelType
<> 0 then
3308 // Ëåâûé âåðõíèé óãîë:
3309 aX
:= X
div ScaleSz
;
3310 aY
:= Y
div ScaleSz
;
3312 aX2
:= max(Width
div ScaleSz
, 1);
3313 aY2
:= max(Height
div ScaleSz
, 1);
3314 // Ïðàâûé íèæíèé óãîë:
3315 aX2
:= aX
+ aX2
- 1;
3316 aY2
:= aY
+ aY2
- 1;
3318 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 128, 128, 128, 0);
3321 if gLifts
<> nil then
3324 for a
:= 0 to High(gLifts
) do
3326 if PanelType
<> 0 then
3328 // Ëåâûé âåðõíèé óãîë:
3329 aX
:= X
div ScaleSz
;
3330 aY
:= Y
div ScaleSz
;
3332 aX2
:= max(Width
div ScaleSz
, 1);
3333 aY2
:= max(Height
div ScaleSz
, 1);
3334 // Ïðàâûé íèæíèé óãîë:
3335 aX2
:= aX
+ aX2
- 1;
3336 aY2
:= aY
+ aY2
- 1;
3339 LIFTTYPE_UP
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 116, 72, 36, 0);
3340 LIFTTYPE_DOWN
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 116, 124, 96, 0);
3341 LIFTTYPE_LEFT
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 200, 80, 4, 0);
3342 LIFTTYPE_RIGHT
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 252, 140, 56, 0);
3346 if gWater
<> nil then
3349 for a
:= 0 to High(gWater
) do
3351 if PanelType
<> 0 then
3353 // Ëåâûé âåðõíèé óãîë:
3354 aX
:= X
div ScaleSz
;
3355 aY
:= Y
div ScaleSz
;
3357 aX2
:= max(Width
div ScaleSz
, 1);
3358 aY2
:= max(Height
div ScaleSz
, 1);
3359 // Ïðàâûé íèæíèé óãîë:
3360 aX2
:= aX
+ aX2
- 1;
3361 aY2
:= aY
+ aY2
- 1;
3363 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 0, 192, 0);
3366 if gAcid1
<> nil then
3368 // Ðèñóåì êèñëîòó 1:
3369 for a
:= 0 to High(gAcid1
) do
3371 if PanelType
<> 0 then
3373 // Ëåâûé âåðõíèé óãîë:
3374 aX
:= X
div ScaleSz
;
3375 aY
:= Y
div ScaleSz
;
3377 aX2
:= max(Width
div ScaleSz
, 1);
3378 aY2
:= max(Height
div ScaleSz
, 1);
3379 // Ïðàâûé íèæíèé óãîë:
3380 aX2
:= aX
+ aX2
- 1;
3381 aY2
:= aY
+ aY2
- 1;
3383 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 176, 0, 0);
3386 if gAcid2
<> nil then
3388 // Ðèñóåì êèñëîòó 2:
3389 for a
:= 0 to High(gAcid2
) do
3391 if PanelType
<> 0 then
3393 // Ëåâûé âåðõíèé óãîë:
3394 aX
:= X
div ScaleSz
;
3395 aY
:= Y
div ScaleSz
;
3397 aX2
:= max(Width
div ScaleSz
, 1);
3398 aY2
:= max(Height
div ScaleSz
, 1);
3399 // Ïðàâûé íèæíèé óãîë:
3400 aX2
:= aX
+ aX2
- 1;
3401 aY2
:= aY
+ aY2
- 1;
3403 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 176, 0, 0, 0);
3406 if gPlayers
<> nil then
3409 for a
:= 0 to High(gPlayers
) do
3410 if gPlayers
[a
] <> nil then with gPlayers
[a
] do
3412 // Ëåâûé âåðõíèé óãîë:
3413 aX
:= Obj
.X
div ScaleSz
+ 1;
3414 aY
:= Obj
.Y
div ScaleSz
+ 1;
3416 aX2
:= max(Obj
.Rect
.Width
div ScaleSz
, 1);
3417 aY2
:= max(Obj
.Rect
.Height
div ScaleSz
, 1);
3418 // Ïðàâûé íèæíèé óãîë:
3419 aX2
:= aX
+ aX2
- 1;
3420 aY2
:= aY
+ aY2
- 1;
3422 if gPlayers
[a
] = p
then
3423 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 255, 0, 0)
3426 TEAM_RED
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 0, 0, 0);
3427 TEAM_BLUE
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 0, 255, 0);
3428 else e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 128, 0, 0);
3433 g_Mons_ForEach(monDraw
);
3438 procedure renderAmbientQuad (hasAmbient
: Boolean; constref ambColor
: TDFColor
);
3440 if not hasAmbient
then exit
;
3441 e_AmbientQuad(sX
, sY
, sWidth
, sHeight
, ambColor
.r
, ambColor
.g
, ambColor
.b
, ambColor
.a
);
3445 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3446 //FIXME: broken for splitscreen mode
3447 procedure renderDynLightsInternal ();
3449 //hasAmbient: Boolean;
3450 //ambColor: TDFColor;
3452 lx
, ly
, lrad
: Integer;
3453 scxywh
: array[0..3] of GLint
;
3456 if e_NoGraphics
then exit
;
3458 //TODO: lights should be in separate grid, i think
3459 // but on the other side: grid may be slower for dynlights, as their lifetime is short
3460 if (not gwin_k8_enable_light_experiments
) or (not gwin_has_stencil
) or (g_dynLightCount
< 1) then exit
;
3463 //ambColor := gCurrentMap['light_ambient'].rgba;
3464 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
3466 { // this will multiply incoming color to alpha from framebuffer
3468 glBlendFunc(GL_DST_ALPHA, GL_ONE);
3472 * light rendering: (INVALID!)
3473 * glStencilFunc(GL_EQUAL, 0, $ff);
3475 * glClear(GL_STENCIL_BUFFER_BIT);
3476 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
3477 * draw shadow volume into stencil buffer
3478 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
3479 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
3481 * draw color-less quad with light alpha (WARNING! don't touch color!)
3482 * glEnable(GL_BLEND);
3483 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
3484 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
3486 wassc
:= (glIsEnabled(GL_SCISSOR_TEST
) <> 0);
3487 if wassc
then glGetIntegerv(GL_SCISSOR_BOX
, @scxywh
[0]) else glGetIntegerv(GL_VIEWPORT
, @scxywh
[0]);
3489 // setup OpenGL parameters
3490 glStencilMask($FFFFFFFF);
3491 glStencilFunc(GL_ALWAYS
, 0, $FFFFFFFF);
3492 glEnable(GL_STENCIL_TEST
);
3493 glEnable(GL_SCISSOR_TEST
);
3494 glClear(GL_STENCIL_BUFFER_BIT
);
3495 glStencilFunc(GL_EQUAL
, 0, $ff);
3497 for lln
:= 0 to g_dynLightCount
-1 do
3499 lx
:= g_dynLights
[lln
].x
;
3500 ly
:= g_dynLights
[lln
].y
;
3501 lrad
:= g_dynLights
[lln
].radius
;
3502 if (lrad
< 3) then continue
;
3504 if (lx
-sX
+lrad
< 0) then continue
;
3505 if (ly
-sY
+lrad
< 0) then continue
;
3506 if (lx
-sX
-lrad
>= gPlayerScreenSize
.X
) then continue
;
3507 if (ly
-sY
-lrad
>= gPlayerScreenSize
.Y
) then continue
;
3509 // set scissor to optimize drawing
3510 if (g_dbg_scale
= 1.0) then
3512 glScissor((lx
-sX
)-lrad
+2, gPlayerScreenSize
.Y
-(ly
-sY
)-lrad
-1+2, lrad
*2-4, lrad
*2-4);
3516 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
3518 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
3519 if (g_dbg_scale
<> 1.0) then glClear(GL_STENCIL_BUFFER_BIT
);
3520 glStencilOp(GL_KEEP
, GL_KEEP
, GL_INCR
);
3521 // draw extruded panels
3522 glDisable(GL_TEXTURE_2D
);
3523 glDisable(GL_BLEND
);
3524 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
); // no need to modify color buffer
3525 if (lrad
> 4) then g_Map_DrawPanelShadowVolumes(lx
, ly
, lrad
);
3526 // render light texture
3527 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
); // modify color buffer
3528 glStencilOp(GL_ZERO
, GL_ZERO
, GL_ZERO
); // draw light, and clear stencil buffer
3531 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
3532 glEnable(GL_TEXTURE_2D
);
3533 // color and opacity
3534 glColor4f(g_dynLights
[lln
].r
, g_dynLights
[lln
].g
, g_dynLights
[lln
].b
, g_dynLights
[lln
].a
);
3535 glBindTexture(GL_TEXTURE_2D
, g_Texture_Light());
3537 glTexCoord2f(0.0, 0.0); glVertex2i(lx
-lrad
, ly
-lrad
); // top-left
3538 glTexCoord2f(1.0, 0.0); glVertex2i(lx
+lrad
, ly
-lrad
); // top-right
3539 glTexCoord2f(1.0, 1.0); glVertex2i(lx
+lrad
, ly
+lrad
); // bottom-right
3540 glTexCoord2f(0.0, 1.0); glVertex2i(lx
-lrad
, ly
+lrad
); // bottom-left
3545 glDisable(GL_STENCIL_TEST
);
3546 glDisable(GL_BLEND
);
3547 glDisable(GL_SCISSOR_TEST
);
3548 //glScissor(0, 0, sWidth, sHeight);
3550 glScissor(scxywh
[0], scxywh
[1], scxywh
[2], scxywh
[3]);
3551 if wassc
then glEnable(GL_SCISSOR_TEST
) else glDisable(GL_SCISSOR_TEST
);
3555 function fixViewportForScale (): Boolean;
3557 nx0
, ny0
, nw
, nh
: Integer;
3560 if (g_dbg_scale
<> 1.0) then
3563 nx0
:= round(sX
-(gPlayerScreenSize
.X
-(sWidth
*g_dbg_scale
))/2/g_dbg_scale
);
3564 ny0
:= round(sY
-(gPlayerScreenSize
.Y
-(sHeight
*g_dbg_scale
))/2/g_dbg_scale
);
3565 nw
:= round(sWidth
/g_dbg_scale
);
3566 nh
:= round(sHeight
/g_dbg_scale
);
3575 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
3576 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
3577 procedure renderMapInternal (backXOfs
, backYOfs
: Integer; setTransMatrix
: Boolean);
3579 TDrawCB
= procedure ();
3582 hasAmbient
: Boolean;
3584 doAmbient
: Boolean = false;
3586 procedure drawPanelType (profname
: AnsiString; panType
: DWord
; doDraw
: Boolean);
3591 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
3592 if gdbg_map_use_accel_render
then
3594 tagmask
:= panelTypeToTag(panType
);
3595 while (gDrawPanelList
.count
> 0) do
3597 pan
:= TPanel(gDrawPanelList
.front());
3598 if ((pan
.tag
and tagmask
) = 0) then break
;
3599 if doDraw
then pan
.Draw(doAmbient
, ambColor
);
3600 gDrawPanelList
.popFront();
3605 if doDraw
then g_Map_DrawPanels(panType
, hasAmbient
, ambColor
);
3607 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3610 procedure drawOther (profname
: AnsiString; cb
: TDrawCB
);
3612 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
3613 if assigned(cb
) then cb();
3614 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3618 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('total');
3620 // our accelerated renderer will collect all panels to gDrawPanelList
3621 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
3622 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('collect');
3623 if gdbg_map_use_accel_render
then
3625 g_Map_CollectDrawPanels(sX
, sY
, sWidth
, sHeight
);
3627 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3629 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('skyback');
3630 g_Map_DrawBack(backXOfs
, backYOfs
);
3631 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
3633 if setTransMatrix
then
3635 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
3636 glScalef(g_dbg_scale
, g_dbg_scale
, 1.0);
3637 glTranslatef(-sX
, -sY
, 0);
3641 ambColor
:= gCurrentMap
['light_ambient'].rgba
;
3642 hasAmbient
:= (not ambColor
.isOpaque
) or (not ambColor
.isBlack
);
3647 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3648 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
3649 glClear(GL_COLOR_BUFFER_BIT);
3652 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
3655 drawPanelType('*back', PANEL_BACK
, g_rlayer_back
);
3656 drawPanelType('*step', PANEL_STEP
, g_rlayer_step
);
3657 drawOther('items', @g_Items_Draw
);
3658 drawOther('weapons', @g_Weapon_Draw
);
3659 drawOther('shells', @g_Player_DrawShells
);
3660 drawOther('drawall', @g_Player_DrawAll
);
3661 drawOther('corpses', @g_Player_DrawCorpses
);
3662 drawPanelType('*wall', PANEL_WALL
, g_rlayer_wall
);
3663 drawOther('monsters', @g_Monsters_Draw
);
3664 drawOther('itemdrop', @g_Items_DrawDrop
);
3665 drawPanelType('*door', PANEL_CLOSEDOOR
, g_rlayer_door
);
3666 drawOther('gfx', @g_GFX_Draw
);
3667 drawOther('flags', @g_Map_DrawFlags
);
3668 drawPanelType('*acid1', PANEL_ACID1
, g_rlayer_acid1
);
3669 drawPanelType('*acid2', PANEL_ACID2
, g_rlayer_acid2
);
3670 drawPanelType('*water', PANEL_WATER
, g_rlayer_water
);
3671 drawOther('dynlights', @renderDynLightsInternal
);
3673 if hasAmbient
{and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
3675 renderAmbientQuad(hasAmbient
, ambColor
);
3679 drawPanelType('*fore', PANEL_FORE
, g_rlayer_fore
);
3682 if g_debug_HealthBar
then
3684 g_Monsters_DrawHealth();
3685 g_Player_DrawHealth();
3688 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainEnd(); // map rendering
3692 procedure DrawMapView(x
, y
, w
, h
: Integer);
3699 bx
:= Round(x
/(gMapInfo
.Width
- w
)*(gBackSize
.X
- w
));
3700 by
:= Round(y
/(gMapInfo
.Height
- h
)*(gBackSize
.Y
- h
));
3707 fixViewportForScale();
3708 renderMapInternal(-bx
, -by
, true);
3714 procedure DrawPlayer(p
: TPlayer
);
3716 px
, py
, a
, b
, c
, d
, i
, fX
, fY
: Integer;
3720 if (p
= nil) or (p
.FDummy
) then
3723 g_Map_DrawBack(0, 0);
3728 if (profileFrameDraw
= nil) then profileFrameDraw
:= TProfiler
.Create('RENDER', g_profile_history_size
);
3729 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainBegin(g_profile_frame_draw
);
3735 camObj
:= p
.getCameraObj();
3736 camObj
.lerp(gLerpFactor
, fX
, fY
);
3737 px
:= fX
+ PLAYER_RECT_CX
;
3738 py
:= fY
+ PLAYER_RECT_CY
+nlerp(p
.SlopeOld
, camObj
.slopeUpLeft
, gLerpFactor
);
3740 if (g_dbg_scale
= 1.0) and (not g_dbg_ignore_bounds
) then
3742 if (px
> (gPlayerScreenSize
.X
div 2)) then a
:= -px
+(gPlayerScreenSize
.X
div 2) else a
:= 0;
3743 if (py
> (gPlayerScreenSize
.Y
div 2)) then b
:= -py
+(gPlayerScreenSize
.Y
div 2) else b
:= 0;
3745 if (px
> gMapInfo
.Width
-(gPlayerScreenSize
.X
div 2)) then a
:= -gMapInfo
.Width
+gPlayerScreenSize
.X
;
3746 if (py
> gMapInfo
.Height
-(gPlayerScreenSize
.Y
div 2)) then b
:= -gMapInfo
.Height
+gPlayerScreenSize
.Y
;
3748 if (gMapInfo
.Width
= gPlayerScreenSize
.X
) then a
:= 0
3749 else if (gMapInfo
.Width
< gPlayerScreenSize
.X
) then
3752 a
:= (gPlayerScreenSize
.X
-gMapInfo
.Width
) div 2;
3755 if (gMapInfo
.Height
= gPlayerScreenSize
.Y
) then b
:= 0
3756 else if (gMapInfo
.Height
< gPlayerScreenSize
.Y
) then
3759 b
:= (gPlayerScreenSize
.Y
-gMapInfo
.Height
) div 2;
3764 // scaled, ignore level bounds
3765 a
:= -px
+(gPlayerScreenSize
.X
div 2);
3766 b
:= -py
+(gPlayerScreenSize
.Y
div 2);
3771 sWidth
:= gPlayerScreenSize
.X
;
3772 sHeight
:= gPlayerScreenSize
.Y
;
3773 fixViewportForScale();
3775 i
:= py
- (sY
+ sHeight
div 2);
3776 if (p
.IncCam
> 0) then
3778 // clamp to level bounds
3779 if (sY
- p
.IncCam
< 0) then
3780 p
.IncCam
:= nclamp(sY
, 0, 120);
3781 // clamp around player position
3783 p
.IncCam
:= nclamp(p
.IncCam
, 0, max(0, 120 - i
));
3785 else if (p
.IncCam
< 0) then
3787 // clamp to level bounds
3788 if (sY
+ sHeight
- p
.IncCam
> gMapInfo
.Height
) then
3789 p
.IncCam
:= nclamp(sY
+ sHeight
- gMapInfo
.Height
, -120, 0);
3790 // clamp around player position
3792 p
.IncCam
:= nclamp(p
.IncCam
, min(0, -120 - i
), 0);
3795 sY
:= sY
- nlerp(p
.IncCamOld
, p
.IncCam
, gLerpFactor
);
3797 if (not g_dbg_ignore_bounds
) then
3799 if (sX
+sWidth
> gMapInfo
.Width
) then sX
:= gMapInfo
.Width
-sWidth
;
3800 if (sY
+sHeight
> gMapInfo
.Height
) then sY
:= gMapInfo
.Height
-sHeight
;
3801 if (sX
< 0) then sX
:= 0;
3802 if (sY
< 0) then sY
:= 0;
3805 if (gBackSize
.X
<= gPlayerScreenSize
.X
) or (gMapInfo
.Width
<= sWidth
) then c
:= 0 else c
:= trunc((gBackSize
.X
-gPlayerScreenSize
.X
)*sX
/(gMapInfo
.Width
-sWidth
));
3806 if (gBackSize
.Y
<= gPlayerScreenSize
.Y
) or (gMapInfo
.Height
<= sHeight
) then d
:= 0 else d
:= trunc((gBackSize
.Y
-gPlayerScreenSize
.Y
)*sY
/(gMapInfo
.Height
-sHeight
));
3808 //r_smallmap_h: 0: left; 1: center; 2: right
3809 //r_smallmap_v: 0: top; 1: center; 2: bottom
3811 if (gMapInfo
.Width
= sWidth
) then
3815 else if (gMapInfo
.Width
< sWidth
) then
3817 case r_smallmap_h
of
3818 1: sX
:= -((sWidth
-gMapInfo
.Width
) div 2); // center
3819 2: sX
:= -(sWidth
-gMapInfo
.Width
); // right
3820 else sX
:= 0; // left
3824 if (gMapInfo
.Height
= sHeight
) then
3828 else if (gMapInfo
.Height
< sHeight
) then
3830 case r_smallmap_v
of
3831 1: sY
:= -((sHeight
-gMapInfo
.Height
) div 2); // center
3832 2: sY
:= -(sHeight
-gMapInfo
.Height
); // bottom
3833 else sY
:= 0; // top
3839 p
.viewPortW
:= sWidth
;
3840 p
.viewPortH
:= sHeight
;
3842 {$IFDEF ENABLE_HOLMES}
3843 if (p
= gPlayer1
) then
3845 g_Holmes_plrViewPos(sX
, sY
);
3846 g_Holmes_plrViewSize(sWidth
, sHeight
);
3850 renderMapInternal(-c
, -d
, true);
3852 if (gGameSettings
.GameMode
<> GM_SINGLE
) and (gPlayerIndicator
> 0) then
3853 case gPlayerIndicator
of
3855 p
.DrawIndicator(_RGB(255, 255, 255));
3858 for i
:= 0 to High(gPlayers
) do
3859 if gPlayers
[i
] <> nil then
3860 if gPlayers
[i
] = p
then p
.DrawIndicator(_RGB(255, 255, 255))
3861 else if (gPlayers
[i
].Team
= p
.Team
) and (gPlayers
[i
].Team
<> TEAM_NONE
) then
3862 if gPlayerIndicatorStyle
= 1 then
3863 gPlayers
[i
].DrawIndicator(_RGB(192, 192, 192))
3864 else gPlayers
[i
].DrawIndicator(gPlayers
[i
].GetColor
);
3868 for a := 0 to High(gCollideMap) do
3869 for b := 0 to High(gCollideMap[a]) do
3872 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3874 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3878 1: e_DrawPoint(1, b, a, 200, 200, 200);
3879 2: e_DrawPoint(1, b, a, 64, 64, 255);
3880 3: e_DrawPoint(1, b, a, 255, 0, 255);
3890 if gShowMap
then DrawMinimap(p
, _TRect(0, 0, 128, 128));
3891 if g_Debug_Player
then
3892 g_Player_DrawDebug(p
);
3896 procedure drawProfilers ();
3901 if g_profile_frame_draw
and (profileFrameDraw
<> nil) then
3902 px
-= drawProfiles(px
, py
, profileFrameDraw
);
3904 if g_profile_collision
and (profMapCollision
<> nil) then
3906 px
-= drawProfiles(px
, py
, profMapCollision
);
3907 py
-= calcProfilesHeight(profMonsLOS
);
3910 if g_profile_los
and (profMonsLOS
<> nil) then
3912 px
-= drawProfiles(px
, py
, profMonsLOS
);
3913 py
-= calcProfilesHeight(profMonsLOS
);
3917 procedure g_Game_Draw();
3924 plView1
, plView2
: TPlayer
;
3927 if gExit
= EXIT_QUIT
then Exit
;
3929 Time
:= sys_GetTicks() {div 1000};
3930 FPSCounter
:= FPSCounter
+1;
3931 if Time
- FPSTime
>= 1000 then
3938 e_SetRendertarget(True);
3939 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
3941 if gGameOn
or (gState
= STATE_FOLD
) then
3943 if (gPlayer1
<> nil) and (gPlayer2
<> nil) then
3945 gSpectMode
:= SPECT_NONE
;
3946 if not gRevertPlayers
then
3948 plView1
:= gPlayer1
;
3949 plView2
:= gPlayer2
;
3953 plView1
:= gPlayer2
;
3954 plView2
:= gPlayer1
;
3958 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
3960 gSpectMode
:= SPECT_NONE
;
3961 if gPlayer2
= nil then
3964 plView1
:= gPlayer2
;
3973 if (plView1
= nil) and (plView2
= nil) and (gSpectMode
= SPECT_NONE
) then
3974 gSpectMode
:= SPECT_STATS
;
3976 if gSpectMode
= SPECT_PLAYERS
then
3977 if gPlayers
<> nil then
3979 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
3980 if plView1
= nil then
3982 gSpectPID1
:= GetActivePlayerID_Next();
3983 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
3985 if gSpectViewTwo
then
3987 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
3988 if plView2
= nil then
3990 gSpectPID2
:= GetActivePlayerID_Next();
3991 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
3996 if gSpectMode
= SPECT_MAPVIEW
then
3998 // Ðåæèì ïðîñìîòðà êàðòû
4000 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
4001 DrawMapView(gSpectX
, gSpectY
, gScreenWidth
, gScreenHeight
);
4002 gHearPoint1
.Active
:= True;
4003 gHearPoint1
.Coords
.X
:= gScreenWidth
div 2 + gSpectX
;
4004 gHearPoint1
.Coords
.Y
:= gScreenHeight
div 2 + gSpectY
;
4005 gHearPoint2
.Active
:= False;
4009 Split
:= (plView1
<> nil) and (plView2
<> nil);
4011 // Òî÷êè ñëóõà èãðîêîâ
4012 if plView1
<> nil then
4014 gHearPoint1
.Active
:= True;
4015 gHearPoint1
.Coords
.X
:= plView1
.GameX
+ PLAYER_RECT
.Width
;
4016 gHearPoint1
.Coords
.Y
:= plView1
.GameY
+ PLAYER_RECT
.Height
DIV 2;
4018 gHearPoint1
.Active
:= False;
4019 if plView2
<> nil then
4021 gHearPoint2
.Active
:= True;
4022 gHearPoint2
.Coords
.X
:= plView2
.GameX
+ PLAYER_RECT
.Width
;
4023 gHearPoint2
.Coords
.Y
:= plView2
.GameY
+ PLAYER_RECT
.Height
DIV 2;
4025 gHearPoint2
.Active
:= False;
4027 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4028 gPlayerScreenSize
.X
:= gScreenWidth
-196;
4031 gPlayerScreenSize
.Y
:= gScreenHeight
div 2;
4032 if gScreenHeight
mod 2 = 0 then
4033 Dec(gPlayerScreenSize
.Y
);
4036 gPlayerScreenSize
.Y
:= gScreenHeight
;
4039 if gScreenHeight
mod 2 = 0 then
4040 e_SetViewPort(0, gPlayerScreenSize
.Y
+2, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
)
4042 e_SetViewPort(0, gPlayerScreenSize
.Y
+1, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
4044 DrawPlayer(plView1
);
4045 gPlayer1ScreenCoord
.X
:= sX
;
4046 gPlayer1ScreenCoord
.Y
:= sY
;
4050 e_SetViewPort(0, 0, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
4052 DrawPlayer(plView2
);
4053 gPlayer2ScreenCoord
.X
:= sX
;
4054 gPlayer2ScreenCoord
.Y
:= sY
;
4057 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
4060 e_DrawLine(2, 0, gScreenHeight
div 2, gScreenWidth
, gScreenHeight
div 2, 0, 0, 0);
4063 {$IFDEF ENABLE_HOLMES}
4065 if (g_holmes_enabled
) then g_Holmes_Draw();
4068 if MessageText
<> '' then
4072 e_CharFont_GetSizeFmt(gMenuFont
, MessageText
, w
, h
);
4074 e_CharFont_PrintFmt(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
4075 (gScreenHeight
div 2)-(h
div 2), MessageText
)
4077 e_CharFont_PrintFmt(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
4078 Round(gScreenHeight
/ 2.75)-(h
div 2), MessageText
);
4081 if IsDrawStat
or (gSpectMode
= SPECT_STATS
) then
4084 if gSpectHUD
and (not gChatShow
) and (gSpectMode
<> SPECT_NONE
) and (not gSpectAuto
) then
4086 // Draw spectator GUI
4089 e_TextureFontGetSize(gStdFont
, ww
, hh
);
4092 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Stats', gStdFont
, 255, 255, 255, 1);
4094 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Observe Map', gStdFont
, 255, 255, 255, 1);
4096 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Watch Players', gStdFont
, 255, 255, 255, 1);
4098 e_TextureFontPrintEx(2*ww
, gScreenHeight
- (hh
+2), '< jump >', gStdFont
, 255, 255, 255, 1);
4099 if gSpectMode
= SPECT_STATS
then
4101 e_TextureFontPrintEx(16*ww
, gScreenHeight
- (hh
+2)*2, 'Autoview', gStdFont
, 255, 255, 255, 1);
4102 e_TextureFontPrintEx(16*ww
, gScreenHeight
- (hh
+2), '< fire >', gStdFont
, 255, 255, 255, 1);
4104 if gSpectMode
= SPECT_MAPVIEW
then
4106 e_TextureFontPrintEx(22*ww
, gScreenHeight
- (hh
+2)*2, '[-]', gStdFont
, 255, 255, 255, 1);
4107 e_TextureFontPrintEx(26*ww
, gScreenHeight
- (hh
+2)*2, 'Step ' + IntToStr(gSpectStep
), gStdFont
, 255, 255, 255, 1);
4108 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2)*2, '[+]', gStdFont
, 255, 255, 255, 1);
4109 e_TextureFontPrintEx(18*ww
, gScreenHeight
- (hh
+2), '<prev weap>', gStdFont
, 255, 255, 255, 1);
4110 e_TextureFontPrintEx(30*ww
, gScreenHeight
- (hh
+2), '<next weap>', gStdFont
, 255, 255, 255, 1);
4112 if gSpectMode
= SPECT_PLAYERS
then
4114 e_TextureFontPrintEx(22*ww
, gScreenHeight
- (hh
+2)*2, 'Player 1', gStdFont
, 255, 255, 255, 1);
4115 e_TextureFontPrintEx(20*ww
, gScreenHeight
- (hh
+2), '<left/right>', gStdFont
, 255, 255, 255, 1);
4116 if gSpectViewTwo
then
4118 e_TextureFontPrintEx(37*ww
, gScreenHeight
- (hh
+2)*2, 'Player 2', gStdFont
, 255, 255, 255, 1);
4119 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2), '<prev w/next w>', gStdFont
, 255, 255, 255, 1);
4120 e_TextureFontPrintEx(52*ww
, gScreenHeight
- (hh
+2)*2, '2x View', gStdFont
, 255, 255, 255, 1);
4121 e_TextureFontPrintEx(51*ww
, gScreenHeight
- (hh
+2), '<up/down>', gStdFont
, 255, 255, 255, 1);
4125 e_TextureFontPrintEx(35*ww
, gScreenHeight
- (hh
+2)*2, '2x View', gStdFont
, 255, 255, 255, 1);
4126 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2), '<up/down>', gStdFont
, 255, 255, 255, 1);
4132 if gPauseMain
and gGameOn
and (g_ActiveWindow
= nil) then
4134 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4135 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4137 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_PAUSE
], w
, h
);
4138 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
4139 (gScreenHeight
div 2)-(h
div 2), _lc
[I_MENU_PAUSE
]);
4144 if (gState
= STATE_MENU
) then
4146 if (g_ActiveWindow
= nil) or (g_ActiveWindow
.BackTexture
= '') then DrawMenuBackground('MENU_BACKGROUND');
4147 // F3 at menu will show game loading dialog
4148 if e_KeyPressed(IK_F3
) then g_Menu_Show_LoadMenu(true);
4149 if (g_ActiveWindow
<> nil) then
4151 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4152 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4156 // F3 at titlepic will show game loading dialog
4157 if e_KeyPressed(IK_F3
) then
4159 g_Menu_Show_LoadMenu(true);
4160 if (g_ActiveWindow
<> nil) then e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4165 if gState
= STATE_FOLD
then
4167 e_DrawFillQuad(0, 0, gScreenWidth
-1, gScreenHeight
-1, 0, 0, 0, EndingGameCounter
);
4170 if gState
= STATE_INTERCUSTOM
then
4172 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then
4174 back
:= 'TEXTURE_endpic';
4175 if not g_Texture_Get(back
, ID
) then
4176 back
:= _lc
[I_TEXTURE_ENDPIC
];
4181 DrawMenuBackground(back
);
4185 if g_ActiveWindow
<> nil then
4187 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4188 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4192 if gState
= STATE_INTERSINGLE
then
4194 if EndingGameCounter
> 0 then
4196 e_DrawFillQuad(0, 0, gScreenWidth
-1, gScreenHeight
-1, 0, 0, 0, EndingGameCounter
);
4202 DrawMenuBackground(back
);
4206 if g_ActiveWindow
<> nil then
4208 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4209 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4214 if gState
= STATE_ENDPIC
then
4217 if g_Texture_Get('TEXTURE_endpic', ID
) then DrawMenuBackground('TEXTURE_endpic')
4218 else DrawMenuBackground(_lc
[I_TEXTURE_ENDPIC
]);
4220 if g_ActiveWindow
<> nil then
4222 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4223 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4227 if gState
= STATE_SLIST
then
4229 // if g_Texture_Get('MENU_BACKGROUND', ID) then
4231 // e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
4232 // //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4234 DrawMenuBackground('MENU_BACKGROUND');
4235 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4236 g_Serverlist_Draw(slCurrent
, slTable
);
4240 if g_ActiveWindow
<> nil then
4244 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
4245 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
4247 g_ActiveWindow
.Draw();
4254 if g_debug_Sounds
and gGameOn
then
4256 for w
:= 0 to High(e_SoundsArray
) do
4257 for h
:= 0 to e_SoundsArray
[w
].nRefs
do
4258 e_DrawPoint(1, w
+100, h
+100, 255, 0, 0);
4263 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS
]), gStdFont
);
4264 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS
]), gStdFont
);
4267 if gGameOn
and gShowTime
then
4268 drawTime(gScreenWidth
-72, gScreenHeight
-16);
4270 if gGameOn
then drawProfilers();
4272 // TODO: draw this after the FBO and remap mouse click coordinates
4274 {$IFDEF ENABLE_HOLMES}
4278 // blit framebuffer to screen
4280 e_SetRendertarget(False);
4281 e_SetViewPort(0, 0, gWinSizeX
, gWinSizeY
);
4282 e_BlitFramebuffer(gWinSizeX
, gWinSizeY
);
4284 // draw the overlay stuff on top of it
4289 procedure g_Game_Quit();
4291 g_Game_StopAllSounds(True);
4294 g_PlayerModel_FreeData();
4295 g_Texture_DeleteAll();
4296 g_Frames_DeleteAll();
4298 //g_Menu_Free(); //k8: this segfaults after resolution change; who cares?
4301 if NetInitDone
then g_Net_Free
;
4303 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
4304 if gMapToDelete
<> '' then
4305 g_Game_DeleteTestMap();
4311 procedure g_FatalError(Text: String);
4313 g_Console_Add(Format(_lc
[I_FATAL_ERROR
], [Text]), True);
4314 e_WriteLog(Format(_lc
[I_FATAL_ERROR
], [Text]), TMsgType
.Warning
);
4316 gExit
:= EXIT_SIMPLE
;
4317 if gGameOn
then EndGame
;
4320 procedure g_SimpleError(Text: String);
4322 g_Console_Add(Format(_lc
[I_SIMPLE_ERROR
], [Text]), True);
4323 e_WriteLog(Format(_lc
[I_SIMPLE_ERROR
], [Text]), TMsgType
.Warning
);
4326 procedure g_Game_SetupScreenSize();
4328 RES_FACTOR
= 4.0 / 3.0;
4334 // Ðàçìåð ýêðàíîâ èãðîêîâ:
4335 gPlayerScreenSize
.X
:= gScreenWidth
-196;
4336 if (gPlayer1
<> nil) and (gPlayer2
<> nil) then
4337 gPlayerScreenSize
.Y
:= gScreenHeight
div 2
4339 gPlayerScreenSize
.Y
:= gScreenHeight
;
4341 // Ðàçìåð çàäíåãî ïëàíà:
4342 if BackID
<> DWORD(-1) then
4345 if (gScreenWidth
*s
> gMapInfo
.Width
) or
4346 (gScreenHeight
*s
> gMapInfo
.Height
) then
4348 gBackSize
.X
:= gScreenWidth
;
4349 gBackSize
.Y
:= gScreenHeight
;
4353 e_GetTextureSize(BackID
, @bw
, @bh
);
4354 rf
:= Single(bw
) / Single(bh
);
4355 if (rf
> RES_FACTOR
) then bw
:= Round(Single(bh
) * RES_FACTOR
)
4356 else if (rf
< RES_FACTOR
) then bh
:= Round(Single(bw
) / RES_FACTOR
);
4357 s
:= Max(gScreenWidth
/ bw
, gScreenHeight
/ bh
);
4358 if (s
< 1.0) then s
:= 1.0;
4359 gBackSize
.X
:= Round(bw
*s
);
4360 gBackSize
.Y
:= Round(bh
*s
);
4365 procedure g_Game_ChangeResolution(newWidth
, newHeight
: Word; nowFull
, nowMax
: Boolean);
4367 sys_SetDisplayMode(newWidth
, newHeight
, gBPP
, nowFull
, nowMax
);
4370 procedure g_Game_AddPlayer(Team
: Byte = TEAM_NONE
);
4372 if ((not gGameOn
) and (gState
<> STATE_INTERCUSTOM
))
4373 or (not (gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
])) then
4376 if (gGameSettings
.MaxLives
> 0) and (gLMSRespawn
= LMS_RESPAWN_NONE
) then
4379 if gPlayer1
= nil then
4381 if g_Game_IsClient
then
4383 if NetPlrUID1
> -1 then
4384 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE
);
4388 if not (Team
in [TEAM_RED
, TEAM_BLUE
]) then
4389 Team
:= gPlayer1Settings
.Team
;
4391 // Ñîçäàíèå ïåðâîãî èãðîêà:
4392 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4393 gPlayer1Settings
.Color
,
4395 if gPlayer1
= nil then
4396 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]))
4399 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4400 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4401 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4402 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4403 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4404 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [gPlayer1
.Name
]), True);
4405 if g_Game_IsServer
and g_Game_IsNet
then
4406 MH_SEND_PlayerCreate(gPlayer1
.UID
);
4407 gPlayer1
.Respawn(False, True);
4408 g_Net_Slist_ServerPlayerComes();
4413 if gPlayer2
= nil then
4415 if g_Game_IsClient
then
4417 if NetPlrUID2
> -1 then
4418 gPlayer2
:= g_Player_Get(NetPlrUID2
);
4422 if not (Team
in [TEAM_RED
, TEAM_BLUE
]) then
4423 Team
:= gPlayer2Settings
.Team
;
4425 // Ñîçäàíèå âòîðîãî èãðîêà:
4426 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4427 gPlayer2Settings
.Color
,
4429 if gPlayer2
= nil then
4430 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]))
4433 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4434 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4435 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4436 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4437 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4438 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [gPlayer2
.Name
]), True);
4439 if g_Game_IsServer
and g_Game_IsNet
then
4440 MH_SEND_PlayerCreate(gPlayer2
.UID
);
4441 gPlayer2
.Respawn(False, True);
4442 g_Net_Slist_ServerPlayerComes();
4449 procedure g_Game_RemovePlayer();
4453 if ((not gGameOn
) and (gState
<> STATE_INTERCUSTOM
))
4454 or (not (gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
])) then
4459 if g_Game_IsServer
then
4462 Pl
.Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
4463 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [Pl
.Name
]), True);
4464 g_Player_Remove(Pl
.UID
);
4465 g_Net_Slist_ServerPlayerLeaves();
4469 gSpectLatchPID2
:= Pl
.UID
;
4477 if g_Game_IsServer
then
4480 Pl
.Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
4481 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [Pl
.Name
]), True);
4482 g_Player_Remove(Pl
.UID
);
4483 g_Net_Slist_ServerPlayerLeaves();
4486 gSpectLatchPID1
:= Pl
.UID
;
4488 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE
);
4492 g_Net_Slist_ServerPlayerLeaves();
4495 procedure g_Game_Spectate();
4497 g_Game_RemovePlayer();
4498 if gPlayer1
<> nil then
4499 g_Game_RemovePlayer();
4502 procedure g_Game_SpectateCenterView();
4504 gSpectX
:= Max(gMapInfo
.Width
div 2 - gScreenWidth
div 2, 0);
4505 gSpectY
:= Max(gMapInfo
.Height
div 2 - gScreenHeight
div 2, 0);
4508 procedure g_Game_StartSingle(Map
: String; TwoPlayers
: Boolean; nPlayers
: Byte);
4515 e_WriteLog('Starting singleplayer game...', TMsgType
.Notify
);
4517 g_Game_ClearLoading();
4520 gGameSettings
:= Default(TGameSettings
);
4523 gGameSettings
.GameType
:= GT_SINGLE
;
4524 gGameSettings
.MaxLives
:= 0;
4525 gGameSettings
.Options
:= [TGameOption
.ALLOW_EXIT
, TGameOption
.MONSTERS
,
4526 TGameOption
.BOTS_VS_MONSTERS
, TGameOption
.TEAM_HIT_PROJECTILE
, TGameOption
.TEAM_HIT_TRACE
,
4527 TGameOption
.POWERUP_RANDOM
, TGameOption
.ITEM_ALL_RANDOM
, TGameOption
.ITEM_LIFE_RANDOM
,
4528 TGameOption
.ITEM_AMMO_RANDOM
, TGameOption
.ITEM_WEAPON_RANDOM
];
4529 gSwitchGameMode
:= GM_SINGLE
;
4531 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4532 gLMSRespawnTime
:= 0;
4533 gSpectLatchPID1
:= 0;
4534 gSpectLatchPID2
:= 0;
4536 g_Game_ExecuteEvent('ongamestart');
4538 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4539 g_Game_SetupScreenSize();
4541 // Ñîçäàíèå ïåðâîãî èãðîêà:
4542 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4543 gPlayer1Settings
.Color
,
4544 gPlayer1Settings
.Team
, False));
4545 if gPlayer1
= nil then
4547 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4551 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4552 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4553 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4554 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4555 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4558 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
4561 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4562 gPlayer2Settings
.Color
,
4563 gPlayer2Settings
.Team
, False));
4564 if gPlayer2
= nil then
4566 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]));
4570 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4571 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4572 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4573 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4574 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4578 // Çàãðóçêà è çàïóñê êàðòû:
4579 if not g_Game_StartMap(false{asMegawad}, MAP
, True) then
4581 if (Pos(':\', Map
) > 0) or (Pos(':/', Map
) > 0) then tmps
:= Map
else tmps
:= gGameSettings
.WAD
+ ':\' + MAP
;
4582 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [tmps
]));
4586 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4590 for i
:= nPl
+1 to nPlayers
do
4591 g_Player_Create(STD_PLAYER_MODEL
, _RGB(0, 0, 0), 0, True);
4594 procedure g_Game_StartCustom(Map
: String; GameMode
: Byte;
4595 TimeLimit
, ScoreLimit
: Word;
4597 Options
: TGameOptions
; nPlayers
: Byte);
4603 e_WriteLog('Starting custom game...', TMsgType
.Notify
);
4605 g_Game_ClearLoading();
4608 gGameSettings
.GameType
:= GT_CUSTOM
;
4609 gGameSettings
.GameMode
:= GameMode
;
4610 gSwitchGameMode
:= GameMode
;
4611 gGameSettings
.TimeLimit
:= TimeLimit
;
4612 gGameSettings
.ScoreLimit
:= ScoreLimit
;
4613 gGameSettings
.MaxLives
:= IfThen(GameMode
= GM_CTF
, 0, MaxLives
);
4614 gGameSettings
.Options
:= Options
;
4616 gCoopTotalMonstersKilled
:= 0;
4617 gCoopTotalSecretsFound
:= 0;
4618 gCoopTotalMonsters
:= 0;
4619 gCoopTotalSecrets
:= 0;
4623 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4624 gLMSRespawnTime
:= 0;
4625 gSpectLatchPID1
:= 0;
4626 gSpectLatchPID2
:= 0;
4628 g_Game_ExecuteEvent('ongamestart');
4630 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4631 g_Game_SetupScreenSize();
4633 // Ðåæèì íàáëþäàòåëÿ:
4634 if nPlayers
= 0 then
4641 if nPlayers
>= 1 then
4643 // Ñîçäàíèå ïåðâîãî èãðîêà:
4644 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4645 gPlayer1Settings
.Color
,
4646 gPlayer1Settings
.Team
, False));
4647 if gPlayer1
= nil then
4649 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4653 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4654 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4655 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4656 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4657 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4661 if nPlayers
>= 2 then
4663 // Ñîçäàíèå âòîðîãî èãðîêà:
4664 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4665 gPlayer2Settings
.Color
,
4666 gPlayer2Settings
.Team
, False));
4667 if gPlayer2
= nil then
4669 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]));
4673 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4674 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4675 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4676 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4677 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4681 // Çàãðóçêà è çàïóñê êàðòû:
4682 if not g_Game_StartMap(true{asMegawad}, Map
, True) then
4684 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [Map
]));
4688 // Íåò òî÷åê ïîÿâëåíèÿ:
4689 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1
) +
4690 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2
) +
4691 g_Map_GetPointCount(RESPAWNPOINT_DM
) +
4692 g_Map_GetPointCount(RESPAWNPOINT_RED
)+
4693 g_Map_GetPointCount(RESPAWNPOINT_BLUE
)) < 1 then
4695 g_FatalError(_lc
[I_GAME_ERROR_GET_SPAWN
]);
4699 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4703 for i
:= nPl
+1 to nPlayers
do
4704 g_Player_Create(STD_PLAYER_MODEL
, _RGB(0, 0, 0), 0, True);
4707 procedure g_Game_StartServer(Map
: String; GameMode
: Byte;
4708 TimeLimit
, ScoreLimit
: Word; MaxLives
: Byte;
4709 Options
: TGameOptions
; nPlayers
: Byte;
4710 IPAddr
: LongWord; Port
: Word);
4713 g_Net_Slist_ServerClosed();
4715 e_WriteLog('Starting net game (server)...', TMsgType
.Notify
);
4717 g_Game_ClearLoading();
4722 gGameSettings
.GameType
:= GT_SERVER
;
4723 gGameSettings
.GameMode
:= GameMode
;
4724 gSwitchGameMode
:= GameMode
;
4725 gGameSettings
.TimeLimit
:= TimeLimit
;
4726 gGameSettings
.ScoreLimit
:= ScoreLimit
;
4727 gGameSettings
.MaxLives
:= IfThen(GameMode
= GM_CTF
, 0, MaxLives
);
4728 gGameSettings
.Options
:= Options
;
4730 gCoopTotalMonstersKilled
:= 0;
4731 gCoopTotalSecretsFound
:= 0;
4732 gCoopTotalMonsters
:= 0;
4733 gCoopTotalSecrets
:= 0;
4737 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4738 gLMSRespawnTime
:= 0;
4739 gSpectLatchPID1
:= 0;
4740 gSpectLatchPID2
:= 0;
4742 g_Game_ExecuteEvent('ongamestart');
4744 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
4745 g_Game_SetupScreenSize();
4747 // Ðåæèì íàáëþäàòåëÿ:
4748 if nPlayers
= 0 then
4754 if nPlayers
>= 1 then
4756 // Ñîçäàíèå ïåðâîãî èãðîêà:
4757 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4758 gPlayer1Settings
.Color
,
4759 gPlayer1Settings
.Team
, False));
4760 if gPlayer1
= nil then
4762 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4766 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4767 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4768 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4769 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4770 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4773 if nPlayers
>= 2 then
4775 // Ñîçäàíèå âòîðîãî èãðîêà:
4776 gPlayer2
:= g_Player_Get(g_Player_Create(gPlayer2Settings
.Model
,
4777 gPlayer2Settings
.Color
,
4778 gPlayer2Settings
.Team
, False));
4779 if gPlayer2
= nil then
4781 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [2]));
4785 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
4786 gPlayer2
.WeapSwitchMode
:= gPlayer2Settings
.WeaponSwitch
;
4787 gPlayer2
.setWeaponPrefs(gPlayer2Settings
.WeaponPreferences
);
4788 gPlayer2
.SwitchToEmpty
:= gPlayer2Settings
.SwitchToEmpty
;
4789 gPlayer2
.SkipIronFist
:= gPlayer2Settings
.SkipIronFist
;
4792 g_Game_SetLoadingText(_lc
[I_LOAD_HOST
], 0, False);
4793 if NetForwardPorts
then
4794 g_Game_SetLoadingText(_lc
[I_LOAD_PORTS
], 0, False);
4797 if not g_Net_Host(IPAddr
, Port
, NetMaxClients
) then
4799 g_FatalError(_lc
[I_NET_MSG
] + Format(_lc
[I_NET_ERR_HOST
], [Port
]));
4803 g_Net_Slist_Set(NetMasterList
);
4805 g_Net_Slist_ServerStarted();
4807 // Çàãðóçêà è çàïóñê êàðòû:
4808 if not g_Game_StartMap(false{asMegawad}, Map
, True) then
4810 g_Net_Slist_ServerClosed();
4811 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [Map
]));
4815 // Íåò òî÷åê ïîÿâëåíèÿ:
4816 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1
) +
4817 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2
) +
4818 g_Map_GetPointCount(RESPAWNPOINT_DM
) +
4819 g_Map_GetPointCount(RESPAWNPOINT_RED
)+
4820 g_Map_GetPointCount(RESPAWNPOINT_BLUE
)) < 1 then
4822 g_Net_Slist_ServerClosed();
4823 g_FatalError(_lc
[I_GAME_ERROR_GET_SPAWN
]);
4827 // Íàñòðîéêè èãðîêîâ è áîòîâ:
4830 g_Net_Slist_ServerMapStarted();
4831 NetState
:= NET_STATE_GAME
;
4834 procedure g_Game_StartClient(Addr
: String; Port
: Word; PW
: String);
4849 e_WriteLog('Starting net game (client)...', TMsgType
.Notify
);
4850 e_WriteLog('NET: Trying to connect to ' + Addr
+ ':' + IntToStr(Port
) + '...', TMsgType
.Notify
);
4852 g_Game_ClearLoading();
4857 gGameSettings
.GameType
:= GT_CLIENT
;
4859 gCoopTotalMonstersKilled
:= 0;
4860 gCoopTotalSecretsFound
:= 0;
4861 gCoopTotalMonsters
:= 0;
4862 gCoopTotalSecrets
:= 0;
4866 g_Game_ExecuteEvent('ongamestart');
4868 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4869 g_Game_SetupScreenSize();
4871 NetState
:= NET_STATE_AUTH
;
4873 g_Game_SetLoadingText(_lc
[I_LOAD_CONNECT
], 0, False);
4875 // create (or update) map/resource databases
4876 g_Res_CreateDatabases(true);
4878 gLMSRespawn
:= LMS_RESPAWN_NONE
;
4879 gLMSRespawnTime
:= 0;
4880 gSpectLatchPID1
:= 0;
4881 gSpectLatchPID2
:= 0;
4884 if not g_Net_Connect(Addr
, Port
) then
4886 g_FatalError(_lc
[I_NET_MSG
] + _lc
[I_NET_ERR_CONN
]);
4887 NetState
:= NET_STATE_NONE
;
4891 g_Game_SetLoadingText(_lc
[I_LOAD_SEND_INFO
], 0, False);
4893 g_Game_SetLoadingText(_lc
[I_LOAD_WAIT_INFO
], 0, False);
4898 // fuck! https://www.mail-archive.com/enet-discuss@cubik.org/msg00852.html
4899 // tl;dr: on shitdows, we can get -1 sometimes, and it is *NOT* a failure.
4900 // thank you, enet. let's ignore failures altogether then.
4901 while (enet_host_service(NetHost
, @NetEvent
, 50) > 0) do
4903 if (NetEvent
.kind
= ENET_EVENT_TYPE_RECEIVE
) then
4905 Ptr
:= NetEvent
.packet
^.data
;
4906 if not InMsg
.Init(Ptr
, NetEvent
.packet
^.dataLength
, True) then
4908 enet_packet_destroy(NetEvent
.packet
);
4912 InMsg
.ReadLongWord(); // skip size
4913 MID
:= InMsg
.ReadByte();
4915 if (MID
= NET_MSG_INFO
) and (State
= 0) then
4917 NetMyID
:= InMsg
.ReadByte();
4918 NetPlrUID1
:= InMsg
.ReadWord();
4920 WadName
:= InMsg
.ReadString();
4921 Map
:= InMsg
.ReadString();
4923 gWADHash
:= InMsg
.ReadMD5();
4925 gGameSettings
.GameMode
:= InMsg
.ReadByte();
4926 gSwitchGameMode
:= gGameSettings
.GameMode
;
4927 gGameSettings
.ScoreLimit
:= InMsg
.ReadWord();
4928 gGameSettings
.TimeLimit
:= InMsg
.ReadWord();
4929 gGameSettings
.MaxLives
:= InMsg
.ReadByte();
4930 gGameSettings
.Options
:= TGameOptions(InMsg
.ReadLongWord());
4931 T
:= InMsg
.ReadLongWord();
4933 //newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4934 //if newResPath = '' then
4936 //g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4937 newResPath
:= g_Res_DownloadMapWAD(ExtractFileName(WadName
), gWADHash
);
4938 if newResPath
= '' then
4940 g_FatalError(_lc
[I_NET_ERR_HASH
]);
4941 enet_packet_destroy(NetEvent
.packet
);
4942 NetState
:= NET_STATE_NONE
;
4945 e_LogWritefln('using downloaded map wad [%s] for [%s]`', [newResPath
, WadName
], TMsgType
.Notify
);
4947 //newResPath := ExtractRelativePath(MapsDir, newResPath);
4950 gPlayer1
:= g_Player_Get(g_Player_Create(gPlayer1Settings
.Model
,
4951 gPlayer1Settings
.Color
,
4952 gPlayer1Settings
.Team
, False));
4954 if gPlayer1
= nil then
4956 g_FatalError(Format(_lc
[I_GAME_ERROR_PLAYER_CREATE
], [1]));
4958 enet_packet_destroy(NetEvent
.packet
);
4959 NetState
:= NET_STATE_NONE
;
4963 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
4964 gPlayer1
.WeapSwitchMode
:= gPlayer1Settings
.WeaponSwitch
;
4965 gPlayer1
.setWeaponPrefs(gPlayer1Settings
.WeaponPreferences
);
4966 gPlayer1
.SwitchToEmpty
:= gPlayer1Settings
.SwitchToEmpty
;
4967 gPlayer1
.SkipIronFist
:= gPlayer1Settings
.SkipIronFist
;
4968 gPlayer1
.UID
:= NetPlrUID1
;
4969 gPlayer1
.Reset(True);
4971 if not g_Game_StartMap(false{asMegawad}, newResPath
+ ':\' + Map
, True) then
4973 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [WadName
+ ':\' + Map
]));
4975 enet_packet_destroy(NetEvent
.packet
);
4976 NetState
:= NET_STATE_NONE
;
4984 enet_packet_destroy(NetEvent
.packet
);
4988 enet_packet_destroy(NetEvent
.packet
);
4992 if (NetEvent
.kind
= ENET_EVENT_TYPE_DISCONNECT
) then
4995 if (NetEvent
.data
<= NET_DISC_MAX
) then
4996 g_Console_Add(_lc
[I_NET_MSG_ERROR
] + _lc
[I_NET_ERR_CONN
] + ' ' +
4997 _lc
[TStrings_Locale(Cardinal(I_NET_DISC_NONE
) + NetEvent
.data
)], True);
5004 ProcessLoading(True);
5005 if g_Net_UserRequestExit() then
5014 g_FatalError(_lc
[I_NET_MSG
] + _lc
[I_NET_ERR_CONN
]);
5015 NetState
:= NET_STATE_NONE
;
5020 NetState
:= NET_STATE_GAME
;
5021 MC_SEND_FullStateRequest
;
5022 e_WriteLog('NET: Connection successful.', TMsgType
.Notify
);
5026 lastAsMegaWad
: Boolean = false;
5028 procedure g_Game_ChangeMap(const MapPath
: String);
5032 g_Game_ClearLoading();
5034 Force
:= gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
];
5035 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
5036 if gExitByTrigger
then
5039 gExitByTrigger
:= False;
5041 if not g_Game_StartMap(lastAsMegaWad
, MapPath
, Force
) then
5042 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [MapPath
]));
5045 procedure g_Game_Restart();
5049 if g_Game_IsClient
then
5051 map
:= g_ExtractFileName(gMapInfo
.Map
);
5052 e_LogWritefln('g_Game_Restart: map = "%s" gCurrentMapFileName = "%s"', [map
, gCurrentMapFileName
]);
5056 g_Game_ClearLoading();
5057 g_Game_StartMap(lastAsMegaWad
, Map
, True, gCurrentMapFileName
);
5060 function g_Game_StartMap (asMegawad
: Boolean; Map
: String; Force
: Boolean = False; const oldMapPath
: AnsiString=''): Boolean;
5062 NewWAD
, ResName
: String;
5066 g_Map_Free((Map
<> gCurrentMapFileName
) and (oldMapPath
<> gCurrentMapFileName
));
5067 g_Player_RemoveAllCorpses();
5069 if (not g_Game_IsClient
) and
5070 (gSwitchGameMode
<> gGameSettings
.GameMode
) and
5071 (gGameSettings
.GameMode
<> GM_SINGLE
) then
5073 if gSwitchGameMode
= GM_CTF
then
5074 gGameSettings
.MaxLives
:= 0;
5075 gGameSettings
.GameMode
:= gSwitchGameMode
;
5078 gSwitchGameMode
:= gGameSettings
.GameMode
;
5080 g_Player_ResetTeams();
5082 lastAsMegaWad
:= asMegawad
;
5083 if isWadPath(Map
) then
5085 NewWAD
:= g_ExtractWadName(Map
);
5086 ResName
:= g_ExtractFileName(Map
);
5087 if g_Game_IsServer
then
5089 nws
:= findDiskWad(NewWAD
);
5090 //writeln('000: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']');
5093 if (length(nws
) = 0) then nws
:= e_FindWad(MegawadDirs
, NewWAD
);
5094 if (length(nws
) = 0) then nws
:= e_FindWad(MapDirs
, NewWAD
);
5098 if (length(nws
) = 0) then nws
:= e_FindWad(MapDirs
, NewWAD
);
5099 if (length(nws
) = 0) then nws
:= e_FindWad(MegawadDirs
, NewWAD
);
5101 //if (length(nws) = 0) then nws := e_FindWad(MapDownloadDirs, NewWAD);
5102 //writeln('001: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']');
5104 if (length(nws
) = 0) then
5106 ResName
:= ''; // failed
5111 if (g_Game_IsNet
) then gWADHash
:= MD5File(nws
);
5112 //writeln('********: nws=', nws, ' : Map=', Map, ' : nw=', NewWAD, ' : resname=', ResName);
5113 g_Game_LoadWAD(NewWAD
);
5118 // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART
5119 NewWAD
:= g_Game_ClientWAD(NewWAD
, gWADHash
);
5124 NewWAD
:= gGameSettings
.WAD
;
5130 //writeln('********: gsw=', gGameSettings.WAD, '; rn=', ResName);
5132 if (ResName
<> '') and (NewWAD
<> '') then
5134 //result := g_Map_Load(gGameSettings.WAD + ':\' + ResName);
5135 result
:= g_Map_Load(NewWAD
+':\'+ResName
);
5139 g_Player_ResetAll(Force
or gLastMap
, gGameSettings
.GameType
= GT_SINGLE
);
5141 gState
:= STATE_NONE
;
5142 g_ActiveWindow
:= nil;
5148 if gGameSettings
.GameMode
= GM_CTF
then
5150 g_Map_ResetFlag(FLAG_RED
);
5151 g_Map_ResetFlag(FLAG_BLUE
);
5152 // CTF, à ôëàãîâ íåò:
5153 if not g_Map_HaveFlagPoints() then
5154 g_SimpleError(_lc
[I_GAME_ERROR_CTF
]);
5159 gState
:= STATE_MENU
;
5164 gPauseMain
:= false;
5165 gPauseHolmes
:= false;
5166 NetTimeToUpdate
:= 1;
5167 NetTimeToReliable
:= 0;
5168 NetTimeToMaster
:= NetMasterRate
;
5169 gSpectLatchPID1
:= 0;
5170 gSpectLatchPID2
:= 0;
5171 gMissionFailed
:= False;
5174 gCoopMonstersKilled
:= 0;
5175 gCoopSecretsFound
:= 0;
5177 gVoteInProgress
:= False;
5178 gVotePassed
:= False;
5184 if not gGameOn
then Exit
;
5186 g_Game_SpectateCenterView();
5188 if g_Game_IsServer
then
5190 if (gGameSettings
.MaxLives
> 0) and (gGameSettings
.WarmupTime
> 0) then
5192 gLMSRespawn
:= LMS_RESPAWN_WARMUP
;
5193 gLMSRespawnTime
:= gTime
+ gGameSettings
.WarmupTime
*1000;
5194 gLMSSoftSpawn
:= True;
5195 if g_Game_IsNet
then
5196 MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, gLMSRespawnTime
- gTime
);
5200 gLMSRespawn
:= LMS_RESPAWN_NONE
;
5201 gLMSRespawnTime
:= 0;
5205 if NetMode
= NET_SERVER
then
5207 // reset full state flags
5208 if NetClients
<> nil then
5209 for I
:= 0 to High(NetClients
) do
5210 NetClients
[I
].FullUpdateSent
:= False;
5212 MH_SEND_GameEvent(NET_EV_MAPSTART
, gGameSettings
.GameMode
, Map
);
5215 g_Net_Slist_ServerMapStarted();
5217 if NetClients
<> nil then
5218 for I
:= 0 to High(NetClients
) do
5219 if NetClients
[I
].Used
then
5221 NetClients
[I
].Voted
:= False;
5222 if NetClients
[I
].RequestedFullUpdate
then
5224 MH_SEND_Everything((NetClients
[I
].State
= NET_STATE_AUTH
), I
);
5225 NetClients
[I
].RequestedFullUpdate
:= False;
5229 g_Net_UnbanNonPerm();
5234 gCoopTotalMonstersKilled
:= 0;
5235 gCoopTotalSecretsFound
:= 0;
5236 gCoopTotalMonsters
:= 0;
5237 gCoopTotalSecrets
:= 0;
5241 g_Game_ExecuteEvent('onmapstart');
5244 procedure SetFirstLevel
;
5248 MapList
:= g_Map_GetMapsList(gGameSettings
.WAD
);
5249 if MapList
= nil then
5252 SortSArray(MapList
);
5253 gNextMap
:= MapList
[Low(MapList
)];
5258 procedure g_Game_ExitLevel(const Map
: AnsiString);
5262 gCoopTotalMonstersKilled
:= gCoopTotalMonstersKilled
+ gCoopMonstersKilled
;
5263 gCoopTotalSecretsFound
:= gCoopTotalSecretsFound
+ gCoopSecretsFound
;
5264 gCoopTotalMonsters
:= gCoopTotalMonsters
+ gTotalMonsters
;
5265 gCoopTotalSecrets
:= gCoopTotalSecrets
+ gSecretsCount
;
5267 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
5268 if gGameSettings
.GameType
= GT_SINGLE
then
5269 gExit
:= EXIT_ENDLEVELSINGLE
5270 else // Âûøëè â âûõîä â Ñâîåé èãðå
5272 gExit
:= EXIT_ENDLEVELCUSTOM
;
5273 if gGameSettings
.GameMode
= GM_COOP
then
5274 g_Player_RememberAll
;
5276 if not g_Map_Exist(gGameSettings
.WAD
+ ':\' + gNextMap
) then
5279 if gGameSettings
.GameMode
= GM_COOP
then
5282 gStatsPressed
:= True;
5283 gNextMap
:= 'MAP01';
5285 if not g_Map_Exist(gGameSettings
.WAD
+ ':\' + gNextMap
) then
5288 if g_Game_IsNet
then
5290 MH_SEND_GameStats();
5291 MH_SEND_CoopStats();
5297 procedure g_Game_RestartLevel();
5301 if gGameSettings
.GameMode
= GM_SINGLE
then
5306 gExit
:= EXIT_ENDLEVELCUSTOM
;
5307 Map
:= g_ExtractFileName(gMapInfo
.Map
);
5311 function g_Game_ClientWAD (NewWAD
: String; const WHash
: TMD5Digest
): AnsiString;
5313 gWAD
{, xwad}: String;
5316 if not g_Game_IsClient
then Exit
;
5317 //e_LogWritefln('*** g_Game_ClientWAD: `%s`', [NewWAD]);
5319 gWAD
:= g_Res_DownloadMapWAD(ExtractFileName(NewWAD
), WHash
);
5324 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_WAD
], [ExtractFileName(NewWAD
)]));
5328 e_LogWritefln('using downloaded client map wad [%s] for [%s]', [gWAD
, NewWAD
], TMsgType
.Notify
);
5331 g_Game_LoadWAD(NewWAD
);
5335 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then Exit;
5336 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
5339 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
5340 gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash);
5344 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
5348 NewWAD := ExtractRelativePath(MapsDir, gWAD);
5349 g_Game_LoadWAD(NewWAD);
5353 procedure g_Game_RestartRound(NoMapRestart
: Boolean = False);
5355 i
, n
, nb
, nr
: Integer;
5357 if not g_Game_IsServer
then Exit
;
5358 if gLMSRespawn
= LMS_RESPAWN_NONE
then Exit
;
5359 gLMSRespawn
:= LMS_RESPAWN_NONE
;
5360 gLMSRespawnTime
:= 0;
5363 if (gGameSettings
.GameMode
= GM_COOP
) and not NoMapRestart
then
5365 gMissionFailed
:= True;
5366 g_Game_RestartLevel
;
5370 n
:= 0; nb
:= 0; nr
:= 0;
5371 for i
:= Low(gPlayers
) to High(gPlayers
) do
5372 if (gPlayers
[i
] <> nil) and
5373 ((not gPlayers
[i
].FSpectator
) or gPlayers
[i
].FWantsInGame
or
5374 (gPlayers
[i
] is TBot
)) then
5377 if gPlayers
[i
].Team
= TEAM_RED
then Inc(nr
)
5378 else if gPlayers
[i
].Team
= TEAM_BLUE
then Inc(nb
)
5381 if (n
< 1) or ((gGameSettings
.GameMode
= GM_TDM
) and ((nr
= 0) or (nb
= 0))) then
5383 // wait a second until the fuckers finally decide to join
5384 gLMSRespawn
:= LMS_RESPAWN_WARMUP
;
5385 gLMSRespawnTime
:= gTime
+ gGameSettings
.WarmupTime
*1000;
5386 gLMSSoftSpawn
:= NoMapRestart
;
5387 if g_Game_IsNet
then
5388 MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, gLMSRespawnTime
- gTime
);
5392 g_Player_RemoveAllCorpses
;
5393 g_Game_Message(_lc
[I_MESSAGE_LMS_START
], 144);
5394 if g_Game_IsNet
then
5395 MH_SEND_GameEvent(NET_EV_LMS_START
);
5397 for i
:= Low(gPlayers
) to High(gPlayers
) do
5399 if gPlayers
[i
] = nil then continue
;
5400 if gPlayers
[i
] is TBot
then gPlayers
[i
].FWantsInGame
:= True;
5401 // don't touch normal spectators
5402 if gPlayers
[i
].FSpectator
and not gPlayers
[i
].FWantsInGame
then
5404 gPlayers
[i
].FNoRespawn
:= True;
5405 gPlayers
[i
].Lives
:= 0;
5406 if g_Game_IsNet
then
5407 MH_SEND_PlayerStats(gPlayers
[I
].UID
);
5410 gPlayers
[i
].FNoRespawn
:= False;
5411 gPlayers
[i
].Lives
:= gGameSettings
.MaxLives
;
5412 gPlayers
[i
].Respawn(False, True);
5413 if gGameSettings
.GameMode
= GM_COOP
then
5415 gPlayers
[i
].Frags
:= 0;
5416 gPlayers
[i
].RestoreState
;
5418 if (gPlayer1
= nil) and (gSpectLatchPID1
> 0) then
5419 gPlayer1
:= g_Player_Get(gSpectLatchPID1
);
5420 if (gPlayer2
= nil) and (gSpectLatchPID2
> 0) then
5421 gPlayer2
:= g_Player_Get(gSpectLatchPID2
);
5424 g_Items_RestartRound();
5426 gLMSSoftSpawn
:= False;
5429 function g_Game_GetFirstMap(WAD
: String): String;
5433 MapList
:= g_Map_GetMapsList(WAD
);
5434 if MapList
= nil then
5437 SortSArray(MapList
);
5438 Result
:= MapList
[Low(MapList
)];
5440 if not g_Map_Exist(WAD
+ ':\' + Result
) then
5446 function g_Game_GetNextMap(): String;
5453 MapList
:= g_Map_GetMapsList(gGameSettings
.WAD
);
5454 if MapList
= nil then
5457 Map
:= g_ExtractFileName(gMapInfo
.Map
);
5459 SortSArray(MapList
);
5461 for I
:= Low(MapList
) to High(MapList
) do
5462 if Map
= MapList
[I
] then
5468 if MapIndex
<> -255 then
5470 if MapIndex
= High(MapList
) then
5471 Result
:= MapList
[Low(MapList
)]
5473 Result
:= MapList
[MapIndex
+ 1];
5475 if not g_Map_Exist(gGameSettings
.WAD
+ ':\' + Result
) then Result
:= Map
;
5481 procedure g_Game_NextLevel();
5483 if gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
, GM_COOP
] then
5484 gExit
:= EXIT_ENDLEVELCUSTOM
5487 gExit
:= EXIT_ENDLEVELSINGLE
;
5491 if gNextMap
<> '' then Exit
;
5492 gNextMap
:= g_Game_GetNextMap();
5495 function g_Game_IsTestMap(): Boolean;
5497 result
:= StrEquCI1251(TEST_MAP_NAME
, g_ExtractFileName(gMapInfo
.Map
));
5500 procedure g_Game_DeleteTestMap();
5503 //MapName: AnsiString;
5511 a
:= Pos('.wad:\', toLowerCase1251(gMapToDelete
));
5512 if (a
= 0) then a
:= Pos('.wad:/', toLowerCase1251(gMapToDelete
));
5513 if (a
= 0) then exit
;
5515 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
5516 WadName
:= Copy(gMapToDelete
, 1, a
+3);
5517 Delete(gMapToDelete
, 1, a
+5);
5518 gMapToDelete
:= UpperCase(gMapToDelete
);
5520 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
5523 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
5524 if MapName <> TEST_MAP_NAME then
5527 if not gTempDelete then
5529 time := g_GetFileTime(WadName);
5530 WAD := TWADFile.Create();
5533 if not WAD.ReadFile(WadName) then
5534 begin // Íåò òàêîãî WAD-ôàéëà
5539 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
5541 MapList := WAD.GetResourcesList('');
5543 if MapList <> nil then
5544 for a := 0 to High(MapList) do
5545 if MapList[a] = MapName then
5547 // Óäàëÿåì è ñîõðàíÿåì:
5548 WAD.RemoveResource('', MapName);
5549 WAD.SaveTo(WadName);
5554 g_SetFileTime(WadName, time);
5557 if gTempDelete
then DeleteFile(WadName
);
5560 procedure GameCVars(P
: SSArray
);
5563 stat
: TPlayerStatArray
;
5567 procedure ParseGameFlag(Flag
: TGameOption
; OffMsg
, OnMsg
: TStrings_Locale
; OnMapChange
: Boolean = False);
5571 if Length(P
) <= 1 then
5572 x
:= Flag
in gsGameFlags
5578 then gsGameFlags
+= [Flag
]
5579 else gsGameFlags
-= [Flag
];
5581 if g_Game_IsServer
then
5584 then gGameSettings
.Options
+= [Flag
]
5585 else gGameSettings
.Options
-= [Flag
];
5586 if g_Game_IsNet
then MH_SEND_GameSettings
;
5591 then g_Console_Add(_lc
[OnMsg
])
5592 else g_Console_Add(_lc
[OffMsg
]);
5594 if OnMapChange
and g_Game_IsServer
then
5595 g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5600 cmd
:= LowerCase(P
[0]);
5604 if (Length(P
) > 1) then
5606 a
:= g_Game_TextToMode(P
[1]);
5607 if a
= GM_SINGLE
then a
:= GM_COOP
;
5608 gsGameMode
:= g_Game_ModeToText(a
);
5609 if g_Game_IsServer
then
5611 gSwitchGameMode
:= a
;
5612 if (gGameOn
and (gGameSettings
.GameMode
= GM_SINGLE
)) or
5613 (gState
= STATE_INTERSINGLE
) then
5614 gSwitchGameMode
:= GM_SINGLE
;
5616 gGameSettings
.GameMode
:= gSwitchGameMode
;
5620 if gSwitchGameMode
= gGameSettings
.GameMode
then
5621 g_Console_Add(Format(_lc
[I_MSG_GAMEMODE_CURRENT
],
5622 [g_Game_ModeToText(gGameSettings
.GameMode
)]))
5624 g_Console_Add(Format(_lc
[I_MSG_GAMEMODE_CHANGE
],
5625 [g_Game_ModeToText(gGameSettings
.GameMode
),
5626 g_Game_ModeToText(gSwitchGameMode
)]));
5630 ParseGameFlag(TGameOption
.TEAM_DAMAGE
, I_MSG_FRIENDLY_FIRE_OFF
, I_MSG_FRIENDLY_FIRE_ON
);
5631 'g_friendly_absorb_damage':
5632 ParseGameFlag(TGameOption
.TEAM_ABSORB_DAMAGE
, I_MSG_FRIENDLY_ABSORB_DAMAGE_OFF
, I_MSG_FRIENDLY_ABSORB_DAMAGE_ON
);
5633 'g_friendly_hit_trace':
5634 ParseGameFlag(TGameOption
.TEAM_HIT_TRACE
, I_MSG_FRIENDLY_HIT_TRACE_OFF
, I_MSG_FRIENDLY_HIT_TRACE_ON
);
5635 'g_friendly_hit_projectile':
5636 ParseGameFlag(TGameOption
.TEAM_HIT_PROJECTILE
, I_MSG_FRIENDLY_PROJECT_TRACE_OFF
, I_MSG_FRIENDLY_PROJECT_TRACE_ON
);
5637 'g_items_all_respawn_random':
5638 ParseGameFlag(TGameOption
.ITEM_ALL_RANDOM
, I_MSG_ITEM_ALL_RANDOM_OFF
, I_MSG_ITEM_ALL_RANDOM_ON
, False);
5639 'g_items_help_respawn_random':
5640 ParseGameFlag(TGameOption
.ITEM_LIFE_RANDOM
, I_MSG_ITEM_LIFE_RANDOM_OFF
, I_MSG_ITEM_LIFE_RANDOM_ON
, False);
5641 'g_items_ammo_respawn_random':
5642 ParseGameFlag(TGameOption
.ITEM_AMMO_RANDOM
, I_MSG_ITEM_AMMO_RANDOM_OFF
, I_MSG_ITEM_AMMO_RANDOM_ON
, False);
5643 'g_items_weapon_respawn_random':
5644 ParseGameFlag(TGameOption
.ITEM_WEAPON_RANDOM
, I_MSG_ITEM_WEAPON_RANDOM_OFF
, I_MSG_ITEM_WEAPON_RANDOM_ON
);
5645 'g_powerup_randomize_respawn':
5646 ParseGameFlag(TGameOption
.POWERUP_RANDOM
, I_MSG_POWERUP_RANDOM_OFF
, I_MSG_POWERUP_RANDOM_ON
, False);
5648 ParseGameFlag(TGameOption
.WEAPONS_STAY
, I_MSG_WEAPONSTAY_OFF
, I_MSG_WEAPONSTAY_ON
);
5650 ParseGameFlag(TGameOption
.ALLOW_EXIT
, I_MSG_ALLOWEXIT_OFF
, I_MSG_ALLOWEXIT_ON
, True);
5652 ParseGameFlag(TGameOption
.MONSTERS
, I_MSG_ALLOWMON_OFF
, I_MSG_ALLOWMON_ON
, True);
5654 ParseGameFlag(TGameOption
.ALLOW_DROP_FLAG
, I_MSG_ALLOWDROPFLAG_OFF
, I_MSG_ALLOWDROPFLAG_ON
);
5656 ParseGameFlag(TGameOption
.THROW_FLAG
, I_MSG_THROWFLAG_OFF
, I_MSG_THROWFLAG_ON
);
5658 ParseGameFlag(TGameOption
.BOTS_VS_PLAYERS
, I_MSG_BOTSVSPLAYERS_OFF
, I_MSG_BOTSVSPLAYERS_ON
);
5660 ParseGameFlag(TGameOption
.BOTS_VS_MONSTERS
, I_MSG_BOTSVSMONSTERS_OFF
, I_MSG_BOTSVSMONSTERS_ON
);
5662 ParseGameFlag(TGameOption
.DM_KEYS
, I_MSG_DMKEYS_OFF
, I_MSG_DMKEYS_ON
, True);
5664 'g_gameflags': begin
5665 if Length(P
) > 1 then
5667 gsGameFlags
:= TGameOptions(StrToDWordDef(P
[1], LongWord(gsGameFlags
)));
5668 if g_Game_IsServer
then
5670 gGameSettings
.Options
:= gsGameFlags
;
5671 if g_Game_IsNet
then MH_SEND_GameSettings
;
5675 g_Console_Add(Format('%s %u', [cmd
, LongWord(gsGameFlags
)]));
5678 'g_warmup_time': begin
5679 if Length(P
) > 1 then
5681 gsWarmupTime
:= nclamp(StrToIntDef(P
[1], gsWarmupTime
), 0, $FFFF);
5682 if g_Game_IsServer
then
5684 gGameSettings
.WarmupTime
:= gsWarmupTime
;
5685 // extend warmup if it's already going
5686 if gLMSRespawn
= LMS_RESPAWN_WARMUP
then
5688 gLMSRespawnTime
:= gTime
+ gsWarmupTime
* 1000;
5689 if g_Game_IsNet
then MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, gLMSRespawnTime
- gTime
);
5691 if g_Game_IsNet
then MH_SEND_GameSettings
;
5695 g_Console_Add(Format(_lc
[I_MSG_WARMUP
], [Integer(gsWarmupTime
)]));
5696 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5699 'g_spawn_invul': begin
5700 if Length(P
) > 1 then
5702 gsSpawnInvul
:= nclamp(StrToIntDef(P
[1], gsSpawnInvul
), 0, $FFFF);
5703 if g_Game_IsServer
then
5705 gGameSettings
.SpawnInvul
:= gsSpawnInvul
;
5706 if g_Game_IsNet
then MH_SEND_GameSettings
;
5710 g_Console_Add(Format('%s %d', [cmd
, Integer(gsSpawnInvul
)]));
5713 'g_item_respawn_time': begin
5714 if Length(P
) > 1 then
5716 gsItemRespawnTime
:= nclamp(StrToIntDef(P
[1], gsItemRespawnTime
), 0, $FFFF);
5717 if g_Game_IsServer
then
5719 gGameSettings
.ItemRespawnTime
:= gsItemRespawnTime
;
5720 if g_Game_IsNet
then MH_SEND_GameSettings
;
5724 g_Console_Add(Format('%s %d', [cmd
, Integer(gsItemRespawnTime
)]));
5725 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5728 'g_item_time_random': begin
5729 if Length(P
) > 1 then
5731 gsItemRespawnRandom
:= nclamp(StrToIntDef(P
[1], gsItemRespawnRandom
), 0, $FFFF);
5732 if g_Game_IsServer
then
5734 gGameSettings
.ItemRespawnRandom
:= gsItemRespawnRandom
;
5735 if g_Game_IsNet
then MH_SEND_GameSettings
;
5739 g_Console_Add(Format('%s %d', [cmd
, Integer(gsItemRespawnRandom
)]));
5740 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5743 'g_powerup_respawn_time': begin
5744 if Length(P
) > 1 then
5746 gsPowerupRespawnTime
:= nclamp(StrToIntDef(P
[1], gsPowerupRespawnTime
), 0, $FFFF);
5747 if g_Game_IsServer
then
5749 gGameSettings
.PowerupRespawnTime
:= gsPowerupRespawnTime
;
5750 if g_Game_IsNet
then MH_SEND_GameSettings
;
5754 g_Console_Add(Format('%s %d', [cmd
, Integer(gsPowerupRespawnTime
)]));
5755 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5758 'g_powerup_time_random': begin
5759 if Length(P
) > 1 then
5761 gsPowerupRespawnRandom
:= nclamp(StrToIntDef(P
[1], gsPowerupRespawnRandom
), 0, $FFFF);
5762 if g_Game_IsServer
then
5764 gGameSettings
.PowerupRespawnRandom
:= gsPowerupRespawnRandom
;
5765 if g_Game_IsNet
then MH_SEND_GameSettings
;
5769 g_Console_Add(Format('%s %d', [cmd
, Integer(gsPowerupRespawnRandom
)]));
5770 if g_Game_IsServer
then g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
5773 'sv_intertime': begin
5774 if (Length(P
) > 1) then
5775 gDefInterTime
:= Min(Max(StrToIntDef(P
[1], gDefInterTime
), -1), 120);
5777 g_Console_Add(cmd
+ ' = ' + IntToStr(gDefInterTime
));
5780 'g_max_particles': begin
5781 if Length(p
) = 2 then
5783 a
:= Max(0, StrToIntDef(p
[1], 0));
5786 else if Length(p
) = 1 then
5788 e_LogWritefln('%s', [g_GFX_GetMax()])
5792 e_LogWritefln('usage: %s <n>', [cmd
])
5796 'g_max_shells': begin
5797 if Length(p
) = 2 then
5799 a
:= Max(0, StrToIntDef(p
[1], 0));
5802 else if Length(p
) = 1 then
5804 e_LogWritefln('%s', [g_Shells_GetMax()])
5808 e_LogWritefln('usage: %s <n>', [cmd
])
5813 if Length(p
) = 2 then
5815 a
:= Max(0, StrToIntDef(p
[1], 0));
5818 else if Length(p
) = 1 then
5820 e_LogWritefln('%s', [g_Gibs_GetMax()])
5824 e_LogWritefln('usage: %s <n>', [cmd
])
5828 'g_max_corpses': begin
5829 if Length(p
) = 2 then
5831 a
:= Max(0, StrToIntDef(p
[1], 0));
5834 else if Length(p
) = 1 then
5836 e_LogWritefln('%s', [g_Corpses_GetMax()])
5840 e_LogWritefln('usage: %s <n>', [cmd
])
5844 'g_force_model': begin
5845 if Length(p
) = 2 then
5847 a
:= StrToIntDef(p
[1], 0);
5848 g_Force_Model_Set(a
);
5849 if (g_Force_Model_Get() <> 0) and (gPlayers
<> nil) then
5851 for a
:= Low(gPlayers
) to High(gPlayers
) do
5853 if (gPlayers
[a
] <> nil) then
5855 if (gPlayers
[a
].UID
= gPlayer1
.UID
) then
5857 else if (gPlayer2
<> nil) and (gPlayers
[a
].UID
= gPlayer2
.UID
) then
5859 gPlayers
[a
].setModel(g_Forced_Model_GetName());
5863 else if (g_Force_Model_Get() = 0) and (gPlayers
<> nil) then
5865 for a
:= Low(gPlayers
) to High(gPlayers
) do
5867 if (gPlayers
[a
] <> nil) then
5869 if (gPlayers
[a
].UID
= gPlayer1
.UID
) then
5871 else if (gPlayer2
<> nil) and (gPlayers
[a
].UID
= gPlayer2
.UID
) then
5873 gPlayers
[a
].setModel(gPlayers
[a
].FActualModelName
);
5880 'g_force_model_name': begin
5881 if (Length(P
) > 1) then
5883 cmd
:= b_Text_Unformat(P
[1]);
5884 g_Forced_Model_SetName(cmd
);
5885 if (g_Force_Model_Get() <> 0) and (gPlayers
<> nil) then
5887 for a
:= Low(gPlayers
) to High(gPlayers
) do
5889 if (gPlayers
[a
] <> nil) then
5891 if (gPlayers
[a
].UID
= gPlayer1
.UID
) then
5893 else if (gPlayer2
<> nil) and (gPlayers
[a
].UID
= gPlayer2
.UID
) then
5895 gPlayers
[a
].setModel(g_Forced_Model_GetName());
5902 'g_scorelimit': begin
5903 if Length(P
) > 1 then
5905 gsScoreLimit
:= nclamp(StrToIntDef(P
[1], gsScoreLimit
), 0, $FFFF);
5907 if g_Game_IsServer
then
5910 if gGameSettings
.GameMode
= GM_DM
then
5912 stat
:= g_Player_GetStats();
5914 for a
:= 0 to High(stat
) do
5915 if stat
[a
].Frags
> b
then
5919 b
:= Max(gTeamStat
[TEAM_RED
].Score
, gTeamStat
[TEAM_BLUE
].Score
);
5921 // if someone has a higher score, set it to that instead
5922 gsScoreLimit
:= max(gsScoreLimit
, b
);
5923 gGameSettings
.ScoreLimit
:= gsScoreLimit
;
5925 if g_Game_IsNet
then MH_SEND_GameSettings
;
5929 g_Console_Add(Format(_lc
[I_MSG_SCORE_LIMIT
], [Integer(gsScoreLimit
)]));
5932 'g_timelimit': begin
5933 if Length(P
) > 1 then
5935 gsTimeLimit
:= nclamp(StrToIntDef(P
[1], gsTimeLimit
), 0, $FFFF);
5936 if g_Game_IsServer
then
5938 gGameSettings
.TimeLimit
:= gsTimeLimit
;
5939 if g_Game_IsNet
then MH_SEND_GameSettings
;
5942 g_Console_Add(Format(_lc
[I_MSG_TIME_LIMIT
],
5943 [gsTimeLimit
div 3600,
5944 (gsTimeLimit
div 60) mod 60,
5945 gsTimeLimit
mod 60]));
5949 if Length(P
) > 1 then
5950 gMaxBots
:= nclamp(StrToIntDef(P
[1], gMaxBots
), 0, 127);
5951 g_Console_Add('g_max_bots = ' + IntToStr(gMaxBots
));
5955 if Length(P
) > 1 then
5957 gsMaxLives
:= nclamp(StrToIntDef(P
[1], gsMaxLives
), 0, $FFFF);
5958 if g_Game_IsServer
then
5960 gGameSettings
.MaxLives
:= gsMaxLives
;
5961 if g_Game_IsNet
then MH_SEND_GameSettings
;
5965 g_Console_Add(Format(_lc
[I_MSG_LIVES
], [Integer(gsMaxLives
)]));
5970 procedure PlayerSettingsCVars(P
: SSArray
);
5975 function ParseTeam(s
: string): Byte;
5979 'red', '1': result
:= TEAM_RED
;
5980 'blue', '2': result
:= TEAM_BLUE
;
5981 else result
:= TEAM_NONE
;
5985 cmd
:= LowerCase(P
[0]);
5989 if (Length(P
) > 1) then
5991 gPlayer1Settings
.Name
:= b_Text_Unformat(P
[1]);
5992 if g_Game_IsClient
then
5993 MC_SEND_PlayerSettings
5994 else if gGameOn
and (gPlayer1
<> nil) then
5996 gPlayer1
.Name
:= b_Text_Unformat(P
[1]);
5997 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer1
.UID
);
6003 if (Length(P
) > 1) then
6005 gPlayer2Settings
.Name
:= b_Text_Unformat(P
[1]);
6006 if g_Game_IsClient
then
6007 MC_SEND_PlayerSettings
6008 else if gGameOn
and (gPlayer2
<> nil) then
6010 gPlayer2
.Name
:= b_Text_Unformat(P
[1]);
6011 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer2
.UID
);
6017 if Length(P
) > 3 then
6019 gPlayer1Settings
.Color
:= _RGB(EnsureRange(StrToIntDef(P
[1], 0), 0, 255),
6020 EnsureRange(StrToIntDef(P
[2], 0), 0, 255),
6021 EnsureRange(StrToIntDef(P
[3], 0), 0, 255));
6022 if g_Game_IsClient
then
6023 MC_SEND_PlayerSettings
6024 else if gGameOn
and (gPlayer1
<> nil) then
6026 gPlayer1
.SetColor(gPlayer1Settings
.Color
);
6027 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer1
.UID
);
6033 if Length(P
) > 3 then
6035 gPlayer2Settings
.Color
:= _RGB(EnsureRange(StrToIntDef(P
[1], 0), 0, 255),
6036 EnsureRange(StrToIntDef(P
[2], 0), 0, 255),
6037 EnsureRange(StrToIntDef(P
[3], 0), 0, 255));
6038 if g_Game_IsClient
then
6039 MC_SEND_PlayerSettings
6040 else if gGameOn
and (gPlayer2
<> nil) then
6042 gPlayer2
.SetColor(gPlayer2Settings
.Color
);
6043 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer2
.UID
);
6049 if (Length(P
) > 1) then
6051 gPlayer1Settings
.Model
:= P
[1];
6052 if g_Game_IsClient
then
6053 MC_SEND_PlayerSettings
6054 else if gGameOn
and (gPlayer1
<> nil) then
6056 gPlayer1
.FActualModelName
:= gPlayer1Settings
.Model
;
6057 gPlayer1
.SetModel(gPlayer1Settings
.Model
);
6058 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer1
.UID
);
6064 if (Length(P
) > 1) then
6066 gPlayer2Settings
.Model
:= P
[1];
6067 if g_Game_IsClient
then
6068 MC_SEND_PlayerSettings
6069 else if gGameOn
and (gPlayer2
<> nil) then
6071 gPlayer2
.FActualModelName
:= gPlayer2Settings
.Model
;
6072 gPlayer2
.SetModel(gPlayer2Settings
.Model
);
6073 if g_Game_IsNet
then MH_SEND_PlayerSettings(gPlayer2
.UID
);
6079 // TODO: switch teams if in game or store this separately
6080 if (Length(P
) > 1) then
6082 team
:= ParseTeam(P
[1]);
6083 if team
= TEAM_NONE
then
6084 g_Console_Add('expected ''red'', ''blue'', 1 or 2')
6085 else if not gGameOn
and not g_Game_IsNet
then
6086 gPlayer1Settings
.Team
:= team
6088 g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
6093 // TODO: switch teams if in game or store this separately
6094 if (Length(P
) > 1) then
6096 team
:= ParseTeam(P
[1]);
6097 if team
= TEAM_NONE
then
6098 g_Console_Add('expected ''red'', ''blue'', 1 or 2')
6099 else if not gGameOn
and not g_Game_IsNet
then
6100 gPlayer2Settings
.Team
:= team
6102 g_Console_Add(_lc
[I_MSG_ONMAPCHANGE
]);
6107 if (Length(P
) = 2) then
6108 gPlayer1Settings
.WeaponSwitch
:= EnsureRange(StrTointDef(P
[1], 0), 0, 2);
6112 if (Length(P
) = 2) then
6113 gPlayer2Settings
.WeaponSwitch
:= EnsureRange(StrTointDef(P
[1], 0), 0, 2);
6117 if (Length(P
) = 2) then
6118 gPlayer1Settings
.SwitchToEmpty
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6122 if (Length(P
) = 2) then
6123 gPlayer2Settings
.SwitchToEmpty
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6127 if (Length(P
) = 2) then
6128 gPlayer1Settings
.SkipIronFist
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6132 if (Length(P
) = 2) then
6133 gPlayer2Settings
.SkipIronFist
:= EnsureRange(StrTointDef(P
[1], 0), 0, 1);
6135 'p1_priority_ironfist':
6137 if (Length(P
) = 2) then
6138 gPlayer1Settings
.WeaponPreferences
[WEAPON_IRONFIST
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6140 'p2_priority_ironfist':
6142 if (Length(P
) = 2) then
6143 gPlayer2Settings
.WeaponPreferences
[WEAPON_IRONFIST
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6147 if (Length(P
) = 2) then
6148 gPlayer1Settings
.WeaponPreferences
[WEAPON_SAW
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6152 if (Length(P
) = 2) then
6153 gPlayer2Settings
.WeaponPreferences
[WEAPON_SAW
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6155 'p1_priority_pistol':
6157 if (Length(P
) = 2) then
6158 gPlayer1Settings
.WeaponPreferences
[WEAPON_PISTOL
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6160 'p2_priority_pistol':
6162 if (Length(P
) = 2) then
6163 gPlayer2Settings
.WeaponPreferences
[WEAPON_PISTOL
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6165 'p1_priority_shotgun1':
6167 if (Length(P
) = 2) then
6168 gPlayer1Settings
.WeaponPreferences
[WEAPON_SHOTGUN1
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6170 'p2_priority_shotgun1':
6172 if (Length(P
) = 2) then
6173 gPlayer2Settings
.WeaponPreferences
[WEAPON_SHOTGUN1
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6175 'p1_priority_shotgun2':
6177 if (Length(P
) = 2) then
6178 gPlayer1Settings
.WeaponPreferences
[WEAPON_SHOTGUN2
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6180 'p2_priority_shotgun2':
6182 if (Length(P
) = 2) then
6183 gPlayer2Settings
.WeaponPreferences
[WEAPON_SHOTGUN2
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6185 'p1_priority_chaingun':
6187 if (Length(P
) = 2) then
6188 gPlayer1Settings
.WeaponPreferences
[WEAPON_CHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6190 'p2_priority_chaingun':
6192 if (Length(P
) = 2) then
6193 gPlayer2Settings
.WeaponPreferences
[WEAPON_CHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6195 'p1_priority_rocketlauncher':
6197 if (Length(P
) = 2) then
6198 gPlayer1Settings
.WeaponPreferences
[WEAPON_ROCKETLAUNCHER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6200 'p2_priority_rocketlauncher':
6202 if (Length(P
) = 2) then
6203 gPlayer2Settings
.WeaponPreferences
[WEAPON_ROCKETLAUNCHER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6205 'p1_priority_plasma':
6207 if (Length(P
) = 2) then
6208 gPlayer1Settings
.WeaponPreferences
[WEAPON_PLASMA
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6210 'p2_priority_plasma':
6212 if (Length(P
) = 2) then
6213 gPlayer2Settings
.WeaponPreferences
[WEAPON_PLASMA
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6217 if (Length(P
) = 2) then
6218 gPlayer1Settings
.WeaponPreferences
[WEAPON_BFG
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6222 if (Length(P
) = 2) then
6223 gPlayer2Settings
.WeaponPreferences
[WEAPON_BFG
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6225 'p1_priority_superchaingun':
6227 if (Length(P
) = 2) then
6228 gPlayer1Settings
.WeaponPreferences
[WEAPON_SUPERCHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6230 'p2_priority_superchaingun':
6232 if (Length(P
) = 2) then
6233 gPlayer2Settings
.WeaponPreferences
[WEAPON_SUPERCHAINGUN
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6235 'p1_priority_flamethrower':
6237 if (Length(P
) = 2) then
6238 gPlayer1Settings
.WeaponPreferences
[WEAPON_FLAMETHROWER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6240 'p2_priority_flamethrower':
6242 if (Length(P
) = 2) then
6243 gPlayer2Settings
.WeaponPreferences
[WEAPON_FLAMETHROWER
] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6245 'p1_priority_berserk':
6247 if (Length(P
) = 2) then
6248 gPlayer1Settings
.WeaponPreferences
[WP_LAST
+1] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6250 'p2_priority_berserk':
6252 if (Length(P
) = 2) then
6253 gPlayer2Settings
.WeaponPreferences
[WP_LAST
+1] := EnsureRange(StrToIntDef(P
[1], WP_FIRST
), WP_FIRST
, WP_LAST
+1);
6258 procedure PrintHeapStats();
6262 hs
:= GetFPCHeapStatus();
6263 e_LogWriteLn ('v===== heap status =====v');
6264 e_LogWriteFln('max heap size = %d k', [hs
.MaxHeapSize
div 1024]);
6265 e_LogWriteFln('max heap used = %d k', [hs
.MaxHeapUsed
div 1024]);
6266 e_LogWriteFln('cur heap size = %d k', [hs
.CurrHeapSize
div 1024]);
6267 e_LogWriteFln('cur heap used = %d k', [hs
.CurrHeapUsed
div 1024]);
6268 e_LogWriteFln('cur heap free = %d k', [hs
.CurrHeapFree
div 1024]);
6269 e_LogWriteLn ('^=======================^');
6272 procedure DebugCommands(P
: SSArray
);
6279 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
6280 if {gDebugMode}conIsCheatsEnabled
then
6282 cmd
:= LowerCase(P
[0]);
6283 if cmd
= 'd_window' then
6285 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth
, gScreenHeight
]));
6286 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX
, gWinSizeY
]));
6288 else if cmd
= 'd_sounds' then
6290 if (Length(P
) > 1) and
6291 ((P
[1] = '1') or (P
[1] = '0')) then
6292 g_Debug_Sounds
:= (P
[1][1] = '1');
6294 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds
)]));
6296 else if cmd
= 'd_frames' then
6298 if (Length(P
) > 1) and
6299 ((P
[1] = '1') or (P
[1] = '0')) then
6300 g_Debug_Frames
:= (P
[1][1] = '1');
6302 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames
)]));
6304 else if cmd
= 'd_winmsg' then
6306 if (Length(P
) > 1) and
6307 ((P
[1] = '1') or (P
[1] = '0')) then
6308 g_Debug_WinMsgs
:= (P
[1][1] = '1');
6310 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs
)]));
6312 else if (cmd
= 'd_monoff') and not g_Game_IsNet
then
6314 if (Length(P
) > 1) and
6315 ((P
[1] = '1') or (P
[1] = '0')) then
6316 g_Debug_MonsterOff
:= (P
[1][1] = '1');
6318 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff
)]));
6320 else if (cmd
= 'd_botoff') and not g_Game_IsNet
then
6322 if Length(P
) > 1 then
6324 '0': g_debug_BotAIOff
:= 0;
6325 '1': g_debug_BotAIOff
:= 1;
6326 '2': g_debug_BotAIOff
:= 2;
6327 '3': g_debug_BotAIOff
:= 3;
6330 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff
]));
6332 else if cmd
= 'd_monster' then
6334 if gGameOn
and (gPlayer1
<> nil) and (gPlayer1
.alive
) and (not g_Game_IsNet
) then
6335 if Length(P
) < 2 then
6337 g_Console_Add(cmd
+ ' [ID | Name] [behaviour]');
6338 g_Console_Add('ID | Name');
6339 for b
:= MONSTER_DEMON
to MONSTER_MAN
do
6340 g_Console_Add(Format('%2d | %s', [b
, g_Mons_NameByTypeId(b
)]));
6341 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
6344 a
:= StrToIntDef(P
[1], 0);
6345 if (a
< MONSTER_DEMON
) or (a
> MONSTER_MAN
) then
6346 a
:= g_Mons_TypeIdByName(P
[1]);
6348 if (a
< MONSTER_DEMON
) or (a
> MONSTER_MAN
) then
6349 g_Console_Add(Format(_lc
[I_MSG_NO_MONSTER
], [P
[1]]))
6352 with gPlayer1
.Obj
do
6354 mon
:= g_Monsters_Create(a
,
6355 X
+ Rect
.X
+ (Rect
.Width
div 2),
6356 Y
+ Rect
.Y
+ Rect
.Height
,
6357 gPlayer1
.Direction
, True);
6359 if (Length(P
) > 2) and (mon
<> nil) then
6361 if (CompareText(P
[2], 'normal') = 0) then mon
.MonsterBehaviour
:= BH_NORMAL
6362 else if (CompareText(P
[2], 'killer') = 0) then mon
.MonsterBehaviour
:= BH_KILLER
6363 else if (CompareText(P
[2], 'maniac') = 0) then mon
.MonsterBehaviour
:= BH_MANIAC
6364 else if (CompareText(P
[2], 'insane') = 0) then mon
.MonsterBehaviour
:= BH_INSANE
6365 else if (CompareText(P
[2], 'cannibal') = 0) then mon
.MonsterBehaviour
:= BH_CANNIBAL
6366 else if (CompareText(P
[2], 'good') = 0) then mon
.MonsterBehaviour
:= BH_GOOD
6367 else if (CompareText(P
[2], 'friend') = 0) then mon
.MonsterBehaviour
:= BH_GOOD
6368 else if (CompareText(P
[2], 'friendly') = 0) then mon
.MonsterBehaviour
:= BH_GOOD
6369 else mon
.MonsterBehaviour
:= Min(Max(StrToIntDef(P
[2], BH_NORMAL
), BH_NORMAL
), BH_GOOD
);
6374 else if (cmd
= 'd_health') then
6376 if (Length(P
) > 1) and
6377 ((P
[1] = '1') or (P
[1] = '0')) then
6378 g_debug_HealthBar
:= (P
[1][1] = '1');
6380 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar
)]));
6382 else if (cmd
= 'd_player') then
6384 if (Length(P
) > 1) and
6385 ((P
[1] = '1') or (P
[1] = '0')) then
6386 g_debug_Player
:= (P
[1][1] = '1');
6388 g_Console_Add(Format(cmd
+ ' is %d', [Byte(g_Debug_Player
)]));
6390 else if (cmd
= 'd_mem') then
6396 g_Console_Add(_lc
[I_MSG_NOT_DEBUG
]);
6400 procedure GameCheats(P
: SSArray
);
6406 if (not gGameOn
) or (not conIsCheatsEnabled
) then
6408 g_Console_Add('not available');
6414 g_Console_Add('where is the player?!');
6417 cmd
:= LowerCase(P
[0]);
6421 plr
.GodMode
:= not plr
.GodMode
;
6422 if plr
.GodMode
then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
6425 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
6426 if cmd
= 'give' then
6428 if length(P
) < 2 then begin g_Console_Add('give what?!'); exit
; end;
6429 for f
:= 1 to High(P
) do
6431 cmd
:= LowerCase(P
[f
]);
6432 if cmd
= 'health' then begin plr
.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue
; end;
6433 if (cmd
= 'all') {or (cmd = 'weapons')} then begin plr
.TankRamboCheats(False); g_Console_Add('player got the gifts'); continue
; end;
6434 if cmd
= 'exit' then
6436 if gTriggers
<> nil then
6438 for a
:= 0 to High(gTriggers
) do
6440 if gTriggers
[a
].TriggerType
= TRIGGER_EXIT
then
6442 g_Console_Add('player left the map');
6443 gExitByTrigger
:= True;
6444 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
6445 g_Game_ExitLevel(gTriggers
[a
].tgcMap
);
6453 if cmd
= 'air' then begin plr
.GiveItem(ITEM_OXYGEN
); g_Console_Add('player got some air'); continue
; end;
6454 if cmd
= 'jetpack' then begin plr
.GiveItem(ITEM_JETPACK
); g_Console_Add('player got a jetpack'); continue
; end;
6455 if cmd
= 'suit' then begin plr
.GiveItem(ITEM_SUIT
); g_Console_Add('player got an envirosuit'); continue
; end;
6456 if cmd
= 'berserk' then begin plr
.GiveItem(ITEM_MEDKIT_BLACK
); g_Console_Add('player got a berserk pack'); continue
; end;
6457 if cmd
= 'backpack' then begin plr
.GiveItem(ITEM_AMMO_BACKPACK
); g_Console_Add('player got a backpack'); continue
; end;
6459 if cmd
= 'helmet' then begin plr
.GiveItem(ITEM_HELMET
); g_Console_Add('player got a helmet'); continue
; end;
6460 if cmd
= 'bottle' then begin plr
.GiveItem(ITEM_BOTTLE
); g_Console_Add('player got a bottle of health'); continue
; end;
6462 if cmd
= 'stimpack' then begin plr
.GiveItem(ITEM_MEDKIT_SMALL
); g_Console_Add('player got a stimpack'); continue
; end;
6463 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;
6465 if cmd
= 'greenarmor' then begin plr
.GiveItem(ITEM_ARMOR_GREEN
); g_Console_Add('player got a security armor'); continue
; end;
6466 if cmd
= 'bluearmor' then begin plr
.GiveItem(ITEM_ARMOR_BLUE
); g_Console_Add('player got a combat armor'); continue
; end;
6468 if (cmd
= 'soulsphere') or (cmd
= 'soul') then begin plr
.GiveItem(ITEM_SPHERE_BLUE
); g_Console_Add('player got a soul sphere'); continue
; end;
6469 if (cmd
= 'megasphere') or (cmd
= 'mega') then begin plr
.GiveItem(ITEM_SPHERE_WHITE
); g_Console_Add('player got a megasphere'); continue
; end;
6471 if (cmd
= 'invul') or (cmd
= 'invulnerability') then begin plr
.GiveItem(ITEM_INVUL
); g_Console_Add('player got invulnerability'); continue
; end;
6472 if (cmd
= 'invis') or (cmd
= 'invisibility') then begin plr
.GiveItem(ITEM_INVIS
); g_Console_Add('player got invisibility'); continue
; end;
6474 if cmd
= 'redkey' then begin plr
.GiveItem(ITEM_KEY_RED
); g_Console_Add('player got the red key'); continue
; end;
6475 if cmd
= 'greenkey' then begin plr
.GiveItem(ITEM_KEY_GREEN
); g_Console_Add('player got the green key'); continue
; end;
6476 if cmd
= 'bluekey' then begin plr
.GiveItem(ITEM_KEY_BLUE
); g_Console_Add('player got the blue key'); continue
; end;
6478 if (cmd
= 'shotgun') or (cmd
= 'sg') then begin plr
.GiveItem(ITEM_WEAPON_SHOTGUN1
); g_Console_Add('player got a shotgun'); continue
; end;
6479 if (cmd
= 'supershotgun') or (cmd
= 'ssg') then begin plr
.GiveItem(ITEM_WEAPON_SHOTGUN2
); g_Console_Add('player got a supershotgun'); continue
; end;
6480 if cmd
= 'chaingun' then begin plr
.GiveItem(ITEM_WEAPON_CHAINGUN
); g_Console_Add('player got a chaingun'); continue
; end;
6481 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;
6482 if cmd
= 'plasmagun' then begin plr
.GiveItem(ITEM_WEAPON_PLASMA
); g_Console_Add('player got a plasma gun'); continue
; end;
6483 if cmd
= 'bfg' then begin plr
.GiveItem(ITEM_WEAPON_BFG
); g_Console_Add('player got a BFG-9000'); continue
; end;
6485 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;
6486 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;
6487 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;
6488 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;
6489 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;
6490 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;
6492 if cmd
= 'superchaingun' then begin plr
.GiveItem(ITEM_WEAPON_SUPERCHAINGUN
); g_Console_Add('player got a superchaingun'); continue
; end;
6493 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;
6495 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;
6496 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;
6498 if cmd
= 'chainsaw' then begin plr
.GiveItem(ITEM_WEAPON_SAW
); g_Console_Add('player got a chainsaw'); continue
; end;
6500 if cmd
= 'ammo' then
6502 plr
.GiveItem(ITEM_AMMO_SHELLS_BOX
);
6503 plr
.GiveItem(ITEM_AMMO_BULLETS_BOX
);
6504 plr
.GiveItem(ITEM_AMMO_CELL_BIG
);
6505 plr
.GiveItem(ITEM_AMMO_ROCKET_BOX
);
6506 plr
.GiveItem(ITEM_AMMO_FUELCAN
);
6507 g_Console_Add('player got some ammo');
6511 if cmd
= 'clip' then begin plr
.GiveItem(ITEM_AMMO_BULLETS
); g_Console_Add('player got some bullets'); continue
; end;
6512 if cmd
= 'bullets' then begin plr
.GiveItem(ITEM_AMMO_BULLETS_BOX
); g_Console_Add('player got a box of bullets'); continue
; end;
6514 if cmd
= 'shells' then begin plr
.GiveItem(ITEM_AMMO_SHELLS
); g_Console_Add('player got some shells'); continue
; end;
6515 if cmd
= 'shellbox' then begin plr
.GiveItem(ITEM_AMMO_SHELLS_BOX
); g_Console_Add('player got a box of shells'); continue
; end;
6517 if cmd
= 'cells' then begin plr
.GiveItem(ITEM_AMMO_CELL
); g_Console_Add('player got some cells'); continue
; end;
6518 if cmd
= 'battery' then begin plr
.GiveItem(ITEM_AMMO_CELL_BIG
); g_Console_Add('player got cell battery'); continue
; end;
6520 if cmd
= 'rocket' then begin plr
.GiveItem(ITEM_AMMO_ROCKET
); g_Console_Add('player got a rocket'); continue
; end;
6521 if cmd
= 'rocketbox' then begin plr
.GiveItem(ITEM_AMMO_ROCKET_BOX
); g_Console_Add('player got some rockets'); continue
; end;
6523 if (cmd
= 'fuel') or (cmd
= 'fuelcan') then begin plr
.GiveItem(ITEM_AMMO_FUELCAN
); g_Console_Add('player got fuel canister'); continue
; end;
6525 if cmd
= 'weapons' then
6527 plr
.GiveItem(ITEM_WEAPON_SHOTGUN1
);
6528 plr
.GiveItem(ITEM_WEAPON_SHOTGUN2
);
6529 plr
.GiveItem(ITEM_WEAPON_CHAINGUN
);
6530 plr
.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER
);
6531 plr
.GiveItem(ITEM_WEAPON_PLASMA
);
6532 plr
.GiveItem(ITEM_WEAPON_BFG
);
6533 g_Console_Add('player got weapons');
6537 if cmd
= 'keys' then
6539 plr
.GiveItem(ITEM_KEY_RED
);
6540 plr
.GiveItem(ITEM_KEY_GREEN
);
6541 plr
.GiveItem(ITEM_KEY_BLUE
);
6542 g_Console_Add('player got all keys');
6546 g_Console_Add('i don''t know how to give '''+cmd
+'''!');
6551 if cmd
= 'open' then
6553 g_Console_Add('player activated sesame');
6554 g_Triggers_OpenAll();
6561 if gFly
then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
6565 if cmd
= 'noclip' then
6568 g_Console_Add('wall hardeness adjusted');
6572 if cmd
= 'notarget' then
6574 plr
.NoTarget
:= not plr
.NoTarget
;
6575 if plr
.NoTarget
then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
6579 if cmd
= 'noreload' then
6581 plr
.NoReload
:= not plr
.NoReload
;
6582 if plr
.NoReload
then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
6586 if cmd
= 'speedy' then
6588 MAX_RUNVEL
:= 32-MAX_RUNVEL
;
6589 g_Console_Add('speed adjusted');
6593 if cmd
= 'jumpy' then
6595 VEL_JUMP
:= 30-VEL_JUMP
;
6596 g_Console_Add('jump height adjusted');
6600 if cmd
= 'automap' then
6602 gShowMap
:= not gShowMap
;
6603 if gShowMap
then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
6607 if cmd
= 'aimline' then
6609 gAimLine
:= not gAimLine
;
6610 if gAimLine
then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
6615 procedure GameCommands(P
: SSArray
);
6621 pl
: pTNetClient
= nil;
6630 cmd
:= LowerCase(P
[0]);
6632 if cmd
= 'pause' then
6634 if (g_ActiveWindow
= nil) then
6635 g_Game_Pause(not gPauseMain
);
6637 else if cmd
= 'endgame' then
6638 gExit
:= EXIT_SIMPLE
6639 else if cmd
= 'restart' then
6641 if gGameOn
or (gState
in [STATE_INTERSINGLE
, STATE_INTERCUSTOM
]) then
6643 if g_Game_IsClient
then
6645 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6650 g_Console_Add(_lc
[I_MSG_NOT_GAME
]);
6652 else if cmd
= 'kick' then
6654 if g_Game_IsServer
then
6656 if Length(P
) < 2 then
6658 g_Console_Add('kick <name>');
6663 g_Console_Add('kick <name>');
6667 if g_Game_IsNet
then
6668 pl
:= g_Net_Client_ByName(P
[1]);
6671 s
:= g_Net_ClientName_ByID(pl
^.ID
);
6672 g_Net_Host_Kick(pl
^.ID
, NET_DISC_KICK
);
6673 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [s
]));
6674 MH_SEND_GameEvent(NET_EV_PLAYER_KICK
, 0, s
);
6675 g_Net_Slist_ServerPlayerLeaves();
6676 end else if gPlayers
<> nil then
6677 for a
:= Low(gPlayers
) to High(gPlayers
) do
6678 if gPlayers
[a
] <> nil then
6679 if Copy(LowerCase(gPlayers
[a
].Name
), 1, Length(P
[1])) = LowerCase(P
[1]) then
6681 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
6682 if not(gPlayers
[a
] is TBot
) and (gGameSettings
.GameType
= GT_SINGLE
) then
6684 gPlayers
[a
].Lives
:= 0;
6685 gPlayers
[a
].Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
6686 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [gPlayers
[a
].Name
]), True);
6687 g_Player_Remove(gPlayers
[a
].UID
);
6688 g_Net_Slist_ServerPlayerLeaves();
6689 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
6693 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
6695 else if cmd
= 'kick_id' then
6697 if g_Game_IsServer
and g_Game_IsNet
then
6699 if Length(P
) < 2 then
6701 g_Console_Add('kick_id <client ID>');
6706 g_Console_Add('kick_id <client ID>');
6710 a
:= StrToIntDef(P
[1], 0);
6711 if (NetClients
<> nil) and (a
<= High(NetClients
)) then
6713 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6715 s
:= g_Net_ClientName_ByID(NetClients
[a
].ID
);
6716 g_Net_Host_Kick(NetClients
[a
].ID
, NET_DISC_KICK
);
6717 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [s
]));
6718 MH_SEND_GameEvent(NET_EV_PLAYER_KICK
, 0, s
);
6719 g_Net_Slist_ServerPlayerLeaves();
6723 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6725 else if cmd
= 'kick_pid' then
6727 if g_Game_IsServer
and g_Game_IsNet
then
6729 if Length(P
) < 2 then
6731 g_Console_Add('kick_pid <player ID>');
6736 g_Console_Add('kick_pid <player ID>');
6740 a
:= StrToIntDef(P
[1], 0);
6741 pl
:= g_Net_Client_ByPlayer(a
);
6742 if (pl
<> nil) and pl
^.Used
and (pl
^.Peer
<> nil) then
6744 s
:= g_Net_ClientName_ByID(pl
^.ID
);
6745 g_Net_Host_Kick(pl
^.ID
, NET_DISC_KICK
);
6746 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [s
]));
6747 MH_SEND_GameEvent(NET_EV_PLAYER_KICK
, 0, s
);
6748 g_Net_Slist_ServerPlayerLeaves();
6751 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6753 else if cmd
= 'ban' then
6755 if g_Game_IsServer
and g_Game_IsNet
then
6757 if Length(P
) < 2 then
6759 g_Console_Add('ban <name>');
6764 g_Console_Add('ban <name>');
6768 pl
:= g_Net_Client_ByName(P
[1]);
6770 g_Net_Host_Ban(pl
, False)
6772 g_Console_Add(Format(_lc
[I_NET_ERR_NAME404
], [P
[1]]));
6774 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6776 else if cmd
= 'ban_id' then
6778 if g_Game_IsServer
and g_Game_IsNet
then
6780 if Length(P
) < 2 then
6782 g_Console_Add('ban_id <client ID>');
6787 g_Console_Add('ban_id <client ID>');
6791 a
:= StrToIntDef(P
[1], 0);
6792 if (NetClients
<> nil) and (a
<= High(NetClients
)) then
6793 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6794 g_Net_Host_Ban(pl
, False);
6796 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6798 else if cmd
= 'ban_pid' then
6800 if g_Game_IsServer
and g_Game_IsNet
then
6802 if Length(P
) < 2 then
6804 g_Console_Add('ban_pid <player ID>');
6809 g_Console_Add('ban_pid <player ID>');
6813 a
:= StrToIntDef(P
[1], 0);
6814 pl
:= g_Net_Client_ByPlayer(a
);
6815 if (pl
<> nil) and pl
^.Used
and (pl
^.Peer
<> nil) then
6816 g_Net_Host_Ban(pl
, False);
6818 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6820 else if cmd
= 'permban' then
6822 if g_Game_IsServer
and g_Game_IsNet
then
6824 if Length(P
) < 2 then
6826 g_Console_Add('permban <name>');
6831 g_Console_Add('permban <name>');
6835 pl
:= g_Net_Client_ByName(P
[1]);
6837 g_Net_Host_Ban(pl
, True)
6839 g_Console_Add(Format(_lc
[I_NET_ERR_NAME404
], [P
[1]]));
6841 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6843 else if cmd
= 'permban_id' then
6845 if g_Game_IsServer
and g_Game_IsNet
then
6847 if Length(P
) < 2 then
6849 g_Console_Add('permban_id <client ID>');
6854 g_Console_Add('permban_id <client ID>');
6858 a
:= StrToIntDef(P
[1], 0);
6859 if (NetClients
<> nil) and (a
<= High(NetClients
)) then
6860 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6861 g_Net_Host_Ban(@NetClients
[a
], True);
6863 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6865 else if cmd
= 'permban_pid' then
6867 if g_Game_IsServer
and g_Game_IsNet
then
6869 if Length(P
) < 2 then
6871 g_Console_Add('permban_pid <player ID>');
6876 g_Console_Add('permban_pid <player ID>');
6880 a
:= StrToIntDef(P
[1], 0);
6881 pl
:= g_Net_Client_ByPlayer(a
);
6882 if (pl
<> nil) and pl
^.Used
and (pl
^.Peer
<> nil) then
6883 g_Net_Host_Ban(pl
, True);
6885 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6887 else if cmd
= 'permban_ip' then
6889 if g_Game_IsServer
and g_Game_IsNet
then
6891 if Length(P
) < 2 then
6893 g_Console_Add('permban_ip <IP address>');
6898 g_Console_Add('permban_ip <IP address>');
6902 g_Net_BanAddress(P
[1]);
6903 g_Net_SaveBanList();
6904 g_Console_Add(Format(_lc
[I_PLAYER_BAN
], [P
[1]]));
6906 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6908 else if cmd
= 'unban' then
6910 if g_Game_IsServer
and g_Game_IsNet
then
6912 if Length(P
) < 2 then
6914 g_Console_Add('unban <IP Address>');
6919 g_Console_Add('unban <IP Address>');
6923 if g_Net_UnbanAddress(P
[1]) then
6925 g_Console_Add(Format(_lc
[I_MSG_UNBAN_OK
], [P
[1]]));
6926 g_Net_SaveBanList();
6928 g_Console_Add(Format(_lc
[I_MSG_UNBAN_FAIL
], [P
[1]]));
6930 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6932 else if cmd
= 'clientlist' then
6934 if g_Game_IsServer
and g_Game_IsNet
then
6937 if NetClients
<> nil then
6938 for a
:= Low(NetClients
) to High(NetClients
) do
6939 if NetClients
[a
].Used
and (NetClients
[a
].Peer
<> nil) then
6941 plr
:= g_Player_Get(NetClients
[a
].Player
);
6942 if plr
= nil then continue
;
6944 g_Console_Add(Format('#%2d: %-15s | %s', [a
,
6945 IpToStr(NetClients
[a
].Peer
^.address
.host
), plr
.Name
]));
6948 g_Console_Add(_lc
[I_MSG_NOCLIENTS
]);
6950 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
6952 else if cmd
= 'connect' then
6954 if (NetMode
= NET_NONE
) then
6956 if Length(P
) < 2 then
6958 g_Console_Add('connect <IP> [port] [password]');
6963 g_Console_Add('connect <IP> [port] [password]');
6967 if Length(P
) > 2 then
6968 prt
:= StrToIntDef(P
[2], 25666)
6972 if Length(P
) > 3 then
6977 g_Game_StartClient(P
[1], prt
, pw
);
6980 else if cmd
= 'disconnect' then
6982 if (NetMode
= NET_CLIENT
) then
6985 else if cmd
= 'reconnect' then
6987 if (NetMode
= NET_SERVER
) then
6990 if (NetMode
= NET_CLIENT
) then
6993 gExit
:= EXIT_SIMPLE
;
6997 //TODO: Use last successful password to reconnect, instead of ''
6998 g_Game_StartClient(NetClientIP
, NetClientPort
, '');
7000 else if (cmd
= 'addbot') or
7001 (cmd
= 'bot_add') then
7004 1: g_Bot_Add(TEAM_NONE
, 2);
7005 2: g_Bot_Add(TEAM_NONE
, StrToIntDef(P
[1], 2));
7007 g_Bot_Add(TEAM_NONE
, StrToIntDef(P
[1], 2), StrToIntDef(P
[2], 100));
7010 else if cmd
= 'bot_addlist' then
7013 1: g_Bot_AddList(TEAM_NONE
, '');
7014 2: g_Bot_AddList(TEAM_NONE
, P
[1], StrToIntDef(P
[1], -1));
7016 if P
[2] = 'red' then
7018 else if P
[2] = 'blue' then
7024 then g_Bot_AddList(t
, P
[1], StrToIntDef(P
[1], -1))
7025 else g_Bot_AddList(t
, P
[1], StrToIntDef(P
[1], -1), StrToIntDef(P
[3], 100));
7028 else if cmd
= 'bot_removeall' then
7030 else if cmd
= 'chat' then
7032 if g_Game_IsNet
then
7034 if Length(P
) > 1 then
7036 for a
:= 1 to High(P
) do
7037 chstr
:= chstr
+ P
[a
] + ' ';
7039 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7041 if Length(chstr
) < 1 then
7043 g_Console_Add('chat <text>');
7047 chstr
:= b_Text_Format(chstr
);
7048 if g_Game_IsClient
then
7049 MC_SEND_Chat(chstr
, NET_CHAT_PLAYER
)
7051 MH_SEND_Chat(gPlayer1Settings
.Name
+ ': ' + chstr
, NET_CHAT_PLAYER
);
7054 g_Console_Add('chat <text>');
7056 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7058 else if cmd
= 'teamchat' then
7060 if g_Game_IsNet
and (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then
7062 if Length(P
) > 1 then
7064 for a
:= 1 to High(P
) do
7065 chstr
:= chstr
+ P
[a
] + ' ';
7067 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7069 if Length(chstr
) < 1 then
7071 g_Console_Add('teamchat <text>');
7075 chstr
:= b_Text_Format(chstr
);
7076 if g_Game_IsClient
then
7077 MC_SEND_Chat(chstr
, NET_CHAT_TEAM
)
7079 MH_SEND_Chat(gPlayer1Settings
.Name
+ ': ' + chstr
, NET_CHAT_TEAM
,
7080 gPlayer1Settings
.Team
);
7083 g_Console_Add('teamchat <text>');
7085 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7087 else if (cmd
= 'an') or (cmd
= 'announce') then
7089 if g_Game_IsNet
then
7091 if Length(P
) > 1 then
7093 for a
:= 1 to High(P
) do
7094 chstr
:= chstr
+ P
[a
] + ' ';
7096 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7098 if Length(chstr
) < 1 then
7100 g_Console_Add('announce <text>');
7104 chstr
:= 'centerprint 100 ' + b_Text_Format(chstr
);
7105 if g_Game_IsClient
then
7106 MC_SEND_RCONCommand(chstr
)
7108 g_Console_Process(chstr
, True);
7111 g_Console_Add('announce <text>');
7113 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7115 else if cmd
= 'game' then
7117 if gGameSettings
.GameType
<> GT_NONE
then
7119 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7122 if Length(P
) = 1 then
7124 g_Console_Add(cmd
+ ' <WAD> [MAP] [# players]');
7127 // game not started yet, load first map from some wad
7129 s
:= addWadExtension(P
[1]);
7130 found
:= e_FindResource(AllMapDirs
, s
);
7134 P
[1] := ExpandFileName(P
[1]);
7135 // if map not choosed then set first map
7136 if Length(P
) < 3 then
7139 P
[2] := g_Game_GetFirstMap(P
[1]);
7142 s
:= P
[1] + ':\' + UpperCase(P
[2]);
7144 if g_Map_Exist(s
) then
7148 with gGameSettings
do
7150 Options
:= gsGameFlags
;
7151 GameMode
:= g_Game_TextToMode(gsGameMode
);
7152 if gSwitchGameMode
<> GM_NONE
then
7153 GameMode
:= gSwitchGameMode
;
7154 if GameMode
= GM_NONE
then GameMode
:= GM_DM
;
7155 if GameMode
= GM_SINGLE
then GameMode
:= GM_COOP
;
7157 if Length(P
) >= 4 then
7158 b
:= StrToIntDef(P
[3], 1);
7159 g_Game_StartCustom(s
, GameMode
, TimeLimit
,
7160 ScoreLimit
, MaxLives
, Options
, b
);
7165 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[1]]))
7167 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [UpperCase(P
[2]), P
[1]]));
7169 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]));
7171 else if cmd
= 'host' then
7173 if gGameSettings
.GameType
<> GT_NONE
then
7175 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7178 if Length(P
) < 4 then
7180 g_Console_Add(cmd
+ ' <listen IP> <port> <WAD> [MAP] [# players]');
7183 if not StrToIp(P
[1], listen
) then
7185 prt
:= StrToIntDef(P
[2], 25666);
7187 s
:= addWadExtension(P
[3]);
7188 found
:= e_FindResource(AllMapDirs
, s
);
7192 // get first map in wad, if not specified
7193 if Length(P
) < 5 then
7196 P
[4] := g_Game_GetFirstMap(P
[1]);
7198 s
:= P
[3] + ':\' + UpperCase(P
[4]);
7199 if g_Map_Exist(s
) then
7203 with gGameSettings
do
7205 Options
:= gsGameFlags
;
7206 GameMode
:= g_Game_TextToMode(gsGameMode
);
7207 if gSwitchGameMode
<> GM_NONE
then GameMode
:= gSwitchGameMode
;
7208 if GameMode
= GM_NONE
then GameMode
:= GM_DM
;
7209 if GameMode
= GM_SINGLE
then GameMode
:= GM_COOP
;
7211 if Length(P
) >= 6 then
7212 b
:= StrToIntDef(P
[5], 0);
7213 g_Game_StartServer(s
, GameMode
, TimeLimit
, ScoreLimit
, MaxLives
, Options
, b
, listen
, prt
)
7219 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[3]]))
7221 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [UpperCase(P
[4]), P
[3]]))
7226 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[3]]))
7229 else if cmd
= 'map' then
7231 if Length(P
) = 1 then
7233 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7235 g_Console_Add(cmd
+ ' <MAP>');
7236 g_Console_Add(cmd
+ ' <WAD> [MAP]')
7240 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7243 else if not e_IsValidResourceName(P
[1]) then
7245 g_Console_Add('wad name must not be absolute or relative');
7249 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7251 if Length(P
) < 3 then
7253 // first param is map or wad
7254 s
:= UpperCase(P
[1]);
7255 if g_Map_Exist(gGameSettings
.WAD
+ ':\' + s
) then
7257 gExitByTrigger
:= False;
7260 // already in game, finish current map
7262 gExit
:= EXIT_ENDLEVELCUSTOM
;
7266 // intermission, so change map immediately
7273 found
:= e_FindResource(AllMapDirs
, s
);
7275 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [s
, 'WAD ' + P
[1]]));
7278 // no such map, found wad
7281 P
[1] := ExpandFileName(pw
);
7282 P
[2] := g_Game_GetFirstMap(P
[1]);
7283 s
:= P
[1] + ':\' + P
[2];
7284 if g_Map_Exist(s
) then
7286 gExitByTrigger
:= False;
7289 // already in game, finish current map
7291 gExit
:= EXIT_ENDLEVELCUSTOM
7295 // intermission, so change map immediately
7302 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[1]]))
7304 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7309 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7315 s
:= addWadExtension(P
[1]);
7316 found
:= e_FindResource(AllMapDirs
, s
);
7320 P
[2] := UpperCase(P
[2]);
7321 s
:= P
[1] + ':\' + P
[2];
7322 if g_Map_Exist(s
) then
7324 gExitByTrigger
:= False;
7328 gExit
:= EXIT_ENDLEVELCUSTOM
7337 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7342 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7348 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7352 else if cmd
= 'nextmap' then
7354 if not(gGameOn
or (gState
= STATE_INTERCUSTOM
)) then
7356 g_Console_Add(_lc
[I_MSG_NOT_GAME
])
7361 if Length(P
) = 1 then
7363 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7365 g_Console_Add(cmd
+ ' <MAP>');
7366 g_Console_Add(cmd
+ ' <WAD> [MAP]');
7371 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
]);
7374 else if not e_IsValidResourceName(P
[1]) then
7376 g_Console_Add('wad name must not be absolute or relative');
7381 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7383 if Length(P
) < 3 then
7385 // first param is map or wad
7386 s
:= UpperCase(P
[1]);
7387 if g_Map_Exist(gGameSettings
.WAD
+ ':\' + s
) then
7390 gExitByTrigger
:= False;
7396 // no such map, found wad
7397 pw
:= addWadExtension(P
[1]);
7398 found
:= e_FindResource(MapDirs
, pw
);
7400 found
:= e_FindResource(WadDirs
, pw
);
7402 g_Console_Add(Format(_lc
[I_MSG_NO_MAP_FALLBACK
], [s
, P
[1]]));
7405 // map not specified, select first map
7407 P
[2] := g_Game_GetFirstMap(P
[1]);
7408 s
:= P
[1] + ':\' + P
[2];
7409 if g_Map_Exist(s
) then
7411 gExitByTrigger
:= False;
7418 g_Console_Add(Format(_lc
[I_MSG_NO_MAPS
], [P
[1]]))
7420 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7425 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7431 // specified two params wad + map
7432 pw
:= addWadExtension(P
[1]);
7433 found
:= e_FindResource(MapDirs
, pw
);
7435 found
:= e_FindResource(MapDirs
, pw
);
7439 P
[2] := UpperCase(P
[2]);
7440 s
:= P
[1] + ':\' + P
[2];
7441 if g_Map_Exist(s
) then
7443 gExitByTrigger
:= False;
7449 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [P
[2]]))
7454 g_Console_Add(Format(_lc
[I_MSG_NO_WAD
], [P
[1]]))
7460 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7465 if gNextMap
= '' then
7466 g_Console_Add(_lc
[I_MSG_NEXTMAP_UNSET
])
7468 g_Console_Add(Format(_lc
[I_MSG_NEXTMAP_SET
], [gNextMap
]))
7472 else if (cmd
= 'endmap') or (cmd
= 'goodbye') then
7476 g_Console_Add(_lc
[I_MSG_NOT_GAME
])
7480 if g_Game_IsServer
and (gGameSettings
.GameType
<> GT_SINGLE
) then
7482 gExitByTrigger
:= False;
7483 // next map not specified, try to find trigger EXIT
7484 if (gNextMap
= '') and (gTriggers
<> nil) then
7486 for a
:= 0 to High(gTriggers
) do
7488 if gTriggers
[a
].TriggerType
= TRIGGER_EXIT
then
7490 gExitByTrigger
:= True;
7491 //gNextMap := gTriggers[a].Data.MapName;
7492 gNextMap
:= gTriggers
[a
].tgcMap
;
7497 if gNextMap
= '' then
7498 gNextMap
:= g_Game_GetNextMap();
7499 if not isWadPath(gNextMap
) then
7500 s
:= gGameSettings
.WAD
+ ':\' + gNextMap
7503 if g_Map_Exist(s
) then
7504 gExit
:= EXIT_ENDLEVELCUSTOM
7506 g_Console_Add(Format(_lc
[I_MSG_NO_MAP
], [gNextMap
]))
7510 g_Console_Add(_lc
[I_MSG_GM_UNAVAIL
])
7514 else if (cmd
= 'event') then
7516 if (Length(P
) <= 1) then
7518 for a
:= 0 to High(gEvents
) do
7519 if gEvents
[a
].Command
= '' then
7520 g_Console_Add(gEvents
[a
].Name
+ ' <none>')
7522 g_Console_Add(gEvents
[a
].Name
+ ' "' + gEvents
[a
].Command
+ '"');
7525 if (Length(P
) = 2) then
7527 for a
:= 0 to High(gEvents
) do
7528 if gEvents
[a
].Name
= P
[1] then
7529 if gEvents
[a
].Command
= '' then
7530 g_Console_Add(gEvents
[a
].Name
+ ' <none>')
7532 g_Console_Add(gEvents
[a
].Name
+ ' "' + gEvents
[a
].Command
+ '"');
7535 for a
:= 0 to High(gEvents
) do
7536 if gEvents
[a
].Name
= P
[1] then
7538 gEvents
[a
].Command
:= '';
7539 for b
:= 2 to High(P
) do
7540 if Pos(' ', P
[b
]) = 0 then
7541 gEvents
[a
].Command
:= gEvents
[a
].Command
+ ' ' + P
[b
]
7543 gEvents
[a
].Command
:= gEvents
[a
].Command
+ ' "' + P
[b
] + '"';
7544 gEvents
[a
].Command
:= Trim(gEvents
[a
].Command
);
7548 else if cmd
= 'suicide' then
7552 if g_Game_IsClient
then
7553 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE
)
7556 if gPlayer1
<> nil then
7557 gPlayer1
.Damage(SUICIDE_DAMAGE
, gPlayer1
.UID
, 0, 0, HIT_SELF
);
7558 if gPlayer2
<> nil then
7559 gPlayer2
.Damage(SUICIDE_DAMAGE
, gPlayer2
.UID
, 0, 0, HIT_SELF
);
7563 else if cmd
= 'screenshot' then
7567 else if (cmd
= 'weapnext') or (cmd
= 'weapprev') then
7569 a
:= 1 - (ord(cmd
[5]) - ord('n'));
7571 gWeaponAction
[0, WP_PREV
] := True;
7573 gWeaponAction
[0, WP_NEXT
] := True;
7575 else if cmd
= 'weapon' then
7577 if Length(p
) = 2 then
7579 a
:= WP_FIRST
+ StrToIntDef(p
[1], 0) - 1;
7580 if (a
>= WP_FIRST
) and (a
<= WP_LAST
) then
7581 gSelectWeapon
[0, a
] := True
7584 else if (cmd
= 'p1_weapnext') or (cmd
= 'p1_weapprev')
7585 or (cmd
= 'p2_weapnext') or (cmd
= 'p2_weapprev') then
7587 a
:= 1 - (ord(cmd
[8]) - ord('n'));
7588 b
:= ord(cmd
[2]) - ord('1');
7590 gWeaponAction
[b
, WP_PREV
] := True;
7592 gWeaponAction
[b
, WP_NEXT
] := True;
7594 else if (cmd
= 'p1_weapon') or (cmd
= 'p2_weapon') then
7596 if Length(p
) = 2 then
7598 a
:= WP_FIRST
+ StrToIntDef(p
[1], 0) - 1;
7599 b
:= ord(cmd
[2]) - ord('1');
7600 if (a
>= WP_FIRST
) and (a
<= WP_LAST
) then
7601 gSelectWeapon
[b
, a
] := True
7604 else if (cmd
= 'p1_weapbest') or (cmd
= 'p2_weapbest') then
7606 b
:= ord(cmd
[2]) - ord('1');
7608 gSelectWeapon
[b
, gPlayer1
.GetMorePrefered()] := True
7610 gSelectWeapon
[b
, gPlayer2
.GetMorePrefered()] := True;
7612 else if (cmd
= 'dropflag') then
7614 if g_Game_IsServer
then
7616 if gPlayer2
<> nil then gPlayer2
.TryDropFlag();
7617 if gPlayer1
<> nil then gPlayer1
.TryDropFlag();
7620 MC_SEND_CheatRequest(NET_CHEAT_DROPFLAG
);
7622 else if (cmd
= 'p1_dropflag') or (cmd
= 'p2_dropflag') then
7624 b
:= ord(cmd
[2]) - ord('1');
7625 if g_Game_IsServer
then
7627 if (b
= 1) and (gPlayer2
<> nil) then gPlayer2
.TryDropFlag()
7628 else if (b
= 0) and (gPlayer1
<> nil) then gPlayer1
.TryDropFlag();
7631 MC_SEND_CheatRequest(NET_CHEAT_DROPFLAG
);
7633 // Êîìàíäû Ñâîåé èãðû:
7634 else if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
] then
7636 if cmd
= 'bot_addred' then
7638 if Length(P
) > 1 then
7639 g_Bot_Add(TEAM_RED
, StrToIntDef(P
[1], 2))
7641 g_Bot_Add(TEAM_RED
, 2);
7643 else if cmd
= 'bot_addblue' then
7645 if Length(P
) > 1 then
7646 g_Bot_Add(TEAM_BLUE
, StrToIntDef(P
[1], 2))
7648 g_Bot_Add(TEAM_BLUE
, 2);
7650 else if cmd
= 'spectate' then
7656 else if cmd
= 'say' then
7658 if g_Game_IsServer
and g_Game_IsNet
then
7660 if Length(P
) > 1 then
7663 for a
:= 1 to High(P
) do
7664 chstr
:= chstr
+ P
[a
] + ' ';
7666 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7668 if Length(chstr
) < 1 then
7670 g_Console_Add('say <text>');
7674 chstr
:= b_Text_Format(chstr
);
7675 MH_SEND_Chat(chstr
, NET_CHAT_PLAYER
);
7677 else g_Console_Add('say <text>');
7679 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
7681 else if cmd
= 'tell' then
7683 if g_Game_IsServer
and g_Game_IsNet
then
7685 if (Length(P
) > 2) and (P
[1] <> '') then
7688 for a
:= 2 to High(P
) do
7689 chstr
:= chstr
+ P
[a
] + ' ';
7691 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7693 if Length(chstr
) < 1 then
7695 g_Console_Add('tell <playername> <text>');
7699 pl
:= g_Net_Client_ByName(P
[1]);
7701 MH_SEND_Chat(b_Text_Format(chstr
), NET_CHAT_PLAYER
, pl
^.ID
)
7703 g_Console_Add(Format(_lc
[I_NET_ERR_NAME404
], [P
[1]]));
7705 else g_Console_Add('tell <playername> <text>');
7707 g_Console_Add(_lc
[I_MSG_SERVERONLY
]);
7709 else if cmd
= 'centerprint' then
7711 if (Length(P
) > 2) and (P
[1] <> '') then
7714 for a
:= 2 to High(P
) do
7715 chstr
:= chstr
+ P
[a
] + ' ';
7717 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7719 if Length(chstr
) < 1 then
7721 g_Console_Add('centerprint <timeout> <text>');
7725 a
:= StrToIntDef(P
[1], 100);
7726 chstr
:= b_Text_Format(chstr
);
7727 g_Game_Message(chstr
, a
);
7728 if g_Game_IsNet
and g_Game_IsServer
then
7729 MH_SEND_GameEvent(NET_EV_BIGTEXT
, a
, chstr
);
7731 else g_Console_Add('centerprint <timeout> <text>');
7733 else if (cmd
= 'overtime') and not g_Game_IsClient
then
7735 if (Length(P
) = 1) or (StrToIntDef(P
[1], -1) <= 0) then
7737 // Äîïîëíèòåëüíîå âðåìÿ:
7738 gGameSettings
.TimeLimit
:= (gTime
- gGameStartTime
) div 1000 + Word(StrToIntDef(P
[1], 0));
7740 g_Console_Add(Format(_lc
[I_MSG_TIME_LIMIT
],
7741 [gGameSettings
.TimeLimit
div 3600,
7742 (gGameSettings
.TimeLimit
div 60) mod 60,
7743 gGameSettings
.TimeLimit
mod 60]));
7744 if g_Game_IsNet
then MH_SEND_GameSettings
;
7746 else if (cmd
= 'rcon_password') and g_Game_IsClient
then
7748 if (Length(P
) <= 1) then
7749 g_Console_Add('rcon_password <password>')
7751 MC_SEND_RCONPassword(P
[1]);
7753 else if cmd
= 'rcon' then
7755 if g_Game_IsClient
then
7757 if Length(P
) > 1 then
7760 for a
:= 1 to High(P
) do
7761 chstr
:= chstr
+ P
[a
] + ' ';
7763 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7765 if Length(chstr
) < 1 then
7767 g_Console_Add('rcon <command>');
7771 MC_SEND_RCONCommand(chstr
);
7773 else g_Console_Add('rcon <command>');
7776 else if cmd
= 'ready' then
7778 if g_Game_IsServer
and (gLMSRespawn
= LMS_RESPAWN_WARMUP
) then
7779 gLMSRespawnTime
:= gTime
+ 100;
7781 else if (cmd
= 'callvote') and g_Game_IsNet
then
7783 if Length(P
) > 1 then
7786 for a
:= 1 to High(P
) do begin
7787 if a
> 1 then chstr
:= chstr
+ ' ';
7788 chstr
:= chstr
+ P
[a
];
7791 if Length(chstr
) > 200 then SetLength(chstr
, 200);
7793 if Length(chstr
) < 1 then
7795 g_Console_Add('callvote <command>');
7799 if g_Game_IsClient
then
7800 MC_SEND_Vote(True, chstr
)
7802 g_Game_StartVote(chstr
, gPlayer1Settings
.Name
);
7803 g_Console_Process('vote', True);
7806 g_Console_Add('callvote <command>');
7808 else if (cmd
= 'vote') and g_Game_IsNet
then
7810 if g_Game_IsClient
then
7812 else if gVoteInProgress
then
7814 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
7815 a
:= Floor((NetClientCount
+1)/2.0) + 1
7817 a
:= Floor(NetClientCount
/2.0) + 1;
7822 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_REVOKED
], [gPlayer1Settings
.Name
, gVoteCount
, a
]), True);
7823 MH_SEND_VoteEvent(NET_VE_REVOKE
, gPlayer1Settings
.Name
, 'a', gVoteCount
, a
);
7829 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_VOTE
], [gPlayer1Settings
.Name
, gVoteCount
, a
]), True);
7830 MH_SEND_VoteEvent(NET_VE_VOTE
, gPlayer1Settings
.Name
, 'a', gVoteCount
, a
);
7838 procedure SystemCommands(P
: SSArray
);
7842 cmd
:= LowerCase(P
[0]);
7851 gRC_Width
:= Max(1, gRC_Width
);
7852 gRC_Height
:= Max(1, gRC_Height
);
7853 gBPP
:= Max(1, gBPP
);
7854 if sys_SetDisplayMode(gRC_Width
, gRC_Height
, gBPP
, gRC_FullScreen
, gRC_Maximized
) = True then
7855 e_LogWriteln('resolution changed')
7857 e_LogWriteln('resolution not changed');
7858 sys_EnableVSync(gVSync
);
7862 if Length(p
) = 2 then
7864 gMaxFPS
:= StrToIntDef(p
[1], gMaxFPS
);
7866 gFrameTime
:= 1000 div gMaxFPS
7870 e_LogWritefln('r_maxfps %d', [gMaxFPS
]);
7874 if Length(p
) = 2 then
7876 gAskLanguage
:= true;
7877 gLanguage
:= LANGUAGE_ENGLISH
;
7878 case LowerCase(p
[1]) of
7881 gAskLanguage
:= false;
7882 gLanguage
:= LANGUAGE_ENGLISH
;
7886 gAskLanguage
:= false;
7887 gLanguage
:= LANGUAGE_RUSSIAN
;
7891 gAskLanguage
:= true;
7892 gLanguage
:= LANGUAGE_ENGLISH
;
7895 g_Language_Set(gLanguage
);
7899 e_LogWritefln('usage: %s <English|Russian|Ask>', [cmd
]);
7905 procedure g_TakeScreenShot(Filename
: string = '');
7906 var s
: TStream
; t
: TDateTime
; dir
, date
, name
: String;
7908 if e_NoGraphics
then Exit
;
7910 dir
:= e_GetWriteableDir(ScreenshotDirs
);
7912 if Filename
= '' then
7915 DateTimeToString(date
, 'yyyy-mm-dd-hh-nn-ss', t
);
7916 Filename
:= 'screenshot-' + date
;
7919 name
:= e_CatPath(dir
, Filename
+ '.png');
7920 s
:= createDiskFile(name
);
7922 e_MakeScreenshot(s
, gWinSizeX
, gWinSizeY
);
7924 g_Console_Add(Format(_lc
[I_CONSOLE_SCREENSHOT
], [name
]))
7926 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_WRITE
], [name
]));
7931 g_Console_Add('oh shit, i can''t create screenshot!')
7935 procedure g_Game_InGameMenu(Show
: Boolean);
7937 if (g_ActiveWindow
= nil) and Show
then
7939 if gGameSettings
.GameType
= GT_SINGLE
then
7940 g_GUI_ShowWindow('GameSingleMenu')
7943 if g_Game_IsClient
then
7944 g_GUI_ShowWindow('GameClientMenu')
7946 if g_Game_IsNet
then
7947 g_GUI_ShowWindow('GameServerMenu')
7949 g_GUI_ShowWindow('GameCustomMenu');
7951 g_Sound_PlayEx('MENU_OPEN');
7953 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7954 if (not g_Game_IsNet
) then
7958 if (g_ActiveWindow
<> nil) and (not Show
) then
7960 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
7961 if (not g_Game_IsNet
) then
7962 g_Game_Pause(False);
7966 procedure g_Game_Pause (Enable
: Boolean);
7970 if not gGameOn
then exit
;
7972 if not (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
]) then exit
;
7975 gPauseMain
:= Enable
;
7977 if (gPause
<> oldPause
) then g_Game_PauseAllSounds(gPause
);
7980 procedure g_Game_HolmesPause (Enable
: Boolean);
7984 if not gGameOn
then exit
;
7985 if not (gGameSettings
.GameType
in [GT_SINGLE
, GT_CUSTOM
]) then exit
;
7988 gPauseHolmes
:= Enable
;
7990 if (gPause
<> oldPause
) then g_Game_PauseAllSounds(gPause
);
7993 procedure g_Game_PauseAllSounds(Enable
: Boolean);
7998 if gTriggers
<> nil then
7999 for i
:= 0 to High(gTriggers
) do
8000 with gTriggers
[i
] do
8001 if (TriggerType
= TRIGGER_SOUND
) and
8003 Sound
.IsPlaying() then
8005 Sound
.Pause(Enable
);
8009 if gPlayers
<> nil then
8010 for i
:= 0 to High(gPlayers
) do
8011 if gPlayers
[i
] <> nil then
8012 gPlayers
[i
].PauseSounds(Enable
);
8015 if gMusic
<> nil then
8016 gMusic
.Pause(Enable
);
8019 procedure g_Game_StopAllSounds(all
: Boolean);
8023 if gTriggers
<> nil then
8024 for i
:= 0 to High(gTriggers
) do
8025 with gTriggers
[i
] do
8026 if (TriggerType
= TRIGGER_SOUND
) and
8030 if gMusic
<> nil then
8037 procedure g_Game_UpdateTriggerSounds
;
8040 if gTriggers
<> nil then
8041 for i
:= 0 to High(gTriggers
) do
8042 with gTriggers
[i
] do
8043 if (TriggerType
= TRIGGER_SOUND
) and (Sound
<> nil) and tgcLocal
and Sound
.IsPlaying() then
8044 Sound
.SetCoordsRect(X
, Y
, Width
, Height
, tgcVolume
/ 255.0)
8047 function g_Game_IsWatchedPlayer(UID
: Word): Boolean;
8050 if (gPlayer1
<> nil) and (gPlayer1
.UID
= UID
) then
8055 if (gPlayer2
<> nil) and (gPlayer2
.UID
= UID
) then
8060 if gSpectMode
<> SPECT_PLAYERS
then
8062 if gSpectPID1
= UID
then
8067 if gSpectViewTwo
and (gSpectPID2
= UID
) then
8074 function g_Game_IsWatchedTeam(Team
: Byte): Boolean;
8079 if (gPlayer1
<> nil) and (gPlayer1
.Team
= Team
) then
8084 if (gPlayer2
<> nil) and (gPlayer2
.Team
= Team
) then
8089 if gSpectMode
<> SPECT_PLAYERS
then
8091 Pl
:= g_Player_Get(gSpectPID1
);
8092 if (Pl
<> nil) and (Pl
.Team
= Team
) then
8097 if gSpectViewTwo
then
8099 Pl
:= g_Player_Get(gSpectPID2
);
8100 if (Pl
<> nil) and (Pl
.Team
= Team
) then
8108 procedure g_Game_Message(Msg
: string; Time
: Word);
8110 MessageLineLength
:= (gScreenWidth
- 204) div e_CharFont_GetMaxWidth(gMenuFont
);
8111 MessageText
:= b_Text_Wrap(b_Text_Format(Msg
), MessageLineLength
);
8112 MessageTime
:= Time
;
8115 procedure g_Game_ChatSound(Text: String; Taunt
: Boolean = True);
8117 punct
: Array[0..13] of String =
8118 ('.', ',', ':', ';', '!', '?', '(', ')', '''', '"', '/', '\', '*', '^');
8124 function IsPunctuation(S
: String): Boolean;
8129 if Length(S
) <> 1 then
8131 for i
:= Low(punct
) to High(punct
) do
8132 if S
= punct
[i
] then
8138 function FilterPunctuation(S
: String): String;
8142 for i
:= Low(punct
) to High(punct
) do
8143 S
:= StringReplace(S
, punct
[i
], ' ', [rfReplaceAll
]);
8149 if gUseChatSounds
and Taunt
and (gChatSounds
<> nil) and (Pos(': ', Text) > 0) then
8151 // remove player name
8152 Delete(Text, 1, Pos(': ', Text) + 2 - 1);
8153 // for FullWord check
8154 Text := toLowerCase1251(' ' + Text + ' ');
8155 fpText
:= FilterPunctuation(Text);
8157 for i
:= 0 to Length(gChatSounds
) - 1 do
8160 for j
:= 0 to Length(gChatSounds
[i
].Tags
) - 1 do
8162 if gChatSounds
[i
].FullWord
and (not IsPunctuation(gChatSounds
[i
].Tags
[j
])) then
8163 ok
:= Pos(' ' + gChatSounds
[i
].Tags
[j
] + ' ', fpText
) > 0
8165 ok
:= Pos(gChatSounds
[i
].Tags
[j
], Text) > 0;
8171 gChatSounds
[i
].Sound
.Play();
8177 g_Sound_PlayEx('SOUND_GAME_RADIO');
8180 procedure g_Game_Announce_GoodShot(SpawnerUID
: Word);
8189 if not g_Game_IsWatchedPlayer(SpawnerUID
) then
8193 if goodsnd
[a
].IsPlaying() then
8196 goodsnd
[Random(4)].Play();
8199 procedure g_Game_Announce_KillCombo(Param
: Integer);
8206 UID
:= Param
and $FFFF;
8211 Pl
:= g_Player_Get(UID
);
8220 g_Console_Add(Format(_lc
[I_PLAYER_KILL_2X
], [Name
]), True);
8224 g_Console_Add(Format(_lc
[I_PLAYER_KILL_3X
], [Name
]), True);
8228 g_Console_Add(Format(_lc
[I_PLAYER_KILL_4X
], [Name
]), True);
8232 g_Console_Add(Format(_lc
[I_PLAYER_KILL_MX
], [Name
]), True);
8240 if not g_Game_IsWatchedPlayer(UID
) then
8243 if (not g_Game_IsWatchedPlayer(UID
)) and (c
< 4) then
8247 if killsnd
[n
].IsPlaying() then
8252 procedure g_Game_Announce_BodyKill(SpawnerUID
: Word);
8260 if not g_Game_IsWatchedPlayer(SpawnerUID
) then
8264 if hahasnd
[a
].IsPlaying() then
8267 hahasnd
[Random(3)].Play();
8270 procedure g_Game_Effect_Bubbles (fX
, fY
: Integer; count
: Word; devX
, devY
: Byte; Silent
: Boolean);
8272 g_GFX_Bubbles(fX
, fY
, count
, devX
, devY
);
8273 if not Silent
then if Random(2) = 0
8274 then g_Sound_PlayExAt('SOUND_GAME_BUBBLE1', fX
, fY
)
8275 else g_Sound_PlayExAt('SOUND_GAME_BUBBLE2', fX
, fY
);
8278 procedure g_Game_StartVote(Command
, Initiator
: string);
8282 if not gVotesEnabled
then Exit
;
8283 if gGameSettings
.GameType
<> GT_SERVER
then Exit
;
8284 if gVoteInProgress
or gVotePassed
then
8286 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_INPROGRESS
], [gVoteCommand
]), True);
8287 MH_SEND_VoteEvent(NET_VE_INPROGRESS
, gVoteCommand
);
8290 gVoteInProgress
:= True;
8291 gVotePassed
:= False;
8292 gVoteTimer
:= gTime
+ gVoteTimeout
* 1000;
8295 gVoteCommand
:= Command
;
8297 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
8298 Need
:= Floor((NetClientCount
+1)/2.0)+1
8300 Need
:= Floor(NetClientCount
/2.0)+1;
8301 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_STARTED
], [Initiator
, Command
, Need
]), True);
8302 MH_SEND_VoteEvent(NET_VE_STARTED
, Initiator
, Command
, Need
);
8305 procedure g_Game_CheckVote
;
8309 if gGameSettings
.GameType
<> GT_SERVER
then Exit
;
8310 if not gVoteInProgress
then Exit
;
8312 if (gTime
>= gVoteTimer
) then
8314 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
8315 Need
:= Floor((NetClientCount
+1)/2.0) + 1
8317 Need
:= Floor(NetClientCount
/2.0) + 1;
8318 if gVoteCount
>= Need
then
8320 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_PASSED
], [gVoteCommand
]), True);
8321 MH_SEND_VoteEvent(NET_VE_PASSED
, gVoteCommand
);
8322 gVotePassed
:= True;
8323 gVoteCmdTimer
:= gTime
+ 5000;
8327 g_Console_Add(_lc
[I_MESSAGE_VOTE_FAILED
], True);
8328 MH_SEND_VoteEvent(NET_VE_FAILED
);
8330 if NetClients
<> nil then
8331 for I
:= Low(NetClients
) to High(NetClients
) do
8332 if NetClients
[i
].Used
then
8333 NetClients
[i
].Voted
:= False;
8334 gVoteInProgress
:= False;
8340 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
8341 Need
:= Floor((NetClientCount
+1)/2.0) + 1
8343 Need
:= Floor(NetClientCount
/2.0) + 1;
8344 if gVoteCount
>= Need
then
8346 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_PASSED
], [gVoteCommand
]), True);
8347 MH_SEND_VoteEvent(NET_VE_PASSED
, gVoteCommand
);
8348 gVoteInProgress
:= False;
8349 gVotePassed
:= True;
8350 gVoteCmdTimer
:= gTime
+ 5000;
8353 if NetClients
<> nil then
8354 for I
:= Low(NetClients
) to High(NetClients
) do
8355 if NetClients
[i
].Used
then
8356 NetClients
[i
].Voted
:= False;
8361 procedure g_Game_LoadMapList(FileName
: string);
8369 if not FileExists(FileName
) then Exit
;
8371 AssignFile(ListFile
, FileName
);
8373 while not EOF(ListFile
) do
8375 ReadLn(ListFile
, s
);
8378 if s
= '' then Continue
;
8380 SetLength(MapList
, Length(MapList
)+1);
8381 MapList
[High(MapList
)] := s
;
8383 CloseFile(ListFile
);
8386 procedure g_Game_SetDebugMode();
8389 // ×èòû (äàæå â ñâîåé èãðå):
8393 procedure g_Game_SetLoadingText(Text: String; Max
: Integer; reWrite
: Boolean);
8397 if Length(LoadingStat
.Msgs
) = 0 then
8403 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
8404 if NextMsg
= Length(Msgs
) then
8406 for i
:= 0 to High(Msgs
)-1 do
8407 Msgs
[i
] := Msgs
[i
+1];
8415 Msgs
[NextMsg
-1] := Text;
8419 PBarWasHere
:= false;
8422 g_ActiveWindow
:= nil;
8423 ProcessLoading(True);
8426 procedure g_Game_StepLoading(Value
: Integer = -1);
8438 if (ShowCount
> LOADING_SHOW_STEP
) or (Value
> -1) then
8441 ProcessLoading(False);
8446 procedure g_Game_ClearLoading();
8455 len
:= ((gScreenHeight
div 3)*2 - 50) div LOADING_INTERLINE
;
8456 if len
< 1 then len
:= 1;
8457 SetLength(Msgs
, len
);
8458 for len
:= Low(Msgs
) to High(Msgs
) do
8461 PBarWasHere
:= false;
8465 procedure Parse_Params(var pars
: TParamStrValues
);
8472 while i
<= ParamCount
do
8475 if (Length(s
) > 1) and (s
[1] = '-') then
8477 if (Length(s
) > 2) and (s
[2] = '-') then
8478 begin // Îäèíî÷íûé ïàðàìåòð
8479 SetLength(pars
, Length(pars
) + 1);
8480 with pars
[High(pars
)] do
8482 Name
:= LowerCase(s
);
8487 if (i
< ParamCount
) then
8488 begin // Ïàðàìåòð ñî çíà÷åíèåì
8490 SetLength(pars
, Length(pars
) + 1);
8491 with pars
[High(pars
)] do
8493 Name
:= LowerCase(s
);
8494 Value
:= LowerCase(ParamStr(i
));
8503 function Find_Param_Value(var pars
: TParamStrValues
; aName
: String): String;
8508 for i
:= 0 to High(pars
) do
8509 if pars
[i
].Name
= aName
then
8511 Result
:= pars
[i
].Value
;
8516 procedure g_Game_Process_Params();
8518 pars
: TParamStrValues
;
8521 LimT
, LimS
: Integer;
8532 s
:= Find_Param_Value(pars
, '--debug');
8535 g_Game_SetDebugMode();
8536 s
:= Find_Param_Value(pars
, '--netdump');
8541 // Connect when game loads
8542 ip
:= Find_Param_Value(pars
, '-connect');
8546 s
:= Find_Param_Value(pars
, '-port');
8547 if (s
= '') or not TryStrToInt(s
, Port
) then
8550 s
:= Find_Param_Value(pars
, '-pw');
8552 g_Game_StartClient(ip
, Port
, s
);
8556 s
:= LowerCase(Find_Param_Value(pars
, '-dbg-mainwad'));
8559 gDefaultMegawadStart
:= s
;
8562 if (Find_Param_Value(pars
, '--dbg-mainwad-restore') <> '') or
8563 (Find_Param_Value(pars
, '--dbg-mainwad-default') <> '') then
8565 gDefaultMegawadStart
:= DF_Default_Megawad_Start
;
8568 // Start map when game loads:
8569 map
:= LowerCase(Find_Param_Value(pars
, '-map'));
8570 if isWadPath(map
) then
8573 s
:= Find_Param_Value(pars
, '-gm');
8574 GMode
:= g_Game_TextToMode(s
);
8575 if GMode
= GM_NONE
then GMode
:= GM_DM
;
8576 if GMode
= GM_SINGLE
then GMode
:= GM_COOP
;
8579 s
:= Find_Param_Value(pars
, '-limt');
8580 if (s
= '') or (not TryStrToInt(s
, LimT
)) then
8586 s
:= Find_Param_Value(pars
, '-lims');
8587 if (s
= '') or (not TryStrToInt(s
, LimS
)) then
8593 s
:= Find_Param_Value(pars
, '-lives');
8594 if (s
= '') or (not TryStrToInt(s
, Lives
)) then
8600 s
:= Find_Param_Value(pars
, '-opt');
8602 then Opt
:= gsGameFlags
8603 else Opt
:= TGameOptions(StrToIntDef(s
, 0));
8606 s
:= Find_Param_Value(pars
, '--close');
8610 // Override map to test:
8611 s
:= LowerCase(Find_Param_Value(pars
, '-testmap'));
8614 if e_IsValidResourceName(s
) then
8615 e_FindResource(AllMapDirs
, s
);
8616 gTestMap
:= ExpandFileName(s
);
8619 // Delete test map after play:
8620 s
:= Find_Param_Value(pars
, '--testdelete');
8623 //gMapToDelete := MapsDir + map;
8624 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType
.Fatal
);
8628 // Delete temporary WAD after play:
8629 s
:= Find_Param_Value(pars
, '--tempdelete');
8630 if (s
<> '') and (gTestMap
<> '') then
8632 gMapToDelete
:= gTestMap
;
8633 gTempDelete
:= True;
8636 // Number of players:
8637 s
:= Find_Param_Value(pars
, '-pl');
8639 n
:= DEFAULT_PLAYERS
8641 n
:= StrToIntDef(s
, DEFAULT_PLAYERS
);
8644 s
:= Find_Param_Value(pars
, '-port');
8645 if (s
= '') or not TryStrToInt(s
, Port
)
8646 then g_Game_StartCustom(map
, GMode
, LimT
, LimS
, Lives
, Opt
, n
)
8647 else g_Game_StartServer(map
, GMode
, LimT
, LimS
, Lives
, Opt
, n
, 0, Port
);
8650 // Execute script when game loads:
8651 s
:= Find_Param_Value(pars
, '-exec');
8654 // if not isWadPath(s) then
8655 // s := GameDir + '/' + s;
8660 if IOResult
<> 0 then
8662 e_WriteLog(Format(_lc
[I_SIMPLE_ERROR
], ['Failed to read file: ' + s
]), TMsgType
.Warning
);
8663 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_READ
], [s
]));
8667 e_WriteLog('Executing script: ' + s
, TMsgType
.Notify
);
8668 g_Console_Add(Format(_lc
[I_CONSOLE_EXEC
], [s
]));
8673 if IOResult
<> 0 then
8675 e_WriteLog(Format(_lc
[I_SIMPLE_ERROR
], ['Failed to read file: ' + s
]), TMsgType
.Warning
);
8676 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_READ
], [s
]));
8680 if Pos('#', s
) <> 1 then // script comment
8681 g_Console_Process(s
, True);
8692 conRegVar('pf_draw_frame', @g_profile_frame_draw
, 'draw frame rendering profiles', 'render profiles');
8693 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
8694 conRegVar('pf_coldet', @g_profile_collision
, 'draw collision detection profiles', 'coldet profiles');
8695 conRegVar('pf_los', @g_profile_los
, 'draw monster LOS profiles', 'monster LOS profiles');
8697 conRegVar('r_sq_draw', @gdbg_map_use_accel_render
, 'accelerated spatial queries in rendering', 'accelerated rendering');
8698 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet
, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
8699 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel
, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
8700 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace
, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
8702 conRegVar('pr_enabled', @gpart_dbg_enabled
, 'enable/disable particles', 'particles');
8703 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled
, 'enable/disable particle physics', 'particle physics');
8705 conRegVar('los_enabled', @gmon_dbg_los_enabled
, 'enable/disable monster LOS calculations', 'monster LOS', true);
8706 conRegVar('mon_think', @gmon_debug_think
, 'enable/disable monster thinking', 'monster thinking', true);
8708 {$IFDEF ENABLE_HOLMES}
8709 conRegVar('dbg_holmes', @g_holmes_enabled
, 'enable/disable Holmes', 'Holmes', true);
8712 conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds
, 'ignore level bounds', '', false);
8714 conRegVar('r_scale', @g_dbg_scale
, 0.01, 100.0, 'render scale', '', false);
8715 conRegVar('r_resolution_scale', @r_pixel_scale
, 0.01, 100.0, 'upscale factor', '', false);
8717 conRegVar('light_enabled', @gwin_k8_enable_light_experiments
, 'enable/disable dynamic lighting', 'lighting');
8718 conRegVar('light_player_halo', @g_playerLight
, 'enable/disable player halo', 'player light halo');
8720 conRegVar('r_smallmap_align_h', @r_smallmap_h
, 'halign: 0: left; 1: center; 2: right', 'horizontal aligning of small maps');
8721 conRegVar('r_smallmap_align_v', @r_smallmap_v
, 'valign: 0: top; 1: center; 2: bottom', 'vertial aligning of small maps');
8723 conRegVar('r_showfps', @gShowFPS
, 'draw fps counter', 'draw fps counter');
8724 conRegVar('r_showtime', @gShowTime
, 'show game time', 'show game time');
8725 conRegVar('r_showping', @gShowPing
, 'show ping', 'show ping');
8726 conRegVar('r_showscore', @gShowScore
, 'show score', 'show score');
8727 conRegVar('r_showkillmsg', @gShowKillMsg
, 'show kill log', 'show kill log');
8728 conRegVar('r_showlives', @gShowLives
, 'show lives', 'show lives');
8729 conRegVar('r_showspect', @gSpectHUD
, 'show spectator hud', 'show spectator hud');
8730 conRegVar('r_showstat', @gShowStat
, 'show stats', 'show stats');
8731 conRegVar('r_showpids', @gShowPIDs
, 'show PIDs', 'show PIDs');