/* $Id: client.cpp,v 1.206.2.5 2006/03/16 20:10:24 chfreund Exp $ */ #include "client.hpp" #include #include #include #include "global.hpp" #include "event.hpp" #include "video.hpp" #include "audio.hpp" #include "avatar.hpp" #include "particles.hpp" #include "signalhandler.hpp" #include "spritesequence.cpp" #include "spriteset.cpp" #include "wopsprites.cpp" #include "flag.hpp" #include "rope.hpp" #include "serverentry.hpp" #include "bot.hpp" #include "server.hpp" /**********************************************************/ Client::Client( Server* server ) : m_communicator( this ), m_world( NULL ), m_server( server ), m_status( DEFAULT ), m_background( NULL ), m_player( NULL ), m_gui( NULL ), m_viewX( 0 ), m_viewY( 0 ), m_viewVelX( 0 ), m_viewVelY( 0 ), m_doScreenshot( false ), m_limitFps( true ) { ASSERT( !SDL_InitSubSystem( SDL_INIT_TIMER ), "Client::Client: could not initialize timer\n" ); // init loader video object Loader::setVideo( &m_video ); // create GUI for the client m_gui = new WopGUI( this, m_video.getScreen() ); DBG( 1 ) ASSERT( m_gui != NULL, "Client::Client: couldn't initialize gui\n" ); } /**********************************************************/ Client::~Client() { LOG( 1 ) INFO( "Client::~Client: starting destructor\n" ); if( m_gui ) { delete m_gui; m_gui = 0; } if( m_background ) { delete m_background; m_background = 0; } LOG( 1 ) INFO( "Client::~Client: done\n" ); } /**********************************************************/ void Client::run() { init(); eventLoop(); LOG( 2 ) INFO( "Client::run: exiting\n" ); } /**********************************************************/ int Client::eventLoop() { SignalHandler* signalHandler = SignalHandler::getInstance(); while ( ! signalHandler->hasQuit() && m_status != SHUTDOWN ) { SDL_Event event; SDL_WaitEvent( &event ); // first try if the GUI can consume this event otherwise // let's try it ourselves if ( ! m_gui->handleEvent( &event ) ) { handleEvent( &event ); } m_audio->update(); } LOG( 2 ) INFO( "Client::eventLoop: starting shutdown\n" ); m_status = SHUTDOWN; SDL_WaitThread( m_displayThread, 0 ); LOG( 3 ) INFO( "Client::eventLoop: joined display thread\n" ); SDL_DestroyMutex( m_displayLoopMutex ); SDL_DestroyCond( m_displayLoopCond ); return 0; } /**********************************************************/ int Client::exit() { if ( m_communicator.isConnected() ) disconnect(); else m_status = SHUTDOWN; if ( m_server ) { m_server->exit(); delete m_server; m_server = 0; } return 0; } /**********************************************************/ void Client::init() { m_showWorld = false; // ignore all events until completely initialized SDL_SetEventFilter( filterAllEvents ); SDL_Surface* wmIcon = Loader::getInstance()->getImage( "images/gui/wop32.png", true ); SDL_WM_SetIcon( wmIcon, 0 ); // get WoP configuration WopSettings* const settings = WopSettings::getInstance(); // Initialize audio m_audio = Audio::getInstance(); m_audio->loadTrack( 0 ); // menu track m_audio->playTrack(); // set cursor m_video.setMouseCursor( " + + " // 1 " + + " // 2 " + + " // 3 "++ + + ++" // 4 " ++ + + ++ " // 5 " ++ ++ " // 6 " " // 7 " # " // 8 " " // 9 " ++ ++ " // 10 " ++ + + ++ " // 11 "++ + + ++" // 12 " + + " // 13 " + + " // 14 " + + ", 15, 15, 7, 7 ); m_video.showMouseCursor( true ); // reset logging object and initialize it m_ProgressLog.reset(); m_ProgressLog.create( 30, 1 ); m_displayLoopMutex = SDL_CreateMutex(); m_displayLoopCond = SDL_CreateCond(); m_displayThread = SDL_CreateThread( displayLoopWrapper, this ); m_gui->showSplashScreen(); // load all sprites (if this does not succeed, // the program is terminated) m_gui->showInitializationWindow(); m_gui->setInitializationValue( static_cast( 100.0 * 0 ) ); m_video.loadSprites( &m_ProgressLog ); m_gui->hideInitializationWindow(); m_myEvent.clear(); // initialize keys m_keyboardConfig.readSettings( settings ); if ( !strcmp( settings->getKeyboard(), "de" ) && !settings->getSetting( "key_rope_on" ) ) { m_keyboardConfig.key[KeyboardConfig::ROPE_ON] = SDLK_y; } // hide splash screen m_gui->hideSplashScreen(); // accept events SDL_SetEventFilter( 0 ); // if a server name is given, try to connect to this one if ( settings->getSetting( "server" ) ) { IPaddress address; SDLNet_ResolveHost( &address, (char*)settings->getServer(), 0xb8b8 ); RemoteServerEntry server( address ); // connect to the server connectToServer( server ); m_gui->showVersionLabel(); } else { m_gui->showMainMenu(); m_gui->showVersionLabel(); } } /**********************************************************/ void Client::startLocalServer() { if ( m_server ) { m_server->exit(); delete m_server; } m_server = new Server(); m_server->runThreaded(); } bool Client::connectToLocalServer() { DirectServerEntry entry; entry.server = m_server; return connectToServer( entry ); } bool Client::connectToServer( ServerEntry& server ) { // open connection if ( !m_communicator.openConnection( server ) ) { m_gui->showInfoBox( "Connection failed", 2000 ); SDL_Delay( 2000 ); m_gui->showMainMenu(); return false; } InfoMessage* info = m_communicator.getServerInfo(); // create a new player Player* player = new Player(); // some attributes for the player WopSettings* settings = WopSettings::getInstance(); player->setName( settings->getName() ); player->setPlayerColor( settings->getPlayerColor() ); player->setTeamID( settings->getTeamID() ); // add bots std::vector bots; Sint32 ibotName = 0; const char* botName = 0x0; while( 0x0 != (botName = settings->getBots(ibotName++)) ) { Bot* newBot = Bot::createBot( botName ); if( newBot ) { bots.push_back( newBot ); } } m_gui->showGameInfoWindow( &server, info, player, bots, &m_video ); return true; } bool Client::connectToServer( const std::string& name ) { IPaddress address; // SDLNet_ResolveHost takes a "char*" as second parameter. // Since "name" is passed as constant object, we need this // brute force cast here. if ( SDLNet_ResolveHost( &address, (char*)name.c_str(), 0xb8b8 ) == -1 ) { LOG( 4 ) INFO( "Client::connectToServer: address could not be resolved: %s\n", SDLNet_GetError() ); m_gui->showInfoBox( "Unknown server name", 2000 ); SDL_Delay( 2000 ); m_gui->showMainMenu(); return false; } RemoteServerEntry serverEntry( address ); return connectToServer( serverEntry ); } void Client::setActive( Player* player, std::vector bots ) { // stop title music and switch to jukebox mode m_audio->fadeOutTrack( 10000 ); m_audio->setJukeboxMode( true ); // let's get busy m_communicator.setActive(); m_world = m_communicator.getWorld(); // initialize message mechanisms for ( Uint8 i = 0; i < m_world->getNumberPlayers(); i++ ) { m_world->getPlayer( i )->getAvatar()->setMessageSink( this ); } m_world->setMessageSink( this ); // connect the sprite class in the video class with the world // using the interface base class of WopSprites LOG( 2 ) INFO( "Client::connect: attaching sprite interface to world\n" ); DBG( 1 ) ASSERT( m_video.getSpriteInterface(), "Client::connect: m_video.getSpriteInterface() == NULL\n" ); m_world->setSpriteInterface( m_video.getSpriteInterface() ); // create a surface from the pixel values in the map ClientMap* clientMap = dynamic_cast( m_world->getMap() ); if ( clientMap ) m_mapSurface = clientMap->getPixels(); else { DBG( 2 ) INFO( "Client::connectToServer: no ClientMap, creating surface from ServerMap\n" ); ServerMap* serverMap = dynamic_cast( m_world->getMap() ); ASSERT( serverMap, "Client::connectToServer: no ClientMap _and_ no ServerMap? I'm confused.\n" ); m_mapSurface = SDL_CreateRGBSurfaceFrom( serverMap->getPixels()->getMemory(), serverMap->getSizeX(), serverMap->getSizeY(), serverMap->getPixelFormat()->BitsPerPixel, serverMap->getPixelFormat()->BytesPerPixel * serverMap->getSizeX(), serverMap->getPixelFormat()->Rmask, serverMap->getPixelFormat()->Gmask, serverMap->getPixelFormat()->Bmask, serverMap->getPixelFormat()->Amask ); } ASSERT( m_mapSurface, "Client::connectToServer: Could not create map surface (%s)\n", SDL_GetError() ); SDL_SetColorKey( m_mapSurface, SDL_SRCCOLORKEY, 0 ); m_mapRect.x = m_mapRect.y = 0; m_mapRect.w = m_video.getScreen()->w; m_mapRect.h = m_video.getScreen()->h; WopSettings* settings = WopSettings::getInstance(); String themePath( settings->getData() ); themePath += '/'; themePath += THEME_CONFIG_PATH; Theme theme( settings->getTheme() ); ASSERT( theme.load((char*)themePath, THEME_CONFIG), "Client::setActive: could not load theme \"%s/"THEME_CONFIG ":%s\"\n", (char*)themePath, (char*)theme.getName() ); INFO( "Theme background is '%s'\n", theme.getBackgroundPath().getString() ); if ( theme.hasBackgroundPath() ) { String backgroundPath( "images/" ); backgroundPath += theme.getBackgroundPath(); m_background = NEW WopBackground( m_world->getMap()->getSizeX(), m_world->getMap()->getSizeY(), backgroundPath.getString(), m_video.getScreen()->format ); } else { m_background = NEW WopBackground( m_world->getMap()->getSizeX(), m_world->getMap()->getSizeY(), "images/mapstuff/background/dark_earth.jpg", m_video.getScreen()->format ); } ASSERT( m_background, "Client::connectToServer: Could not create background surface\n" ); // create a new player m_player = player; m_communicator.addPlayer( m_player, this ); // mark this player as local player m_player->setLocalPlayer(); // add bots for ( unsigned int i = 0; i < bots.size(); i++ ) { m_communicator.addPlayer( bots[i], bots[i] ); } m_world->scanObjectsForFocus(); m_gui->hideVersionLabel(); m_gui->showGameWidgets(); m_showWorld = true; // return true; } void Client::disconnect() { m_gui->hideGameWidgets(); // temporarily limit fps such that we can get the mutex bool saveLimitFps = m_limitFps; m_limitFps = true; SDL_mutexP( m_displayLoopMutex ); // stop displaying the world m_showWorld = false; SDL_mutexV( m_displayLoopMutex ); m_limitFps = saveLimitFps; // disconnect from server LOG( 3 ) INFO( "Client::disconnect: disconnecting from server\n" ); m_communicator.closeConnection(); // start playing intro sound m_audio->setJukeboxMode( false ); m_audio->loadTrack( 0 ); m_audio->fadeInTrack( 3000 ); // reset all connection dependent objects delete m_background; m_background = 0; m_world = 0; // refresh server list std::vector servers; servers = m_communicator.lookForServers(); m_gui->showServerListWindow( servers ); m_gui->showVersionLabel(); } void Client::lostConnection() { m_gui->showInfoBox( "Lost connection to server", 2000 ); SDL_Delay( 2000 ); disconnect(); } bool Client::handleEvent( SDL_Event* event ) { // the events the client is interested in are the key events for // controlling the player's avatar, ENTER for writing a chat message // and ESCAPE for leaving the game // and much more now (Tom) if ( event->type == SDL_KEYDOWN ) { if ( event->key.keysym.sym == SDLK_ESCAPE ) exit(); else if ( event->key.keysym.sym == SDLK_RETURN ) { if ( m_communicator.isConnected() ) { SDL_EnableUNICODE( true ); m_gui->showChatWindow(); } } else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::LEFT] ) m_myEvent.addAction( Event::LEFT ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::RIGHT] ) m_myEvent.addAction( Event::RIGHT ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::DOWN] ) m_myEvent.addAction( Event::DOWN ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::UP] ) m_myEvent.addAction( Event::UP ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::SHOOT] ) m_myEvent.addAction( Event::SHOOT ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::JUMP] ) m_myEvent.addAction( Event::JUMP ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::DIG] ) m_myEvent.addAction( Event::DIG ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::JET] ) m_myEvent.addAction( Event::JET ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::ROPE_ON] ) m_myEvent.addAction( Event::ROPE_ON ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::ROPE_OFF] ) m_myEvent.addAction( Event::ROPE_OFF ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::ROPE_IN] ) m_myEvent.addAction( Event::ROPE_IN ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::ROPE_OUT] ) m_myEvent.addAction( Event::ROPE_OUT ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::ROPE_REL] ) m_myEvent.addAction( Event::ROPE_REL ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::SCREENSHOT] ) m_myEvent.addAction( Event::SCREENSHOT ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::DEBUG] ) m_myEvent.addAction( Event::DEBUG ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::DEC_VOL] ) m_audio->decreaseTrackVolume(); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::INC_VOL] ) m_audio->increaseTrackVolume(); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::TOGGLE_JUKEBOX] ) { if ( m_communicator.isConnected() ) m_audio->toggleJukeboxMode(); } else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::INSERT_REPLAY_BOOKMARK] ) { addMessage( std::string( m_player->getName() + " set a bookmark" )); m_myEvent.addAction( Event::INSERT_REPLAY_BOOKMARK ); } else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::CHANGE_FOCUS] ) m_world->focusNextAvatar(); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON1] ) m_myEvent.setWeapon( 1 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON2] ) m_myEvent.setWeapon( 2 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON3] ) m_myEvent.setWeapon( 3 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON4] ) m_myEvent.setWeapon( 4 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON5] ) m_myEvent.setWeapon( 5 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON6] ) m_myEvent.setWeapon( 6 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON7] ) m_myEvent.setWeapon( 7 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON8] ) m_myEvent.setWeapon( 8 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON9] ) m_myEvent.setWeapon( 9 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON0] ) m_myEvent.setWeapon( 10 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::SPAWN] ) m_myEvent.addAction( Event::SPAWN ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::NEXT_WEAPON] && m_player ) m_myEvent.setWeapon( ( ( m_player->getActiveWeaponIndex() + 1 ) % (Player::m_MAX_WEAPONS-Player::m_IGNORE_N_LAST_WEAPONS) ) + 1 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::PREV_WEAPON] && m_player ) m_myEvent.setWeapon( ( ( m_player->getActiveWeaponIndex() + Player::m_MAX_WEAPONS-Player::m_IGNORE_N_LAST_WEAPONS-1 ) % (Player::m_MAX_WEAPONS-Player::m_IGNORE_N_LAST_WEAPONS) ) + 1 ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::TOGGLE_FULLSCREEN] ) { SDL_mutexP( m_displayLoopMutex ); m_video.toggleFullscreen(); SDL_mutexV( m_displayLoopMutex ); } else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::SHOW_FPS] ) m_gui->toggleFps(); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::LIMIT_FPS] ) { m_limitFps = !m_limitFps; m_gui->setLimitFps( m_limitFps ); } } else if ( event->type == SDL_KEYUP ) { if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::LEFT] ) m_myEvent.removeAction( Event::LEFT ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::RIGHT] ) m_myEvent.removeAction( Event::RIGHT ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::DOWN] ) m_myEvent.removeAction( Event::DOWN ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::UP] ) m_myEvent.removeAction( Event::UP ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::SHOOT] ) m_myEvent.removeAction( Event::SHOOT ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::JUMP] ) m_myEvent.removeAction( Event::JUMP ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::DIG] ) m_myEvent.removeAction( Event::DIG ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::JET] ) m_myEvent.removeAction( Event::JET ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::ROPE_ON] ) m_myEvent.removeAction( Event::ROPE_ON ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::ROPE_OFF] ) m_myEvent.removeAction( Event::ROPE_OFF ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::ROPE_IN] ) m_myEvent.removeAction( Event::ROPE_IN ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::ROPE_OUT] ) m_myEvent.removeAction( Event::ROPE_OUT ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::ROPE_REL] ) m_myEvent.removeAction( Event::ROPE_REL ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::SCREENSHOT] ) m_myEvent.removeAction( Event::SCREENSHOT ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::DEBUG] ) m_myEvent.removeAction( Event::DEBUG ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::INSERT_REPLAY_BOOKMARK] ) m_myEvent.removeAction( Event::INSERT_REPLAY_BOOKMARK ); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON1] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON2] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON3] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON4] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON5] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON6] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON7] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON8] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON9] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::WEAPON0] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::NEXT_WEAPON] || event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::PREV_WEAPON] ) m_myEvent.removeWeapon(); else if ( event->key.keysym.sym == m_keyboardConfig.key[KeyboardConfig::SPAWN] ) m_myEvent.removeAction( Event::SPAWN ); } else if ( event->type == SDL_QUIT ) { exit(); exit(); } return false; } /**********************************************************/ void Client::joinPlayer( Player* player ) { // display info message String message = "Player "; message += player->getName() + " joined the game"; SDL_Color white; white.r = white.g = white.b = 255; showMessage( message, white ); // set message sink player->getAvatar()->setMessageSink( this ); m_gui->hideProgressWindow(); } void Client::leavePlayer( Player* player ) { // display a message String message = "Player "; message += player->getName() + " has left the game"; SDL_Color white; white.r = white.g = white.b = 255; showMessage( message, white ); } /**********************************************************/ void Client::sendChatMessage( String& message ) { String displayedMessage = m_player->getName() + ": " + message; SDL_Color color; color.r = TPLCOL_RED( m_player->getPlayerColor() ); color.g = TPLCOL_GREEN( m_player->getPlayerColor() ); color.b = TPLCOL_BLUE( m_player->getPlayerColor() ); m_communicator.sendChatMessage( displayedMessage, color ); // m_gui->showMessage( displayedMessage, color ); } void Client::addMessage( std::string s ) { SDL_Color white; white.r = white.g = white.b = 255; String message( s.c_str() ); m_gui->showMessage( message, white ); } void Client::showMessage( String& message, const SDL_Color& color ) { m_gui->showMessage( message, color ); } void Client::initializeConnectProgress() { m_gui->setProgressTitle( "Loading world" ); m_gui->setProgressComment( "Get ready tö höp intö the äctiön..." ); m_gui->setProgressValue( 0 ); m_gui->showProgressWindow(); } void Client::updateConnectProgress( Sint32 total, Sint32 received ) { m_gui->setProgressValue( static_cast( 100.0 * received / total ) ); } void Client::hideConnectProgress() { m_gui->hideProgressWindow(); } void Client::showJoinProgress( Sint32 total, Sint32 sent ) { m_gui->setProgressTitle( "New player is joining" ); m_gui->setProgressComment( "Stay tuned..." ); m_gui->setProgressValue( static_cast( 100.0 * sent / total ) ); m_gui->showProgressWindow(); } void Client::hideJoinProgress() { m_gui->hideProgressWindow(); } /**********************************************************/ static inline bool checkAccel( const real dist, const real vel, const real accel, const real decel ) { const int N = ROUND( (FABS_REAL( vel )+accel)/decel + 0.9999 ); return 0.5*N*(N+1)*decel <= FABS_REAL( dist ); } /**********************************************************/ void Client::updateGraphics() { if ( m_showWorld ) { const real viewAccel = 1.5, viewDecel = 0.5; const Avatar* localAvatar = m_player->getAvatar(); int focusX = m_mapRect.w/2, focusY = m_mapRect.h/2; if( m_world->getFocusedObject() ) { focusX = m_world->getFocusedObject()->getIntPosX(); focusY = m_world->getFocusedObject()->getIntPosY(); } m_mapRect.w = m_video.getScreen()->w; m_mapRect.h = m_video.getScreen()->h; if ( m_player ) { #define INERT_VIEWPORT #ifdef INERT_VIEWPORT ///////////////////////////////////// // arrange inert moving of viewport if( (m_viewVelX >= 0 || checkAccel( m_viewX, m_viewVelX, viewAccel, viewDecel )) && (m_viewVelX <= 0 || checkAccel( m_mapSurface->w - m_viewX - m_mapRect.w, m_viewVelX, viewAccel, viewDecel )) && checkAccel( m_viewX + m_mapRect.w/2 - focusX, m_viewVelX, viewAccel, viewDecel ) ) { if ( m_viewX + m_mapRect.w/2 - focusX > 0 ) m_viewVelX -= viewAccel; else if( m_viewX + m_mapRect.w/2 - focusX < 0 ) m_viewVelX += viewAccel; } else { if ( m_viewVelX > 0) m_viewVelX -= viewDecel; else if( m_viewVelX < 0) m_viewVelX += viewDecel; } if( (m_viewVelY >= 0 || checkAccel( m_viewY, m_viewVelY, viewAccel, viewDecel )) && (m_viewVelY <= 0 || checkAccel( m_mapSurface->h - m_viewY - m_mapRect.h, m_viewVelY, viewAccel, viewDecel )) && checkAccel( m_viewY + m_mapRect.h/2 - focusY, m_viewVelY, viewAccel, viewDecel ) ) { if ( m_viewY + m_mapRect.h/2 - focusY > 0 ) m_viewVelY -= viewAccel; else if( m_viewY + m_mapRect.h/2 - focusY < 0 ) m_viewVelY += viewAccel; } else { if ( m_viewVelY > 0) m_viewVelY -= viewDecel; else if( m_viewVelY < 0) m_viewVelY += viewDecel; } m_viewX += m_viewVelX; m_viewY += m_viewVelY; #else m_viewX = focusX - m_mapRect.w/2; m_viewY = focusY - m_mapRect.h/2; #endif if( m_viewX < 0 ) { m_viewX = 0; m_viewVelX = 0; } if( m_viewX > m_mapSurface->w - m_mapRect.w + 80 ) { m_viewX = m_mapSurface->w - m_mapRect.w + 80; m_viewVelX = 0; } if( m_viewY < 0 ) { m_viewY = 0; m_viewVelY = 0; } if( m_viewY > m_mapSurface->h - m_mapRect.h + 50 ) { m_viewY = m_mapSurface->h - m_mapRect.h + 50; m_viewVelY = 0; } m_mapRect.x = ROUND( m_viewX ); m_mapRect.y = ROUND( m_viewY ); if ( m_mapRect.x > m_mapSurface->w - m_video.getScreen()->w ) { m_mapRect.w = m_mapSurface->w - m_mapRect.x; SDL_Rect clearRect; clearRect.x = m_mapRect.w; clearRect.y = 0; clearRect.w = m_video.getScreen()->w - m_mapRect.w; clearRect.h = m_video.getScreen()->h; SDL_FillRect( m_video.getScreen(), &clearRect, 0 ); } if ( m_mapRect.y > m_mapSurface->h - m_video.getScreen()->h ) { m_mapRect.h = m_mapSurface->h - m_mapRect.y; SDL_Rect clearRect; clearRect.x = 0; clearRect.y = m_mapRect.h; clearRect.w = m_video.getScreen()->w; clearRect.h = m_video.getScreen()->h - m_mapRect.h; SDL_FillRect( m_video.getScreen(), &clearRect, 0 ); } // end: inert moving of viewport ///////////////////////////////////// // set viewport position for stereo audio m_audio->setListeningPosition( focusX, focusY ); } else { m_viewX = 0; m_viewY = 0; m_mapRect.x = 0; m_mapRect.y = 0; } // draw the background image SDL_BlitSurface( m_background->getGraphic(), &m_mapRect, m_video.getScreen(), 0 ); // draw the map SDL_BlitSurface( m_mapSurface, &m_mapRect, m_video.getScreen(), NULL ); // draw the world m_world->draw( m_video.getScreen(), m_video.getSprites(), m_mapRect ); if ( m_player ) { // draw aiming target if( localAvatar->isLiving() && ! localAvatar->isInGMMode() && ! localAvatar->isInGunMode() ) { const int x = ROUND( localAvatar->getPosX() + 25 * localAvatar->getAimingDX() ) - m_mapRect.x; const int y = ROUND( localAvatar->getPosY() + 25 * localAvatar->getAimingDY() ) + localAvatar->getWeaponPivotDY() - m_mapRect.y; if( x >= 1 && y >= 1 && x < m_mapRect.w-1 && y < m_mapRect.h-1 ) { Uint32 color = SDL_MapRGB( m_video.getScreen()->format, 255, 255, 255 ); Graphics::setPixel( m_video.getScreen(), x , y-1, color ); Graphics::setPixel( m_video.getScreen(), x-1, y , color ); Graphics::setPixel( m_video.getScreen(), x , y , color ); Graphics::setPixel( m_video.getScreen(), x+1, y , color ); Graphics::setPixel( m_video.getScreen(), x , y+1, color ); } } m_gui->lock(); if( m_player && m_player->getAvatar() && m_player->getAvatar()->wasHurt() ) { m_gui->blinkMessageWindow(); m_player->getAvatar()->setWasHurt( false ); } else { } // set weapon indicators for ( int i = 0; i < Player::m_MAX_WEAPONS-Player::m_IGNORE_N_LAST_WEAPONS; i++ ) { Weapon* weapon = m_player->getWeapon( i ); if ( weapon->isCharging() ) { SDL_Color chargeColor; chargeColor.r = 217; chargeColor.g = 167; chargeColor.b = 167; if ( weapon != m_player->getActiveWeapon() ) { chargeColor.r /= 2; chargeColor.g /= 2; chargeColor.b /= 2; } else { m_gui->setActiveWeaponColor( chargeColor ); m_gui->setActiveWeaponValue( weapon->getChargingPercentage() ); } m_gui->setWeaponColor( i, chargeColor ); m_gui->setWeaponValue( i, weapon->getChargingPercentage() ); } else { SDL_Color weaponColor; weaponColor.r = 167; weaponColor.g = 167; weaponColor.b = 217; if ( weapon != m_player->getActiveWeapon() ) { weaponColor.r /= 2; weaponColor.g /= 2; weaponColor.b /= 2; } else { m_gui->setActiveWeaponColor( weaponColor ); m_gui->setActiveWeaponValue( ( weapon->getCurrentAmmo() * 100 ) / weapon->getMaximumAmmo() ); } m_gui->setWeaponColor( i, weaponColor ); m_gui->setWeaponValue( i, ( weapon->getCurrentAmmo() * 100 ) / weapon->getMaximumAmmo() ); } } // update health indicator m_gui->setHealthValue( localAvatar->getHealth() / 10000 ); // update rope indicator Weapon* ropeWeapon = m_player->getWeapon( Player::m_MAX_WEAPONS-Player::m_IGNORE_N_LAST_WEAPONS ); if ( ropeWeapon->isCharging() ) m_gui->setRopeValue( ropeWeapon->getChargingPercentage() ); else m_gui->setRopeValue( ( ropeWeapon->getCurrentAmmo() * 100 ) / ropeWeapon->getMaximumAmmo() ); // update fuel indicator m_gui->setFuelValue( static_cast( max( 0.0, localAvatar->getFuel()*100 ) / Avatar::MAX_FUEL ) ); } // update scores Uint8* keyState = SDL_GetKeyState( NULL ); if ( keyState[SDLK_TAB] ) { m_gui->updateScores( m_world ); m_gui->showScoreTable(); } else { m_gui->hideScoreTable(); } m_gui->unlock(); } else { // just clear the screen Uint32 colorBlack = SDL_MapRGB( m_video.getScreen()->format, 0, 0, 0 ); SDL_FillRect( m_video.getScreen(), 0, colorBlack ); } // update initialization DBG( 2 ) { ASSERT( m_ProgressLog.getNumLevels() > 0, "Client::updateGraphics: progress logging object " "not initialized\n" ); } m_gui->lock(); m_gui->setInitializationValue( static_cast( 100.0 * m_ProgressLog.getProgress() ) ); std::string initString( m_ProgressLog.getTitle().getString() ); m_gui->setInitializationTitle( initString ); m_gui->unlock(); // draw the GUI stuff m_gui->draw(); SDL_Flip( m_video.getScreen() ); } /**********************************************************/ void Client::displayLoop() { Uint32 frame_counter = 0; int tick_reference = SDL_GetTicks(); int tick_counter = 0; while( m_status != SHUTDOWN ) { SDL_mutexP( m_displayLoopMutex ); if ( m_limitFps ) { LOG( 5 ) { CHECK( SDL_CondWaitTimeout( m_displayLoopCond, m_displayLoopMutex, 40 ) == 0, "Client::displayLoop: timeout when waiting for display condition\n" ); } else { SDL_CondWaitTimeout( m_displayLoopCond, m_displayLoopMutex, 40 ); } } else SDL_CondWaitTimeout( m_displayLoopCond, m_displayLoopMutex, 1 ); updateGraphics(); // is there a pending screenshot? if( m_doScreenshot ) { static int screenshotNumber = 0; String filename; filename.format( "wop_%02i_%02i.bmp", m_player->getPlayerID(), screenshotNumber++ ); m_video.saveScreenshot( filename ); m_doScreenshot = false; } frame_counter++; tick_counter = SDL_GetTicks(); if ( tick_counter - tick_reference >= 5000 ) { LOG( 5 ) INFO( "Client::displayLoop: drawing with about %4.1f fps\n", frame_counter / 5.0 ); m_gui->setFps( frame_counter / 5.0 ); frame_counter = 0; tick_reference = tick_counter; } SDL_mutexV( m_displayLoopMutex ); } LOG( 2 ) INFO( "Client::displayLoop: exiting display loop\n" ); } void Client::signalDisplayCond() { if ( m_displayLoopCond ) { SDL_CondSignal( m_displayLoopCond ); } } /**********************************************************/ int filterAllEvents( const SDL_Event *event ) { return 0; } int displayLoopWrapper( void* data ) { Client* clientPtr = static_cast( data ); clientPtr->displayLoop(); return 0; } /**********************************************************/