NS/main/source/ui/UIHud.cpp

719 lines
18 KiB
C++
Raw Normal View History

#include "ui/UIHud.h"
#include "textrep/TRFactory.h"
#include "cl_dll/cl_util.h"
#include "cl_dll/hud.h"
#include "VGUI_Scheme.h"
#include "VGUI_Panel.h"
#include "mod/AvHClientVariables.h"
#include "VGUI_App.h"
#include <stdio.h>
//@linux support
#include "../util/LinuxSupport.h"
#ifdef _WIN32
#include "winsani_in.h"
#include <windows.h>
#include "winsani_out.h"
#endif
#pragma warning(push)
#pragma warning(disable: 311)
//#include <fmoddyn.h>
#include "winsani_in.h"
#include <fmoddyn.h>
#include <fmod_errors.h>
#include "winsani_out.h"
#pragma warning(pop)
using namespace vgui;
extern BitmapTGA* LoadTGA( const char* pImageName );
// Initialize hud from specified input file
UIHud::UIHud(const string& inFilename, UIFactory* inFactory) : CHud(), mFilename(inFilename), mManager(inFactory)
{
this->mFMOD = NULL;
// Set flag false so we load the first time
this->mInitted = false;
this->mSoundInitialized = false;
this->mMusicEnabled = false;
this->mMusicAllowed = false;
this->mCurrentSongStream = NULL;
this->mCurrentInternetStream = NULL;
this->mBytesInCurrentSong = 0;
this->mTimeSongEnded = -1;
this->mSongIsPlaying = false;
this->mRandomSecondsBetweenSongs = 0;
this->mCurrentVolume = 0;
this->mCurrentChannel = -1;
this->mInternetStreamChannel = -1;
// Init cursor variables
this->mArrowBitmap = NULL;
this->mArrowCursor = NULL;
if(TRFactory::ReadDescriptions(mFilename, this->mDescriptionList))
{
}
else
{
// TODO: Emit error
}
this->mSchemeManager = NULL;
}
UIHud::~UIHud(void)
{
this->ShutdownMusic();
}
bool UIHud::GetIsTimeForSong(float inCurrentTime) const
{
bool theTimeForNewSong = false;
if(this->mMusicEnabled && this->mMusicAllowed && !this->mSongIsPlaying)
{
float theTimeElapsedSinceSongStopped = inCurrentTime - this->mTimeSongEnded;
if((this->mTimeSongEnded == -1) || (theTimeElapsedSinceSongStopped > this->mRandomSecondsBetweenSongs && cl_musicdelay->value != -1.0f))
{
theTimeForNewSong = true;
}
}
return theTimeForNewSong;
}
UIManager& UIHud::GetManager(void)
{
return this->mManager;
}
const UIManager& UIHud::GetManager(void) const
{
return this->mManager;
}
string UIHud::GetTextHeader(void) const
{
return "Default header\r\n";
}
void UIHud::Init(void)
{
CHud::Init();
}
void UIHud::InitializeSound(void)
{
#ifdef _WIN32
char theFileName[2][_MAX_PATH]; // _MAX_PATH is evil
sprintf(theFileName[0], "%s/fmod.dll", getModDirectory());
sprintf(theFileName[1], "fmod.dll");
#else
char theFileName[2][PATH_MAX];
sprintf(theFileName[0], "%s/libfmod-3.75.so", getModDirectory());
sprintf(theFileName[1], "libfmod-3.75.so");
#endif
for (int i = 0; i < 2 && mFMOD == NULL; ++i)
{
mFMOD = FMOD_CreateInstance(theFileName[i]);
// Check sound version
if (mFMOD != NULL && mFMOD->FSOUND_GetVersion() == FMOD_VERSION)
{
// Init sound
if (mFMOD->FSOUND_Init(44100, 32, 0))
{
this->mSoundInitialized = true;
//this->PlayRandomSong();
}
else
{
char theErrorMessage[512];
sprintf(theErrorMessage, "say %s", FMOD_ErrorString(mFMOD->FSOUND_GetError()));
ClientCmd(theErrorMessage); // to fix
}
}
else
{
FMOD_FreeInstance(mFMOD);
mFMOD = NULL;
}
}
}
bool UIHud::InitializeScheme(const char* inName, Scheme* inScheme)
{
bool theSuccess = false;
// TODO: How to detect if invalid scheme handle?
SchemeHandle_t hPrimaryScheme = this->mSchemeManager->getSchemeHandle( inName );
{
int r, g, b, a;
// font
inScheme->setFont( Scheme::sf_primary1, this->mSchemeManager->getFont(hPrimaryScheme) );
// text color
this->mSchemeManager->getFgColor( hPrimaryScheme, r, g, b, a );
inScheme->setColor(Scheme::sc_primary1, r, g, b, a ); // sc_primary1 is non-transparent orange
// background color (transparent black)
this->mSchemeManager->getBgColor( hPrimaryScheme, r, g, b, a );
inScheme->setColor(Scheme::sc_primary3, r, g, b, a );
// armed foreground color
this->mSchemeManager->getFgArmedColor( hPrimaryScheme, r, g, b, a );
inScheme->setColor(Scheme::sc_secondary2, r, g, b, a );
// armed background color
this->mSchemeManager->getBgArmedColor( hPrimaryScheme, r, g, b, a );
inScheme->setColor(Scheme::sc_primary2, r, g, b, a );
//!! need to get this color from scheme file
// used for orange borders around buttons
this->mSchemeManager->getBorderColor( hPrimaryScheme, r, g, b, a );
inScheme->setColor(Scheme::sc_secondary1, r, g, b, a );
theSuccess = true;
}
return theSuccess;
}
void UIHud::LoadSchemes(void)
{
// Create scheme manager, we have screen dimensions by now
this->mSchemeManager = new CSchemeManager(ScreenWidth(), ScreenHeight());
Scheme* pScheme = App::getInstance()->getScheme();
this->InitializeScheme("PieMenuScheme", pScheme);
// this->InitializeScheme("TechButtonScheme", pScheme);
// this->InitializeScheme("ChainOfCommandScheme", pScheme);
// this->InitializeScheme("TitleFont", pScheme);
// this->InitializeScheme("Heading1", pScheme);
}
// @linux needs to be reinplementet for linux
bool UIHud::PickRandomSong(string& outRelativeSongName) const
{
#ifdef WIN32
const string kDelimiter("\\");
#else
const string kDelimiter("/");
#endif
StringList theSongList;
bool theFoundSong = false;
size_t theNumSongs;
// Find random song in directory
string thePath;
if(strcmp(cl_musicdir->string, ""))
{
thePath = cl_musicdir->string;
}
else
{
thePath = string(getModDirectory()) + kDelimiter + string(kMusicDirectory);
}
string theFileQualifier = thePath + kDelimiter +string("*.mp3");
#ifdef WIN32
WIN32_FIND_DATA theFindData;
HANDLE theFileHandle;
theFileHandle = FindFirstFile(theFileQualifier.c_str(), &theFindData);
if (theFileHandle != INVALID_HANDLE_VALUE)
{
do
{
// Remove title track from in-game music here so people updating don't have to remove the file.
if (string(theFindData.cFileName) != string("ns_titlescreen.mp3"))
{
theSongList.push_back(thePath + kDelimiter + string(theFindData.cFileName));
}
}
while(FindNextFile(theFileHandle, &theFindData));
FindClose(theFileHandle);
theFileHandle = INVALID_HANDLE_VALUE;
}
#else
string theFoundFilename;
FIND_DATA theFindData;
int theRC = FindFirstFile(theFileQualifier.c_str(), &theFindData);
if(theRC != -1)
{
do
{
theSongList.push_back(thePath + kDelimiter + string(theFindData.cFileName));
}
while(FindNextFile(0, &theFindData));
}
#endif
// Pick a random song in the list
theNumSongs = theSongList.size();
if(theNumSongs > 0)
{
int theSongOffset = gEngfuncs.pfnRandomLong( 0, (long)theNumSongs -1 );
outRelativeSongName = string(theSongList[theSongOffset]);
theFoundSong = true;
}
return theFoundSong;
}
void UIHud::ClearStream(FSOUND_STREAM*& ioStream)
{
// TODO: Check error codes to make sure we don't leak?
if (mSoundInitialized)
{
mFMOD->FSOUND_Stream_Stop(ioStream);
mFMOD->FSOUND_Stream_Close(ioStream);
}
ioStream = NULL;
}
bool UIHud::PlaySong(string& inSongName, int& ioVolume, bool inLooping, FSOUND_STREAM*& outStream, int& outChannel, int& outBytesInSong, float inTimeElapsed)
{
bool theSuccess = false;
// Replace '/' with '\\'
std::replace(inSongName.begin(), inSongName.end(), '/', '\\');
// Load song
char theSongName[255];
strcpy(theSongName, inSongName.c_str());
// Open
unsigned int thePlayMode = FSOUND_STEREO;//FSOUND_NORMAL;
thePlayMode |= (inLooping ? FSOUND_LOOP_NORMAL : FSOUND_LOOP_OFF);
if(!this->mSoundInitialized)
{
this->InitializeSound();
}
if (mSoundInitialized)
{
// If alt-tabbing away at exactly the wrong time, music initialization can fail, don't crash
//ASSERT(this->mSoundInitialized);
outStream = mFMOD->FSOUND_Stream_Open(theSongName, thePlayMode, /*FSOUND_NONBLOCKING*/ 0, 0);
if(outStream)
{
outChannel = mFMOD->FSOUND_Stream_Play(FSOUND_FREE, outStream);
if (outChannel >= 0)
{
// Set song progress
if(inTimeElapsed != -1)
{
int theOffsetInMS = inTimeElapsed*1000;
int theSongTimeInMS = mFMOD->FSOUND_Stream_GetLengthMs(outStream);
if((theOffsetInMS > theSongTimeInMS) && inLooping)
{
theOffsetInMS %= theSongTimeInMS;
}
mFMOD->FSOUND_Stream_SetTime(outStream, theOffsetInMS);
}
// Set volume to half way. TODO: This should use a real music volume variable
mFMOD->FSOUND_SetPan(outChannel, FSOUND_STEREOPAN);
ioVolume = min(max(0, ioVolume), 255);
mFMOD->FSOUND_SetVolume(outChannel, ioVolume);
outBytesInSong = mFMOD->FSOUND_Stream_GetLength(outStream);
theSuccess = true;
}
else
{
}
}
else
{
// TODO: Print out this error message somewhere
char theErrorMessage[512];
sprintf(theErrorMessage, "say %s", FMOD_ErrorString(mFMOD->FSOUND_GetError()));
}
}
return theSuccess;
}
bool UIHud::PlayInternetStream(const string& inStreamName, string& outError)
{
bool theSuccess = false;
// Initialize sound system
if(!this->mSoundInitialized)
{
this->InitializeSound();
}
// If initialized
if(this->mSoundInitialized)
{
// If steam is currently playing, clear/stop it
// Reset stream channel
if(this->mInternetStreamChannel != -1)
gHUD.StopInternetStream();
// If stream channel is reset
if(this->mInternetStreamChannel == -1)
{
// Set buffer size and properties
mFMOD->FSOUND_Stream_SetBufferSize(100);
mFMOD->FSOUND_Stream_Net_SetBufferProperties(64000, 60, 80);
// Open stream for playing
this->mCurrentInternetStream = mFMOD->FSOUND_Stream_Open(inStreamName.c_str(), FSOUND_NORMAL | FSOUND_NONBLOCKING, 0, 0);
if(!this->mCurrentInternetStream)
{
outError = FMOD_ErrorString(mFMOD->FSOUND_GetError());
}
else
{
theSuccess = true;
}
}
}
return theSuccess;
}
void UIHud::StopInternetStream()
{
this->ClearStream(this->mCurrentInternetStream);
this->mInternetStreamChannel = -1;
}
bool UIHud::UpdateInternetStream(float inCurrentTime, string& outError)
{
bool theSuccess = true;
// If we're playing a stream
if(this->mCurrentInternetStream)
{
if(this->mInternetStreamChannel < 0)
{
this->mInternetStreamChannel = mFMOD->FSOUND_Stream_PlayEx(FSOUND_FREE, this->mCurrentInternetStream, NULL, TRUE);
mFMOD->FSOUND_SetPaused(this->mInternetStreamChannel, FALSE);
//if (channel != -1)
//{
// FSOUND_Stream_Net_SetMetadataCallback(this->mCurrentInternetStream, metacallback, 0);
// }
}
// If we get an error, add it as a tooltip
int theOpenState = mFMOD->FSOUND_Stream_GetOpenState(this->mCurrentInternetStream);
if((theOpenState == -1) || (theOpenState == -3))
{
//printf("\nERROR: failed to open stream!\n");
//printf("SERVER: %s\n", );
//break;
outError = mFMOD->FSOUND_Stream_Net_GetLastServerStatus();
theSuccess = false;
gHUD.StopStream();
}
int read_percent = 0, driver = 0, channel = -1, status = 0, bitrate;
unsigned int flags;
mFMOD->FSOUND_Stream_Net_GetStatus(this->mCurrentInternetStream, &status, &read_percent, &bitrate, &flags);
}
return theSuccess;
}
void UIHud::PlayRandomSong(void)
{
// Pick random song first
string theRelativeSongName;
if(this->PickRandomSong(theRelativeSongName))
{
// Remove current song if any
if(this->mCurrentSongStream)
{
this->StopMusic();
}
// Load song
// char theSongName[255];
// strcpy(theSongName, theRelativeSongName.c_str());
// this->mCurrentSongStream = FSOUND_Stream_OpenMpeg(theSongName, FSOUND_NORMAL | FSOUND_LOOP_OFF);
// if(this->mCurrentSongStream)
// {
// // Play default song
// //FMUSIC_PlaySong(mod);
//
// this->mCurrentChannel = FSOUND_Stream_Play(FSOUND_FREE, this->mCurrentSongStream);
// if (this->mCurrentChannel < 0)
// {
// // MessageBox(hwnd, "Error. Cannot start song", "Playing a song", MB_ICONHAND|MB_OK|MB_SYSTEMMODAL);
// }
// else
// {
// // Set volume to half way. TODO: This should use a real music volume variable
// FSOUND_SetPan(this->mCurrentChannel, FSOUND_STEREOPAN);
//
//2023 - Making "volume" cvar a master volume. Multiplying by 155 as it was the original default value on the previous 0-255 cvar range.
int theVolume = min(max(0, cl_musicvolume->value * CVAR_GET_FLOAT("volume") * 155), 255);
// this->mBytesInCurrentSong = FSOUND_Stream_GetLength(this->mCurrentSongStream);
if(this->PlaySong(theRelativeSongName, theVolume, false, this->mCurrentSongStream, this->mCurrentChannel, this->mBytesInCurrentSong))
{
this->mCurrentVolume = theVolume;
int theDelay = (int)(cl_musicdelay->value);
this->mRandomSecondsBetweenSongs = gEngfuncs.pfnRandomLong(0, theDelay);
//this->mTimeSongEnded = -1.0f;
this->mSongIsPlaying = true;
}
// this->mCurrentVolume = (int)(cl_musicvolume->value);
// this->mCurrentVolume = min(max(0, this->mCurrentVolume), 255);
// cl_musicvolume->value = this->mCurrentVolume;
// FSOUND_SetVolume(this->mCurrentChannel, this->mCurrentVolume);
//
// this->mBytesInCurrentSong = FSOUND_Stream_GetLength(this->mCurrentSongStream);
// this->mRandomSecondsBetweenSongs = gEngfuncs.pfnRandomLong(5, 90);
// this->mTimeSongEnded = 0.0f;
// this->mSongIsPlaying = true;
// }
// }
// else
// {
// // TODO: Print out this error message somewhere
// char theErrorMessage[512];
// sprintf(theErrorMessage, "say %s", FMOD_ErrorString(FSOUND_GetError()));
// }
}
}
void UIHud::PostUIInit(void)
{
}
int UIHud::Redraw(float flTime, int intermission)
{
int rc = 0;
rc = CHud::Redraw(flTime, intermission);
// Now that the file has been parsed, pass it off to the UI manager to build everything
// This has to happen after CHud::VidInit because the components depend on the screen resolution to
// be sized properly
if(!this->mInitted)
{
this->LoadSchemes();
if(this->mManager.Initialize(this->mDescriptionList, this->mSchemeManager))
{
this->PostUIInit();
this->mInitted = true;
}
else
{
// TODO: Emit error
}
// Initialize music the first time through
if (CVAR_GET_FLOAT("cl_ambientsound") != 0 || CVAR_GET_FLOAT("cl_musicenabled") != 0)
this->InitializeSound();
}
string theErrorString;
this->Update(flTime, theErrorString);
return rc;
}
void UIHud::ResetGame()
{
this->mTimeSongEnded = -1;
this->mRandomSecondsBetweenSongs = 0;
}
void UIHud::SetMusicAllowed(bool inState)
{
this->mMusicAllowed = inState;
}
void UIHud::SetUsingVGUI(bool inState)
{
this->mManager.SetUsingVGUI(inState);
}
void UIHud::Shutdown(void)
{
this->ShutdownMusic();
}
void UIHud::ShutdownMusic(void)
{
if(this->mSoundInitialized)
{
this->StopMusic();
this->StopInternetStream();
////Commented out to fix the dreaded hang on exit! Let HL and the OS take care of it.
//mFMOD->FSOUND_Close();
//
//FMOD_FreeInstance(mFMOD);
mFMOD = NULL;
this->mSoundInitialized = false;
}
}
void UIHud::StopMusic()
{
if(this->mCurrentSongStream && this->mSoundInitialized)
{
this->ClearStream(this->mCurrentSongStream);
this->mSongIsPlaying = false;
//this->mMusicEnabled = false;
//this->mMusicAllowed = false;
}
}
void UIHud::Think(void)
{
CHud::Think();
}
void UIHud::ToggleEditMode(void)
{
// For now this also changes us into edit mode
if(this->mManager.InMouseMode())
{
if(this->mManager.ToggleEditMode())
{
// Now save this bad-boy out!
this->mManager.Save(this->mFilename, this->GetTextHeader());
}
}
}
void UIHud::ShowMouse(void)
{
// For now this also changes us into edit mode
if ( this->mManager.InMouseMode() == false )
this->mManager.ToggleMouse();
}
void UIHud::ToggleMouse(void)
{
// For now this also changes us into edit mode
this->mManager.ToggleMouse();
}
bool UIHud::Update(float inCurrentTime, string& outError)
{
this->mManager.Update(inCurrentTime);
bool theSuccess = true;
if (CVAR_GET_FLOAT("cl_ambientsound") != 0 || CVAR_GET_FLOAT("cl_musicenabled") != 0) {
this->UpdateMusic(inCurrentTime);
//bool theSuccess = this->UpdateInternetStream(inCurrentTime, outError);
theSuccess = this->UpdateInternetStream(inCurrentTime, outError);
}
return theSuccess;
}
int UIHud::UpdateClientData(client_data_t *cdata, float time)
{
return CHud::UpdateClientData(cdata, time);
}
void UIHud::UpdateMusic(float inCurrentTime)
{
bool theJustTurnedOnMusic = false;
// When song is done, wait a time, then pick another song
if(this->mSoundInitialized)
{
if(!this->mMusicEnabled && this->mMusicAllowed)
{
this->mMusicEnabled = true;
theJustTurnedOnMusic = true;
}
else if(this->mMusicEnabled && !this->mMusicAllowed)
{
this->StopMusic();
}
//2023 - Making "volume" cvar a master volume. Multiplying by 155 as it was the original default value on the previous 0-255 cvar range.
int musicVol = min(max(0, cl_musicvolume->value * CVAR_GET_FLOAT("volume") * 155), 255);;
if (this->mSongIsPlaying && (musicVol != this->mCurrentVolume))
{
this->mCurrentVolume = musicVol;
//this->mCurrentVolume = min(max(0, this->mCurrentVolume), 255);
//cl_musicvolume->value = this->mCurrentVolume;
mFMOD->FSOUND_SetVolume(this->mCurrentChannel, this->mCurrentVolume);
}
// If the current song is done and a random time has elapsed since OR
// we just turned on music
if(theJustTurnedOnMusic || this->GetIsTimeForSong(inCurrentTime))
{
// Play a random song
this->PlayRandomSong();
}
if(this->mMusicEnabled && (this->mCurrentSongStream != NULL))
{
if((int)(mFMOD->FSOUND_Stream_GetPosition(this->mCurrentSongStream)) >= this->mBytesInCurrentSong)
{
this->mTimeSongEnded = inCurrentTime;
this->mSongIsPlaying = false;
}
}
// if(this->mTimeSongEnded != 0.0f && thi)
// {
// float theTimeElapsedSinceSongStopped = inCurrentTime - this->mTimeSongEnded;
// if(theTimeElapsedSinceSongStopped > this->mRandomSecondsBetweenSongs)
// {
// this->PlayRandomSong();
// }
// }
}
}
void UIHud::VidInit(void)
{
CHud::VidInit();
GetManager().VidInit();
}
FMOD_INSTANCE* UIHud::GetFMOD()
{
return mFMOD;
}