From c37e1eec0df53433cf1cffdd0b263bbcd8169179 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 2 Feb 2015 16:31:54 +1100 Subject: [PATCH 001/117] Updated documentation and minor style changes. --- src/audio/music_information.cpp | 16 +++++------ src/audio/music_information.hpp | 48 ++++++++++++++++++++------------- src/audio/music_manager.cpp | 12 ++++++--- src/audio/music_manager.hpp | 3 ++- src/audio/sfx_manager.cpp | 5 ++-- 5 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/audio/music_information.cpp b/src/audio/music_information.cpp index 1ee0d9c58..d332cb1db 100644 --- a/src/audio/music_information.cpp +++ b/src/audio/music_information.cpp @@ -137,6 +137,8 @@ void MusicInformation::addMusicToTracks() } // addMusicToTracks //----------------------------------------------------------------------------- +/** Starts the music. + */ void MusicInformation::startMusic() { m_time_since_faster = 0.0f; @@ -153,7 +155,7 @@ void MusicInformation::startMusic() return; } - if (m_normal_music != NULL) delete m_normal_music; + if (m_normal_music) delete m_normal_music; #if HAVE_OGGVORBIS m_normal_music = new MusicOggStream(); @@ -175,7 +177,7 @@ void MusicInformation::startMusic() // Then (if available) load the music for the last track // ----------------------------------------------------- - if (m_fast_music != NULL) delete m_fast_music; + if (m_fast_music) delete m_fast_music; if (m_fast_filename == "") { m_fast_music = NULL; @@ -246,14 +248,12 @@ void MusicInformation::update(float dt) break; } case SOUND_NORMAL: - if ( m_normal_music == NULL ) break; - - m_normal_music->update(); + if ( m_normal_music ) + m_normal_music->update(); break; case SOUND_FAST: - if ( m_fast_music == NULL ) break; - - m_fast_music->update(); + if ( m_fast_music ) + m_fast_music->update(); break; } // switch diff --git a/src/audio/music_information.hpp b/src/audio/music_information.hpp index 296f039c1..d25ca3afe 100644 --- a/src/audio/music_information.hpp +++ b/src/audio/music_information.hpp @@ -84,26 +84,38 @@ public: #endif ~MusicInformation (); static MusicInformation *create(const std::string &filename); - const stringw& getComposer () const {return m_composer; } - const stringw& getTitle () const {return m_title; } - const std::string& getNormalFilename() const {return m_normal_filename; } - const std::string& getFastFilename () const {return m_fast_filename; } - //int getNumLoops () const {return m_numLoops; } - float getFasterTime () const {return m_faster_time; } - float getMaxPitch () const {return m_max_pitch; } - void setMusicWaiting () {m_music_waiting = true;} - void addMusicToTracks (); - void update (float dt); - void startMusic (); - void stopMusic (); - void pauseMusic (); - void resumeMusic (); - void volumeMusic (float gain); - + void addMusicToTracks(); + void update(float dt); + void startMusic(); + void stopMusic(); + void pauseMusic(); + void resumeMusic(); + void volumeMusic(float gain); void setTemporaryVolume(float gain); - void resetTemporaryVolume() { volumeMusic(m_adjusted_gain); } - void switchToFastMusic(); bool isPlaying() const; + + // ------------------------------------------------------------------------ + /** Returns the composer of the music. */ + const stringw& getComposer() const { return m_composer; } + // ------------------------------------------------------------------------ + /** Returns the title of the music. */ + const stringw& getTitle() const { return m_title; } + // ------------------------------------------------------------------------ + /** Returns the filename of the normal speed music. */ + const std::string& getNormalFilename() const { return m_normal_filename; } + // ------------------------------------------------------------------------ + /** If available, returns the file name of the faster/last-lap music. */ + const std::string& getFastFilename() const { return m_fast_filename; } + // ------------------------------------------------------------------------ + float getMaxPitch() const { return m_max_pitch; } + // ------------------------------------------------------------------------ + /** Sets the music to be waiting, i.e. startMusic still needs to be + * called. Used to pre-load track music during track loading time. */ + void setMusicWaiting () {m_music_waiting = true;} + // ------------------------------------------------------------------------ + /** Resets a temporary volume change. */ + void resetTemporaryVolume() { volumeMusic(m_adjusted_gain); } + }; // MusicInformation #endif diff --git a/src/audio/music_manager.cpp b/src/audio/music_manager.cpp index 3bb713804..3e1cb33d5 100644 --- a/src/audio/music_manager.cpp +++ b/src/audio/music_manager.cpp @@ -162,7 +162,11 @@ void MusicManager::addMusicToTracks() } // addMusicToTracks //----------------------------------------------------------------------------- -void MusicManager::startMusic(MusicInformation* mi, bool startRightNow) +/** Schedules the indicated music to be played next. + * \param mi Music information of the music to be played. + * \param start_right_now + */ +void MusicManager::startMusic(MusicInformation* mi, bool start_right_now) { // If this music is already playing, ignore this call. if (m_current_music != NULL && @@ -181,8 +185,10 @@ void MusicManager::startMusic(MusicInformation* mi, bool startRightNow) if(!mi || !UserConfigParams::m_music || !m_initialized) return; mi->volumeMusic(m_masterGain); - if (startRightNow) mi->startMusic(); - else mi->setMusicWaiting(); + if (start_right_now) + mi->startMusic(); + else + mi->setMusicWaiting(); } // startMusic //----------------------------------------------------------------------------- diff --git a/src/audio/music_manager.hpp b/src/audio/music_manager.hpp index cee900a25..0ff27203c 100644 --- a/src/audio/music_manager.hpp +++ b/src/audio/music_manager.hpp @@ -53,7 +53,8 @@ public: MusicManager(); virtual ~MusicManager(); - void startMusic(MusicInformation* mi, bool startRightNow=true); + void startMusic(MusicInformation* mi, + bool start_right_now=true); void stopMusic(); bool initialized() const {return m_initialized; } void update(float dt) {if(m_current_music) diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 63c7769b5..5ab246c54 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -597,7 +597,7 @@ void SFXManager::deleteSFXMapping(const std::string &name) } // deleteSFXMapping //---------------------------------------------------------------------------- -/** Make sures that the sfx thread is started at least one per frame. It also +/** Make sure that the sfx thread is started at least once per frame. It also * adds an update command for the music manager. * \param dt Time step size. */ @@ -610,7 +610,8 @@ void SFXManager::update(float dt) //---------------------------------------------------------------------------- /** Updates the status of all playing sfx (to test if they are finished). - * This function is executed once per thread (triggered by the + * This function is executed once per frame (triggered by the audio thread). + * \param current The sfx command - used to get timestep information. */ void SFXManager::reallyUpdateNow(SFXCommand *current) { From b9a7c11dab5417ccdd3060b3052fa7f294dd6901 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 3 Feb 2015 14:30:18 +1100 Subject: [PATCH 002/117] Removed unnecessary #includes. --- src/graphics/camera.cpp | 2 +- src/items/swatter.cpp | 1 - src/karts/kart.cpp | 1 - src/modes/cutscene_world.cpp | 1 - src/states_screens/grand_prix_lose.cpp | 2 -- src/states_screens/grand_prix_win.cpp | 1 - src/states_screens/options_screen_ui.cpp | 1 - src/states_screens/options_screen_video.cpp | 1 - src/tracks/track_manager.cpp | 15 +++++++-------- 9 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp index 0ce966d24..06d477186 100644 --- a/src/graphics/camera.cpp +++ b/src/graphics/camera.cpp @@ -21,7 +21,7 @@ #include -#include "audio/music_manager.hpp" +#include "audio/sfx_manager.hpp" #include "config/user_config.hpp" #include "graphics/irr_driver.hpp" #include "io/xml_node.hpp" diff --git a/src/items/swatter.cpp b/src/items/swatter.cpp index ebdfc172d..9a72cb592 100644 --- a/src/items/swatter.cpp +++ b/src/items/swatter.cpp @@ -26,7 +26,6 @@ #include "items/swatter.hpp" #include "achievements/achievement_info.hpp" -#include "audio/music_manager.hpp" #include "audio/sfx_base.hpp" #include "audio/sfx_manager.hpp" #include "config/player_manager.hpp" diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 870bcc8af..a885883d3 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -19,7 +19,6 @@ #include "karts/kart.hpp" #include "graphics/central_settings.hpp" -#include "audio/music_manager.hpp" #include "audio/sfx_manager.hpp" #include "audio/sfx_base.hpp" #include "challenges/challenge_status.hpp" diff --git a/src/modes/cutscene_world.cpp b/src/modes/cutscene_world.cpp index 0aff4dfed..1354c97de 100644 --- a/src/modes/cutscene_world.cpp +++ b/src/modes/cutscene_world.cpp @@ -19,7 +19,6 @@ #include "animations/animation_base.hpp" #include "animations/three_d_animation.hpp" -#include "audio/music_manager.hpp" #include "audio/sfx_manager.hpp" #include "challenges/unlock_manager.hpp" #include "config/player_manager.hpp" diff --git a/src/states_screens/grand_prix_lose.cpp b/src/states_screens/grand_prix_lose.cpp index c81dc4f3f..e5c316fb0 100644 --- a/src/states_screens/grand_prix_lose.cpp +++ b/src/states_screens/grand_prix_lose.cpp @@ -18,8 +18,6 @@ #include "states_screens/grand_prix_lose.hpp" - -#include "audio/music_manager.hpp" #include "audio/sfx_manager.hpp" #include "challenges/unlock_manager.hpp" #include "config/player_manager.hpp" diff --git a/src/states_screens/grand_prix_win.cpp b/src/states_screens/grand_prix_win.cpp index 99004ef4e..8e900c2bf 100644 --- a/src/states_screens/grand_prix_win.cpp +++ b/src/states_screens/grand_prix_win.cpp @@ -18,7 +18,6 @@ #include "states_screens/grand_prix_win.hpp" -#include "audio/music_manager.hpp" #include "audio/sfx_manager.hpp" #include "challenges/unlock_manager.hpp" #include "config/player_manager.hpp" diff --git a/src/states_screens/options_screen_ui.cpp b/src/states_screens/options_screen_ui.cpp index 7532b06b7..f1c2e8728 100644 --- a/src/states_screens/options_screen_ui.cpp +++ b/src/states_screens/options_screen_ui.cpp @@ -18,7 +18,6 @@ #include "states_screens/options_screen_ui.hpp" #include "addons/news_manager.hpp" -#include "audio/music_manager.hpp" #include "audio/sfx_manager.hpp" #include "audio/sfx_base.hpp" #include "config/hardware_stats.hpp" diff --git a/src/states_screens/options_screen_video.cpp b/src/states_screens/options_screen_video.cpp index f7b1950fe..e875daa1c 100644 --- a/src/states_screens/options_screen_video.cpp +++ b/src/states_screens/options_screen_video.cpp @@ -17,7 +17,6 @@ #include "states_screens/options_screen_video.hpp" -#include "audio/music_manager.hpp" #include "audio/sfx_manager.hpp" #include "audio/sfx_base.hpp" #include "config/user_config.hpp" diff --git a/src/tracks/track_manager.cpp b/src/tracks/track_manager.cpp index 3a7cb0885..37911fd7c 100644 --- a/src/tracks/track_manager.cpp +++ b/src/tracks/track_manager.cpp @@ -17,19 +17,18 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "tracks/track_manager.hpp" -#include "graphics/irr_driver.hpp" -#include -#include -#include -#include -#include - -#include "audio/music_manager.hpp" #include "config/stk_config.hpp" +#include "graphics/irr_driver.hpp" #include "io/file_manager.hpp" #include "tracks/track.hpp" +#include +#include +#include +#include +#include + TrackManager* track_manager = 0; std::vector TrackManager::m_track_search_path; From 4658bd83de06e6b58260e2135d79ef001253ca7c Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 3 Feb 2015 16:27:05 +1100 Subject: [PATCH 003/117] Handle playing and stopping of music in separate thread. Make the sfx manager thread use CanBeDeleted to allow for timed deleting.S --- src/audio/music_information.cpp | 5 +++-- src/audio/music_manager.cpp | 14 +++++++------- src/audio/music_manager.hpp | 2 +- src/audio/music_ogg.cpp | 12 ++++++++---- src/audio/music_ogg.hpp | 4 +++- src/audio/sfx_buffer.cpp | 24 ++++++++++++++---------- src/audio/sfx_manager.cpp | 25 ++++++++++++++++++++++--- src/audio/sfx_manager.hpp | 26 ++++++++++++++++++++++---- src/main.cpp | 16 ++++++++++++++-- 9 files changed, 94 insertions(+), 34 deletions(-) diff --git a/src/audio/music_information.cpp b/src/audio/music_information.cpp index d332cb1db..ce00991bd 100644 --- a/src/audio/music_information.cpp +++ b/src/audio/music_information.cpp @@ -187,7 +187,7 @@ void MusicInformation::startMusic() if(StringUtils::getExtension(m_fast_filename)!="ogg") { Log::warn( - "Music file %s format not recognized, fast music is ignored\n", + "Music file %s format not recognized, fast music is ignored", m_fast_filename.c_str()); return; } @@ -334,7 +334,8 @@ void MusicInformation::switchToFastMusic() bool MusicInformation::isPlaying() const { - return (m_normal_music != NULL && m_normal_music->isPlaying()) || (m_fast_music != NULL && m_fast_music->isPlaying()); + return (m_normal_music != NULL && m_normal_music->isPlaying()) || + (m_fast_music != NULL && m_fast_music->isPlaying()); } //----------------------------------------------------------------------------- diff --git a/src/audio/music_manager.cpp b/src/audio/music_manager.cpp index 3e1cb33d5..c3d41ef19 100644 --- a/src/audio/music_manager.cpp +++ b/src/audio/music_manager.cpp @@ -93,8 +93,6 @@ MusicManager::MusicManager() //----------------------------------------------------------------------------- MusicManager::~MusicManager() { - stopMusic(); - for(std::map::iterator i=m_all_music.begin(); i!=m_all_music.end(); i++) { @@ -185,16 +183,18 @@ void MusicManager::startMusic(MusicInformation* mi, bool start_right_now) if(!mi || !UserConfigParams::m_music || !m_initialized) return; mi->volumeMusic(m_masterGain); - if (start_right_now) - mi->startMusic(); - else - mi->setMusicWaiting(); + SFXManager::get()->queue(start_right_now ? SFXManager::SFX_MUSIC_START + : SFXManager::SFX_MUSIC_WAITING, + mi); } // startMusic //----------------------------------------------------------------------------- +/** Queues a stop current music event for the audio thread. + */ void MusicManager::stopMusic() { - if(m_current_music) m_current_music->stopMusic(); + if (m_current_music) + SFXManager::get()->queue(SFXManager::SFX_MUSIC_STOP, m_current_music); } // stopMusic //----------------------------------------------------------------------------- diff --git a/src/audio/music_manager.hpp b/src/audio/music_manager.hpp index 0ff27203c..dbe04ca6e 100644 --- a/src/audio/music_manager.hpp +++ b/src/audio/music_manager.hpp @@ -56,7 +56,7 @@ public: void startMusic(MusicInformation* mi, bool start_right_now=true); void stopMusic(); - bool initialized() const {return m_initialized; } + bool initialized() const { return m_initialized; } void update(float dt) {if(m_current_music) m_current_music->update(dt); } void pauseMusic() {if(m_current_music) diff --git a/src/audio/music_ogg.cpp b/src/audio/music_ogg.cpp index 4e8981ece..e95ed3d8a 100644 --- a/src/audio/music_ogg.cpp +++ b/src/audio/music_ogg.cpp @@ -61,12 +61,14 @@ bool MusicOggStream::load(const std::string& filename) if(!m_oggFile) { - Log::error("MusicOgg", "Loading Music: %s failed (fopen returned NULL)\n", m_fileName.c_str()); + Log::error("MusicOgg", "Loading Music: %s failed (fopen returned NULL)", + m_fileName.c_str()); return false; } #if defined( WIN32 ) || defined( WIN64 ) - const int result = ov_open_callbacks((void *)m_oggFile, &m_oggStream, NULL, 0, OV_CALLBACKS_DEFAULT); + const int result = ov_open_callbacks((void *)m_oggFile, &m_oggStream, NULL, + 0, OV_CALLBACKS_DEFAULT ); #else const int result = ov_open(m_oggFile, &m_oggStream, NULL, 0); #endif @@ -98,7 +100,8 @@ bool MusicOggStream::load(const std::string& filename) errorMessage = "Unknown Error"; } - Log::error("MusicOgg", "Loading Music: %s failed : ov_open returned error code %i (%s)\n", + Log::error("MusicOgg", "Loading Music: %s failed : " + "ov_open returned error code %i (%s)", m_fileName.c_str(), result, errorMessage); return false; } @@ -359,7 +362,8 @@ bool MusicOggStream::check(const char* what) if (error != AL_NO_ERROR) { - Log::error("MusicOgg", "[MusicOggStream] OpenAL error at %s : %s (%i)\n", what, SFXManager::getErrorString(error).c_str(), error); + Log::error("MusicOgg", "OpenAL error at %s : %s (%i)", what, + SFXManager::getErrorString(error).c_str(), error); return false; } diff --git a/src/audio/music_ogg.hpp b/src/audio/music_ogg.hpp index 2478ee454..96ed7b62d 100644 --- a/src/audio/music_ogg.hpp +++ b/src/audio/music_ogg.hpp @@ -85,7 +85,9 @@ private: ALenum nb_channels; bool m_pausedMusic; - static const int m_buffer_size = 11025*4;//one full second of audio at 44100 samples per second + + //one full second of audio at 44100 samples per second + static const int m_buffer_size = 11025*4; }; #endif diff --git a/src/audio/sfx_buffer.cpp b/src/audio/sfx_buffer.cpp index 0ac37f2d2..3efa72e0d 100644 --- a/src/audio/sfx_buffer.cpp +++ b/src/audio/sfx_buffer.cpp @@ -111,7 +111,8 @@ bool SFXBuffer::load() if (!loadVorbisBuffer(m_file, m_buffer)) { - Log::error("SFXBuffer", "Could not load sound effect %s\n", m_file.c_str()); + Log::error("SFXBuffer", "Could not load sound effect %s", + m_file.c_str()); // TODO: free al buffer here? return false; } @@ -165,20 +166,22 @@ bool SFXBuffer::loadVorbisBuffer(const std::string &name, ALuint buffer) if(!file) { - Log::error("SFXBuffer", "[SFXBuffer] LoadVorbisBuffer() - couldn't open file!\n"); + Log::error("SFXBuffer", "LoadVorbisBuffer() - couldn't open file!"); return false; } if (ov_open_callbacks(file, &oggFile, NULL, 0, OV_CALLBACKS_NOCLOSE) != 0) { fclose(file); - Log::error("SFXBuffer", "[SFXBuffer] LoadVorbisBuffer() - ov_open_callbacks() failed, file isn't vorbis?\n"); + Log::error("SFXBuffer", "LoadVorbisBuffer() - ov_open_callbacks() failed, " + "file isn't vorbis?"); return false; } info = ov_info(&oggFile, -1); - long len = (long)ov_pcm_total(&oggFile, -1) * info->channels * 2; // always 16 bit data + // always 16 bit data + long len = (long)ov_pcm_total(&oggFile, -1) * info->channels * 2; char *data = (char *) malloc(len); if(!data) @@ -213,12 +216,13 @@ bool SFXBuffer::loadVorbisBuffer(const std::string &name, ALuint buffer) // duration (which is the norm), compute it: if(m_duration < 0) { - ALint buffer_size, frequency, bits_per_sample, channels; - alGetBufferi(buffer, AL_SIZE, &buffer_size ); - alGetBufferi(buffer, AL_FREQUENCY, &frequency ); - alGetBufferi(buffer, AL_CHANNELS, &channels ); - alGetBufferi(buffer, AL_BITS, &bits_per_sample); - m_duration = float(buffer_size) / (frequency*channels*(bits_per_sample / 8)); + ALint buffer_size, frequency, bits_per_sample, channels; + alGetBufferi(buffer, AL_SIZE, &buffer_size ); + alGetBufferi(buffer, AL_FREQUENCY, &frequency ); + alGetBufferi(buffer, AL_CHANNELS, &channels ); + alGetBufferi(buffer, AL_BITS, &bits_per_sample); + m_duration = float(buffer_size) + / (frequency*channels*(bits_per_sample / 8)); } return success; #else diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 5ab246c54..ded724e45 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -201,6 +201,13 @@ void SFXManager::queue(SFXCommands command, SFXBase *sfx, const Vec3 &p) queueCommand(sfx_command); } // queue (Vec3) +//---------------------------------------------------------------------------- +void SFXManager::queue(SFXCommands command, MusicInformation *mi) +{ + SFXCommand *sfx_command = new SFXCommand(command, mi); + queueCommand(sfx_command); +} // queue(MusicInformation) + //---------------------------------------------------------------------------- /** Enqueues a command to the sfx queue threadsafe. Then signal the * sfx manager to wake up. @@ -213,7 +220,7 @@ void SFXManager::queueCommand(SFXCommand *command) race_manager->getMinorMode() != RaceManager::MINOR_MODE_CUTSCENE) { if(command->m_command==SFX_POSITION || command->m_command==SFX_LOOP || - command->m_command==SFX_PLAY || command->m_command==SFX_SPEED ) + command->m_command==SFX_SPEED ) { delete command; static int count_messages = 0; @@ -303,6 +310,12 @@ void* SFXManager::mainLoop(void *obj) case SFX_RESUME_ALL: me->reallyResumeAllNow(); break; case SFX_LISTENER: me->reallyPositionListenerNow(); break; case SFX_UPDATE: me->reallyUpdateNow(current); break; + case SFX_MUSIC_START: + current->m_music_information->startMusic(); break; + case SFX_MUSIC_STOP: + current->m_music_information->stopMusic(); break; + case SFX_MUSIC_WAITING: + current->m_music_information->setMusicWaiting(); break; default: assert("Not yet supported."); } delete current; @@ -311,6 +324,11 @@ void* SFXManager::mainLoop(void *obj) } // while + // Signal that the sfx manager can now be deleted. + // We signal this even before cleaning up memory, since there is no + // need to keep the user waiting for STK to exit. + me->setCanBeDeleted(); + // Clean up memory to avoid leak detection while(!me->m_sfx_commands.getData().empty()) { @@ -449,7 +467,8 @@ SFXBuffer* SFXManager::addSingleSfx(const std::string &sfx_name, const bool load) { - SFXBuffer* buffer = new SFXBuffer(sfx_file, positional, rolloff, max_width, gain); + SFXBuffer* buffer = new SFXBuffer(sfx_file, positional, rolloff, + max_width, gain); m_all_sfx_types[sfx_name] = buffer; @@ -617,7 +636,7 @@ void SFXManager::reallyUpdateNow(SFXCommand *current) { assert(current->m_command==SFX_UPDATE); float dt = current->m_parameter.getX(); - music_manager->update(dt); + music_manager->getCurrentMusic()->update(dt); m_all_sfx.lock(); for (std::vector::iterator i = m_all_sfx.getData().begin(); i != m_all_sfx.getData().end(); i++) diff --git a/src/audio/sfx_manager.hpp b/src/audio/sfx_manager.hpp index 01cbf0038..5c3a43ca3 100644 --- a/src/audio/sfx_manager.hpp +++ b/src/audio/sfx_manager.hpp @@ -19,6 +19,7 @@ #ifndef HEADER_SFX_MANAGER_HPP #define HEADER_SFX_MANAGER_HPP +#include "utils/can_be_deleted.hpp" #include "utils/leak_check.hpp" #include "utils/no_copy.hpp" #include "utils/synchronised.hpp" @@ -38,7 +39,7 @@ typedef unsigned int ALuint; #endif - +class MusicInformation; class SFXBase; class SFXBuffer; class XMLNode; @@ -50,7 +51,7 @@ class XMLNode; * on of the (shared) buffers from the sound manager. * \ingroup audio */ -class SFXManager : public NoCopy +class SFXManager : public NoCopy, public CanBeDeleted { private: /** Singleton pointer. */ @@ -76,6 +77,10 @@ public: SFX_LOOP, SFX_LISTENER, SFX_UPDATE, + SFX_MUSIC_START, + SFX_MUSIC_STOP, + SFX_MUSIC_UPDATE, + SFX_MUSIC_WAITING, SFX_EXIT, }; // SFXCommands @@ -108,10 +113,15 @@ private: LEAK_CHECK() public: /** The sound effect for which the command should be executed. */ - SFXBase *m_sfx; + SFXBase *m_sfx; + + /** Stores music information for music commands. */ + MusicInformation *m_music_information; + /** The command to execute. */ SFXCommands m_command; - /** Optional parameter for commands that need more input. */ + /** Optional parameter for commands that need more input. Single + * floating point values are stored in the X component. */ Vec3 m_parameter; // -------------------------------------------------------------------- SFXCommand(SFXCommands command, SFXBase *base) @@ -120,6 +130,13 @@ private: m_sfx = base; } // SFXCommand() // -------------------------------------------------------------------- + /** Constructor for music information commands. */ + SFXCommand(SFXCommands command, MusicInformation *mi) + { + m_command = command; + m_music_information = mi; + } // SFXCommnd + // -------------------------------------------------------------------- SFXCommand(SFXCommands command, SFXBase *base, float parameter) { m_command = command; @@ -187,6 +204,7 @@ public: void queue(SFXCommands command, SFXBase *sfx=NULL); void queue(SFXCommands command, SFXBase *sfx, float f); void queue(SFXCommands command, SFXBase *sfx, const Vec3 &p); + void queue(SFXCommands command, MusicInformation *mi); // ------------------------------------------------------------------------ /** Static function to get the singleton sfx manager. */ static SFXManager *get() diff --git a/src/main.cpp b/src/main.cpp index 0ab5e467a..18bea6fea 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1462,6 +1462,10 @@ static void cleanSuperTuxKart() if(Online::RequestManager::isRunning()) Online::RequestManager::get()->stopNetworkThread(); + + // Stop music (this request will go into the sfx manager queue, so it needs + // to be done before stopping the thread). + music_manager->stopMusic(); SFXManager::get()->stopThread(); irr_driver->updateConfigIfRelevant(); AchievementsManager::destroy(); @@ -1479,8 +1483,6 @@ static void cleanSuperTuxKart() if(material_manager) delete material_manager; if(history) delete history; ReplayRecorder::destroy(); - SFXManager::destroy(); - if(music_manager) delete music_manager; delete ParticleKindManager::get(); PlayerManager::destroy(); if(unlock_manager) delete unlock_manager; @@ -1507,6 +1509,16 @@ static void cleanSuperTuxKart() } Online::RequestManager::deallocate(); + if (!SFXManager::get()->waitForReadyToDeleted(2.0f)) + { + Log::info("Thread", "SFXManager not stopping, exiting anyway."); + } + SFXManager::destroy(); + + // Music manager can not be deleted before the sfx thread is stopped + // (since sfx commands can contain music information). + delete music_manager; + // The addons manager might still be called from a currenty running request // in the request manager, so it can not be deleted earlier. if(addons_manager) delete addons_manager; From 6aa9ff86a4dfc9c7592cfb2a717b283d4c31d6ac Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 3 Feb 2015 16:29:54 +1100 Subject: [PATCH 004/117] Removed unnecessary function. --- src/audio/music_manager.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/audio/music_manager.hpp b/src/audio/music_manager.hpp index dbe04ca6e..98b33537a 100644 --- a/src/audio/music_manager.hpp +++ b/src/audio/music_manager.hpp @@ -57,8 +57,6 @@ public: bool start_right_now=true); void stopMusic(); bool initialized() const { return m_initialized; } - void update(float dt) {if(m_current_music) - m_current_music->update(dt); } void pauseMusic() {if(m_current_music) m_current_music->pauseMusic(); } void resumeMusic() {if(m_current_music) From eb37092643c8560d5bcfd9761aeb7bb418384489 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 4 Feb 2015 17:08:22 +1100 Subject: [PATCH 005/117] Moved all remaining music commands to be handled by sfx thread. --- src/audio/music_information.hpp | 39 ++++++++++------ src/audio/music_manager.cpp | 77 +++++++++++++++++++++++++++--- src/audio/music_manager.hpp | 66 +++++++++++++------------- src/audio/sfx_manager.cpp | 83 ++++++++++++++++++++++----------- src/audio/sfx_manager.hpp | 18 ++++++- src/main.cpp | 3 +- src/modes/linear_world.cpp | 5 +- src/modes/overworld.cpp | 5 +- 8 files changed, 207 insertions(+), 89 deletions(-) diff --git a/src/audio/music_information.hpp b/src/audio/music_information.hpp index d25ca3afe..b35fd5385 100644 --- a/src/audio/music_information.hpp +++ b/src/audio/music_information.hpp @@ -76,6 +76,30 @@ private: // The constructor is private so that the // static create function must be used. MusicInformation (const XMLNode *root, const std::string &filename); + + // Declare the following functions private, but allow the SFXManager + // and music manager to access them. This makes sure that only the sfx thread calls + // openal/vorbis etc, and so makes it all thread safe. +private: + friend class SFXManager; + friend class MusicManager; + void update(float dt); + void startMusic(); + void stopMusic(); + void pauseMusic(); + void resumeMusic(); + void volumeMusic(float gain); + void switchToFastMusic(); + void setTemporaryVolume(float gain); + // ------------------------------------------------------------------------ + /** Resets a temporary volume change. */ + void resetTemporaryVolume() { volumeMusic(m_adjusted_gain); } + // ------------------------------------------------------------------------ + /** Sets the music to be waiting, i.e. startMusic still needs to be + * called. Used to pre-load track music during track loading time. */ + void setMusicWaiting() { m_music_waiting = true; } + // ------------------------------------------------------------------------ + public: LEAK_CHECK() @@ -85,14 +109,6 @@ public: ~MusicInformation (); static MusicInformation *create(const std::string &filename); void addMusicToTracks(); - void update(float dt); - void startMusic(); - void stopMusic(); - void pauseMusic(); - void resumeMusic(); - void volumeMusic(float gain); - void setTemporaryVolume(float gain); - void switchToFastMusic(); bool isPlaying() const; // ------------------------------------------------------------------------ @@ -109,13 +125,6 @@ public: const std::string& getFastFilename() const { return m_fast_filename; } // ------------------------------------------------------------------------ float getMaxPitch() const { return m_max_pitch; } - // ------------------------------------------------------------------------ - /** Sets the music to be waiting, i.e. startMusic still needs to be - * called. Used to pre-load track music during track loading time. */ - void setMusicWaiting () {m_music_waiting = true;} - // ------------------------------------------------------------------------ - /** Resets a temporary volume change. */ - void resetTemporaryVolume() { volumeMusic(m_adjusted_gain); } }; // MusicInformation #endif diff --git a/src/audio/music_manager.cpp b/src/audio/music_manager.cpp index c3d41ef19..5ee8ca732 100644 --- a/src/audio/music_manager.cpp +++ b/src/audio/music_manager.cpp @@ -159,6 +159,16 @@ void MusicManager::addMusicToTracks() } } // addMusicToTracks +//----------------------------------------------------------------------------- +/** Special shortcut vor overworld (which skips other phases where the music + * would normally be started. + */ +void MusicManager::startMusic() +{ + if (m_current_music) + SFXManager::get()->queue(SFXManager::SFX_MUSIC_START, m_current_music); +} // startMusic + //----------------------------------------------------------------------------- /** Schedules the indicated music to be played next. * \param mi Music information of the music to be played. @@ -182,7 +192,7 @@ void MusicManager::startMusic(MusicInformation* mi, bool start_right_now) if(!mi || !UserConfigParams::m_music || !m_initialized) return; - mi->volumeMusic(m_masterGain); + mi->volumeMusic(m_master_gain); SFXManager::get()->queue(start_right_now ? SFXManager::SFX_MUSIC_START : SFXManager::SFX_MUSIC_WAITING, mi); @@ -198,6 +208,59 @@ void MusicManager::stopMusic() } // stopMusic //----------------------------------------------------------------------------- +/** Insert a command into the sfx queue to pause the current music. + */ +void MusicManager::pauseMusic() +{ + if (m_current_music) + SFXManager::get()->queue(SFXManager::SFX_MUSIC_PAUSE, m_current_music); +} // pauseMusic + +//----------------------------------------------------------------------------- +/** Inserts a resume current music event into the queue. + */ +void MusicManager::resumeMusic() +{ + if (m_current_music) + SFXManager::get()->queue(SFXManager::SFX_MUSIC_RESUME, m_current_music); +} // resumeMusic + +//----------------------------------------------------------------------------- +/** Switches to fast (last lap ) music (if defined for the current music). + */ +void MusicManager::switchToFastMusic() +{ + if (m_current_music) + SFXManager::get()->queue(SFXManager::SFX_MUSIC_SWITCH_FAST, + m_current_music); +} // switchToFastMusic + +//----------------------------------------------------------------------------- +/** Queues a command to temporarily change the volume. This is used to make + * the music a bit quieter while the 'last lap fanfare' is being played. + * \param gain The temporary volume value. + */ +void MusicManager::setTemporaryVolume(float gain) +{ + if (m_current_music) + SFXManager::get()->queue(SFXManager::SFX_MUSIC_SET_TMP_VOLUME, + m_current_music, gain); +} // setTemporaryVolume + +//----------------------------------------------------------------------------- +/** Queues a command for the sfx manager to reset a temporary volume change. + */ +void MusicManager::resetTemporaryVolume() +{ + if (m_current_music) + SFXManager::get()->queue(SFXManager::SFX_MUSIC_RESET_TMP_VOLUME, + m_current_music); +} // resetTemporaryVolume + +//----------------------------------------------------------------------------- +/** Sets the master music volume. + * \param gain The volume. + */ void MusicManager::setMasterMusicVolume(float gain) { if(gain > 1.0) @@ -205,15 +268,15 @@ void MusicManager::setMasterMusicVolume(float gain) if(gain < 0.0f) gain = 0.0f; - m_masterGain = gain; - if(m_current_music) m_current_music->volumeMusic(m_masterGain); + m_master_gain = gain; + if(m_current_music) m_current_music->volumeMusic(m_master_gain); - UserConfigParams::m_music_volume = m_masterGain; + UserConfigParams::m_music_volume = m_master_gain; } // setMasterMusicVolume //----------------------------------------------------------------------------- -/** - */ +/** @throw runtime_error if the music file could not be found/opened +*/ MusicInformation* MusicManager::getMusicInformation(const std::string& filename) { if(filename=="") @@ -229,7 +292,7 @@ MusicInformation* MusicManager::getMusicInformation(const std::string& filename) MusicInformation *mi = MusicInformation::create(filename); if(mi) { - mi->volumeMusic(m_masterGain); + mi->volumeMusic(m_master_gain); m_all_music[basename] = mi; } return mi; diff --git a/src/audio/music_manager.hpp b/src/audio/music_manager.hpp index 98b33537a..f77ce1a0d 100644 --- a/src/audio/music_manager.hpp +++ b/src/audio/music_manager.hpp @@ -42,42 +42,44 @@ private: /** If the sound could not be initialized, e.g. if the player doesn't has * a sound card, we want to avoid anything sound related so we crash the * game. */ - bool m_initialized; - std::map - m_all_music; + bool m_initialized; - void loadMusicInformation(); - float m_masterGain; + /** Stores all music information files (read from the .music files). */ + std::map + m_all_music; + float m_master_gain; + + void loadMusicInformation(); + void loadMusicFromOneDir(const std::string& dir); public: - MusicManager(); - virtual ~MusicManager(); + MusicManager(); + virtual ~MusicManager(); + MusicInformation* getMusicInformation(const std::string& filename); + void addMusicToTracks(); - void startMusic(MusicInformation* mi, - bool start_right_now=true); - void stopMusic(); - bool initialized() const { return m_initialized; } - void pauseMusic() {if(m_current_music) - m_current_music->pauseMusic(); } - void resumeMusic() {if(m_current_music) - m_current_music->resumeMusic(); } - void switchToFastMusic() {if(m_current_music) - m_current_music->switchToFastMusic();} - - void setMasterMusicVolume(float gain); - float getMasterMusicVolume() const { return m_masterGain; } - - MusicInformation *getCurrentMusic() {return m_current_music; } - - /** - * @throw runtime_error if the music file could not be found/opened - */ - MusicInformation *getMusicInformation(const std::string& filename); - - void loadMusicFromOneDir(const std::string& dir); - void addMusicToTracks(); - - void clearCurrentMusic() { m_current_music = NULL; } + void startMusic(); + void startMusic(MusicInformation* mi, + bool start_right_now=true); + void stopMusic(); + void pauseMusic(); + void resumeMusic(); + void switchToFastMusic(); + void setMasterMusicVolume(float gain); + void resetTemporaryVolume(); + void setTemporaryVolume(float gain); + // ------------------------------------------------------------------------ + /** Returns the master volume. */ + float getMasterMusicVolume() const { return m_master_gain; } + // ------------------------------------------------------------------------ + /** Returns if the music system is initialised. */ + bool initialized() const { return m_initialized; } + // ------------------------------------------------------------------------ + /** Returns the information object of the current music. */ + MusicInformation* getCurrentMusic() { return m_current_music; } + // ------------------------------------------------------------------------ + /** Stops and removes the current music. */ + void clearCurrentMusic() { m_current_music = NULL; } }; extern MusicManager* music_manager; diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index ded724e45..1baf827be 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -202,11 +202,25 @@ void SFXManager::queue(SFXCommands command, SFXBase *sfx, const Vec3 &p) } // queue (Vec3) //---------------------------------------------------------------------------- +/** Queues a command for the music manager. + * \param mi The music for which the command is. + */ void SFXManager::queue(SFXCommands command, MusicInformation *mi) { SFXCommand *sfx_command = new SFXCommand(command, mi); queueCommand(sfx_command); } // queue(MusicInformation) +//---------------------------------------------------------------------------- +/** Queues a command for the music manager that takes a floating point value + * (e.g. setTemporaryVolume). + * \param mi The music for which the command is. + * \param f The floating point parameter. + */ +void SFXManager::queue(SFXCommands command, MusicInformation *mi, float f) +{ + SFXCommand *sfx_command = new SFXCommand(command, mi, f); + queueCommand(sfx_command); +} // queue(MusicInformation) //---------------------------------------------------------------------------- /** Enqueues a command to the sfx queue threadsafe. Then signal the @@ -286,40 +300,57 @@ void* SFXManager::mainLoop(void *obj) break; } me->m_sfx_commands.unlock(); - switch(current->m_command) + switch (current->m_command) { - case SFX_PLAY: current->m_sfx->reallyPlayNow(); break; - case SFX_STOP: current->m_sfx->reallyStopNow(); break; - case SFX_PAUSE: current->m_sfx->reallyPauseNow(); break; - case SFX_RESUME: current->m_sfx->reallyResumeNow(); break; + case SFX_PLAY: current->m_sfx->reallyPlayNow(); break; + case SFX_STOP: current->m_sfx->reallyStopNow(); break; + case SFX_PAUSE: current->m_sfx->reallyPauseNow(); break; + case SFX_RESUME: current->m_sfx->reallyResumeNow(); break; case SFX_SPEED: current->m_sfx->reallySetSpeed( - current->m_parameter.getX()); break; + current->m_parameter.getX()); break; case SFX_POSITION: current->m_sfx->reallySetPosition( - current->m_parameter); break; + current->m_parameter); break; case SFX_VOLUME: current->m_sfx->reallySetVolume( - current->m_parameter.getX()); break; - case SFX_MASTER_VOLUME: - current->m_sfx->reallySetMasterVolumeNow( - current->m_parameter.getX()); break; + current->m_parameter.getX()); break; + case SFX_MASTER_VOLUME: + current->m_sfx->reallySetMasterVolumeNow( + current->m_parameter.getX()); break; case SFX_LOOP: current->m_sfx->reallySetLoop( - current->m_parameter.getX()!=0); break; - case SFX_DELETE: { - me->deleteSFX(current->m_sfx); break; - } - case SFX_PAUSE_ALL: me->reallyPauseAllNow(); break; - case SFX_RESUME_ALL: me->reallyResumeAllNow(); break; - case SFX_LISTENER: me->reallyPositionListenerNow(); break; - case SFX_UPDATE: me->reallyUpdateNow(current); break; + current->m_parameter.getX() != 0); break; + case SFX_DELETE: me->deleteSFX(current->m_sfx); break; + case SFX_PAUSE_ALL: me->reallyPauseAllNow(); break; + case SFX_RESUME_ALL: me->reallyResumeAllNow(); break; + case SFX_LISTENER: me->reallyPositionListenerNow(); break; + case SFX_UPDATE: me->reallyUpdateNow(current); break; case SFX_MUSIC_START: - current->m_music_information->startMusic(); break; + current->m_music_information->startMusic(); break; case SFX_MUSIC_STOP: - current->m_music_information->stopMusic(); break; - case SFX_MUSIC_WAITING: - current->m_music_information->setMusicWaiting(); break; + current->m_music_information->stopMusic(); break; + case SFX_MUSIC_PAUSE: + current->m_music_information->pauseMusic(); break; + case SFX_MUSIC_RESUME: + current->m_music_information->resumeMusic(); break; + case SFX_MUSIC_SWITCH_FAST: + current->m_music_information->switchToFastMusic(); break; + case SFX_MUSIC_SET_TMP_VOLUME: + { + MusicInformation *mi = current->m_music_information; + mi->setTemporaryVolume(current->m_parameter.getX()); break; + } + case SFX_MUSIC_RESET_TMP_VOLUME: + { + MusicInformation *mi = current->m_music_information; + mi->resetTemporaryVolume(); break; + } + case SFX_MUSIC_WAITING: + current->m_music_information->setMusicWaiting(); break; default: assert("Not yet supported."); } - delete current; - current = NULL; + static SFXCommand *prev = NULL; + delete prev; + prev = current; + //delete current; + //current = NULL; me->m_sfx_commands.lock(); } // while @@ -622,7 +653,7 @@ void SFXManager::deleteSFXMapping(const std::string &name) */ void SFXManager::update(float dt) { - queue(SFX_UPDATE, NULL, dt); + queue(SFX_UPDATE, (SFXBase*)NULL, dt); // Wake up the sfx thread to handle all queued up audio commands. pthread_cond_signal(&m_cond_request); } // update diff --git a/src/audio/sfx_manager.hpp b/src/audio/sfx_manager.hpp index 5c3a43ca3..7cfa2e743 100644 --- a/src/audio/sfx_manager.hpp +++ b/src/audio/sfx_manager.hpp @@ -79,7 +79,11 @@ public: SFX_UPDATE, SFX_MUSIC_START, SFX_MUSIC_STOP, - SFX_MUSIC_UPDATE, + SFX_MUSIC_PAUSE, + SFX_MUSIC_RESUME, + SFX_MUSIC_SWITCH_FAST, + SFX_MUSIC_SET_TMP_VOLUME, + SFX_MUSIC_RESET_TMP_VOLUME, SFX_MUSIC_WAITING, SFX_EXIT, }; // SFXCommands @@ -135,7 +139,16 @@ private: { m_command = command; m_music_information = mi; - } // SFXCommnd + } // SFXCommnd(MusicInformation*) + // -------------------------------------------------------------------- + /** Constructor for music information commands that take a floating + * point parameter (which is stored in the X value of m_parameter). */ + SFXCommand(SFXCommands command, MusicInformation *mi, float f) + { + m_command = command; + m_parameter.setX(f); + m_music_information = mi; + } // SFXCommnd(MusicInformation *, float) // -------------------------------------------------------------------- SFXCommand(SFXCommands command, SFXBase *base, float parameter) { @@ -205,6 +218,7 @@ public: void queue(SFXCommands command, SFXBase *sfx, float f); void queue(SFXCommands command, SFXBase *sfx, const Vec3 &p); void queue(SFXCommands command, MusicInformation *mi); + void queue(SFXCommands command, MusicInformation *mi, float f); // ------------------------------------------------------------------------ /** Static function to get the singleton sfx manager. */ static SFXManager *get() diff --git a/src/main.cpp b/src/main.cpp index 18bea6fea..5acfb4f71 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1516,7 +1516,8 @@ static void cleanSuperTuxKart() SFXManager::destroy(); // Music manager can not be deleted before the sfx thread is stopped - // (since sfx commands can contain music information). + // (since sfx commands can contain music information, which are + // deleted by the music manager). delete music_manager; // The addons manager might still be called from a currenty running request diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 4d884189e..4b83ee3b2 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -157,8 +157,7 @@ void LinearWorld::update(float dt) if (m_last_lap_sfx_playing && m_last_lap_sfx->getStatus() != SFXBase::SFX_PLAYING) { - if(music_manager->getCurrentMusic()) - music_manager->getCurrentMusic()->resetTemporaryVolume(); + music_manager->resetTemporaryVolume(); m_last_lap_sfx_playing = false; } @@ -288,7 +287,7 @@ void LinearWorld::newLap(unsigned int kart_index) if(music_manager->getCurrentMusic() && music_manager->getMasterMusicVolume() > 0.2f) { - music_manager->getCurrentMusic()->setTemporaryVolume(0.2f); + music_manager->setTemporaryVolume(0.2f); } } else diff --git a/src/modes/overworld.cpp b/src/modes/overworld.cpp index 187856fe9..9097c4add 100644 --- a/src/modes/overworld.cpp +++ b/src/modes/overworld.cpp @@ -114,9 +114,8 @@ void OverWorld::update(float dt) // so we have to start music 'manually', since we skip all phases. World::getWorld()->getTrack()->startMusic(); - if (music_manager->getCurrentMusic() != NULL && - UserConfigParams::m_music) - music_manager->getCurrentMusic()->startMusic(); + if (UserConfigParams::m_music) + music_manager->startMusic(); m_karts[0]->startEngineSFX(); } WorldWithRank::update(dt); From ddc60f98a11547a214ec252fc541e66c09679233 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 4 Feb 2015 18:18:37 +1100 Subject: [PATCH 006/117] Removed unnecesary #include. --- src/items/projectile_manager.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/items/projectile_manager.hpp b/src/items/projectile_manager.hpp index 111da8db2..0ef2c1e0d 100644 --- a/src/items/projectile_manager.hpp +++ b/src/items/projectile_manager.hpp @@ -26,7 +26,6 @@ namespace irr namespace scene { class IMesh; } } -#include "audio/sfx_manager.hpp" #include "items/powerup_manager.hpp" #include "utils/no_copy.hpp" From 1aa6f7c173043f95bcc454b2a34e805228977a23 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Wed, 4 Feb 2015 14:42:10 +0100 Subject: [PATCH 007/117] Improve the layout of the race result screen --- src/states_screens/race_result_gui.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 7736a6c33..53afd1455 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -515,9 +515,7 @@ void RaceResultGUI::determineTableLayout() if(m_distance_between_rows * num_karts > height) m_distance_between_rows = height / num_karts; - m_width_icon = table_area->m_h<600 - ? 27 - : (int)(40*(table_area->m_w/800.0f)); + m_width_icon = table_area->m_h / 18; m_width_column_space = 10; @@ -1250,6 +1248,10 @@ void RaceResultGUI::displayHighScores() // prevent excessive long name unsigned int max_characters = 15; + unsigned int max_width = (UserConfigParams::m_width / 2 - 200) / 10; + if (max_width < 15) + max_characters = max_width; + float time; for (int i = 0; i < scores->getNumberEntries(); i++) { @@ -1267,7 +1269,7 @@ void RaceResultGUI::displayHighScores() } int current_x = x; - int current_y = y+(i+1)*50; + int current_y = y + (int) ((i + 1) * m_distance_between_rows * 1.5f); const KartProperties* prop = kart_properties_manager->getKart(kart_name); if (prop != NULL) @@ -1298,7 +1300,7 @@ void RaceResultGUI::displayHighScores() text_color, false, false, NULL, true /* ignoreRTL */); - current_x += 180; + current_x = (int) (UserConfigParams::m_width * 0.85f); // Finally draw the time std::string time_string = StringUtils::timeToString(time); From 1e994748cc3f7c86ead7c9d58084bb20f97389ea Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 5 Feb 2015 09:22:13 +1100 Subject: [PATCH 008/117] Properly handle volume setting from sfx thread, removed MusicManager from friends of MusicInformation to enforce this. Added error checks to properly detect location of warning when faster music is plaued (still under investigation). --- src/audio/music_information.hpp | 5 ++--- src/audio/music_manager.cpp | 10 +++++++--- src/audio/music_ogg.cpp | 3 ++- src/audio/sfx_manager.cpp | 21 +++++++++++++++------ src/audio/sfx_manager.hpp | 1 + src/audio/sfx_openal.cpp | 3 ++- 6 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/audio/music_information.hpp b/src/audio/music_information.hpp index b35fd5385..21b622d30 100644 --- a/src/audio/music_information.hpp +++ b/src/audio/music_information.hpp @@ -78,11 +78,10 @@ private: MusicInformation (const XMLNode *root, const std::string &filename); // Declare the following functions private, but allow the SFXManager - // and music manager to access them. This makes sure that only the sfx thread calls - // openal/vorbis etc, and so makes it all thread safe. + // to access them. This makes sure that only the sfx thread calls + // openal/vorbis etc, and so makes it is all thread safe. private: friend class SFXManager; - friend class MusicManager; void update(float dt); void startMusic(); void stopMusic(); diff --git a/src/audio/music_manager.cpp b/src/audio/music_manager.cpp index 5ee8ca732..12ddb70d2 100644 --- a/src/audio/music_manager.cpp +++ b/src/audio/music_manager.cpp @@ -192,7 +192,6 @@ void MusicManager::startMusic(MusicInformation* mi, bool start_right_now) if(!mi || !UserConfigParams::m_music || !m_initialized) return; - mi->volumeMusic(m_master_gain); SFXManager::get()->queue(start_right_now ? SFXManager::SFX_MUSIC_START : SFXManager::SFX_MUSIC_WAITING, mi); @@ -269,7 +268,12 @@ void MusicManager::setMasterMusicVolume(float gain) gain = 0.0f; m_master_gain = gain; - if(m_current_music) m_current_music->volumeMusic(m_master_gain); + if (m_current_music) + { + // Sets the music volume to m_master_gain + SFXManager::get()->queue(SFXManager::SFX_MUSIC_VOLUME, + m_current_music); + } UserConfigParams::m_music_volume = m_master_gain; } // setMasterMusicVolume @@ -292,7 +296,7 @@ MusicInformation* MusicManager::getMusicInformation(const std::string& filename) MusicInformation *mi = MusicInformation::create(filename); if(mi) { - mi->volumeMusic(m_master_gain); + SFXManager::get()->queue(SFXManager::SFX_MUSIC_VOLUME, mi); m_all_music[basename] = mi; } return mi; diff --git a/src/audio/music_ogg.cpp b/src/audio/music_ogg.cpp index e95ed3d8a..68678a6cf 100644 --- a/src/audio/music_ogg.cpp +++ b/src/audio/music_ogg.cpp @@ -189,7 +189,7 @@ bool MusicOggStream::playMusic() alSourcePlay(m_soundSource); m_pausedMusic = false; m_playing = true; - + check("playMusic"); return true; } // playMusic @@ -253,6 +253,7 @@ void MusicOggStream::volumeMusic(float gain) if (gain < 0.0f) gain = 0.0f; alSourcef(m_soundSource, AL_GAIN, gain); + check("volume music"); // clear errors } // volumeMusic //----------------------------------------------------------------------------- diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 1baf827be..01df82cfb 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -22,6 +22,7 @@ #include "audio/sfx_buffer.hpp" #include "config/user_config.hpp" #include "io/file_manager.hpp" +#include "modes/world.hpp" #include "race/race_manager.hpp" #include @@ -230,7 +231,8 @@ void SFXManager::queue(SFXCommands command, MusicInformation *mi, float f) void SFXManager::queueCommand(SFXCommand *command) { m_sfx_commands.lock(); - if(m_sfx_commands.getData().size() > 20*race_manager->getNumberOfKarts()+20 && + if(World::getWorld() && + m_sfx_commands.getData().size() > 20*race_manager->getNumberOfKarts()+20 && race_manager->getMinorMode() != RaceManager::MINOR_MODE_CUTSCENE) { if(command->m_command==SFX_POSITION || command->m_command==SFX_LOOP || @@ -263,6 +265,7 @@ void SFXManager::stopThread() pthread_cond_signal(&m_cond_request); } // stopThread +#include "audio/sfx_openal.hpp" //---------------------------------------------------------------------------- /** This loops runs in a different threads, and starts sfx to be played. * This can sometimes take up to 5 ms, so it needs to be handled in a thread @@ -323,7 +326,11 @@ void* SFXManager::mainLoop(void *obj) case SFX_LISTENER: me->reallyPositionListenerNow(); break; case SFX_UPDATE: me->reallyUpdateNow(current); break; case SFX_MUSIC_START: + { + float gain = music_manager->getMasterMusicVolume(); + current->m_music_information->volumeMusic(gain); current->m_music_information->startMusic(); break; + } case SFX_MUSIC_STOP: current->m_music_information->stopMusic(); break; case SFX_MUSIC_PAUSE: @@ -344,13 +351,15 @@ void* SFXManager::mainLoop(void *obj) } case SFX_MUSIC_WAITING: current->m_music_information->setMusicWaiting(); break; + case SFX_MUSIC_VOLUME: + { + float gain = music_manager->getMasterMusicVolume(); + current->m_music_information->volumeMusic(gain); + } default: assert("Not yet supported."); } - static SFXCommand *prev = NULL; - delete prev; - prev = current; - //delete current; - //current = NULL; + delete current; + current = NULL; me->m_sfx_commands.lock(); } // while diff --git a/src/audio/sfx_manager.hpp b/src/audio/sfx_manager.hpp index 7cfa2e743..5f99a3aeb 100644 --- a/src/audio/sfx_manager.hpp +++ b/src/audio/sfx_manager.hpp @@ -85,6 +85,7 @@ public: SFX_MUSIC_SET_TMP_VOLUME, SFX_MUSIC_RESET_TMP_VOLUME, SFX_MUSIC_WAITING, + SFX_MUSIC_VOLUME, SFX_EXIT, }; // SFXCommands diff --git a/src/audio/sfx_openal.cpp b/src/audio/sfx_openal.cpp index 572f296d7..8f86afb9d 100644 --- a/src/audio/sfx_openal.cpp +++ b/src/audio/sfx_openal.cpp @@ -174,6 +174,7 @@ void SFXOpenAL::reallySetSpeed(float factor) factor = 0.5f; } alSourcef(m_sound_source,AL_PITCH,factor); + SFXManager::checkError("setting speed"); } // reallySetSpeed //----------------------------------------------------------------------------- @@ -415,7 +416,7 @@ void SFXOpenAL::reallySetPosition(const Vec3 &position) alSource3f(m_sound_source, AL_POSITION, position.getX(), position.getY(), -position.getZ()); - if (SFXManager::get()->getListenerPos().distance(position) + if (SFXManager::get()->getListenerPos().distance(position) > m_sound_buffer->getMaxDist()) { alSourcef(m_sound_source, AL_GAIN, 0); From ecad7c2996e5b0625bbdcd01d3207464fe3c4f8e Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Wed, 4 Feb 2015 20:13:06 -0500 Subject: [PATCH 009/117] Mark some emitters as important so that they are never disabled by options, fixes #1808 --- src/graphics/particle_emitter.cpp | 4 ++- src/graphics/particle_emitter.hpp | 5 +++- src/karts/abstract_kart.hpp | 2 ++ src/karts/kart.cpp | 3 ++ src/karts/kart.hpp | 3 ++ src/karts/kart_gfx.cpp | 49 ++++++++++++++++++------------- src/karts/kart_gfx.hpp | 2 +- 7 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/graphics/particle_emitter.cpp b/src/graphics/particle_emitter.cpp index 50380e1ab..5ba8d0342 100644 --- a/src/graphics/particle_emitter.cpp +++ b/src/graphics/particle_emitter.cpp @@ -288,7 +288,8 @@ public: ParticleEmitter::ParticleEmitter(const ParticleKind* type, const Vec3 &position, scene::ISceneNode* parent, - bool randomize_initial_y) + bool randomize_initial_y, + bool important) : m_position(position) { assert(type != NULL); @@ -300,6 +301,7 @@ ParticleEmitter::ParticleEmitter(const ParticleKind* type, m_emission_decay_rate = 0; m_is_glsl = CVS->isGLSL(); m_randomize_initial_y = randomize_initial_y; + m_important = important; setParticleType(type); diff --git a/src/graphics/particle_emitter.hpp b/src/graphics/particle_emitter.hpp index e45654ae3..4e360d5c9 100644 --- a/src/graphics/particle_emitter.hpp +++ b/src/graphics/particle_emitter.hpp @@ -76,6 +76,8 @@ private: bool m_randomize_initial_y; + bool m_important; + public: LEAK_CHECK() @@ -83,7 +85,8 @@ public: ParticleEmitter (const ParticleKind* type, const Vec3 &position, scene::ISceneNode* parent = NULL, - bool randomize_initial_y = false); + bool randomize_initial_y = false, + bool important = false); virtual ~ParticleEmitter(); virtual void update (float dt); void setCreationRateAbsolute(float fraction); diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 47188cafb..e47285daf 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -233,6 +233,8 @@ public: * skidding related values). */ virtual const Skidding *getSkidding() const = 0; // ------------------------------------------------------------------------ + virtual RaceManager::KartType getType() const = 0; + // ------------------------------------------------------------------------ /** Returns the skidding object for this kart (which can be used to query * skidding related values), non-const. */ virtual Skidding *getSkidding() = 0; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index a885883d3..ea446fe6e 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -132,6 +132,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, m_fire_clicked = 0; m_wrongway_counter = 0; m_nitro_light = NULL; + m_type = RaceManager::KT_AI; m_view_blocked_by_plunger = 0; m_has_caught_nolok_bubblegum = false; @@ -178,6 +179,8 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, */ void Kart::init(RaceManager::KartType type) { + m_type = type; + // In multiplayer mode, sounds are NOT positional if (race_manager->getNumLocalPlayers() > 1) { diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 37bcc3b4b..7c685d66d 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -217,6 +217,7 @@ private: SFXBase *m_goo_sound; SFXBase *m_boing_sound; float m_time_last_crash; + RaceManager::KartType m_type; /** To prevent using nitro in too short bursts */ float m_min_nitro_time; @@ -359,6 +360,8 @@ public: * skidding related values) - non-const. */ virtual Skidding *getSkidding() { return m_skidding; } // ------------------------------------------------------------------------ + virtual RaceManager::KartType getType() const { return m_type; } + // ------------------------------------------------------------------------ /** Returns the bullet vehicle which represents this kart. */ virtual btKart *getVehicle() const {return m_vehicle; } // ------------------------------------------------------------------------ diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index cb144167a..19bf92bf0 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -24,6 +24,8 @@ #include "graphics/particle_kind.hpp" #include "graphics/particle_kind_manager.hpp" #include "karts/abstract_kart.hpp" +#include "karts/controller/controller.hpp" +#include "karts/kart.hpp" #include "karts/kart_properties.hpp" #include "karts/skidding.hpp" #include "physics/btKart.hpp" @@ -33,12 +35,12 @@ KartGFX::KartGFX(const AbstractKart *kart) { - if(!UserConfigParams::m_graphical_effects) - { - for(unsigned int i=0; igetType() == RaceManager::KT_AI)) + { + m_all_emitters.push_back(NULL); + return; + } + const ParticleKind *kind = NULL; ParticleEmitter *emitter = NULL; try @@ -113,9 +122,9 @@ void KartGFX::addEffect(KartGFXType type, const std::string &file_name, else if(type==KGFX_TERRAIN) // Terrain is NOT a child of the kart, since bullet returns the // raycast info in world coordinates - emitter = new ParticleEmitter(kind, position); + emitter = new ParticleEmitter(kind, position, NULL, false, important); else - emitter = new ParticleEmitter(kind, position, m_kart->getNode()); + emitter = new ParticleEmitter(kind, position, m_kart->getNode(), false, important); } catch (std::runtime_error& e) { @@ -293,8 +302,6 @@ void KartGFX::updateTerrain(const ParticleKind *pk) */ void KartGFX::update(float dt) { - if(!UserConfigParams::m_graphical_effects) return; - m_wheel_toggle = 1 - m_wheel_toggle; for(unsigned int i=0; i Date: Thu, 5 Feb 2015 02:43:20 +0100 Subject: [PATCH 010/117] Fix clang build, freeing memory too early --- src/modes/linear_world.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 4d884189e..18399da5a 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -350,10 +350,13 @@ void LinearWorld::newLap(unsigned int kart_index) std::string s = StringUtils::timeToString(time_per_lap); - irr::core::stringw m_fastest_lap_message; + // Store the temporary string because clang would mess this up + // (remove the stringw before the wchar_t* is used). + core::stringw css = kart->getName(); + //I18N: as in "fastest lap: 60 seconds by Wilber" - m_fastest_lap_message += _C("fastest_lap", "%s by %s", s.c_str(), - core::stringw(kart->getName())); + irr::core::stringw m_fastest_lap_message = + _C("fastest_lap", "%s by %s", s.c_str(), css.c_str()); m_race_gui->addMessage(m_fastest_lap_message, NULL, 3.0f, video::SColor(255, 255, 255, 255), false); From 5f59985ef1b556cb1251bf1dd6c3eae80000d1e8 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 5 Feb 2015 13:59:16 +1100 Subject: [PATCH 011/117] Added documentation about which asset version is used for which STK version. --- doc/assets_version | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 doc/assets_version diff --git a/doc/assets_version b/doc/assets_version new file mode 100644 index 000000000..7bc1f5cb6 --- /dev/null +++ b/doc/assets_version @@ -0,0 +1,6 @@ +This file keeps track of which svn assets version is used for which stk release. +Atm there is no mechanism in place to verify this. + +stk release svn version of assets +--------------------------------------- +0.8.2-beta2 needs r15985 From 0ab3d12eabf9985738049ebd5f957f4ac4d39fd7 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 5 Feb 2015 15:22:20 +1100 Subject: [PATCH 012/117] Fixed invalid name openal error when setting the volume of fast music (apparently caused by trying to set the music of the actually freeded normal music just before). --- src/audio/music_information.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/audio/music_information.cpp b/src/audio/music_information.cpp index ce00991bd..7f07c2b38 100644 --- a/src/audio/music_information.cpp +++ b/src/audio/music_information.cpp @@ -301,8 +301,10 @@ void MusicInformation::resumeMusic() void MusicInformation::volumeMusic(float gain) { m_adjusted_gain = m_gain * gain; - if (m_normal_music != NULL) m_normal_music->volumeMusic(m_adjusted_gain); - if (m_fast_music != NULL) m_fast_music->volumeMusic(m_adjusted_gain); + if (m_normal_music && m_normal_music->isPlaying()) + m_normal_music->volumeMusic(m_adjusted_gain); + if (m_fast_music && m_fast_music->isPlaying()) + m_fast_music->volumeMusic(m_adjusted_gain); } // volumeMusic //----------------------------------------------------------------------------- From 2d4200c7546e2a375fe1ba0d120e5c55f7e8b122 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 5 Feb 2015 18:29:05 +1100 Subject: [PATCH 013/117] Removed rather embarrassing left-over debug code. --- src/audio/sfx_manager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 01df82cfb..edb5fd05b 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -265,7 +265,6 @@ void SFXManager::stopThread() pthread_cond_signal(&m_cond_request); } // stopThread -#include "audio/sfx_openal.hpp" //---------------------------------------------------------------------------- /** This loops runs in a different threads, and starts sfx to be played. * This can sometimes take up to 5 ms, so it needs to be handled in a thread From 31d4c34fbb7819e359cea4ef9e333fb39637125d Mon Sep 17 00:00:00 2001 From: Flakebi Date: Thu, 5 Feb 2015 15:18:57 +0100 Subject: [PATCH 014/117] Improve clang bugfix --- src/modes/linear_world.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 18399da5a..40cfa20b8 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -352,11 +352,11 @@ void LinearWorld::newLap(unsigned int kart_index) // Store the temporary string because clang would mess this up // (remove the stringw before the wchar_t* is used). - core::stringw css = kart->getName(); + core::stringw kartName = kart->getName(); //I18N: as in "fastest lap: 60 seconds by Wilber" irr::core::stringw m_fastest_lap_message = - _C("fastest_lap", "%s by %s", s.c_str(), css.c_str()); + _C("fastest_lap", "%s by %s", s.c_str(), kartName); m_race_gui->addMessage(m_fastest_lap_message, NULL, 3.0f, video::SColor(255, 255, 255, 255), false); From d56472c224997139167a5e5dbb85f7e171e33efb Mon Sep 17 00:00:00 2001 From: Flakebi Date: Thu, 5 Feb 2015 21:32:17 +0100 Subject: [PATCH 015/117] Rename some .h files to .hpp --- src/guiengine/CGUISpriteBank.cpp | 2 +- src/guiengine/{CGUISpriteBank.h => CGUISpriteBank.hpp} | 0 src/guiengine/widgets/CGUIEditBox.cpp | 2 +- src/guiengine/widgets/{CGUIEditBox.h => CGUIEditBox.hpp} | 0 src/guiengine/widgets/CGUISTKListBox.cpp | 2 +- .../widgets/{CGUISTKListBox.h => CGUISTKListBox.hpp} | 0 src/guiengine/widgets/list_widget.cpp | 4 ++-- src/guiengine/widgets/list_widget.hpp | 2 +- src/guiengine/widgets/text_box_widget.cpp | 2 +- src/states_screens/addons_screen.cpp | 2 +- src/states_screens/edit_gp_screen.cpp | 2 +- src/states_screens/options_screen_input.cpp | 2 +- src/states_screens/options_screen_input2.cpp | 2 +- 13 files changed, 11 insertions(+), 11 deletions(-) rename src/guiengine/{CGUISpriteBank.h => CGUISpriteBank.hpp} (100%) rename src/guiengine/widgets/{CGUIEditBox.h => CGUIEditBox.hpp} (100%) rename src/guiengine/widgets/{CGUISTKListBox.h => CGUISTKListBox.hpp} (100%) diff --git a/src/guiengine/CGUISpriteBank.cpp b/src/guiengine/CGUISpriteBank.cpp index 925a2e1c9..19150fc2f 100644 --- a/src/guiengine/CGUISpriteBank.cpp +++ b/src/guiengine/CGUISpriteBank.cpp @@ -2,7 +2,7 @@ // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h -#include "guiengine/CGUISpriteBank.h" +#include "guiengine/CGUISpriteBank.hpp" #ifdef _IRR_COMPILE_WITH_GUI_ #include "IGUIEnvironment.h" diff --git a/src/guiengine/CGUISpriteBank.h b/src/guiengine/CGUISpriteBank.hpp similarity index 100% rename from src/guiengine/CGUISpriteBank.h rename to src/guiengine/CGUISpriteBank.hpp diff --git a/src/guiengine/widgets/CGUIEditBox.cpp b/src/guiengine/widgets/CGUIEditBox.cpp index 114c63854..9bac16163 100644 --- a/src/guiengine/widgets/CGUIEditBox.cpp +++ b/src/guiengine/widgets/CGUIEditBox.cpp @@ -2,7 +2,7 @@ // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h -#include "CGUIEditBox.h" +#include "guiengine/widgets/CGUIEditBox.hpp" #include "IGUISkin.h" #include "IGUIEnvironment.h" diff --git a/src/guiengine/widgets/CGUIEditBox.h b/src/guiengine/widgets/CGUIEditBox.hpp similarity index 100% rename from src/guiengine/widgets/CGUIEditBox.h rename to src/guiengine/widgets/CGUIEditBox.hpp diff --git a/src/guiengine/widgets/CGUISTKListBox.cpp b/src/guiengine/widgets/CGUISTKListBox.cpp index 21d3bbfe6..0ce2cb155 100644 --- a/src/guiengine/widgets/CGUISTKListBox.cpp +++ b/src/guiengine/widgets/CGUISTKListBox.cpp @@ -3,7 +3,7 @@ // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h -#include "guiengine/widgets/CGUISTKListBox.h" +#include "guiengine/widgets/CGUISTKListBox.hpp" #include "IGUISkin.h" #include "IGUIEnvironment.h" diff --git a/src/guiengine/widgets/CGUISTKListBox.h b/src/guiengine/widgets/CGUISTKListBox.hpp similarity index 100% rename from src/guiengine/widgets/CGUISTKListBox.h rename to src/guiengine/widgets/CGUISTKListBox.hpp diff --git a/src/guiengine/widgets/list_widget.cpp b/src/guiengine/widgets/list_widget.cpp index 81f5df6c6..24ffc1cdd 100644 --- a/src/guiengine/widgets/list_widget.cpp +++ b/src/guiengine/widgets/list_widget.cpp @@ -17,14 +17,14 @@ #include "guiengine/widgets/list_widget.hpp" -#include "guiengine/CGUISpriteBank.h" +#include "guiengine/CGUISpriteBank.hpp" #include "guiengine/engine.hpp" #include "io/file_manager.hpp" #include #include #include -#include "IGUIFontBitmap.h" +#include #include diff --git a/src/guiengine/widgets/list_widget.hpp b/src/guiengine/widgets/list_widget.hpp index 0c0ebeb17..4bbfe5c0c 100644 --- a/src/guiengine/widgets/list_widget.hpp +++ b/src/guiengine/widgets/list_widget.hpp @@ -22,7 +22,7 @@ #include -#include "guiengine/widgets/CGUISTKListBox.h" +#include "guiengine/widgets/CGUISTKListBox.hpp" #include "guiengine/widget.hpp" #include "guiengine/widgets/button_widget.hpp" #include "utils/leak_check.hpp" diff --git a/src/guiengine/widgets/text_box_widget.cpp b/src/guiengine/widgets/text_box_widget.cpp index 0d17dd4aa..461f1692b 100644 --- a/src/guiengine/widgets/text_box_widget.cpp +++ b/src/guiengine/widgets/text_box_widget.cpp @@ -19,7 +19,7 @@ #include "guiengine/modaldialog.hpp" #include "guiengine/widgets/text_box_widget.hpp" -#include "guiengine/widgets/CGUIEditBox.h" +#include "guiengine/widgets/CGUIEditBox.hpp" #include "utils/ptr_vector.hpp" #include "utils/translation.hpp" diff --git a/src/states_screens/addons_screen.cpp b/src/states_screens/addons_screen.cpp index dd603b214..c7c851ec7 100644 --- a/src/states_screens/addons_screen.cpp +++ b/src/states_screens/addons_screen.cpp @@ -20,7 +20,7 @@ #include "addons/addons_manager.hpp" #include "addons/news_manager.hpp" #include "config/user_config.hpp" -#include "guiengine/CGUISpriteBank.h" +#include "guiengine/CGUISpriteBank.hpp" #include "guiengine/modaldialog.hpp" #include "guiengine/scalable_font.hpp" #include "guiengine/widget.hpp" diff --git a/src/states_screens/edit_gp_screen.cpp b/src/states_screens/edit_gp_screen.cpp index 7e4202b73..92f9eeeb1 100644 --- a/src/states_screens/edit_gp_screen.cpp +++ b/src/states_screens/edit_gp_screen.cpp @@ -18,7 +18,7 @@ #include "states_screens/edit_gp_screen.hpp" #include "graphics/irr_driver.hpp" -#include "guiengine/CGUISpriteBank.h" +#include "guiengine/CGUISpriteBank.hpp" #include "guiengine/widgets/dynamic_ribbon_widget.hpp" #include "guiengine/widgets/icon_button_widget.hpp" #include "guiengine/widgets/label_widget.hpp" diff --git a/src/states_screens/options_screen_input.cpp b/src/states_screens/options_screen_input.cpp index 0e9eb42cc..1d9b34041 100644 --- a/src/states_screens/options_screen_input.cpp +++ b/src/states_screens/options_screen_input.cpp @@ -18,7 +18,7 @@ #include "states_screens/options_screen_input.hpp" #include "graphics/irr_driver.hpp" -#include "guiengine/CGUISpriteBank.h" +#include "guiengine/CGUISpriteBank.hpp" #include "guiengine/screen.hpp" #include "guiengine/widget.hpp" #include "guiengine/widgets/button_widget.hpp" diff --git a/src/states_screens/options_screen_input2.cpp b/src/states_screens/options_screen_input2.cpp index 9e2dc5cbf..0f60f181e 100644 --- a/src/states_screens/options_screen_input2.cpp +++ b/src/states_screens/options_screen_input2.cpp @@ -18,7 +18,7 @@ #include "states_screens/options_screen_input2.hpp" #include "config/user_config.hpp" -#include "guiengine/CGUISpriteBank.h" +#include "guiengine/CGUISpriteBank.hpp" #include "guiengine/scalable_font.hpp" #include "guiengine/screen.hpp" #include "guiengine/widget.hpp" From 13393ef04bc364534cd0a1b134eecddcff5dc67c Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Sat, 7 Feb 2015 01:55:14 +0100 Subject: [PATCH 016/117] Fix 1808 --- src/graphics/render.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 13fe63705..40e3986b9 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -378,6 +378,13 @@ void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned po glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); renderSolidFirstPass(); } + else + { + // We need a cleared depth buffer for some effect (eg particles depth blending) + m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind(); + glClear(GL_DEPTH_BUFFER_BIT); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } PROFILER_POP_CPU_MARKER(); From 758eb6e0034c945efc1d72c4195b02262272d471 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Fri, 6 Feb 2015 20:11:15 -0500 Subject: [PATCH 017/117] Use label with word_wrap instead of list for terms. See #1812 --- data/gui/online/registration_terms.stkgui | 2 +- .../dialogs/registration_dialog.cpp | 19 +++++++------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/data/gui/online/registration_terms.stkgui b/data/gui/online/registration_terms.stkgui index cf6d202a4..1447495be 100644 --- a/data/gui/online/registration_terms.stkgui +++ b/data/gui/online/registration_terms.stkgui @@ -7,7 +7,7 @@ - +
diff --git a/src/states_screens/dialogs/registration_dialog.cpp b/src/states_screens/dialogs/registration_dialog.cpp index 13acc1959..653a8586d 100644 --- a/src/states_screens/dialogs/registration_dialog.cpp +++ b/src/states_screens/dialogs/registration_dialog.cpp @@ -37,19 +37,14 @@ RegistrationDialog::RegistrationDialog() : ModalDialog(0.8f,0.9f) { loadFromFile("online/registration_terms.stkgui"); - ListWidget * terms_widget = getWidget("terms"); + LabelWidget* terms_widget = getWidget("terms"); - - terms_widget->addItem("title", "=== STK Terms and Conditions ===", -1 , true ); - terms_widget->addItem("par1", "You must agree to these terms in order to register an account for STK.", -1 , false ); - terms_widget->addItem("par2", - "Still needs actual content. Preferably in an XML document which can then be parsed to be put here." - , -1 , false ); - terms_widget->addItem("par3", - "By checking the box below, you are confirming that you understand these terms." - "If you have any questions or comments regarding these terms," - "one of the members of the development team would gladly assist you." - , -1 , false ); + core::stringw terms = core::stringw(L"You must agree to these terms in order to register an account for STK." + L"Still needs actual content. Preferably in an XML document which can then be parsed to be put here." + L"By checking the box below, you are confirming that you understand these terms." + L"If you have any questions or comments regarding these terms," + L"one of the members of the development team would gladly assist you."); + terms_widget->setText(terms, false); // showRegistrationTerms(); } From 43aadcef348795e11eb789a8404234bff973353b Mon Sep 17 00:00:00 2001 From: hiker Date: Sun, 8 Feb 2015 22:12:08 +1100 Subject: [PATCH 018/117] Follow coding style for #includes. --- src/audio/music_manager.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/audio/music_manager.hpp b/src/audio/music_manager.hpp index f77ce1a0d..a4c858b36 100644 --- a/src/audio/music_manager.hpp +++ b/src/audio/music_manager.hpp @@ -20,14 +20,14 @@ #ifndef HEADER_MUSICMANAGER_HPP #define HEADER_MUSICMANAGER_HPP -#include -#include -#include - #include "audio/music.hpp" #include "audio/music_information.hpp" #include "utils/no_copy.hpp" +#include +#include +#include + class Vec3; /** From ca2b5335dc291c17d8c0a8653ae3382fcf7d32d5 Mon Sep 17 00:00:00 2001 From: hiker Date: Sun, 8 Feb 2015 22:22:06 +1100 Subject: [PATCH 019/117] Removed hopefully unnecessary string copy. --- src/modes/linear_world.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 78bd96236..d8bd33c07 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -351,11 +351,11 @@ void LinearWorld::newLap(unsigned int kart_index) // Store the temporary string because clang would mess this up // (remove the stringw before the wchar_t* is used). - core::stringw kartName = kart->getName(); + const core::stringw &kart_name = kart->getName(); //I18N: as in "fastest lap: 60 seconds by Wilber" irr::core::stringw m_fastest_lap_message = - _C("fastest_lap", "%s by %s", s.c_str(), kartName); + _C("fastest_lap", "%s by %s", s.c_str(), kart_name); m_race_gui->addMessage(m_fastest_lap_message, NULL, 3.0f, video::SColor(255, 255, 255, 255), false); From 70ed2b47d0629fbbc5cc876df050c50580516eec Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 9 Feb 2015 08:22:35 +1100 Subject: [PATCH 020/117] Fix #1962. --- src/states_screens/options_screen_audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/states_screens/options_screen_audio.cpp b/src/states_screens/options_screen_audio.cpp index 819a38a0d..3049e40dd 100644 --- a/src/states_screens/options_screen_audio.cpp +++ b/src/states_screens/options_screen_audio.cpp @@ -156,7 +156,7 @@ void OptionsScreenAudio::eventCallback(Widget* widget, const std::string& name, if(w->getState() == false) music_manager->stopMusic(); else - music_manager->startMusic(music_manager->getCurrentMusic(), 0); + music_manager->startMusic(music_manager->getCurrentMusic(), true); } else if(name == "sfx_enabled") { From 671e3829a4d153097261eafa8908d3f847c901f4 Mon Sep 17 00:00:00 2001 From: auria Date: Sun, 8 Feb 2015 18:05:42 -0500 Subject: [PATCH 021/117] Do not extract strings from wip-tracks. Fixes #1967 --- data/po/supertuxkart.pot | 984 ++++++++++++++++++++------------------- data/po/update_pot.sh | 2 +- 2 files changed, 500 insertions(+), 486 deletions(-) diff --git a/data/po/supertuxkart.pot b/data/po/supertuxkart.pot index 00929fde6..996cb474d 100644 --- a/data/po/supertuxkart.pot +++ b/data/po/supertuxkart.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: supertuxkart\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-01-02 18:36-0500\n" +"POT-Creation-Date: 2015-02-08 18:04-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -27,7 +27,7 @@ msgstr "" #. I18N: Cutscene subtitle from ../stk-assets/tracks/introcutscene2/scene.xml #. I18N: ../stk-assets/tracks/introcutscene2/scene.xml -#: data/po/gui_strings.h:1484 data/po/gui_strings.h:1487 +#: data/po/gui_strings.h:1447 data/po/gui_strings.h:1450 msgid "" " But you pathetic little twerps will never be able to beat me - King of the " "Karts!" @@ -69,8 +69,7 @@ msgctxt "addons" msgid "%s by %s" msgstr "" -#. I18N: as in "fastest lap: 60 seconds by Wilber" -#: src/modes/linear_world.cpp:355 +#: src/modes/linear_world.cpp:358 #, c-format msgctxt "fastest_lap" msgid "%s by %s" @@ -101,20 +100,20 @@ msgstr "" msgid "%s, %s and %s are now online." msgstr "" -#: src/modes/world.cpp:1149 +#: src/modes/world.cpp:1148 #, c-format msgid "'%s' has been eliminated." msgstr "" #. I18N: for empty highscores entries #. I18N: ./data/gui/track_info.stkgui -#: src/states_screens/track_info_screen.cpp:252 data/po/gui_strings.h:65 -#: data/po/gui_strings.h:68 data/po/gui_strings.h:71 +#: src/states_screens/track_info_screen.cpp:252 data/po/gui_strings.h:71 +#: data/po/gui_strings.h:74 data/po/gui_strings.h:77 msgid "(Empty)" msgstr "" #. I18N: ./data/gui/help4.stkgui -#: data/po/gui_strings.h:1002 +#: data/po/gui_strings.h:1016 msgid "(network play is not yet available)" msgstr "" @@ -128,13 +127,13 @@ msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: in the help screen -#: data/po/gui_strings.h:977 +#: data/po/gui_strings.h:991 msgid "* Current key bindings can be seen/changed in menu Options" msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1060 +#: data/po/gui_strings.h:1074 msgid "" "* Most of these game modes can also be played in a Grand Prix fashion: " "instead of playing a single race, you play many in a row. The better you " @@ -144,12 +143,12 @@ msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:874 +#: data/po/gui_strings.h:888 msgid "* Restart STK to apply new settings" msgstr "" #. I18N: ./data/gui/options_input.stkgui -#: data/po/gui_strings.h:1102 +#: data/po/gui_strings.h:1116 msgid "" "* Which config to use will be inferred from which 'Select' key is pressed to " "join the game." @@ -160,9 +159,9 @@ msgstr "" #. I18N: ./data/gui/online/profile_settings.stkgui #. I18N: ./data/gui/online/profile_friends.stkgui #. I18N: ./data/gui/online/profile_achievements_tab.stkgui -#: data/po/gui_strings.h:451 data/po/gui_strings.h:529 -#: data/po/gui_strings.h:734 data/po/gui_strings.h:748 -#: data/po/gui_strings.h:774 +#: data/po/gui_strings.h:457 data/po/gui_strings.h:535 +#: data/po/gui_strings.h:748 data/po/gui_strings.h:762 +#: data/po/gui_strings.h:788 msgid "..." msgstr "" @@ -193,7 +192,7 @@ msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1048 +#: data/po/gui_strings.h:1062 msgid "" "3 Strikes Battle: Only in multiplayer games. Hit others with weapons until " "they lose all their lives." @@ -212,13 +211,13 @@ msgid "9 months" msgstr "" #. I18N: ./data/gui/track_info.stkgui -#: data/po/gui_strings.h:62 +#: data/po/gui_strings.h:68 msgid "= Highscores =" msgstr "" #. I18N: ./data/gui/gp_info.stkgui #. I18N: In the grand prix info screen -#: data/po/gui_strings.h:195 +#: data/po/gui_strings.h:201 msgid "AI karts" msgstr "" @@ -228,7 +227,7 @@ msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: In the main screen -#: data/po/gui_strings.h:187 +#: data/po/gui_strings.h:193 msgid "About" msgstr "" @@ -253,7 +252,7 @@ msgstr "" #. I18N: User info dialog #. I18N: ./data/gui/online/registration_terms.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:552 data/po/gui_strings.h:623 +#: data/po/gui_strings.h:558 data/po/gui_strings.h:629 msgid "Accept" msgstr "" @@ -261,13 +260,13 @@ msgstr "" #. I18N: In the recovery dialog #. I18N: ./data/gui/online/recovery_info.stkgui #. I18N: In the recovery dialog -#: data/po/gui_strings.h:507 data/po/gui_strings.h:763 +#: data/po/gui_strings.h:513 data/po/gui_strings.h:777 msgid "Account Recovery" msgstr "" #. I18N: ./data/gui/online/profile_settings.stkgui #. I18N: Section in the profile screen -#: src/states_screens/online_profile_base.cpp:101 data/po/gui_strings.h:738 +#: src/states_screens/online_profile_base.cpp:101 data/po/gui_strings.h:752 msgid "Account Settings" msgstr "" @@ -275,8 +274,8 @@ msgstr "" #. I18N: In the main screen #. I18N: ./data/gui/online/profile_achievements_tab.stkgui #. I18N: Section in the profile screen -#: src/states_screens/online_profile_base.cpp:100 data/po/gui_strings.h:179 -#: data/po/gui_strings.h:778 +#: src/states_screens/online_profile_base.cpp:100 data/po/gui_strings.h:185 +#: data/po/gui_strings.h:792 msgid "Achievements" msgstr "" @@ -286,13 +285,13 @@ msgstr "" #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1173 +#: data/po/gui_strings.h:1187 msgid "Add" msgstr "" #. I18N: ./data/gui/online/user_info_dialog.stkgui #. I18N: User info dialog -#: data/po/gui_strings.h:548 +#: data/po/gui_strings.h:554 msgid "Add Friend" msgstr "" @@ -303,7 +302,7 @@ msgstr "" #. I18N: ./data/gui/options_players.stkgui #. I18N: In the player configuration screen -#: data/po/gui_strings.h:365 +#: data/po/gui_strings.h:371 msgid "Add Player" msgstr "" @@ -314,7 +313,7 @@ msgstr "" #. I18N: ./data/gui/options_input.stkgui #. I18N: In the input configuration screen -#: data/po/gui_strings.h:1099 +#: data/po/gui_strings.h:1113 msgid "Add a device" msgstr "" @@ -322,7 +321,7 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/user_screen.stkgui #. I18N: In the user screen -#: data/po/gui_strings.h:246 data/po/gui_strings.h:335 +#: data/po/gui_strings.h:252 data/po/gui_strings.h:341 msgid "Add user" msgstr "" @@ -338,8 +337,8 @@ msgstr "" #: src/states_screens/grand_prix_editor_screen.cpp:336 #: src/states_screens/kart_selection.cpp:286 #: src/states_screens/arenas_screen.cpp:83 -#: src/states_screens/easter_egg_screen.cpp:145 data/po/gui_strings.h:1277 -#: data/po/gui_strings.h:1313 +#: src/states_screens/easter_egg_screen.cpp:145 data/po/gui_strings.h:1291 +#: data/po/gui_strings.h:1327 msgid "Add-Ons" msgstr "" @@ -349,18 +348,18 @@ msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: Main menu button -#: data/po/gui_strings.h:163 +#: data/po/gui_strings.h:169 msgid "Addons" msgstr "" #. I18N: ../stk-assets/karts/20_adiumy/kart.xml -#: data/po/gui_strings.h:1355 +#: data/po/gui_strings.h:1471 msgid "Adiumy" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:810 +#: data/po/gui_strings.h:824 msgid "Advanced pipeline (lights, etc.)" msgstr "" @@ -375,15 +374,15 @@ msgstr "" #. I18N: ./data/gui/easter_egg.stkgui #. I18N: track group #: src/states_screens/tracks_screen.cpp:145 -#: src/states_screens/options_screen_video.cpp:412 +#: src/states_screens/options_screen_video.cpp:411 #: src/states_screens/grand_prix_editor_screen.cpp:333 #: src/states_screens/addons_screen.cpp:49 #: src/states_screens/kart_selection.cpp:278 #: src/states_screens/edit_track_screen.cpp:147 #: src/states_screens/arenas_screen.cpp:75 #: src/states_screens/easter_egg_screen.cpp:137 -#: src/states_screens/gp_info_screen.cpp:76 data/po/gui_strings.h:1281 -#: data/po/gui_strings.h:1317 +#: src/states_screens/gp_info_screen.cpp:76 data/po/gui_strings.h:1295 +#: data/po/gui_strings.h:1331 msgid "All" msgstr "" @@ -391,7 +390,7 @@ msgstr "" #. I18N: In the track selection screen #. I18N: ./data/gui/easter_egg.stkgui #. I18N: Section in easter egg tracks selection screen -#: data/po/gui_strings.h:423 data/po/gui_strings.h:1305 +#: data/po/gui_strings.h:429 data/po/gui_strings.h:1319 msgid "All Tracks" msgstr "" @@ -401,18 +400,18 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1121 +#: data/po/gui_strings.h:1135 msgid "Always show login screen" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:826 +#: data/po/gui_strings.h:840 msgid "Ambient Occlusion" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:442 +#: src/states_screens/options_screen_video.cpp:441 #, c-format msgid "Ambient occlusion : %s" msgstr "" @@ -422,25 +421,25 @@ msgid "An error occurred while trying to save your grand prix." msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:406 +#: data/po/gui_strings.h:412 msgid "Anchor - slows down greatly the kart in the first position." msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:866 +#: data/po/gui_strings.h:880 msgid "Animated Characters" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:428 +#: src/states_screens/options_screen_video.cpp:427 #, c-format msgid "Animated Characters : %s" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:854 +#: data/po/gui_strings.h:868 msgid "Animated Scenery" msgstr "" @@ -448,24 +447,24 @@ msgstr "" #. tooltip = tooltip + L"\n" + _("Pixel shaders : %s", #. UserConfigParams::m_pixel_shaders ? enabled : disabled); #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:422 +#: src/states_screens/options_screen_video.cpp:421 #, c-format msgid "Animated Scenery : %s" msgstr "" -#: src/states_screens/dialogs/custom_video_settings.cpp:80 +#: src/states_screens/dialogs/custom_video_settings.cpp:81 msgid "Anisotropic x16" msgstr "" -#: src/states_screens/dialogs/custom_video_settings.cpp:77 +#: src/states_screens/dialogs/custom_video_settings.cpp:78 msgid "Anisotropic x2" msgstr "" -#: src/states_screens/dialogs/custom_video_settings.cpp:78 +#: src/states_screens/dialogs/custom_video_settings.cpp:79 msgid "Anisotropic x4" msgstr "" -#: src/states_screens/dialogs/custom_video_settings.cpp:79 +#: src/states_screens/dialogs/custom_video_settings.cpp:80 msgid "Anisotropic x8" msgstr "" @@ -475,24 +474,24 @@ msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:838 +#: data/po/gui_strings.h:852 msgid "Anti-aliasing" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:439 +#: src/states_screens/options_screen_video.cpp:438 #, c-format msgid "Anti-aliasing : %s" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui -#: data/po/gui_strings.h:877 +#: data/po/gui_strings.h:891 msgid "Apply" msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1265 +#: data/po/gui_strings.h:1279 msgid "Apply new resolution" msgstr "" @@ -516,45 +515,45 @@ msgstr "" #. I18N: Section in arena tracks selection screen #. I18N: ./data/gui/addons_screen.stkgui #. I18N: In the addons screen -#: data/po/gui_strings.h:1269 data/po/gui_strings.h:1340 +#: data/po/gui_strings.h:1283 data/po/gui_strings.h:1354 msgid "Arenas" msgstr "" #. I18N: ../stk-assets/tracks/lighthouse/track.xml -#: data/po/gui_strings.h:1457 +#: data/po/gui_strings.h:1420 msgid "Around the lighthouse" msgstr "" #. I18N: ./data/gui/press_a_key_dialog.stkgui #. I18N: When configuring input -#: data/po/gui_strings.h:1144 +#: data/po/gui_strings.h:1158 msgid "Assign nothing" msgstr "" #. I18N: ./data/gui/press_a_key_dialog.stkgui #. I18N: When configuring input -#: data/po/gui_strings.h:1140 +#: data/po/gui_strings.h:1154 msgid "Assign to ESC key" msgstr "" #. I18N: ./data/grandprix/4_atworldsend.grandprix -#: data/po/gui_strings.h:1343 +#: data/po/gui_strings.h:1357 msgid "At World's End" msgstr "" #. I18N: ./data/gui/options_audio.stkgui #. I18N: Section in the settings menu -#: src/states_screens/user_screen.cpp:598 +#: src/states_screens/user_screen.cpp:642 #: src/states_screens/options_screen_input2.cpp:86 -#: src/states_screens/options_screen_video.cpp:136 +#: src/states_screens/options_screen_video.cpp:135 #: src/states_screens/options_screen_input.cpp:139 -#: src/states_screens/options_screen_ui.cpp:119 data/po/gui_strings.h:269 +#: src/states_screens/options_screen_ui.cpp:118 data/po/gui_strings.h:275 msgid "Audio" msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: In the help menu -#: src/tracks/track_object_presentation.cpp:883 data/po/gui_strings.h:961 +#: src/tracks/track_object_presentation.cpp:883 data/po/gui_strings.h:975 msgid "Avoid bananas!" msgstr "" @@ -580,19 +579,19 @@ msgstr "" #. I18N: ./data/gui/addons_loading.stkgui #. I18N: Add-on screen action #: src/states_screens/dialogs/addons_loading.cpp:284 -#: src/input/gamepad_config.cpp:216 data/po/gui_strings.h:1297 +#: src/input/gamepad_config.cpp:216 data/po/gui_strings.h:1311 msgid "Back" msgstr "" #. I18N: ./data/gui/overworld_dialog.stkgui #. I18N: In the in-game dialog -#: data/po/gui_strings.h:884 +#: data/po/gui_strings.h:898 msgid "Back to Game" msgstr "" #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:907 +#: data/po/gui_strings.h:921 msgid "Back to Race" msgstr "" @@ -602,13 +601,13 @@ msgstr "" #. I18N: ./data/gui/options_device.stkgui #. I18N: In the input configuration screen -#: data/po/gui_strings.h:1200 +#: data/po/gui_strings.h:1214 msgid "Back to device list" msgstr "" #. I18N: ./data/gui/overworld_dialog.stkgui #. I18N: In the in-game dialog -#: data/po/gui_strings.h:900 +#: data/po/gui_strings.h:914 msgid "Back to menu" msgstr "" @@ -622,33 +621,33 @@ msgid "Banana Lover" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:412 +#: data/po/gui_strings.h:418 msgid "" "Basket Ball - bounces after the leader, and might squash and slow down karts " "down on the way." msgstr "" #. I18N: ../stk-assets/tracks/battleisland/track.xml -#: data/po/gui_strings.h:1499 +#: data/po/gui_strings.h:1462 msgid "Battle Island" msgstr "" #. I18N: ../stk-assets/karts/beagle/kart.xml -#: data/po/gui_strings.h:1403 +#: data/po/gui_strings.h:1519 msgid "Beagle" msgstr "" #. I18N: ../stk-assets/karts/beastie/kart.xml -#: data/po/gui_strings.h:1358 +#: data/po/gui_strings.h:1474 msgid "Beastie" msgstr "" #. I18N: when failing a GP -#: src/states_screens/grand_prix_lose.cpp:163 +#: src/states_screens/grand_prix_lose.cpp:161 msgid "Better luck next time!" msgstr "" -#: src/states_screens/dialogs/custom_video_settings.cpp:75 +#: src/states_screens/dialogs/custom_video_settings.cpp:76 msgid "Bilinear" msgstr "" @@ -658,33 +657,33 @@ msgid "Black" msgstr "" #. I18N: ../stk-assets/tracks/mansion/track.xml -#: data/po/gui_strings.h:1418 +#: data/po/gui_strings.h:1381 msgid "Blackhill Mansion" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:818 +#: data/po/gui_strings.h:832 msgid "Bloom" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:449 +#: src/states_screens/options_screen_video.cpp:450 #, c-format msgid "Bloom : %s" msgstr "" -#: src/states_screens/race_result_gui.cpp:960 +#: src/states_screens/race_result_gui.cpp:958 msgid "Blue Team Wins" msgstr "" #. I18N: ../stk-assets/tracks/farm/track.xml -#: data/po/gui_strings.h:1442 +#: data/po/gui_strings.h:1405 msgid "Bovine Barnyard" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:400 +#: data/po/gui_strings.h:406 msgid "" "Bowling Ball - bounces off walls. If you are looking back, it will be thrown " "backwards." @@ -696,7 +695,7 @@ msgid "Brake" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:391 +#: data/po/gui_strings.h:397 msgid "" "BubbleGum - protect yourself with a shield, or use while looking back to " "leave a sticky pink puddle behind you." @@ -704,12 +703,12 @@ msgstr "" #. I18N: Cutscene subtitle from ../stk-assets/tracks/introcutscene2/scene.xml #. I18N: ../stk-assets/tracks/introcutscene2/scene.xml -#: data/po/gui_strings.h:1472 data/po/gui_strings.h:1475 +#: data/po/gui_strings.h:1435 data/po/gui_strings.h:1438 msgid "But I'm a fair creature, so I'll make you a deal." msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:394 +#: data/po/gui_strings.h:400 msgid "" "Cake - thrown at the closest rival, best on short ranges and long straights." msgstr "" @@ -741,15 +740,15 @@ msgstr "" #. I18N: In the 'add new grand prix' dialog #. I18N: ./data/gui/enter_player_name_dialog.stkgui #. I18N: In the 'add new player' dialog -#: src/states_screens/user_screen.cpp:83 +#: src/states_screens/user_screen.cpp:112 #: src/states_screens/dialogs/addons_loading.cpp:227 #: src/states_screens/dialogs/add_device_dialog.cpp:128 -#: data/po/gui_strings.h:135 data/po/gui_strings.h:143 -#: data/po/gui_strings.h:258 data/po/gui_strings.h:308 -#: data/po/gui_strings.h:347 data/po/gui_strings.h:526 -#: data/po/gui_strings.h:627 data/po/gui_strings.h:655 -#: data/po/gui_strings.h:695 data/po/gui_strings.h:711 -#: data/po/gui_strings.h:1218 data/po/gui_strings.h:1230 +#: data/po/gui_strings.h:141 data/po/gui_strings.h:149 +#: data/po/gui_strings.h:264 data/po/gui_strings.h:314 +#: data/po/gui_strings.h:353 data/po/gui_strings.h:532 +#: data/po/gui_strings.h:633 data/po/gui_strings.h:661 +#: data/po/gui_strings.h:709 data/po/gui_strings.h:725 +#: data/po/gui_strings.h:1232 data/po/gui_strings.h:1244 msgid "Cancel" msgstr "" @@ -763,26 +762,26 @@ msgid "Cancel/Back" msgstr "" #. I18N: ../stk-assets/tracks/cave/track.xml -#: data/po/gui_strings.h:1424 +#: data/po/gui_strings.h:1387 msgid "Cave X" msgstr "" -#: src/states_screens/feature_unlocked.cpp:492 +#: src/states_screens/feature_unlocked.cpp:493 msgid "Challenge Completed" msgstr "" -#: src/states_screens/race_gui.cpp:311 src/states_screens/race_gui.cpp:313 +#: src/states_screens/race_gui.cpp:313 src/states_screens/race_gui.cpp:315 msgid "Challenge Failed" msgstr "" #. I18N: ./data/gui/challenges.stkgui #. I18N: Title for challenges screen -#: data/po/gui_strings.h:91 +#: data/po/gui_strings.h:97 msgid "Challenges : Trophy Room" msgstr "" #. I18N: ./data/gui/online/profile_settings.stkgui -#: data/po/gui_strings.h:745 +#: data/po/gui_strings.h:759 msgid "Change" msgstr "" @@ -790,7 +789,7 @@ msgstr "" #. I18N: In the kart selection (player setup) screen #. I18N: ./data/gui/karts_online.stkgui #. I18N: In the kart selection (player setup) screen -#: data/po/gui_strings.h:1285 data/po/gui_strings.h:1301 +#: data/po/gui_strings.h:1299 data/po/gui_strings.h:1315 msgid "Choose a Kart" msgstr "" @@ -800,7 +799,7 @@ msgid "Christoffel Columbus" msgstr "" #. I18N: ./data/gui/help1.stkgui -#: data/po/gui_strings.h:953 +#: data/po/gui_strings.h:967 msgid "Click here to play the tutorial" msgstr "" @@ -816,14 +815,14 @@ msgstr "" #. I18N: User info dialog #. I18N: ./data/gui/online/recovery_info.stkgui #. I18N: In the recovery dialog -#: data/po/gui_strings.h:503 data/po/gui_strings.h:564 -#: data/po/gui_strings.h:611 data/po/gui_strings.h:635 -#: data/po/gui_strings.h:719 data/po/gui_strings.h:771 +#: data/po/gui_strings.h:509 data/po/gui_strings.h:570 +#: data/po/gui_strings.h:617 data/po/gui_strings.h:641 +#: data/po/gui_strings.h:733 data/po/gui_strings.h:785 msgid "Close" msgstr "" #. I18N: ../stk-assets/tracks/30_chocolate/track.xml -#: data/po/gui_strings.h:1436 +#: data/po/gui_strings.h:1399 msgid "Cocoa Temple" msgstr "" @@ -834,7 +833,7 @@ msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:957 +#: data/po/gui_strings.h:971 msgid "Collect blue boxes : they will give you weapons or other powerups" msgstr "" @@ -854,7 +853,7 @@ msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:965 +#: data/po/gui_strings.h:979 msgid "" "Collecting nitro allows you to get speed boosts whenever you wish by " "pressing the appropriate key. You can see your current level of nitro in the " @@ -874,7 +873,7 @@ msgstr "" #. I18N: In the change password dialog #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:495 data/po/gui_strings.h:679 +#: data/po/gui_strings.h:501 data/po/gui_strings.h:693 msgid "Confirm" msgstr "" @@ -886,7 +885,7 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1133 +#: data/po/gui_strings.h:1147 msgid "Connect to the Internet" msgstr "" @@ -908,15 +907,15 @@ msgstr "" #. I18N: ./data/gui/feature_unlocked.stkgui #. I18N: ./data/gui/grand_prix_win.stkgui #: src/states_screens/race_result_gui.cpp:175 -#: src/states_screens/dialogs/gp_info_dialog.cpp:212 data/po/gui_strings.h:117 -#: data/po/gui_strings.h:262 data/po/gui_strings.h:1151 -#: data/po/gui_strings.h:1157 data/po/gui_strings.h:1203 +#: src/states_screens/dialogs/gp_info_dialog.cpp:212 data/po/gui_strings.h:123 +#: data/po/gui_strings.h:268 data/po/gui_strings.h:1165 +#: data/po/gui_strings.h:1171 data/po/gui_strings.h:1217 msgid "Continue" msgstr "" #. I18N: ./data/gui/gp_info.stkgui #. I18N: In the grand prix info screen -#: src/states_screens/gp_info_screen.cpp:142 data/po/gui_strings.h:215 +#: src/states_screens/gp_info_screen.cpp:146 data/po/gui_strings.h:221 msgid "Continue saved GP" msgstr "" @@ -928,22 +927,22 @@ msgstr "" #. I18N: Section in the settings menu #. I18N: ./data/gui/options_device.stkgui #. I18N: Section in the settings menu -#: src/states_screens/user_screen.cpp:600 +#: src/states_screens/user_screen.cpp:644 #: src/states_screens/options_screen_audio.cpp:67 -#: src/states_screens/options_screen_video.cpp:139 -#: src/states_screens/options_screen_ui.cpp:121 data/po/gui_strings.h:1091 -#: data/po/gui_strings.h:1192 +#: src/states_screens/options_screen_video.cpp:138 +#: src/states_screens/options_screen_ui.cpp:120 data/po/gui_strings.h:1105 +#: data/po/gui_strings.h:1206 msgid "Controls" msgstr "" #. I18N: ./data/gui/gpeditor.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1072 +#: data/po/gui_strings.h:1086 msgid "Copy" msgstr "" -#: src/states_screens/register_screen.cpp:178 -#: src/states_screens/register_screen.cpp:185 +#: src/states_screens/register_screen.cpp:216 +#: src/states_screens/register_screen.cpp:223 #, c-format msgid "Could not create player '%s'." msgstr "" @@ -954,43 +953,35 @@ msgstr "" #. I18N: ./data/gui/online/create_server.stkgui #. I18N: In the server creation screen -#. I18N: ./data/gui/enter_gp_name_dialog.stkgui -#. I18N: In the 'add new grand prix' dialog -#: data/po/gui_strings.h:651 data/po/gui_strings.h:1214 +#: data/po/gui_strings.h:657 msgid "Create" msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:471 +#: data/po/gui_strings.h:477 msgid "Create Server" msgstr "" #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:659 +#: data/po/gui_strings.h:665 msgid "Create User" msgstr "" -#. I18N: ./data/gui/online/register.stkgui -#. I18N: In the registration dialog -#: data/po/gui_strings.h:667 -msgid "Create online account" -msgstr "" - #: src/states_screens/create_server_screen.cpp:117 msgid "Creating server" msgstr "" #. I18N: ./data/gui/credits.stkgui #. I18N: Title in credits screen -#: data/po/gui_strings.h:121 +#: data/po/gui_strings.h:127 msgid "Credits" msgstr "" #. I18N: ./data/gui/online/change_password.stkgui #. I18N: In the change password dialog -#: data/po/gui_strings.h:487 +#: data/po/gui_strings.h:493 msgid "Current Password" msgstr "" @@ -1003,13 +994,13 @@ msgid "Currently not signed in" msgstr "" #. I18N: custom video settings -#: src/states_screens/options_screen_video.cpp:389 +#: src/states_screens/options_screen_video.cpp:388 msgid "Custom" msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1245 +#: data/po/gui_strings.h:1259 msgid "Custom settings..." msgstr "" @@ -1035,7 +1026,7 @@ msgstr "" #. I18N: ./data/gui/online/user_info_dialog.stkgui #. I18N: User info dialog -#: data/po/gui_strings.h:556 +#: data/po/gui_strings.h:562 msgid "Decline" msgstr "" @@ -1047,24 +1038,24 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/user_screen.stkgui #. I18N: In the user screen -#: data/po/gui_strings.h:250 data/po/gui_strings.h:339 +#: data/po/gui_strings.h:256 data/po/gui_strings.h:345 msgid "Delete" msgstr "" #. I18N: ./data/gui/options_device.stkgui #. I18N: In the input configuration screen -#: src/states_screens/options_screen_input2.cpp:111 data/po/gui_strings.h:1196 +#: src/states_screens/options_screen_input2.cpp:111 data/po/gui_strings.h:1210 msgid "Delete Configuration" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:846 +#: data/po/gui_strings.h:860 msgid "Depth of field" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:461 +#: src/states_screens/options_screen_video.cpp:462 #, c-format msgid "Depth of field : %s" msgstr "" @@ -1077,15 +1068,15 @@ msgstr "" #. I18N: in the graphical options tooltip; #. indicates a graphical feature is disabled -#: src/states_screens/dialogs/custom_video_settings.cpp:61 -#: src/states_screens/dialogs/custom_video_settings.cpp:85 -#: src/states_screens/options_screen_video.cpp:410 +#: src/states_screens/dialogs/custom_video_settings.cpp:62 +#: src/states_screens/dialogs/custom_video_settings.cpp:86 +#: src/states_screens/options_screen_video.cpp:409 msgid "Disabled" msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1117 +#: data/po/gui_strings.h:1131 msgid "Display FPS" msgstr "" @@ -1094,7 +1085,7 @@ msgid "Do you really want to abort the Grand Prix?" msgstr "" #. I18N: In the player info dialog (when deleting) -#: src/states_screens/user_screen.cpp:534 +#: src/states_screens/user_screen.cpp:578 #, c-format msgid "Do you really want to delete player '%s' ?" msgstr "" @@ -1115,12 +1106,12 @@ msgstr "" #. I18N: ./data/gui/track_info.stkgui #. I18N: In the track info screen -#: data/po/gui_strings.h:83 +#: data/po/gui_strings.h:89 msgid "Drive in reverse" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:433 +#: src/states_screens/options_screen_video.cpp:432 #, c-format msgid "Dynamic lights : %s" msgstr "" @@ -1129,13 +1120,13 @@ msgstr "" #. I18N: Menu item #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1076 data/po/gui_strings.h:1177 +#: data/po/gui_strings.h:1090 data/po/gui_strings.h:1191 msgid "Edit" msgstr "" #. I18N: ./data/gui/gpedit.stkgui #. I18N: Title in edit grand prix screen -#: data/po/gui_strings.h:1161 +#: data/po/gui_strings.h:1175 msgid "Edit Grand Prix" msgstr "" @@ -1146,7 +1137,7 @@ msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1056 +#: data/po/gui_strings.h:1070 msgid "Egg hunt: Explore tracks to find all hidden eggs." msgstr "" @@ -1156,12 +1147,12 @@ msgid "Eggs: %d / %d" msgstr "" #. I18N: ../stk-assets/karts/elephpant/kart.xml -#: data/po/gui_strings.h:1400 +#: data/po/gui_strings.h:1516 msgid "Elephpant" msgstr "" #: src/states_screens/race_result_gui.cpp:463 -#: src/states_screens/race_result_gui.cpp:824 +#: src/states_screens/race_result_gui.cpp:822 msgid "Eliminated" msgstr "" @@ -1169,24 +1160,24 @@ msgstr "" #. I18N: In the recovery dialog #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:518 data/po/gui_strings.h:683 +#: data/po/gui_strings.h:524 data/po/gui_strings.h:697 msgid "Email" msgstr "" -#: src/states_screens/register_screen.cpp:246 +#: src/states_screens/register_screen.cpp:291 msgid "Email has to be between 4 and 50 characters long!" msgstr "" -#: src/states_screens/register_screen.cpp:252 +#: src/states_screens/register_screen.cpp:297 msgid "Email is invalid!" msgstr "" -#: src/states_screens/register_screen.cpp:230 +#: src/states_screens/register_screen.cpp:275 msgid "Emails don't match!" msgstr "" #. I18N: ../stk-assets/karts/emule/kart.xml -#: data/po/gui_strings.h:1394 +#: data/po/gui_strings.h:1510 msgid "Emule" msgstr "" @@ -1198,7 +1189,7 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1129 +#: data/po/gui_strings.h:1143 msgid "Enable handicapped users" msgstr "" @@ -1206,19 +1197,19 @@ msgstr "" #. indicates a graphical feature is enabled #. I18N: ./data/gui/options_audio.stkgui #. I18N: In the audio options screen -#: src/states_screens/options_screen_video.cpp:407 data/po/gui_strings.h:277 -#: data/po/gui_strings.h:289 +#: src/states_screens/options_screen_video.cpp:406 data/po/gui_strings.h:283 +#: data/po/gui_strings.h:295 msgid "Enabled" msgstr "" #. I18N: animations setting (all karts are animated) -#: src/states_screens/dialogs/custom_video_settings.cpp:65 +#: src/states_screens/dialogs/custom_video_settings.cpp:66 msgid "Enabled for all" msgstr "" #. I18N: ./data/gui/enter_player_name_dialog.stkgui #. I18N: In the 'add new player' dialog -#: data/po/gui_strings.h:1222 +#: data/po/gui_strings.h:1236 msgid "Enter the new player's name" msgstr "" @@ -1233,19 +1224,25 @@ msgid "" "Press 'Select' now to join the game!" msgstr "" +#. I18N: ./data/gui/online/register.stkgui +#. I18N: Section in the register screen +#: data/po/gui_strings.h:673 +msgid "Existing Online Account" +msgstr "" + #. I18N: ./data/gui/online/lobby.stkgui #. I18N: In the networking lobby -#: data/po/gui_strings.h:580 +#: data/po/gui_strings.h:586 msgid "Exit" msgstr "" #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:931 +#: data/po/gui_strings.h:945 msgid "Exit Race" msgstr "" -#: src/states_screens/user_screen.cpp:82 +#: src/states_screens/user_screen.cpp:111 msgid "Exit game" msgstr "" @@ -1253,7 +1250,7 @@ msgstr "" #. I18N: Difficulty #. I18N: ./data/gui/racesetup.stkgui #. I18N: Difficulty -#: data/po/gui_strings.h:442 data/po/gui_strings.h:796 +#: data/po/gui_strings.h:448 data/po/gui_strings.h:810 msgid "Expert" msgstr "" @@ -1261,12 +1258,12 @@ msgstr "" msgid "Explore tracks to find all hidden eggs" msgstr "" -#: src/graphics/irr_driver.cpp:1776 +#: src/graphics/irr_driver.cpp:1791 #, c-format msgid "FPS: %d/%d/%d - PolyCount: %d Solid, %d Shadows - LightDist : %d" msgstr "" -#: src/graphics/irr_driver.cpp:1785 +#: src/graphics/irr_driver.cpp:1800 #, c-format msgid "FPS: %d/%d/%d - %d KTris" msgstr "" @@ -1291,19 +1288,19 @@ msgid "Fetching servers" msgstr "" #. I18N: ./data/gui/online/recovery_input.stkgui -#: data/po/gui_strings.h:510 +#: data/po/gui_strings.h:516 msgid "" "Fill in the username and email address you supplied at registration to be " "able to reset your password." msgstr "" -#: src/modes/linear_world.cpp:277 +#: src/modes/linear_world.cpp:276 msgid "Final lap!" msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:467 +#: data/po/gui_strings.h:473 msgid "Find Server" msgstr "" @@ -1314,7 +1311,7 @@ msgstr "" #. I18N: ./data/gui/help4.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1006 +#: data/po/gui_strings.h:1020 msgid "" "First, you will need several input devices (having multiple gamepads or " "joysticks is the best way to play with several people). Go in the input " @@ -1335,7 +1332,7 @@ msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1044 +#: data/po/gui_strings.h:1058 msgid "" "Follow the leader: Run for second place, as the last kart will be " "disqualified every time the counter hits zero. Beware : going in front of " @@ -1343,7 +1340,7 @@ msgid "" msgstr "" #. I18N: ../stk-assets/tracks/fortmagma/track.xml -#: data/po/gui_strings.h:1502 +#: data/po/gui_strings.h:1465 msgid "Fort Magma" msgstr "" @@ -1374,13 +1371,13 @@ msgstr "" #. I18N: ./data/gui/online/profile_friends.stkgui #. I18N: Section in the profile screen -#: src/states_screens/online_profile_base.cpp:99 data/po/gui_strings.h:752 +#: src/states_screens/online_profile_base.cpp:99 data/po/gui_strings.h:766 msgid "Friends" msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1257 +#: data/po/gui_strings.h:1271 msgid "Fullscreen" msgstr "" @@ -1402,14 +1399,14 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help3.stkgui #. I18N: Tab in help menu -#: data/po/gui_strings.h:380 data/po/gui_strings.h:946 -#: data/po/gui_strings.h:992 data/po/gui_strings.h:1025 +#: data/po/gui_strings.h:386 data/po/gui_strings.h:960 +#: data/po/gui_strings.h:1006 data/po/gui_strings.h:1039 msgid "Game Modes" msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui #. I18N: In soccer setup screen -#: data/po/gui_strings.h:106 +#: data/po/gui_strings.h:112 msgid "Game type (Goals limit / Time limit)" msgstr "" @@ -1433,43 +1430,43 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help3.stkgui #. I18N: Tab in help menu -#: data/po/gui_strings.h:372 data/po/gui_strings.h:938 -#: data/po/gui_strings.h:984 data/po/gui_strings.h:1017 +#: data/po/gui_strings.h:378 data/po/gui_strings.h:952 +#: data/po/gui_strings.h:998 data/po/gui_strings.h:1031 msgid "General" msgstr "" #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:919 +#: data/po/gui_strings.h:933 msgid "Give Up Race" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:830 +#: data/po/gui_strings.h:844 msgid "Global illumination" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:465 +#: src/states_screens/options_screen_video.cpp:466 #, c-format msgid "Global illumination : %s" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:834 +#: data/po/gui_strings.h:848 msgid "Glow (outlines)" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:453 +#: src/states_screens/options_screen_video.cpp:454 #, c-format msgid "Glow (outlines) : %s" msgstr "" #. I18N: ../stk-assets/karts/gnu/kart.xml -#: data/po/gui_strings.h:1370 +#: data/po/gui_strings.h:1486 msgid "Gnu" msgstr "" @@ -1484,56 +1481,56 @@ msgid "Gold driver" msgstr "" #. I18N: ../stk-assets/tracks/20_island/track.xml -#: data/po/gui_strings.h:1490 +#: data/po/gui_strings.h:1453 msgid "Gran Paradiso Island" msgstr "" #. I18N: ./data/gui/tracks.stkgui #. I18N: In the track selection screen #: src/states_screens/dialogs/select_challenge.cpp:145 -#: data/po/gui_strings.h:419 +#: data/po/gui_strings.h:425 msgid "Grand Prix" msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: In the main screen -#: data/po/gui_strings.h:183 +#: data/po/gui_strings.h:189 msgid "Grand Prix Editor" msgstr "" #. I18N: ./data/gui/gpeditor.stkgui #. I18N: Title in grand prix editor screen -#: data/po/gui_strings.h:1064 +#: data/po/gui_strings.h:1078 msgid "Grand Prix editor" msgstr "" -#: src/states_screens/race_result_gui.cpp:1205 +#: src/states_screens/race_result_gui.cpp:1203 msgid "Grand Prix progress:" msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1241 +#: data/po/gui_strings.h:1255 msgid "Graphical Effects Level" msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: Section in the settings menu -#: src/states_screens/user_screen.cpp:597 +#: src/states_screens/user_screen.cpp:641 #: src/states_screens/options_screen_input2.cpp:85 #: src/states_screens/options_screen_audio.cpp:64 #: src/states_screens/options_screen_input.cpp:138 -#: src/states_screens/options_screen_ui.cpp:118 data/po/gui_strings.h:1237 +#: src/states_screens/options_screen_ui.cpp:117 data/po/gui_strings.h:1251 msgid "Graphics" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui -#: data/po/gui_strings.h:806 +#: data/po/gui_strings.h:820 msgid "Graphics Settings" msgstr "" #. I18N: ../stk-assets/tracks/greenvalley/track.xml -#: data/po/gui_strings.h:1496 +#: data/po/gui_strings.h:1459 msgid "Green Valley" msgstr "" @@ -1550,12 +1547,12 @@ msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: In the login dialog -#: data/po/gui_strings.h:599 -msgid "Guest Sign in" +#: data/po/gui_strings.h:605 +msgid "Guest Log in" msgstr "" #. I18N: ../stk-assets/tracks/hacienda/track.xml -#: data/po/gui_strings.h:1454 +#: data/po/gui_strings.h:1417 msgid "Hacienda" msgstr "" @@ -1565,17 +1562,17 @@ msgstr "" #. I18N: In the in-game dialog #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:171 data/po/gui_strings.h:896 -#: data/po/gui_strings.h:927 +#: data/po/gui_strings.h:177 data/po/gui_strings.h:910 +#: data/po/gui_strings.h:941 msgid "Help" msgstr "" #. I18N: ../stk-assets/karts/hexley/kart.xml -#: data/po/gui_strings.h:1361 +#: data/po/gui_strings.h:1477 msgid "Hexley" msgstr "" -#: src/states_screens/race_result_gui.cpp:1243 +#: src/states_screens/race_result_gui.cpp:1241 msgid "Highscores" msgstr "" @@ -1596,38 +1593,38 @@ msgid "Hit the same kart at least 5 times in one race." msgstr "" #. I18N: animations setting (only karts with human players are animated) -#: src/states_screens/dialogs/custom_video_settings.cpp:63 +#: src/states_screens/dialogs/custom_video_settings.cpp:64 msgid "Human players only" msgstr "" #. I18N: ./data/gui/online/registration_terms.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:619 +#: data/po/gui_strings.h:625 msgid "I agree to the above terms and am 13 years or older. " msgstr "" #. I18N: Cutscene subtitle from ../stk-assets/tracks/introcutscene2/scene.xml #. I18N: ../stk-assets/tracks/introcutscene2/scene.xml -#: data/po/gui_strings.h:1478 data/po/gui_strings.h:1481 +#: data/po/gui_strings.h:1441 data/po/gui_strings.h:1444 msgid "If you can beat me at racing, I will free the old codger." msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:969 +#: data/po/gui_strings.h:983 msgid "" "If you see a button with a lock like this one, you need to complete a " "challenge to unlock it." msgstr "" -#: src/input/input_manager.cpp:716 +#: src/input/input_manager.cpp:719 #, c-format msgid "Ignoring '%s', you needed to join earlier to play!" msgstr "" #. I18N: ./data/gui/addons_loading.stkgui #. I18N: Add-on screen action -#: data/po/gui_strings.h:1289 +#: data/po/gui_strings.h:1303 msgid "Install" msgstr "" @@ -1635,16 +1632,16 @@ msgstr "" #. I18N: Difficulty #. I18N: ./data/gui/racesetup.stkgui #. I18N: Difficulty -#: data/po/gui_strings.h:438 data/po/gui_strings.h:792 +#: data/po/gui_strings.h:444 data/po/gui_strings.h:806 msgid "Intermediate" msgstr "" -#: src/states_screens/user_screen.cpp:283 -#: src/states_screens/register_screen.cpp:347 +#: src/states_screens/user_screen.cpp:324 +#: src/states_screens/register_screen.cpp:400 msgid "Internet access is disabled, please enable it in the options" msgstr "" -#: src/states_screens/race_result_gui.cpp:965 +#: src/states_screens/race_result_gui.cpp:963 msgid "It's a draw" msgstr "" @@ -1655,7 +1652,7 @@ msgstr "" #. I18N: ./data/gui/online/server_info_dialog.stkgui #. I18N: In the server info dialog -#: data/po/gui_strings.h:707 +#: data/po/gui_strings.h:721 msgid "Join" msgstr "" @@ -1665,13 +1662,13 @@ msgstr "" #. I18N: ./data/gui/addons_screen.stkgui #. I18N: In the addons screen -#: data/po/gui_strings.h:1332 +#: data/po/gui_strings.h:1346 msgid "Karts" msgstr "" #. I18N: ./data/gui/confirm_resolution_dialog.stkgui #. I18N: In the 'confirm resolution' dialog, that's shown when switching resoluton -#: data/po/gui_strings.h:304 +#: data/po/gui_strings.h:310 msgid "Keep this resolution" msgstr "" @@ -1690,7 +1687,7 @@ msgid "Keyboard %i" msgstr "" #. I18N: ../stk-assets/karts/konqi/kart.xml -#: data/po/gui_strings.h:1388 +#: data/po/gui_strings.h:1504 msgid "Konqi" msgstr "" @@ -1699,7 +1696,7 @@ msgstr "" msgid "Lap" msgstr "" -#: src/modes/linear_world.cpp:304 +#: src/modes/linear_world.cpp:303 #, c-format msgid "Lap %i" msgstr "" @@ -1761,12 +1758,12 @@ msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:822 +#: data/po/gui_strings.h:836 msgid "Light shaft (God rays)" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:457 +#: src/states_screens/options_screen_video.cpp:458 #, c-format msgid "Light shaft (God rays) : %s" msgstr "" @@ -1777,22 +1774,16 @@ msgstr "" #. I18N: ./data/gui/online/lobby.stkgui #. I18N: In networking lobby -#: data/po/gui_strings.h:568 +#: data/po/gui_strings.h:574 msgid "Lobby" msgstr "" #. I18N: ./data/gui/online/lobby_settings.stkgui #. I18N: In the lobby settings screen -#: data/po/gui_strings.h:723 +#: data/po/gui_strings.h:737 msgid "Lobby Settings" msgstr "" -#. I18N: ./data/gui/online/register.stkgui -#. I18N: In the registration dialog -#: data/po/gui_strings.h:663 -msgid "Local Username" -msgstr "" - #: src/states_screens/kart_selection.cpp:853 msgid "Locked" msgstr "" @@ -1812,11 +1803,55 @@ msgstr "" msgid "Locked!" msgstr "" +#. I18N: ./data/gui/online/guest_login.stkgui +#. I18N: In the login dialog +#: data/po/gui_strings.h:613 +msgid "Log In" +msgstr "" + +#. I18N: ./data/gui/online/main.stkgui +#. I18N: In the online multiplayer screen +#: data/po/gui_strings.h:485 +msgid "Log Out" +msgstr "" + +#: src/states_screens/online_screen.cpp:139 +#, c-format +msgid "Logged in as: %s." +msgstr "" + +#: src/states_screens/online_screen.cpp:155 +msgid "Logging in" +msgstr "" + +#: src/states_screens/user_screen.cpp:489 +#, c-format +msgid "Logging in '%s'" +msgstr "" + +#. I18N: ./data/gui/online/guest_login.stkgui +#. I18N: In the login dialog +#: data/po/gui_strings.h:609 +msgid "" +"Logging in as a guest allows you to participate in online races, but it does " +"not allow you to vote for addons, or collect any achievements while being " +"online." +msgstr "" + +#: src/states_screens/online_screen.cpp:160 +msgid "Logging out" +msgstr "" + +#: src/states_screens/user_screen.cpp:488 +#, c-format +msgid "Logging out '%s'" +msgstr "" + #. I18N: ./data/gui/user_screen.stkgui #. I18N: ./data/gui/online/guest_login.stkgui #: src/states_screens/main_menu_screen.cpp:175 -#: src/states_screens/main_menu_screen.cpp:186 data/po/gui_strings.h:311 -#: data/po/gui_strings.h:583 +#: src/states_screens/main_menu_screen.cpp:186 data/po/gui_strings.h:317 +#: data/po/gui_strings.h:589 msgid "Login" msgstr "" @@ -1827,15 +1862,10 @@ msgstr "" #. I18N: ./data/gui/online/profile_friends.stkgui #. I18N: In the profile screen -#: data/po/gui_strings.h:756 +#: data/po/gui_strings.h:770 msgid "Look for more friends:" msgstr "" -#. I18N: ../stk-assets/wip-tracks/20_luna/track.xml -#: data/po/gui_strings.h:1508 -msgid "Luna" -msgstr "" - #. I18N: ./data/achievements.xml #: data/po/gui_strings.h:29 msgid "Make 5 skidding in a single lap." @@ -1855,18 +1885,18 @@ msgstr "" #. I18N: In the server creation screen #. I18N: ./data/gui/online/lobby_settings.stkgui #. I18N: In the lobby settings screen -#: data/po/gui_strings.h:647 data/po/gui_strings.h:731 +#: data/po/gui_strings.h:653 data/po/gui_strings.h:745 msgid "Max. number of players" msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui #. I18N: In soccer setup screen -#: data/po/gui_strings.h:102 +#: data/po/gui_strings.h:108 msgid "Maximum time (min.)" msgstr "" #. I18N: if some kart animations are enabled -#: src/states_screens/options_screen_video.cpp:414 +#: src/states_screens/options_screen_video.cpp:413 msgid "Me Only" msgstr "" @@ -1876,18 +1906,23 @@ msgid "Menu Keys" msgstr "" #. I18N: ../stk-assets/tracks/minigolf/track.xml -#: data/po/gui_strings.h:1406 +#: data/po/gui_strings.h:1369 msgid "Minigolf" msgstr "" +#. I18N: ./data/achievements.xml +#: data/po/gui_strings.h:62 +msgid "Mosquito Hunter" +msgstr "" + #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:842 +#: data/po/gui_strings.h:856 msgid "Motion blur" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:436 +#: src/states_screens/options_screen_video.cpp:435 #, c-format msgid "Motion blur: %s" msgstr "" @@ -1906,19 +1941,19 @@ msgstr "" #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1169 +#: data/po/gui_strings.h:1183 msgid "Move down" msgstr "" #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1165 +#: data/po/gui_strings.h:1179 msgid "Move up" msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: Main menu button -#: data/po/gui_strings.h:155 +#: data/po/gui_strings.h:161 msgid "Multiplayer" msgstr "" @@ -1930,24 +1965,27 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help3.stkgui #. I18N: Tab in help menu -#: data/po/gui_strings.h:384 data/po/gui_strings.h:950 -#: data/po/gui_strings.h:996 data/po/gui_strings.h:1029 +#: data/po/gui_strings.h:390 data/po/gui_strings.h:964 +#: data/po/gui_strings.h:1010 data/po/gui_strings.h:1043 msgid "Multi­player" msgstr "" #. I18N: ./data/gui/options_audio.stkgui #. I18N: In the audio options screen -#: data/po/gui_strings.h:273 +#: data/po/gui_strings.h:279 msgid "Music" msgstr "" #. I18N: ./data/gui/online/user_info_dialog.stkgui #. I18N: User info dialog +#. I18N: ./data/gui/online/register.stkgui +#. I18N: In the registration dialog #. I18N: ./data/gui/online/server_info_dialog.stkgui #. I18N: In the server info dialog #: src/states_screens/server_selection.cpp:91 #: src/states_screens/online_profile_achievements.cpp:73 -#: data/po/gui_strings.h:540 data/po/gui_strings.h:703 +#: data/po/gui_strings.h:546 data/po/gui_strings.h:681 +#: data/po/gui_strings.h:717 msgid "Name" msgstr "" @@ -1967,13 +2005,13 @@ msgstr "" #. I18N: In the server creation screen #. I18N: ./data/gui/online/lobby_settings.stkgui #. I18N: In the lobby settings screen -#: data/po/gui_strings.h:643 data/po/gui_strings.h:727 +#: data/po/gui_strings.h:649 data/po/gui_strings.h:741 msgid "Name of the server" msgstr "" #. I18N: ./data/gui/gpeditor.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1068 +#: data/po/gui_strings.h:1082 msgid "New" msgstr "" @@ -1982,9 +2020,15 @@ msgstr "" msgid "New Grand Prix '%s' now available" msgstr "" +#. I18N: ./data/gui/online/register.stkgui +#. I18N: Section in the register screen +#: data/po/gui_strings.h:669 +msgid "New Online Account" +msgstr "" + #. I18N: ./data/gui/online/change_password.stkgui #. I18N: In the change password dialog -#: data/po/gui_strings.h:491 +#: data/po/gui_strings.h:497 msgid "New Password" msgstr "" @@ -1997,7 +2041,7 @@ msgstr "" msgid "New difficulty '%s' now available" msgstr "" -#: src/modes/linear_world.cpp:361 +#: src/modes/linear_world.cpp:363 msgid "New fastest lap" msgstr "" @@ -2026,17 +2070,17 @@ msgid "Nitro challenge" msgstr "" #: src/states_screens/edit_gp_screen.cpp:253 -#: src/states_screens/dialogs/message_dialog.cpp:104 +#: src/states_screens/dialogs/message_dialog.cpp:128 msgid "No" msgstr "" #. I18N: ../stk-assets/karts/nolok/kart.xml -#: data/po/gui_strings.h:1364 +#: data/po/gui_strings.h:1480 msgid "Nolok" msgstr "" #. I18N: if no kart animations are enabled -#: src/states_screens/options_screen_video.cpp:416 +#: src/states_screens/options_screen_video.cpp:415 #: src/states_screens/gp_info_screen.cpp:75 msgid "None" msgstr "" @@ -2047,7 +2091,7 @@ msgid "Normal Race" msgstr "" #. I18N: ../stk-assets/tracks/snowmountain/track.xml -#: data/po/gui_strings.h:1409 +#: data/po/gui_strings.h:1372 msgid "Northern Resort" msgstr "" @@ -2061,7 +2105,7 @@ msgstr "" #. I18N: Difficulty #. I18N: ./data/gui/racesetup.stkgui #. I18N: Difficulty -#: data/po/gui_strings.h:434 data/po/gui_strings.h:788 +#: data/po/gui_strings.h:440 data/po/gui_strings.h:802 msgid "Novice" msgstr "" @@ -2072,25 +2116,25 @@ msgstr "" #. I18N: ./data/gui/track_info.stkgui #. I18N: In the track info screen -#: data/po/gui_strings.h:79 +#: data/po/gui_strings.h:85 msgid "Number of AI karts" msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui #. I18N: In soccer setup screen -#: data/po/gui_strings.h:98 +#: data/po/gui_strings.h:104 msgid "Number of goals to win" msgstr "" #. I18N: ./data/gui/track_info.stkgui #. I18N: In the track info screen -#: data/po/gui_strings.h:75 +#: data/po/gui_strings.h:81 msgid "Number of laps" msgstr "" #. I18N: ./data/gui/edit_track.stkgui #. I18N: In the edit track screen -#: data/po/gui_strings.h:125 +#: data/po/gui_strings.h:131 msgid "Number of laps:" msgstr "" @@ -2101,17 +2145,20 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog +#. I18N: ./data/gui/enter_gp_name_dialog.stkgui +#. I18N: In the 'add new grand prix' dialog #. I18N: ./data/gui/enter_player_name_dialog.stkgui #. I18N: In the 'add new player' dialog -#: src/states_screens/dialogs/message_dialog.cpp:98 -#: src/states_screens/dialogs/message_dialog.cpp:111 data/po/gui_strings.h:132 -#: data/po/gui_strings.h:242 data/po/gui_strings.h:331 -#: data/po/gui_strings.h:691 data/po/gui_strings.h:1226 +#: src/states_screens/dialogs/message_dialog.cpp:122 +#: src/states_screens/dialogs/message_dialog.cpp:135 data/po/gui_strings.h:138 +#: data/po/gui_strings.h:248 data/po/gui_strings.h:337 +#: data/po/gui_strings.h:705 data/po/gui_strings.h:1228 +#: data/po/gui_strings.h:1240 msgid "OK" msgstr "" #. I18N: ./data/grandprix/2_offthebeatentrack.grandprix -#: data/po/gui_strings.h:1349 +#: data/po/gui_strings.h:1363 msgid "Off the beaten track" msgstr "" @@ -2119,19 +2166,25 @@ msgstr "" msgid "Offline" msgstr "" +#. I18N: ./data/gui/online/register.stkgui +#. I18N: Section in the register screen +#: data/po/gui_strings.h:677 +msgid "Offline Account" +msgstr "" + #. I18N: Cutscene subtitle from ../stk-assets/tracks/introcutscene2/scene.xml #. I18N: ../stk-assets/tracks/introcutscene2/scene.xml -#: data/po/gui_strings.h:1466 data/po/gui_strings.h:1469 +#: data/po/gui_strings.h:1429 data/po/gui_strings.h:1432 msgid "Oh yes, see, he's in my castle now and will be served for supper..." msgstr "" #. I18N: ../stk-assets/tracks/mines/track.xml -#: data/po/gui_strings.h:1445 +#: data/po/gui_strings.h:1408 msgid "Old Mine" msgstr "" #. I18N: ../stk-assets/tracks/olivermath/track.xml -#: data/po/gui_strings.h:1412 +#: data/po/gui_strings.h:1375 msgid "Oliver's Math Class" msgstr "" @@ -2143,32 +2196,32 @@ msgstr "" #. I18N: In the user screen #: src/states_screens/online_profile_friends.cpp:149 #: src/states_screens/main_menu_screen.cpp:170 -#: src/states_screens/main_menu_screen.cpp:185 data/po/gui_strings.h:159 -#: data/po/gui_strings.h:226 data/po/gui_strings.h:315 +#: src/states_screens/main_menu_screen.cpp:185 data/po/gui_strings.h:165 +#: data/po/gui_strings.h:232 data/po/gui_strings.h:321 msgid "Online" msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:459 +#: data/po/gui_strings.h:465 msgid "Online Multiplayer" msgstr "" #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:671 +#: data/po/gui_strings.h:685 msgid "Online Username" msgstr "" -#: src/states_screens/register_screen.cpp:234 +#: src/states_screens/register_screen.cpp:279 msgid "Online username has to be between 3 and 30 characters long!" msgstr "" -#: src/states_screens/register_screen.cpp:238 +#: src/states_screens/register_screen.cpp:283 msgid "Online username must not start with a number!" msgstr "" -#: src/input/input_manager.cpp:746 +#: src/input/input_manager.cpp:749 msgid "Only the Game Master may act at this point!" msgstr "" @@ -2183,14 +2236,14 @@ msgstr "" #. I18N: In the in-game dialog #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:167 data/po/gui_strings.h:892 -#: data/po/gui_strings.h:923 +#: data/po/gui_strings.h:173 data/po/gui_strings.h:906 +#: data/po/gui_strings.h:937 msgid "Options" msgstr "" #. I18N: ./data/gui/online/profile_overview.stkgui #. I18N: Section in the profile screen -#: data/po/gui_strings.h:455 +#: data/po/gui_strings.h:461 msgid "Overview" msgstr "" @@ -2199,7 +2252,7 @@ msgid "POWER" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:403 +#: data/po/gui_strings.h:409 msgid "Parachute - slows down all karts in a better position." msgstr "" @@ -2209,24 +2262,24 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:238 data/po/gui_strings.h:327 -#: data/po/gui_strings.h:675 +#: data/po/gui_strings.h:244 data/po/gui_strings.h:333 +#: data/po/gui_strings.h:689 msgid "Password" msgstr "" #. I18N: ./data/gui/online/profile_settings.stkgui #. I18N: In the online account settings screen -#: data/po/gui_strings.h:742 +#: data/po/gui_strings.h:756 msgid "Password :" msgstr "" #. I18N: ./data/gui/online/change_password.stkgui #. I18N: In the change password dialog -#: data/po/gui_strings.h:483 +#: data/po/gui_strings.h:489 msgid "Password Change" msgstr "" -#: src/states_screens/register_screen.cpp:242 +#: src/states_screens/register_screen.cpp:287 #: src/states_screens/dialogs/change_password_dialog.cpp:140 msgid "Password has to be between 8 and 30 characters long!" msgstr "" @@ -2235,7 +2288,7 @@ msgstr "" msgid "Password successfully changed." msgstr "" -#: src/states_screens/register_screen.cpp:226 +#: src/states_screens/register_screen.cpp:271 #: src/states_screens/dialogs/change_password_dialog.cpp:147 msgid "Passwords don't match!" msgstr "" @@ -2247,7 +2300,7 @@ msgstr "" #. I18N: ./data/gui/overworld_dialog.stkgui #. I18N: ./data/gui/race_paused_dialog.stkgui -#: data/po/gui_strings.h:880 data/po/gui_strings.h:903 +#: data/po/gui_strings.h:894 data/po/gui_strings.h:917 msgid "Paused" msgstr "" @@ -2261,7 +2314,7 @@ msgid "Pending" msgstr "" #. I18N: ./data/grandprix/1_penguinplayground.grandprix -#: data/po/gui_strings.h:1346 +#: data/po/gui_strings.h:1360 msgid "Penguin Playground" msgstr "" @@ -2270,12 +2323,12 @@ msgid "Performing vote" msgstr "" #. I18N: ../stk-assets/karts/pidgin/kart.xml -#: data/po/gui_strings.h:1382 +#: data/po/gui_strings.h:1498 msgid "Pidgin" msgstr "" #. I18N: ./data/gui/tutorial.stkgui -#: data/po/gui_strings.h:300 +#: data/po/gui_strings.h:306 msgid "Play all" msgstr "" @@ -2291,16 +2344,16 @@ msgstr "" #: src/states_screens/options_screen_input2.cpp:88 #: src/states_screens/server_selection.cpp:92 #: src/states_screens/options_screen_audio.cpp:66 -#: src/states_screens/options_screen_video.cpp:138 +#: src/states_screens/options_screen_video.cpp:137 #: src/states_screens/options_screen_input.cpp:141 -#: src/states_screens/options_screen_ui.cpp:120 data/po/gui_strings.h:222 -#: data/po/gui_strings.h:354 +#: src/states_screens/options_screen_ui.cpp:119 data/po/gui_strings.h:228 +#: data/po/gui_strings.h:360 msgid "Players" msgstr "" #. I18N: ./data/gui/enter_gp_name_dialog.stkgui #. I18N: In the 'add new grand prix' dialog -#: data/po/gui_strings.h:1210 +#: data/po/gui_strings.h:1224 msgid "Please enter the name of the grand prix" msgstr "" @@ -2318,7 +2371,7 @@ msgid "Please wait while the add-ons are loading" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:397 +#: data/po/gui_strings.h:403 msgid "" "Plunger - throw straight to pull an opponent back, or throw while looking " "back to make one lose sight." @@ -2338,24 +2391,24 @@ msgstr "" #. I18N: ./data/gui/press_a_key_dialog.stkgui #. I18N: When configuring input -#: data/po/gui_strings.h:1148 +#: data/po/gui_strings.h:1162 msgid "Press ESC to cancel" msgstr "" #. I18N: ./data/gui/press_a_key_dialog.stkgui -#: data/po/gui_strings.h:1136 +#: data/po/gui_strings.h:1150 msgid "Press a key" msgstr "" #. I18N: ./data/gui/options_input.stkgui #. I18N: In the input configuration screen -#: data/po/gui_strings.h:1095 +#: data/po/gui_strings.h:1109 msgid "Press enter or double-click on a device to configure it" msgstr "" #. I18N: ./data/gui/options_players.stkgui #. I18N: In the player configuration screen -#: data/po/gui_strings.h:361 +#: data/po/gui_strings.h:367 msgid "Press enter or double-click on a player to edit him/her" msgstr "" @@ -2389,7 +2442,7 @@ msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:475 +#: data/po/gui_strings.h:481 msgid "Profile" msgstr "" @@ -2398,7 +2451,7 @@ msgid "Progress" msgstr "" #. I18N: ../stk-assets/karts/puffy/kart.xml -#: data/po/gui_strings.h:1376 +#: data/po/gui_strings.h:1492 msgid "Puffy" msgstr "" @@ -2410,13 +2463,13 @@ msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:463 +#: data/po/gui_strings.h:469 msgid "Quick Play" msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: In the main screen -#: data/po/gui_strings.h:191 +#: data/po/gui_strings.h:197 msgid "Quit" msgstr "" @@ -2427,8 +2480,8 @@ msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui #. I18N: ./data/gui/select_challenge.stkgui #. I18N: ./data/gui/racesetup.stkgui -#: data/po/gui_strings.h:94 data/po/gui_strings.h:426 -#: data/po/gui_strings.h:781 +#: data/po/gui_strings.h:100 data/po/gui_strings.h:432 +#: data/po/gui_strings.h:795 msgid "Race Setup" msgstr "" @@ -2460,15 +2513,10 @@ msgstr "" #. I18N: ./data/gui/addons_screen.stkgui #. I18N: In addons screen, in the filtering bar, to enable a filter that will show only items with good rating -#: data/po/gui_strings.h:1328 +#: data/po/gui_strings.h:1342 msgid "Rating >=" msgstr "" -#. I18N: ../stk-assets/wip-tracks/20_mansion/track.xml -#: data/po/gui_strings.h:1514 -msgid "Ravenbridge mansion" -msgstr "" - #. I18N: as in "ready, set, go", shown at the beginning of the race #: src/states_screens/race_gui_base.cpp:64 msgid "Ready!" @@ -2479,26 +2527,26 @@ msgstr "" msgid "Really ... a secret." msgstr "" -#: src/states_screens/race_result_gui.cpp:956 +#: src/states_screens/race_result_gui.cpp:954 msgid "Red Team Wins" msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: Tab in login menu -#: data/po/gui_strings.h:595 +#: data/po/gui_strings.h:601 msgid "Register" msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1036 +#: data/po/gui_strings.h:1050 msgid "" "Regular Race: All blows allowed, so catch weapons and make clever use of " "them!" msgstr "" -#: src/states_screens/gp_info_screen.cpp:149 -#: src/states_screens/gp_info_screen.cpp:174 +#: src/states_screens/gp_info_screen.cpp:153 +#: src/states_screens/gp_info_screen.cpp:178 msgid "Reload" msgstr "" @@ -2506,13 +2554,13 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/user_screen.stkgui #. I18N: In the user screen -#: data/po/gui_strings.h:230 data/po/gui_strings.h:319 +#: data/po/gui_strings.h:236 data/po/gui_strings.h:325 msgid "Remember password" msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1261 +#: data/po/gui_strings.h:1275 msgid "Remember window location" msgstr "" @@ -2522,8 +2570,8 @@ msgstr "" #. I18N: Menu item #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:544 data/po/gui_strings.h:1080 -#: data/po/gui_strings.h:1181 +#: data/po/gui_strings.h:550 data/po/gui_strings.h:1094 +#: data/po/gui_strings.h:1195 msgid "Remove" msgstr "" @@ -2533,8 +2581,8 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/gpeditor.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:254 data/po/gui_strings.h:343 -#: data/po/gui_strings.h:1084 +#: data/po/gui_strings.h:260 data/po/gui_strings.h:349 +#: data/po/gui_strings.h:1098 msgid "Rename" msgstr "" @@ -2560,7 +2608,7 @@ msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1253 +#: data/po/gui_strings.h:1267 msgid "Resolution" msgstr "" @@ -2570,19 +2618,19 @@ msgstr "" #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:915 +#: data/po/gui_strings.h:929 msgid "Restart Race" msgstr "" #. I18N: ./data/gui/gp_info.stkgui #. I18N: In the grand prix info screen -#: data/po/gui_strings.h:199 +#: data/po/gui_strings.h:205 msgid "Reverse" msgstr "" #. I18N: ./data/gui/edit_track.stkgui #. I18N: In the edit track screen -#: data/po/gui_strings.h:129 +#: data/po/gui_strings.h:135 msgid "Reverse:" msgstr "" @@ -2640,35 +2688,35 @@ msgid "SPEED" msgstr "" #. I18N: ../stk-assets/tracks/startrack/track.xml -#: data/po/gui_strings.h:1493 +#: data/po/gui_strings.h:1456 msgid "STK Enterprise" msgstr "" #. I18N: ../stk-assets/karts/20_sara_the_wizard/kart.xml -#: data/po/gui_strings.h:1373 +#: data/po/gui_strings.h:1489 msgid "Sara the wizard" msgstr "" #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1185 +#: data/po/gui_strings.h:1199 msgid "Save" msgstr "" #. I18N: ./data/gui/grand_prix_lose.stkgui #. I18N: ./data/gui/grand_prix_win.stkgui -#: data/po/gui_strings.h:1154 data/po/gui_strings.h:1206 +#: data/po/gui_strings.h:1168 data/po/gui_strings.h:1220 msgid "Save Grand Prix" msgstr "" #. I18N: ../stk-assets/tracks/scotland/track.xml -#: data/po/gui_strings.h:1433 +#: data/po/gui_strings.h:1396 msgid "Scotland" msgstr "" #. I18N: ./data/gui/online/user_search.stkgui #. I18N: ./data/gui/online/profile_friends.stkgui -#: data/po/gui_strings.h:448 data/po/gui_strings.h:759 +#: data/po/gui_strings.h:454 data/po/gui_strings.h:773 msgid "Search" msgstr "" @@ -2683,12 +2731,12 @@ msgid "Select" msgstr "" #. I18N: ./data/gui/racesetup.stkgui -#: data/po/gui_strings.h:784 +#: data/po/gui_strings.h:798 msgid "Select a difficulty" msgstr "" #. I18N: ./data/gui/racesetup.stkgui -#: data/po/gui_strings.h:803 +#: data/po/gui_strings.h:817 msgid "Select a game mode" msgstr "" @@ -2698,36 +2746,36 @@ msgstr "" #. I18N: ./data/gui/overworld_dialog.stkgui #. I18N: In the in-game dialog -#: data/po/gui_strings.h:888 +#: data/po/gui_strings.h:902 msgid "Select kart" msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1125 +#: data/po/gui_strings.h:1139 msgid "Send anonymous HW statistics" msgstr "" #. I18N: ./data/gui/online/create_server.stkgui #. I18N: In the server creation screen -#: data/po/gui_strings.h:639 +#: data/po/gui_strings.h:645 msgid "Server Creation" msgstr "" #. I18N: ./data/gui/online/server_info_dialog.stkgui #. I18N: In the server info dialog -#: data/po/gui_strings.h:699 +#: data/po/gui_strings.h:713 msgid "Server Info" msgstr "" #. I18N: ./data/gui/online/server_selection.stkgui -#: data/po/gui_strings.h:532 +#: data/po/gui_strings.h:538 msgid "Server Selection" msgstr "" #. I18N: ./data/gui/online/lobby.stkgui #. I18N: In the networking lobby -#: data/po/gui_strings.h:572 +#: data/po/gui_strings.h:578 msgid "Server name :" msgstr "" @@ -2742,91 +2790,55 @@ msgstr "" #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: src/states_screens/race_result_gui.cpp:198 data/po/gui_strings.h:911 +#: src/states_screens/race_result_gui.cpp:198 data/po/gui_strings.h:925 msgid "Setup New Race" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:814 +#: data/po/gui_strings.h:828 msgid "Shadows" msgstr "" -#. I18N: in graphical options +#: src/states_screens/options_screen_video.cpp:447 +#, c-format +msgid "Shadows: %i" +msgstr "" + #: src/states_screens/options_screen_video.cpp:445 #, c-format msgid "Shadows: %s" msgstr "" #. I18N: ../stk-assets/tracks/sandtrack/track.xml -#: data/po/gui_strings.h:1415 +#: data/po/gui_strings.h:1378 msgid "Shifting Sands" msgstr "" #. I18N: ../stk-assets/tracks/city/track.xml -#: data/po/gui_strings.h:1430 +#: data/po/gui_strings.h:1393 msgid "Shiny Suburbs" msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: Tab in login menu -#. I18N: ./data/gui/online/guest_login.stkgui -#. I18N: In the login dialog -#: data/po/gui_strings.h:587 data/po/gui_strings.h:607 +#: data/po/gui_strings.h:593 msgid "Sign In" msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: Tab in login menu -#: data/po/gui_strings.h:591 +#: data/po/gui_strings.h:597 msgid "Sign In As Guest" msgstr "" -#. I18N: ./data/gui/online/main.stkgui -#. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:479 -msgid "Sign Out" -msgstr "" - -#: src/states_screens/online_screen.cpp:139 -#, c-format -msgid "Signed in as: %s." -msgstr "" - -#: src/states_screens/online_screen.cpp:155 -msgid "Signing in" -msgstr "" - -#: src/states_screens/user_screen.cpp:445 -#, c-format -msgid "Signing in '%s'" -msgstr "" - -#. I18N: ./data/gui/online/guest_login.stkgui -#. I18N: In the login dialog -#: data/po/gui_strings.h:603 -msgid "" -"Signing in as a guest allows you to participate in online races, but it does " -"not allow you to vote for addons, or collect any achievements while being " -"online." -msgstr "" - -#: src/states_screens/online_screen.cpp:160 -msgid "Signing out" -msgstr "" - -#: src/states_screens/user_screen.cpp:444 -#, c-format -msgid "Signing out '%s'" -msgstr "" - #: src/states_screens/online_profile_friends.cpp:70 msgid "Since" msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: Main menu button -#: data/po/gui_strings.h:151 +#: data/po/gui_strings.h:157 msgid "Singleplayer" msgstr "" @@ -2847,12 +2859,12 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1113 +#: data/po/gui_strings.h:1127 msgid "Skin" msgstr "" #. I18N: ../stk-assets/tracks/snowtuxpeak/track.xml -#: data/po/gui_strings.h:1451 +#: data/po/gui_strings.h:1414 msgid "Snow Peak" msgstr "" @@ -2862,13 +2874,13 @@ msgid "Soccer" msgstr "" #. I18N: ../stk-assets/tracks/soccer_field/track.xml -#: data/po/gui_strings.h:1448 +#: data/po/gui_strings.h:1411 msgid "Soccer field" msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1052 +#: data/po/gui_strings.h:1066 msgid "" "Soccer: Only in multiplayer games. Use your kart to push the ball into the " "goal." @@ -2888,7 +2900,7 @@ msgstr "" #. I18N: ./data/gui/options_audio.stkgui #. I18N: In the audio options screen -#: data/po/gui_strings.h:285 +#: data/po/gui_strings.h:291 msgid "Sound Effects" msgstr "" @@ -2897,7 +2909,7 @@ msgstr "" #. I18N: ./data/gui/easter_egg.stkgui #. I18N: track group #: src/states_screens/grand_prix_editor_screen.cpp:334 -#: data/po/gui_strings.h:1273 data/po/gui_strings.h:1309 +#: data/po/gui_strings.h:1287 data/po/gui_strings.h:1323 msgid "Standard" msgstr "" @@ -2914,7 +2926,7 @@ msgstr "" #. I18N: In the track info screen #. I18N: ./data/gui/gp_info.stkgui #. I18N: In the grand prix info screen -#: data/po/gui_strings.h:87 data/po/gui_strings.h:211 +#: data/po/gui_strings.h:93 data/po/gui_strings.h:217 msgid "Start Race" msgstr "" @@ -2934,7 +2946,7 @@ msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: Main menu button -#: data/po/gui_strings.h:147 +#: data/po/gui_strings.h:153 msgid "Story Mode" msgstr "" @@ -2947,23 +2959,23 @@ msgstr "" #. I18N: In the change password dialog #. I18N: ./data/gui/online/recovery_input.stkgui #. I18N: In the recovery dialog -#: data/po/gui_strings.h:499 data/po/gui_strings.h:522 +#: data/po/gui_strings.h:505 data/po/gui_strings.h:528 msgid "Submit" msgstr "" #. I18N: ../stk-assets/tracks/subsea/track.xml -#: data/po/gui_strings.h:1439 +#: data/po/gui_strings.h:1402 msgid "Subsea" msgstr "" #. I18N: ./data/gui/racesetup.stkgui #. I18N: Difficulty -#: data/po/gui_strings.h:800 +#: data/po/gui_strings.h:814 msgid "SuperTux" msgstr "" #. I18N: ./data/gui/addons_screen.stkgui -#: data/po/gui_strings.h:1320 +#: data/po/gui_strings.h:1334 msgid "SuperTuxKart Addons" msgstr "" @@ -2971,8 +2983,8 @@ msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: ./data/gui/help4.stkgui #. I18N: ./data/gui/help3.stkgui -#: data/po/gui_strings.h:368 data/po/gui_strings.h:934 -#: data/po/gui_strings.h:980 data/po/gui_strings.h:1013 +#: data/po/gui_strings.h:374 data/po/gui_strings.h:948 +#: data/po/gui_strings.h:994 data/po/gui_strings.h:1027 msgid "SuperTuxKart Help" msgstr "" @@ -2983,24 +2995,24 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: ./data/gui/options_device.stkgui #. I18N: ./data/gui/options_video.stkgui -#: data/po/gui_strings.h:218 data/po/gui_strings.h:265 -#: data/po/gui_strings.h:350 data/po/gui_strings.h:1087 -#: data/po/gui_strings.h:1105 data/po/gui_strings.h:1188 -#: data/po/gui_strings.h:1233 +#: data/po/gui_strings.h:224 data/po/gui_strings.h:271 +#: data/po/gui_strings.h:356 data/po/gui_strings.h:1101 +#: data/po/gui_strings.h:1119 data/po/gui_strings.h:1202 +#: data/po/gui_strings.h:1247 msgid "SuperTuxKart Options" msgstr "" #. I18N: ./data/gui/help4.stkgui -#: data/po/gui_strings.h:999 +#: data/po/gui_strings.h:1013 msgid "SuperTuxKart can be played in multiplayer mode on the same computer" msgstr "" #. I18N: ./data/gui/help3.stkgui -#: data/po/gui_strings.h:1032 +#: data/po/gui_strings.h:1046 msgid "SuperTuxKart features several game modes" msgstr "" -#: src/main.cpp:1129 +#: src/main.cpp:1138 msgid "" "SuperTuxKart may connect to a server to download add-ons and notify you of " "updates. We also collect anonymous hardware statistics to help with the " @@ -3010,53 +3022,60 @@ msgid "" msgstr "" #. I18N: ../stk-assets/karts/suzanne/kart.xml -#: data/po/gui_strings.h:1397 +#: data/po/gui_strings.h:1513 msgid "Suzanne" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:409 +#: data/po/gui_strings.h:415 msgid "" "Swapper - gift boxes are transformed into bananas and vice versa for a short " "time." msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:415 +#: data/po/gui_strings.h:421 msgid "Swatter - will squash karts close by, slowing them down." msgstr "" #. I18N: in the language choice, to select the same language as the OS -#: src/states_screens/options_screen_ui.cpp:186 +#: src/states_screens/options_screen_ui.cpp:185 msgid "System Language" msgstr "" +#. I18N: ./data/achievements.xml +#: data/po/gui_strings.h:65 +msgid "" +"Take your opponents for mosquitos! With the swatter, squash at least 5 of " +"them in a race." +msgstr "" + #. I18N: ./data/gui/online/registration_terms.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:615 +#: data/po/gui_strings.h:621 msgid "Terms and Agreement" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:858 +#: data/po/gui_strings.h:872 msgid "Texture compression" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:870 +#: data/po/gui_strings.h:884 msgid "Texture filtering" msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: in the help menu -#: data/po/gui_strings.h:973 +#: data/po/gui_strings.h:987 msgid "The 'skidding' key allows you to skid in sharp turns and get a boost." msgstr "" #. I18N: ../stk-assets/tracks/stadium/track.xml -#: data/po/gui_strings.h:1505 +#: data/po/gui_strings.h:1468 msgid "The Stadium" msgstr "" @@ -3081,7 +3100,7 @@ msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1040 +#: data/po/gui_strings.h:1054 msgid "Time Trial: Contains no powerups, so only your driving skills matter!" msgstr "" @@ -3099,12 +3118,12 @@ msgstr "" #. I18N: ./data/gui/help2.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:388 +#: data/po/gui_strings.h:394 msgid "To help you win, there are some powerups you can collect :" msgstr "" #. I18N: ./data/grandprix/3_tothemoonandback.grandprix -#: data/po/gui_strings.h:1352 +#: data/po/gui_strings.h:1366 msgid "To the moon and back" msgstr "" @@ -3123,7 +3142,7 @@ msgstr "" msgid "Track" msgstr "" -#: src/states_screens/race_result_gui.cpp:1121 +#: src/states_screens/race_result_gui.cpp:1119 #, c-format msgid "Track %i/%i" msgstr "" @@ -3137,7 +3156,7 @@ msgstr "" #. I18N: ./data/gui/gp_info.stkgui #. I18N: In the grand prix info screen -#: data/po/gui_strings.h:207 +#: data/po/gui_strings.h:213 msgid "Track group" msgstr "" @@ -3145,11 +3164,11 @@ msgstr "" #. I18N: In the grand prix info screen #. I18N: ./data/gui/addons_screen.stkgui #. I18N: In the addons screen -#: data/po/gui_strings.h:203 data/po/gui_strings.h:1336 +#: data/po/gui_strings.h:209 data/po/gui_strings.h:1350 msgid "Tracks" msgstr "" -#: src/states_screens/dialogs/custom_video_settings.cpp:76 +#: src/states_screens/dialogs/custom_video_settings.cpp:77 msgid "Trilinear" msgstr "" @@ -3160,24 +3179,24 @@ msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: In the main screen -#: src/states_screens/race_gui_overworld.cpp:455 data/po/gui_strings.h:175 +#: src/states_screens/race_gui_overworld.cpp:455 data/po/gui_strings.h:181 msgid "Tutorial" msgstr "" #. I18N: ./data/gui/tutorial.stkgui #. I18N: Title for tutorials screen -#: data/po/gui_strings.h:297 +#: data/po/gui_strings.h:303 msgid "Tutorial : Selection Room" msgstr "" #. I18N: ../stk-assets/karts/tux/kart.xml -#: data/po/gui_strings.h:1367 +#: data/po/gui_strings.h:1483 msgid "Tux" msgstr "" #. I18N: ./data/gui/select_challenge.stkgui #. I18N: Type of race, in a challenge -#: data/po/gui_strings.h:430 +#: data/po/gui_strings.h:436 msgid "Type :" msgstr "" @@ -3193,7 +3212,7 @@ msgstr "" #. I18N: ./data/gui/addons_loading.stkgui #. I18N: Add-on screen action -#: data/po/gui_strings.h:1293 +#: data/po/gui_strings.h:1307 msgid "Uninstall" msgstr "" @@ -3213,7 +3232,7 @@ msgstr "" #. I18N: ./data/gui/addons_screen.stkgui #. I18N: In addons screen, in the filtering bar, to enable a filter that will show only recently updated items -#: data/po/gui_strings.h:1324 +#: data/po/gui_strings.h:1338 msgid "Updated" msgstr "" @@ -3228,13 +3247,13 @@ msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:862 +#: data/po/gui_strings.h:876 msgid "Use high definition textures" msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui #. I18N: In soccer setup screen -#: data/po/gui_strings.h:110 +#: data/po/gui_strings.h:116 msgid "Use left/right to choose your team and press fire" msgstr "" @@ -3245,17 +3264,17 @@ msgstr "" #. I18N: ./data/gui/online/user_info_dialog.stkgui #. I18N: User info dialog' dialog -#: data/po/gui_strings.h:536 +#: data/po/gui_strings.h:542 msgid "User Info" msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: Section in the settings menu -#: src/states_screens/user_screen.cpp:599 +#: src/states_screens/user_screen.cpp:643 #: src/states_screens/options_screen_input2.cpp:87 #: src/states_screens/options_screen_audio.cpp:65 -#: src/states_screens/options_screen_video.cpp:137 -#: src/states_screens/options_screen_input.cpp:140 data/po/gui_strings.h:1109 +#: src/states_screens/options_screen_video.cpp:136 +#: src/states_screens/options_screen_input.cpp:140 data/po/gui_strings.h:1123 msgid "User Interface" msgstr "" @@ -3264,7 +3283,7 @@ msgid "User defined" msgstr "" #. I18N: ./data/gui/online/user_search.stkgui -#: data/po/gui_strings.h:445 +#: data/po/gui_strings.h:451 msgid "User search" msgstr "" @@ -3275,8 +3294,8 @@ msgstr "" #. I18N: ./data/gui/online/recovery_input.stkgui #. I18N: In the recovery dialog #: src/states_screens/online_user_search.cpp:74 -#: src/states_screens/online_profile_friends.cpp:67 data/po/gui_strings.h:234 -#: data/po/gui_strings.h:323 data/po/gui_strings.h:514 +#: src/states_screens/online_profile_friends.cpp:67 data/po/gui_strings.h:240 +#: data/po/gui_strings.h:329 data/po/gui_strings.h:520 msgid "Username" msgstr "" @@ -3285,11 +3304,11 @@ msgid "Username and/or email address invalid." msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui -#: data/po/gui_strings.h:113 +#: data/po/gui_strings.h:119 msgid "VS" msgstr "" -#: src/states_screens/register_screen.cpp:302 +#: src/states_screens/register_screen.cpp:352 #: src/states_screens/dialogs/change_password_dialog.cpp:235 #: src/states_screens/dialogs/recovery_dialog.cpp:209 msgid "Validating info" @@ -3302,7 +3321,7 @@ msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1249 +#: data/po/gui_strings.h:1263 msgid "Vertical Sync (requires restart)" msgstr "" @@ -3310,19 +3329,19 @@ msgstr "" #. I18N: User info dialog #. I18N: ./data/gui/online/notification_dialog.stkgui #. I18N: User info dialog -#: data/po/gui_strings.h:560 data/po/gui_strings.h:715 +#: data/po/gui_strings.h:566 data/po/gui_strings.h:729 msgid "View" msgstr "" #. I18N: ./data/gui/options_audio.stkgui #. I18N: In the audio options screen -#: data/po/gui_strings.h:281 data/po/gui_strings.h:293 +#: data/po/gui_strings.h:287 data/po/gui_strings.h:299 msgid "Volume" msgstr "" #. I18N: ./data/gui/online/vote_dialog.stkgui #. I18N: In the vote dialog -#: data/po/gui_strings.h:631 +#: data/po/gui_strings.h:637 msgid "Vote" msgstr "" @@ -3334,7 +3353,7 @@ msgstr "" msgid "WEIGHT" msgstr "" -#: src/modes/linear_world.cpp:880 +#: src/modes/linear_world.cpp:882 msgid "WRONG WAY!" msgstr "" @@ -3353,32 +3372,32 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help3.stkgui #. I18N: Tab in help menu -#: data/po/gui_strings.h:376 data/po/gui_strings.h:942 -#: data/po/gui_strings.h:988 data/po/gui_strings.h:1021 +#: data/po/gui_strings.h:382 data/po/gui_strings.h:956 +#: data/po/gui_strings.h:1002 data/po/gui_strings.h:1035 msgid "Weapons" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:850 +#: data/po/gui_strings.h:864 msgid "Weather Effects" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:425 +#: src/states_screens/options_screen_video.cpp:424 #, c-format msgid "Weather Effects : %s" msgstr "" #. I18N: Cutscene subtitle from ../stk-assets/tracks/introcutscene2/scene.xml #. I18N: ../stk-assets/tracks/introcutscene2/scene.xml -#: data/po/gui_strings.h:1460 data/po/gui_strings.h:1463 +#: data/po/gui_strings.h:1423 data/po/gui_strings.h:1426 msgid "What's wrong, little hippies? Your great gnu leader is missing?" msgstr "" #. I18N: ./data/gui/help4.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1010 +#: data/po/gui_strings.h:1024 msgid "" "When input devices are configured, you are ready to play. Select the " "'multiplayer race' icon in the main menu. When it is time to choose a kart, " @@ -3394,7 +3413,7 @@ msgid "White" msgstr "" #. I18N: ../stk-assets/karts/wilber/kart.xml -#: data/po/gui_strings.h:1385 +#: data/po/gui_strings.h:1501 msgid "Wilber" msgstr "" @@ -3409,18 +3428,18 @@ msgid "Win in all single player modes, against at least 3 opponents." msgstr "" #. I18N: ../stk-assets/tracks/xr591/track.xml -#: data/po/gui_strings.h:1421 +#: data/po/gui_strings.h:1384 msgid "XR591" msgstr "" #. I18N: ../stk-assets/karts/20_xue/kart.xml -#: data/po/gui_strings.h:1391 +#: data/po/gui_strings.h:1507 msgid "Xue" msgstr "" #. I18N: ./data/gui/confirm_dialog.stkgui #. I18N: In a 'are you sure?' dialog -#: src/states_screens/edit_gp_screen.cpp:253 data/po/gui_strings.h:139 +#: src/states_screens/edit_gp_screen.cpp:253 data/po/gui_strings.h:145 msgid "Yes" msgstr "" @@ -3429,7 +3448,7 @@ msgid "You are now ready to race. Good luck!" msgstr "" #. I18N: ./data/gui/options_players.stkgui -#: data/po/gui_strings.h:357 +#: data/po/gui_strings.h:363 msgid "You are playing as" msgstr "" @@ -3453,15 +3472,15 @@ msgstr "" #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:687 +#: data/po/gui_strings.h:701 msgid "" -"You can play without creating an online account by unchecking the online " -"checkbox, though then you can not connect to friends, vote for addons etc. " +"You can play without creating an online account by selecting an offline " +"account. Though then you can not connect to friends, vote for addons etc. " "Please read our privacy statement at http://privacy.supertuxkart.net" msgstr "" #: src/states_screens/race_result_gui.cpp:164 -#: src/states_screens/grand_prix_win.cpp:125 +#: src/states_screens/grand_prix_win.cpp:124 msgid "You completed a challenge!" msgstr "" @@ -3469,7 +3488,7 @@ msgstr "" msgid "You completed challenges!" msgstr "" -#: src/states_screens/grand_prix_win.cpp:280 +#: src/states_screens/grand_prix_win.cpp:279 msgid "You completed the Grand Prix!" msgstr "" @@ -3490,7 +3509,7 @@ msgid "" "You completed the intermediate challenge! Points earned on this level: %i/%i" msgstr "" -#: src/karts/kart.cpp:855 +#: src/karts/kart.cpp:857 msgid "You finished the race!" msgstr "" @@ -3503,7 +3522,7 @@ msgstr "" msgid "You have a new friend request!" msgstr "" -#: src/modes/world.cpp:1146 +#: src/modes/world.cpp:1145 msgid "You have been eliminated!" msgstr "" @@ -3513,7 +3532,7 @@ msgid "" "clicking the stars beneath" msgstr "" -#: src/karts/kart.cpp:1898 +#: src/karts/kart.cpp:1900 msgid "" "You need more points\n" "to enter this challenge!\n" @@ -3521,33 +3540,33 @@ msgid "" "available challenges." msgstr "" -#: src/states_screens/user_screen.cpp:423 +#: src/states_screens/user_screen.cpp:467 msgid "You need to enter a password." msgstr "" -#: src/states_screens/feature_unlocked.cpp:567 +#: src/states_screens/feature_unlocked.cpp:568 msgid "You unlocked grand prix %0" msgstr "" -#: src/states_screens/feature_unlocked.cpp:529 +#: src/states_screens/feature_unlocked.cpp:530 msgid "You unlocked track %0" msgstr "" #. I18N: ./data/gui/online/recovery_info.stkgui #. I18N: In the recovery dialog -#: data/po/gui_strings.h:767 +#: data/po/gui_strings.h:781 msgid "" "You will receive an email with further instructions on how to reset your " "password. Please be patient and be sure to check your spam folder." msgstr "" -#: src/states_screens/register_screen.cpp:310 +#: src/states_screens/register_screen.cpp:360 msgid "" "You will receive an email with further instructions regarding account " "activation. Please be patient and be sure to check your spam folder." msgstr "" -#: src/karts/kart.cpp:855 src/karts/kart.cpp:874 +#: src/karts/kart.cpp:857 src/karts/kart.cpp:876 msgid "You won the race!" msgstr "" @@ -3563,6 +3582,11 @@ msgid "" "created." msgstr "" +#: src/main.cpp:1302 +msgid "" +"Your driver version is too old. Please install the latest video drivers." +msgstr "" + #: src/input/device_manager.cpp:501 msgid "Your input config file is not compatible with this version of STK." msgstr "" @@ -3573,7 +3597,7 @@ msgid "Your profile" msgstr "" #. I18N: ../stk-assets/tracks/zengarden/track.xml -#: data/po/gui_strings.h:1427 +#: data/po/gui_strings.h:1390 msgid "Zen Garden" msgstr "" @@ -3584,7 +3608,7 @@ msgstr "" #. I18N: ./data/gui/online/lobby.stkgui #. I18N: In networking lobby -#: data/po/gui_strings.h:576 +#: data/po/gui_strings.h:582 msgid "actions" msgstr "" @@ -3602,26 +3626,16 @@ msgstr "" msgid "featured" msgstr "" -#. I18N: ../stk-assets/wip-tracks/20_harvest/track.xml -#: data/po/gui_strings.h:1511 -msgid "harvest" -msgstr "" - -#: src/states_screens/dialogs/custom_video_settings.cpp:87 +#: src/states_screens/dialogs/custom_video_settings.cpp:88 msgid "high" msgstr "" -#: src/states_screens/dialogs/custom_video_settings.cpp:86 +#: src/states_screens/dialogs/custom_video_settings.cpp:87 msgid "low" msgstr "" -#. I18N: ../stk-assets/wip-tracks/20_newton/track.xml -#: data/po/gui_strings.h:1517 -msgid "newton" -msgstr "" - #. I18N: ../stk-assets/karts/20_sara_the_racer/kart.xml -#: data/po/gui_strings.h:1379 +#: data/po/gui_strings.h:1495 msgid "sara the racer" msgstr "" diff --git a/data/po/update_pot.sh b/data/po/update_pot.sh index b2496b4c3..d62874692 100755 --- a/data/po/update_pot.sh +++ b/data/po/update_pot.sh @@ -8,7 +8,7 @@ CPP_FILE_LIST="`find ./src \ -name '*.hpp' -or \ -name "*.h" \ `" -XML_FILE_LIST="`find ./data ../stk-assets ../supertuxkart-assets \ +XML_FILE_LIST="`find ./data ../stk-assets/tracks ../stk-assets/karts \ -name 'achievements.xml' -or \ -name 'kart.xml' -or \ -name 'track.xml' -or \ From 13bfee5ce131f722b02ad92bd37beddf7cf1542f Mon Sep 17 00:00:00 2001 From: auria Date: Sun, 8 Feb 2015 18:16:08 -0500 Subject: [PATCH 022/117] Do not output line numbers for strings that come from XML, since XML comments are extracted from a fake .h ffile and their location in that intermediary file has no use --- data/po/supertuxkart.pot | 397 ++++----------------------------------- data/po/update_pot.sh | 14 +- 2 files changed, 45 insertions(+), 366 deletions(-) diff --git a/data/po/supertuxkart.pot b/data/po/supertuxkart.pot index 996cb474d..dd24398ed 100644 --- a/data/po/supertuxkart.pot +++ b/data/po/supertuxkart.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: supertuxkart\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-08 18:04-0500\n" +"POT-Creation-Date: 2015-02-08 18:14-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -27,7 +27,6 @@ msgstr "" #. I18N: Cutscene subtitle from ../stk-assets/tracks/introcutscene2/scene.xml #. I18N: ../stk-assets/tracks/introcutscene2/scene.xml -#: data/po/gui_strings.h:1447 data/po/gui_strings.h:1450 msgid "" " But you pathetic little twerps will never be able to beat me - King of the " "Karts!" @@ -105,15 +104,13 @@ msgstr "" msgid "'%s' has been eliminated." msgstr "" -#. I18N: for empty highscores entries #. I18N: ./data/gui/track_info.stkgui -#: src/states_screens/track_info_screen.cpp:252 data/po/gui_strings.h:71 -#: data/po/gui_strings.h:74 data/po/gui_strings.h:77 +#. I18N: for empty highscores entries +#: src/states_screens/track_info_screen.cpp:252 msgid "(Empty)" msgstr "" #. I18N: ./data/gui/help4.stkgui -#: data/po/gui_strings.h:1016 msgid "(network play is not yet available)" msgstr "" @@ -127,13 +124,11 @@ msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: in the help screen -#: data/po/gui_strings.h:991 msgid "* Current key bindings can be seen/changed in menu Options" msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1074 msgid "" "* Most of these game modes can also be played in a Grand Prix fashion: " "instead of playing a single race, you play many in a row. The better you " @@ -143,12 +138,10 @@ msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:888 msgid "* Restart STK to apply new settings" msgstr "" #. I18N: ./data/gui/options_input.stkgui -#: data/po/gui_strings.h:1116 msgid "" "* Which config to use will be inferred from which 'Select' key is pressed to " "join the game." @@ -159,9 +152,6 @@ msgstr "" #. I18N: ./data/gui/online/profile_settings.stkgui #. I18N: ./data/gui/online/profile_friends.stkgui #. I18N: ./data/gui/online/profile_achievements_tab.stkgui -#: data/po/gui_strings.h:457 data/po/gui_strings.h:535 -#: data/po/gui_strings.h:748 data/po/gui_strings.h:762 -#: data/po/gui_strings.h:788 msgid "..." msgstr "" @@ -192,7 +182,6 @@ msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1062 msgid "" "3 Strikes Battle: Only in multiplayer games. Hit others with weapons until " "they lose all their lives." @@ -211,13 +200,11 @@ msgid "9 months" msgstr "" #. I18N: ./data/gui/track_info.stkgui -#: data/po/gui_strings.h:68 msgid "= Highscores =" msgstr "" #. I18N: ./data/gui/gp_info.stkgui #. I18N: In the grand prix info screen -#: data/po/gui_strings.h:201 msgid "AI karts" msgstr "" @@ -227,7 +214,6 @@ msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: In the main screen -#: data/po/gui_strings.h:193 msgid "About" msgstr "" @@ -252,7 +238,6 @@ msgstr "" #. I18N: User info dialog #. I18N: ./data/gui/online/registration_terms.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:558 data/po/gui_strings.h:629 msgid "Accept" msgstr "" @@ -260,13 +245,12 @@ msgstr "" #. I18N: In the recovery dialog #. I18N: ./data/gui/online/recovery_info.stkgui #. I18N: In the recovery dialog -#: data/po/gui_strings.h:513 data/po/gui_strings.h:777 msgid "Account Recovery" msgstr "" #. I18N: ./data/gui/online/profile_settings.stkgui #. I18N: Section in the profile screen -#: src/states_screens/online_profile_base.cpp:101 data/po/gui_strings.h:752 +#: src/states_screens/online_profile_base.cpp:101 msgid "Account Settings" msgstr "" @@ -274,8 +258,7 @@ msgstr "" #. I18N: In the main screen #. I18N: ./data/gui/online/profile_achievements_tab.stkgui #. I18N: Section in the profile screen -#: src/states_screens/online_profile_base.cpp:100 data/po/gui_strings.h:185 -#: data/po/gui_strings.h:792 +#: src/states_screens/online_profile_base.cpp:100 msgid "Achievements" msgstr "" @@ -285,13 +268,11 @@ msgstr "" #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1187 msgid "Add" msgstr "" #. I18N: ./data/gui/online/user_info_dialog.stkgui #. I18N: User info dialog -#: data/po/gui_strings.h:554 msgid "Add Friend" msgstr "" @@ -302,7 +283,6 @@ msgstr "" #. I18N: ./data/gui/options_players.stkgui #. I18N: In the player configuration screen -#: data/po/gui_strings.h:371 msgid "Add Player" msgstr "" @@ -313,7 +293,6 @@ msgstr "" #. I18N: ./data/gui/options_input.stkgui #. I18N: In the input configuration screen -#: data/po/gui_strings.h:1113 msgid "Add a device" msgstr "" @@ -321,24 +300,22 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/user_screen.stkgui #. I18N: In the user screen -#: data/po/gui_strings.h:252 data/po/gui_strings.h:341 msgid "Add user" msgstr "" -#. I18N: track group name -#. I18N: kart group name -#. I18N: arena group name -#. I18N: track group name #. I18N: ./data/gui/arenas.stkgui #. I18N: track group #. I18N: ./data/gui/easter_egg.stkgui #. I18N: track group +#. I18N: track group name +#. I18N: kart group name +#. I18N: arena group name +#. I18N: track group name #: src/states_screens/tracks_screen.cpp:155 #: src/states_screens/grand_prix_editor_screen.cpp:336 #: src/states_screens/kart_selection.cpp:286 #: src/states_screens/arenas_screen.cpp:83 -#: src/states_screens/easter_egg_screen.cpp:145 data/po/gui_strings.h:1291 -#: data/po/gui_strings.h:1327 +#: src/states_screens/easter_egg_screen.cpp:145 msgid "Add-Ons" msgstr "" @@ -348,31 +325,28 @@ msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: Main menu button -#: data/po/gui_strings.h:169 msgid "Addons" msgstr "" #. I18N: ../stk-assets/karts/20_adiumy/kart.xml -#: data/po/gui_strings.h:1471 msgid "Adiumy" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:824 msgid "Advanced pipeline (lights, etc.)" msgstr "" +#. I18N: ./data/gui/arenas.stkgui +#. I18N: track group +#. I18N: ./data/gui/easter_egg.stkgui +#. I18N: track group #. I18N: name of the tab that will show tracks from all groups #. I18N: if all kart animations are enabled #. I18N: Time filters for add-ons #. I18N: name of the tab that will show tracks from all groups #. I18N: name of the tab that will show arenas from all groups #. I18N: name of the tab that will show tracks from all groups -#. I18N: ./data/gui/arenas.stkgui -#. I18N: track group -#. I18N: ./data/gui/easter_egg.stkgui -#. I18N: track group #: src/states_screens/tracks_screen.cpp:145 #: src/states_screens/options_screen_video.cpp:411 #: src/states_screens/grand_prix_editor_screen.cpp:333 @@ -381,8 +355,7 @@ msgstr "" #: src/states_screens/edit_track_screen.cpp:147 #: src/states_screens/arenas_screen.cpp:75 #: src/states_screens/easter_egg_screen.cpp:137 -#: src/states_screens/gp_info_screen.cpp:76 data/po/gui_strings.h:1295 -#: data/po/gui_strings.h:1331 +#: src/states_screens/gp_info_screen.cpp:76 msgid "All" msgstr "" @@ -390,7 +363,6 @@ msgstr "" #. I18N: In the track selection screen #. I18N: ./data/gui/easter_egg.stkgui #. I18N: Section in easter egg tracks selection screen -#: data/po/gui_strings.h:429 data/po/gui_strings.h:1319 msgid "All Tracks" msgstr "" @@ -400,13 +372,11 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1135 msgid "Always show login screen" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:840 msgid "Ambient Occlusion" msgstr "" @@ -421,13 +391,11 @@ msgid "An error occurred while trying to save your grand prix." msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:412 msgid "Anchor - slows down greatly the kart in the first position." msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:880 msgid "Animated Characters" msgstr "" @@ -439,7 +407,6 @@ msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:868 msgid "Animated Scenery" msgstr "" @@ -474,7 +441,6 @@ msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:852 msgid "Anti-aliasing" msgstr "" @@ -485,18 +451,15 @@ msgid "Anti-aliasing : %s" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui -#: data/po/gui_strings.h:891 msgid "Apply" msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1279 msgid "Apply new resolution" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:14 msgid "Arch Enemy" msgstr "" @@ -515,29 +478,24 @@ msgstr "" #. I18N: Section in arena tracks selection screen #. I18N: ./data/gui/addons_screen.stkgui #. I18N: In the addons screen -#: data/po/gui_strings.h:1283 data/po/gui_strings.h:1354 msgid "Arenas" msgstr "" #. I18N: ../stk-assets/tracks/lighthouse/track.xml -#: data/po/gui_strings.h:1420 msgid "Around the lighthouse" msgstr "" #. I18N: ./data/gui/press_a_key_dialog.stkgui #. I18N: When configuring input -#: data/po/gui_strings.h:1158 msgid "Assign nothing" msgstr "" #. I18N: ./data/gui/press_a_key_dialog.stkgui #. I18N: When configuring input -#: data/po/gui_strings.h:1154 msgid "Assign to ESC key" msgstr "" #. I18N: ./data/grandprix/4_atworldsend.grandprix -#: data/po/gui_strings.h:1357 msgid "At World's End" msgstr "" @@ -547,13 +505,13 @@ msgstr "" #: src/states_screens/options_screen_input2.cpp:86 #: src/states_screens/options_screen_video.cpp:135 #: src/states_screens/options_screen_input.cpp:139 -#: src/states_screens/options_screen_ui.cpp:118 data/po/gui_strings.h:275 +#: src/states_screens/options_screen_ui.cpp:118 msgid "Audio" msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: In the help menu -#: src/tracks/track_object_presentation.cpp:883 data/po/gui_strings.h:975 +#: src/tracks/track_object_presentation.cpp:883 msgid "Avoid bananas!" msgstr "" @@ -575,23 +533,21 @@ msgstr "" msgid "Axis %d inverted" msgstr "" -#. I18N: name of buttons on gamepads #. I18N: ./data/gui/addons_loading.stkgui #. I18N: Add-on screen action +#. I18N: name of buttons on gamepads #: src/states_screens/dialogs/addons_loading.cpp:284 -#: src/input/gamepad_config.cpp:216 data/po/gui_strings.h:1311 +#: src/input/gamepad_config.cpp:216 msgid "Back" msgstr "" #. I18N: ./data/gui/overworld_dialog.stkgui #. I18N: In the in-game dialog -#: data/po/gui_strings.h:898 msgid "Back to Game" msgstr "" #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:921 msgid "Back to Race" msgstr "" @@ -601,13 +557,11 @@ msgstr "" #. I18N: ./data/gui/options_device.stkgui #. I18N: In the input configuration screen -#: data/po/gui_strings.h:1214 msgid "Back to device list" msgstr "" #. I18N: ./data/gui/overworld_dialog.stkgui #. I18N: In the in-game dialog -#: data/po/gui_strings.h:914 msgid "Back to menu" msgstr "" @@ -616,29 +570,24 @@ msgid "Back to the menu" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:50 msgid "Banana Lover" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:418 msgid "" "Basket Ball - bounces after the leader, and might squash and slow down karts " "down on the way." msgstr "" #. I18N: ../stk-assets/tracks/battleisland/track.xml -#: data/po/gui_strings.h:1462 msgid "Battle Island" msgstr "" #. I18N: ../stk-assets/karts/beagle/kart.xml -#: data/po/gui_strings.h:1519 msgid "Beagle" msgstr "" #. I18N: ../stk-assets/karts/beastie/kart.xml -#: data/po/gui_strings.h:1474 msgid "Beastie" msgstr "" @@ -657,13 +606,11 @@ msgid "Black" msgstr "" #. I18N: ../stk-assets/tracks/mansion/track.xml -#: data/po/gui_strings.h:1381 msgid "Blackhill Mansion" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:832 msgid "Bloom" msgstr "" @@ -678,12 +625,10 @@ msgid "Blue Team Wins" msgstr "" #. I18N: ../stk-assets/tracks/farm/track.xml -#: data/po/gui_strings.h:1405 msgid "Bovine Barnyard" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:406 msgid "" "Bowling Ball - bounces off walls. If you are looking back, it will be thrown " "backwards." @@ -695,7 +640,6 @@ msgid "Brake" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:397 msgid "" "BubbleGum - protect yourself with a shield, or use while looking back to " "leave a sticky pink puddle behind you." @@ -703,12 +647,10 @@ msgstr "" #. I18N: Cutscene subtitle from ../stk-assets/tracks/introcutscene2/scene.xml #. I18N: ../stk-assets/tracks/introcutscene2/scene.xml -#: data/po/gui_strings.h:1435 data/po/gui_strings.h:1438 msgid "But I'm a fair creature, so I'll make you a deal." msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:400 msgid "" "Cake - thrown at the closest rival, best on short ranges and long straights." msgstr "" @@ -743,12 +685,6 @@ msgstr "" #: src/states_screens/user_screen.cpp:112 #: src/states_screens/dialogs/addons_loading.cpp:227 #: src/states_screens/dialogs/add_device_dialog.cpp:128 -#: data/po/gui_strings.h:141 data/po/gui_strings.h:149 -#: data/po/gui_strings.h:264 data/po/gui_strings.h:314 -#: data/po/gui_strings.h:353 data/po/gui_strings.h:532 -#: data/po/gui_strings.h:633 data/po/gui_strings.h:661 -#: data/po/gui_strings.h:709 data/po/gui_strings.h:725 -#: data/po/gui_strings.h:1232 data/po/gui_strings.h:1244 msgid "Cancel" msgstr "" @@ -762,7 +698,6 @@ msgid "Cancel/Back" msgstr "" #. I18N: ../stk-assets/tracks/cave/track.xml -#: data/po/gui_strings.h:1387 msgid "Cave X" msgstr "" @@ -776,12 +711,10 @@ msgstr "" #. I18N: ./data/gui/challenges.stkgui #. I18N: Title for challenges screen -#: data/po/gui_strings.h:97 msgid "Challenges : Trophy Room" msgstr "" #. I18N: ./data/gui/online/profile_settings.stkgui -#: data/po/gui_strings.h:759 msgid "Change" msgstr "" @@ -789,17 +722,14 @@ msgstr "" #. I18N: In the kart selection (player setup) screen #. I18N: ./data/gui/karts_online.stkgui #. I18N: In the kart selection (player setup) screen -#: data/po/gui_strings.h:1299 data/po/gui_strings.h:1315 msgid "Choose a Kart" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:2 msgid "Christoffel Columbus" msgstr "" #. I18N: ./data/gui/help1.stkgui -#: data/po/gui_strings.h:967 msgid "Click here to play the tutorial" msgstr "" @@ -815,25 +745,19 @@ msgstr "" #. I18N: User info dialog #. I18N: ./data/gui/online/recovery_info.stkgui #. I18N: In the recovery dialog -#: data/po/gui_strings.h:509 data/po/gui_strings.h:570 -#: data/po/gui_strings.h:617 data/po/gui_strings.h:641 -#: data/po/gui_strings.h:733 data/po/gui_strings.h:785 msgid "Close" msgstr "" #. I18N: ../stk-assets/tracks/30_chocolate/track.xml -#: data/po/gui_strings.h:1399 msgid "Cocoa Temple" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:53 msgid "Collect at least 5 bananas in one race." msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:971 msgid "Collect blue boxes : they will give you weapons or other powerups" msgstr "" @@ -853,7 +777,6 @@ msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:979 msgid "" "Collecting nitro allows you to get speed boosts whenever you wish by " "pressing the appropriate key. You can see your current level of nitro in the " @@ -873,7 +796,6 @@ msgstr "" #. I18N: In the change password dialog #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:501 data/po/gui_strings.h:693 msgid "Confirm" msgstr "" @@ -885,7 +807,6 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1147 msgid "Connect to the Internet" msgstr "" @@ -907,15 +828,13 @@ msgstr "" #. I18N: ./data/gui/feature_unlocked.stkgui #. I18N: ./data/gui/grand_prix_win.stkgui #: src/states_screens/race_result_gui.cpp:175 -#: src/states_screens/dialogs/gp_info_dialog.cpp:212 data/po/gui_strings.h:123 -#: data/po/gui_strings.h:268 data/po/gui_strings.h:1165 -#: data/po/gui_strings.h:1171 data/po/gui_strings.h:1217 +#: src/states_screens/dialogs/gp_info_dialog.cpp:212 msgid "Continue" msgstr "" #. I18N: ./data/gui/gp_info.stkgui #. I18N: In the grand prix info screen -#: src/states_screens/gp_info_screen.cpp:146 data/po/gui_strings.h:221 +#: src/states_screens/gp_info_screen.cpp:146 msgid "Continue saved GP" msgstr "" @@ -930,14 +849,12 @@ msgstr "" #: src/states_screens/user_screen.cpp:644 #: src/states_screens/options_screen_audio.cpp:67 #: src/states_screens/options_screen_video.cpp:138 -#: src/states_screens/options_screen_ui.cpp:120 data/po/gui_strings.h:1105 -#: data/po/gui_strings.h:1206 +#: src/states_screens/options_screen_ui.cpp:120 msgid "Controls" msgstr "" #. I18N: ./data/gui/gpeditor.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1086 msgid "Copy" msgstr "" @@ -953,19 +870,16 @@ msgstr "" #. I18N: ./data/gui/online/create_server.stkgui #. I18N: In the server creation screen -#: data/po/gui_strings.h:657 msgid "Create" msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:477 msgid "Create Server" msgstr "" #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:665 msgid "Create User" msgstr "" @@ -975,13 +889,11 @@ msgstr "" #. I18N: ./data/gui/credits.stkgui #. I18N: Title in credits screen -#: data/po/gui_strings.h:127 msgid "Credits" msgstr "" #. I18N: ./data/gui/online/change_password.stkgui #. I18N: In the change password dialog -#: data/po/gui_strings.h:493 msgid "Current Password" msgstr "" @@ -1000,7 +912,6 @@ msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1259 msgid "Custom settings..." msgstr "" @@ -1026,7 +937,6 @@ msgstr "" #. I18N: ./data/gui/online/user_info_dialog.stkgui #. I18N: User info dialog -#: data/po/gui_strings.h:562 msgid "Decline" msgstr "" @@ -1038,19 +948,17 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/user_screen.stkgui #. I18N: In the user screen -#: data/po/gui_strings.h:256 data/po/gui_strings.h:345 msgid "Delete" msgstr "" #. I18N: ./data/gui/options_device.stkgui #. I18N: In the input configuration screen -#: src/states_screens/options_screen_input2.cpp:111 data/po/gui_strings.h:1210 +#: src/states_screens/options_screen_input2.cpp:111 msgid "Delete Configuration" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:860 msgid "Depth of field" msgstr "" @@ -1076,7 +984,6 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1131 msgid "Display FPS" msgstr "" @@ -1106,7 +1013,6 @@ msgstr "" #. I18N: ./data/gui/track_info.stkgui #. I18N: In the track info screen -#: data/po/gui_strings.h:89 msgid "Drive in reverse" msgstr "" @@ -1120,13 +1026,11 @@ msgstr "" #. I18N: Menu item #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1090 data/po/gui_strings.h:1191 msgid "Edit" msgstr "" #. I18N: ./data/gui/gpedit.stkgui #. I18N: Title in edit grand prix screen -#: data/po/gui_strings.h:1175 msgid "Edit Grand Prix" msgstr "" @@ -1137,7 +1041,6 @@ msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1070 msgid "Egg hunt: Explore tracks to find all hidden eggs." msgstr "" @@ -1147,7 +1050,6 @@ msgid "Eggs: %d / %d" msgstr "" #. I18N: ../stk-assets/karts/elephpant/kart.xml -#: data/po/gui_strings.h:1516 msgid "Elephpant" msgstr "" @@ -1160,7 +1062,6 @@ msgstr "" #. I18N: In the recovery dialog #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:524 data/po/gui_strings.h:697 msgid "Email" msgstr "" @@ -1177,7 +1078,6 @@ msgid "Emails don't match!" msgstr "" #. I18N: ../stk-assets/karts/emule/kart.xml -#: data/po/gui_strings.h:1510 msgid "Emule" msgstr "" @@ -1189,16 +1089,14 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1143 msgid "Enable handicapped users" msgstr "" -#. I18N: in the graphical options tooltip; -#. indicates a graphical feature is enabled #. I18N: ./data/gui/options_audio.stkgui #. I18N: In the audio options screen -#: src/states_screens/options_screen_video.cpp:406 data/po/gui_strings.h:283 -#: data/po/gui_strings.h:295 +#. I18N: in the graphical options tooltip; +#. indicates a graphical feature is enabled +#: src/states_screens/options_screen_video.cpp:406 msgid "Enabled" msgstr "" @@ -1209,7 +1107,6 @@ msgstr "" #. I18N: ./data/gui/enter_player_name_dialog.stkgui #. I18N: In the 'add new player' dialog -#: data/po/gui_strings.h:1236 msgid "Enter the new player's name" msgstr "" @@ -1226,19 +1123,16 @@ msgstr "" #. I18N: ./data/gui/online/register.stkgui #. I18N: Section in the register screen -#: data/po/gui_strings.h:673 msgid "Existing Online Account" msgstr "" #. I18N: ./data/gui/online/lobby.stkgui #. I18N: In the networking lobby -#: data/po/gui_strings.h:586 msgid "Exit" msgstr "" #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:945 msgid "Exit Race" msgstr "" @@ -1250,7 +1144,6 @@ msgstr "" #. I18N: Difficulty #. I18N: ./data/gui/racesetup.stkgui #. I18N: Difficulty -#: data/po/gui_strings.h:448 data/po/gui_strings.h:810 msgid "Expert" msgstr "" @@ -1288,7 +1181,6 @@ msgid "Fetching servers" msgstr "" #. I18N: ./data/gui/online/recovery_input.stkgui -#: data/po/gui_strings.h:516 msgid "" "Fill in the username and email address you supplied at registration to be " "able to reset your password." @@ -1300,7 +1192,6 @@ msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:473 msgid "Find Server" msgstr "" @@ -1311,7 +1202,6 @@ msgstr "" #. I18N: ./data/gui/help4.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1020 msgid "" "First, you will need several input devices (having multiple gamepads or " "joysticks is the best way to play with several people). Go in the input " @@ -1332,7 +1222,6 @@ msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1058 msgid "" "Follow the leader: Run for second place, as the last kart will be " "disqualified every time the counter hits zero. Beware : going in front of " @@ -1340,7 +1229,6 @@ msgid "" msgstr "" #. I18N: ../stk-assets/tracks/fortmagma/track.xml -#: data/po/gui_strings.h:1465 msgid "Fort Magma" msgstr "" @@ -1371,13 +1259,12 @@ msgstr "" #. I18N: ./data/gui/online/profile_friends.stkgui #. I18N: Section in the profile screen -#: src/states_screens/online_profile_base.cpp:99 data/po/gui_strings.h:766 +#: src/states_screens/online_profile_base.cpp:99 msgid "Friends" msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1271 msgid "Fullscreen" msgstr "" @@ -1399,14 +1286,11 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help3.stkgui #. I18N: Tab in help menu -#: data/po/gui_strings.h:386 data/po/gui_strings.h:960 -#: data/po/gui_strings.h:1006 data/po/gui_strings.h:1039 msgid "Game Modes" msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui #. I18N: In soccer setup screen -#: data/po/gui_strings.h:112 msgid "Game type (Goals limit / Time limit)" msgstr "" @@ -1430,20 +1314,16 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help3.stkgui #. I18N: Tab in help menu -#: data/po/gui_strings.h:378 data/po/gui_strings.h:952 -#: data/po/gui_strings.h:998 data/po/gui_strings.h:1031 msgid "General" msgstr "" #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:933 msgid "Give Up Race" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:844 msgid "Global illumination" msgstr "" @@ -1455,7 +1335,6 @@ msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:848 msgid "Glow (outlines)" msgstr "" @@ -1466,7 +1345,6 @@ msgid "Glow (outlines) : %s" msgstr "" #. I18N: ../stk-assets/karts/gnu/kart.xml -#: data/po/gui_strings.h:1486 msgid "Gnu" msgstr "" @@ -1476,31 +1354,26 @@ msgid "Go!" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:32 msgid "Gold driver" msgstr "" #. I18N: ../stk-assets/tracks/20_island/track.xml -#: data/po/gui_strings.h:1453 msgid "Gran Paradiso Island" msgstr "" #. I18N: ./data/gui/tracks.stkgui #. I18N: In the track selection screen #: src/states_screens/dialogs/select_challenge.cpp:145 -#: data/po/gui_strings.h:425 msgid "Grand Prix" msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: In the main screen -#: data/po/gui_strings.h:189 msgid "Grand Prix Editor" msgstr "" #. I18N: ./data/gui/gpeditor.stkgui #. I18N: Title in grand prix editor screen -#: data/po/gui_strings.h:1078 msgid "Grand Prix editor" msgstr "" @@ -1510,7 +1383,6 @@ msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1255 msgid "Graphical Effects Level" msgstr "" @@ -1520,17 +1392,15 @@ msgstr "" #: src/states_screens/options_screen_input2.cpp:85 #: src/states_screens/options_screen_audio.cpp:64 #: src/states_screens/options_screen_input.cpp:138 -#: src/states_screens/options_screen_ui.cpp:117 data/po/gui_strings.h:1251 +#: src/states_screens/options_screen_ui.cpp:117 msgid "Graphics" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui -#: data/po/gui_strings.h:820 msgid "Graphics Settings" msgstr "" #. I18N: ../stk-assets/tracks/greenvalley/track.xml -#: data/po/gui_strings.h:1459 msgid "Green Valley" msgstr "" @@ -1547,12 +1417,10 @@ msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: In the login dialog -#: data/po/gui_strings.h:605 msgid "Guest Log in" msgstr "" #. I18N: ../stk-assets/tracks/hacienda/track.xml -#: data/po/gui_strings.h:1417 msgid "Hacienda" msgstr "" @@ -1562,13 +1430,10 @@ msgstr "" #. I18N: In the in-game dialog #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:177 data/po/gui_strings.h:910 -#: data/po/gui_strings.h:941 msgid "Help" msgstr "" #. I18N: ../stk-assets/karts/hexley/kart.xml -#: data/po/gui_strings.h:1477 msgid "Hexley" msgstr "" @@ -1577,7 +1442,6 @@ msgid "Highscores" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:11 msgid "Hit 10 karts with a bowling-ball." msgstr "" @@ -1588,7 +1452,6 @@ msgid "" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:17 msgid "Hit the same kart at least 5 times in one race." msgstr "" @@ -1599,19 +1462,16 @@ msgstr "" #. I18N: ./data/gui/online/registration_terms.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:625 msgid "I agree to the above terms and am 13 years or older. " msgstr "" #. I18N: Cutscene subtitle from ../stk-assets/tracks/introcutscene2/scene.xml #. I18N: ../stk-assets/tracks/introcutscene2/scene.xml -#: data/po/gui_strings.h:1441 data/po/gui_strings.h:1444 msgid "If you can beat me at racing, I will free the old codger." msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:983 msgid "" "If you see a button with a lock like this one, you need to complete a " "challenge to unlock it." @@ -1624,7 +1484,6 @@ msgstr "" #. I18N: ./data/gui/addons_loading.stkgui #. I18N: Add-on screen action -#: data/po/gui_strings.h:1303 msgid "Install" msgstr "" @@ -1632,7 +1491,6 @@ msgstr "" #. I18N: Difficulty #. I18N: ./data/gui/racesetup.stkgui #. I18N: Difficulty -#: data/po/gui_strings.h:444 data/po/gui_strings.h:806 msgid "Intermediate" msgstr "" @@ -1646,13 +1504,11 @@ msgid "It's a draw" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:56 msgid "It's secret" msgstr "" #. I18N: ./data/gui/online/server_info_dialog.stkgui #. I18N: In the server info dialog -#: data/po/gui_strings.h:721 msgid "Join" msgstr "" @@ -1662,13 +1518,11 @@ msgstr "" #. I18N: ./data/gui/addons_screen.stkgui #. I18N: In the addons screen -#: data/po/gui_strings.h:1346 msgid "Karts" msgstr "" #. I18N: ./data/gui/confirm_resolution_dialog.stkgui #. I18N: In the 'confirm resolution' dialog, that's shown when switching resoluton -#: data/po/gui_strings.h:310 msgid "Keep this resolution" msgstr "" @@ -1687,7 +1541,6 @@ msgid "Keyboard %i" msgstr "" #. I18N: ../stk-assets/karts/konqi/kart.xml -#: data/po/gui_strings.h:1504 msgid "Konqi" msgstr "" @@ -1758,7 +1611,6 @@ msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:836 msgid "Light shaft (God rays)" msgstr "" @@ -1774,13 +1626,11 @@ msgstr "" #. I18N: ./data/gui/online/lobby.stkgui #. I18N: In networking lobby -#: data/po/gui_strings.h:574 msgid "Lobby" msgstr "" #. I18N: ./data/gui/online/lobby_settings.stkgui #. I18N: In the lobby settings screen -#: data/po/gui_strings.h:737 msgid "Lobby Settings" msgstr "" @@ -1805,13 +1655,11 @@ msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: In the login dialog -#: data/po/gui_strings.h:613 msgid "Log In" msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:485 msgid "Log Out" msgstr "" @@ -1831,7 +1679,6 @@ msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: In the login dialog -#: data/po/gui_strings.h:609 msgid "" "Logging in as a guest allows you to participate in online races, but it does " "not allow you to vote for addons, or collect any achievements while being " @@ -1850,8 +1697,7 @@ msgstr "" #. I18N: ./data/gui/user_screen.stkgui #. I18N: ./data/gui/online/guest_login.stkgui #: src/states_screens/main_menu_screen.cpp:175 -#: src/states_screens/main_menu_screen.cpp:186 data/po/gui_strings.h:317 -#: data/po/gui_strings.h:589 +#: src/states_screens/main_menu_screen.cpp:186 msgid "Login" msgstr "" @@ -1862,22 +1708,18 @@ msgstr "" #. I18N: ./data/gui/online/profile_friends.stkgui #. I18N: In the profile screen -#: data/po/gui_strings.h:770 msgid "Look for more friends:" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:29 msgid "Make 5 skidding in a single lap." msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:23 msgid "Make a race with 5 laps or more." msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:20 msgid "Marathoner" msgstr "" @@ -1885,13 +1727,11 @@ msgstr "" #. I18N: In the server creation screen #. I18N: ./data/gui/online/lobby_settings.stkgui #. I18N: In the lobby settings screen -#: data/po/gui_strings.h:653 data/po/gui_strings.h:745 msgid "Max. number of players" msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui #. I18N: In soccer setup screen -#: data/po/gui_strings.h:108 msgid "Maximum time (min.)" msgstr "" @@ -1906,18 +1746,15 @@ msgid "Menu Keys" msgstr "" #. I18N: ../stk-assets/tracks/minigolf/track.xml -#: data/po/gui_strings.h:1369 msgid "Minigolf" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:62 msgid "Mosquito Hunter" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:856 msgid "Motion blur" msgstr "" @@ -1941,19 +1778,16 @@ msgstr "" #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1183 msgid "Move down" msgstr "" #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1179 msgid "Move up" msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: Main menu button -#: data/po/gui_strings.h:161 msgid "Multiplayer" msgstr "" @@ -1965,14 +1799,11 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help3.stkgui #. I18N: Tab in help menu -#: data/po/gui_strings.h:390 data/po/gui_strings.h:964 -#: data/po/gui_strings.h:1010 data/po/gui_strings.h:1043 msgid "Multi­player" msgstr "" #. I18N: ./data/gui/options_audio.stkgui #. I18N: In the audio options screen -#: data/po/gui_strings.h:279 msgid "Music" msgstr "" @@ -1984,8 +1815,6 @@ msgstr "" #. I18N: In the server info dialog #: src/states_screens/server_selection.cpp:91 #: src/states_screens/online_profile_achievements.cpp:73 -#: data/po/gui_strings.h:546 data/po/gui_strings.h:681 -#: data/po/gui_strings.h:717 msgid "Name" msgstr "" @@ -2005,13 +1834,11 @@ msgstr "" #. I18N: In the server creation screen #. I18N: ./data/gui/online/lobby_settings.stkgui #. I18N: In the lobby settings screen -#: data/po/gui_strings.h:649 data/po/gui_strings.h:741 msgid "Name of the server" msgstr "" #. I18N: ./data/gui/gpeditor.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1082 msgid "New" msgstr "" @@ -2022,13 +1849,11 @@ msgstr "" #. I18N: ./data/gui/online/register.stkgui #. I18N: Section in the register screen -#: data/po/gui_strings.h:669 msgid "New Online Account" msgstr "" #. I18N: ./data/gui/online/change_password.stkgui #. I18N: In the change password dialog -#: data/po/gui_strings.h:497 msgid "New Password" msgstr "" @@ -2075,7 +1900,6 @@ msgid "No" msgstr "" #. I18N: ../stk-assets/karts/nolok/kart.xml -#: data/po/gui_strings.h:1480 msgid "Nolok" msgstr "" @@ -2091,7 +1915,6 @@ msgid "Normal Race" msgstr "" #. I18N: ../stk-assets/tracks/snowmountain/track.xml -#: data/po/gui_strings.h:1372 msgid "Northern Resort" msgstr "" @@ -2105,7 +1928,6 @@ msgstr "" #. I18N: Difficulty #. I18N: ./data/gui/racesetup.stkgui #. I18N: Difficulty -#: data/po/gui_strings.h:440 data/po/gui_strings.h:802 msgid "Novice" msgstr "" @@ -2116,25 +1938,21 @@ msgstr "" #. I18N: ./data/gui/track_info.stkgui #. I18N: In the track info screen -#: data/po/gui_strings.h:85 msgid "Number of AI karts" msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui #. I18N: In soccer setup screen -#: data/po/gui_strings.h:104 msgid "Number of goals to win" msgstr "" #. I18N: ./data/gui/track_info.stkgui #. I18N: In the track info screen -#: data/po/gui_strings.h:81 msgid "Number of laps" msgstr "" #. I18N: ./data/gui/edit_track.stkgui #. I18N: In the edit track screen -#: data/po/gui_strings.h:131 msgid "Number of laps:" msgstr "" @@ -2150,15 +1968,11 @@ msgstr "" #. I18N: ./data/gui/enter_player_name_dialog.stkgui #. I18N: In the 'add new player' dialog #: src/states_screens/dialogs/message_dialog.cpp:122 -#: src/states_screens/dialogs/message_dialog.cpp:135 data/po/gui_strings.h:138 -#: data/po/gui_strings.h:248 data/po/gui_strings.h:337 -#: data/po/gui_strings.h:705 data/po/gui_strings.h:1228 -#: data/po/gui_strings.h:1240 +#: src/states_screens/dialogs/message_dialog.cpp:135 msgid "OK" msgstr "" #. I18N: ./data/grandprix/2_offthebeatentrack.grandprix -#: data/po/gui_strings.h:1363 msgid "Off the beaten track" msgstr "" @@ -2168,23 +1982,19 @@ msgstr "" #. I18N: ./data/gui/online/register.stkgui #. I18N: Section in the register screen -#: data/po/gui_strings.h:677 msgid "Offline Account" msgstr "" #. I18N: Cutscene subtitle from ../stk-assets/tracks/introcutscene2/scene.xml #. I18N: ../stk-assets/tracks/introcutscene2/scene.xml -#: data/po/gui_strings.h:1429 data/po/gui_strings.h:1432 msgid "Oh yes, see, he's in my castle now and will be served for supper..." msgstr "" #. I18N: ../stk-assets/tracks/mines/track.xml -#: data/po/gui_strings.h:1408 msgid "Old Mine" msgstr "" #. I18N: ../stk-assets/tracks/olivermath/track.xml -#: data/po/gui_strings.h:1375 msgid "Oliver's Math Class" msgstr "" @@ -2196,20 +2006,17 @@ msgstr "" #. I18N: In the user screen #: src/states_screens/online_profile_friends.cpp:149 #: src/states_screens/main_menu_screen.cpp:170 -#: src/states_screens/main_menu_screen.cpp:185 data/po/gui_strings.h:165 -#: data/po/gui_strings.h:232 data/po/gui_strings.h:321 +#: src/states_screens/main_menu_screen.cpp:185 msgid "Online" msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:465 msgid "Online Multiplayer" msgstr "" #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:685 msgid "Online Username" msgstr "" @@ -2236,14 +2043,11 @@ msgstr "" #. I18N: In the in-game dialog #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:173 data/po/gui_strings.h:906 -#: data/po/gui_strings.h:937 msgid "Options" msgstr "" #. I18N: ./data/gui/online/profile_overview.stkgui #. I18N: Section in the profile screen -#: data/po/gui_strings.h:461 msgid "Overview" msgstr "" @@ -2252,7 +2056,6 @@ msgid "POWER" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:409 msgid "Parachute - slows down all karts in a better position." msgstr "" @@ -2262,20 +2065,16 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:244 data/po/gui_strings.h:333 -#: data/po/gui_strings.h:689 msgid "Password" msgstr "" #. I18N: ./data/gui/online/profile_settings.stkgui #. I18N: In the online account settings screen -#: data/po/gui_strings.h:756 msgid "Password :" msgstr "" #. I18N: ./data/gui/online/change_password.stkgui #. I18N: In the change password dialog -#: data/po/gui_strings.h:489 msgid "Password Change" msgstr "" @@ -2300,7 +2099,6 @@ msgstr "" #. I18N: ./data/gui/overworld_dialog.stkgui #. I18N: ./data/gui/race_paused_dialog.stkgui -#: data/po/gui_strings.h:894 data/po/gui_strings.h:917 msgid "Paused" msgstr "" @@ -2314,7 +2112,6 @@ msgid "Pending" msgstr "" #. I18N: ./data/grandprix/1_penguinplayground.grandprix -#: data/po/gui_strings.h:1360 msgid "Penguin Playground" msgstr "" @@ -2323,17 +2120,14 @@ msgid "Performing vote" msgstr "" #. I18N: ../stk-assets/karts/pidgin/kart.xml -#: data/po/gui_strings.h:1498 msgid "Pidgin" msgstr "" #. I18N: ./data/gui/tutorial.stkgui -#: data/po/gui_strings.h:306 msgid "Play all" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:5 msgid "Play every official track at least once." msgstr "" @@ -2346,14 +2140,12 @@ msgstr "" #: src/states_screens/options_screen_audio.cpp:66 #: src/states_screens/options_screen_video.cpp:137 #: src/states_screens/options_screen_input.cpp:141 -#: src/states_screens/options_screen_ui.cpp:119 data/po/gui_strings.h:228 -#: data/po/gui_strings.h:360 +#: src/states_screens/options_screen_ui.cpp:119 msgid "Players" msgstr "" #. I18N: ./data/gui/enter_gp_name_dialog.stkgui #. I18N: In the 'add new grand prix' dialog -#: data/po/gui_strings.h:1224 msgid "Please enter the name of the grand prix" msgstr "" @@ -2371,14 +2163,12 @@ msgid "Please wait while the add-ons are loading" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:403 msgid "" "Plunger - throw straight to pull an opponent back, or throw while looking " "back to make one lose sight." msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:38 msgid "Powerup Love" msgstr "" @@ -2391,24 +2181,20 @@ msgstr "" #. I18N: ./data/gui/press_a_key_dialog.stkgui #. I18N: When configuring input -#: data/po/gui_strings.h:1162 msgid "Press ESC to cancel" msgstr "" #. I18N: ./data/gui/press_a_key_dialog.stkgui -#: data/po/gui_strings.h:1150 msgid "Press a key" msgstr "" #. I18N: ./data/gui/options_input.stkgui #. I18N: In the input configuration screen -#: data/po/gui_strings.h:1109 msgid "Press enter or double-click on a device to configure it" msgstr "" #. I18N: ./data/gui/options_players.stkgui #. I18N: In the player configuration screen -#: data/po/gui_strings.h:367 msgid "Press enter or double-click on a player to edit him/her" msgstr "" @@ -2442,7 +2228,6 @@ msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:481 msgid "Profile" msgstr "" @@ -2451,7 +2236,6 @@ msgid "Progress" msgstr "" #. I18N: ../stk-assets/karts/puffy/kart.xml -#: data/po/gui_strings.h:1492 msgid "Puffy" msgstr "" @@ -2463,13 +2247,11 @@ msgstr "" #. I18N: ./data/gui/online/main.stkgui #. I18N: In the online multiplayer screen -#: data/po/gui_strings.h:469 msgid "Quick Play" msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: In the main screen -#: data/po/gui_strings.h:197 msgid "Quit" msgstr "" @@ -2480,8 +2262,6 @@ msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui #. I18N: ./data/gui/select_challenge.stkgui #. I18N: ./data/gui/racesetup.stkgui -#: data/po/gui_strings.h:100 data/po/gui_strings.h:432 -#: data/po/gui_strings.h:795 msgid "Race Setup" msgstr "" @@ -2513,7 +2293,6 @@ msgstr "" #. I18N: ./data/gui/addons_screen.stkgui #. I18N: In addons screen, in the filtering bar, to enable a filter that will show only items with good rating -#: data/po/gui_strings.h:1342 msgid "Rating >=" msgstr "" @@ -2523,7 +2302,6 @@ msgid "Ready!" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:59 msgid "Really ... a secret." msgstr "" @@ -2533,13 +2311,11 @@ msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: Tab in login menu -#: data/po/gui_strings.h:601 msgid "Register" msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1050 msgid "" "Regular Race: All blows allowed, so catch weapons and make clever use of " "them!" @@ -2554,13 +2330,11 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/user_screen.stkgui #. I18N: In the user screen -#: data/po/gui_strings.h:236 data/po/gui_strings.h:325 msgid "Remember password" msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1275 msgid "Remember window location" msgstr "" @@ -2570,8 +2344,6 @@ msgstr "" #. I18N: Menu item #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:550 data/po/gui_strings.h:1094 -#: data/po/gui_strings.h:1195 msgid "Remove" msgstr "" @@ -2581,8 +2353,6 @@ msgstr "" #. I18N: In the user screen #. I18N: ./data/gui/gpeditor.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:260 data/po/gui_strings.h:349 -#: data/po/gui_strings.h:1098 msgid "Rename" msgstr "" @@ -2608,7 +2378,6 @@ msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1267 msgid "Resolution" msgstr "" @@ -2618,19 +2387,16 @@ msgstr "" #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: data/po/gui_strings.h:929 msgid "Restart Race" msgstr "" #. I18N: ./data/gui/gp_info.stkgui #. I18N: In the grand prix info screen -#: data/po/gui_strings.h:205 msgid "Reverse" msgstr "" #. I18N: ./data/gui/edit_track.stkgui #. I18N: In the edit track screen -#: data/po/gui_strings.h:135 msgid "Reverse:" msgstr "" @@ -2688,35 +2454,29 @@ msgid "SPEED" msgstr "" #. I18N: ../stk-assets/tracks/startrack/track.xml -#: data/po/gui_strings.h:1456 msgid "STK Enterprise" msgstr "" #. I18N: ../stk-assets/karts/20_sara_the_wizard/kart.xml -#: data/po/gui_strings.h:1489 msgid "Sara the wizard" msgstr "" #. I18N: ./data/gui/gpedit.stkgui #. I18N: Menu item -#: data/po/gui_strings.h:1199 msgid "Save" msgstr "" #. I18N: ./data/gui/grand_prix_lose.stkgui #. I18N: ./data/gui/grand_prix_win.stkgui -#: data/po/gui_strings.h:1168 data/po/gui_strings.h:1220 msgid "Save Grand Prix" msgstr "" #. I18N: ../stk-assets/tracks/scotland/track.xml -#: data/po/gui_strings.h:1396 msgid "Scotland" msgstr "" #. I18N: ./data/gui/online/user_search.stkgui #. I18N: ./data/gui/online/profile_friends.stkgui -#: data/po/gui_strings.h:454 data/po/gui_strings.h:773 msgid "Search" msgstr "" @@ -2731,12 +2491,10 @@ msgid "Select" msgstr "" #. I18N: ./data/gui/racesetup.stkgui -#: data/po/gui_strings.h:798 msgid "Select a difficulty" msgstr "" #. I18N: ./data/gui/racesetup.stkgui -#: data/po/gui_strings.h:817 msgid "Select a game mode" msgstr "" @@ -2746,36 +2504,30 @@ msgstr "" #. I18N: ./data/gui/overworld_dialog.stkgui #. I18N: In the in-game dialog -#: data/po/gui_strings.h:902 msgid "Select kart" msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1139 msgid "Send anonymous HW statistics" msgstr "" #. I18N: ./data/gui/online/create_server.stkgui #. I18N: In the server creation screen -#: data/po/gui_strings.h:645 msgid "Server Creation" msgstr "" #. I18N: ./data/gui/online/server_info_dialog.stkgui #. I18N: In the server info dialog -#: data/po/gui_strings.h:713 msgid "Server Info" msgstr "" #. I18N: ./data/gui/online/server_selection.stkgui -#: data/po/gui_strings.h:538 msgid "Server Selection" msgstr "" #. I18N: ./data/gui/online/lobby.stkgui #. I18N: In the networking lobby -#: data/po/gui_strings.h:578 msgid "Server name :" msgstr "" @@ -2790,13 +2542,12 @@ msgstr "" #. I18N: ./data/gui/race_paused_dialog.stkgui #. I18N: Race paused button -#: src/states_screens/race_result_gui.cpp:198 data/po/gui_strings.h:925 +#: src/states_screens/race_result_gui.cpp:198 msgid "Setup New Race" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:828 msgid "Shadows" msgstr "" @@ -2811,24 +2562,20 @@ msgid "Shadows: %s" msgstr "" #. I18N: ../stk-assets/tracks/sandtrack/track.xml -#: data/po/gui_strings.h:1378 msgid "Shifting Sands" msgstr "" #. I18N: ../stk-assets/tracks/city/track.xml -#: data/po/gui_strings.h:1393 msgid "Shiny Suburbs" msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: Tab in login menu -#: data/po/gui_strings.h:593 msgid "Sign In" msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: Tab in login menu -#: data/po/gui_strings.h:597 msgid "Sign In As Guest" msgstr "" @@ -2838,7 +2585,6 @@ msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: Main menu button -#: data/po/gui_strings.h:157 msgid "Singleplayer" msgstr "" @@ -2848,7 +2594,6 @@ msgid "Size: %s" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:26 msgid "Skid-row" msgstr "" @@ -2859,12 +2604,10 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: In the ui settings -#: data/po/gui_strings.h:1127 msgid "Skin" msgstr "" #. I18N: ../stk-assets/tracks/snowtuxpeak/track.xml -#: data/po/gui_strings.h:1414 msgid "Snow Peak" msgstr "" @@ -2874,13 +2617,11 @@ msgid "Soccer" msgstr "" #. I18N: ../stk-assets/tracks/soccer_field/track.xml -#: data/po/gui_strings.h:1411 msgid "Soccer field" msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1066 msgid "" "Soccer: Only in multiplayer games. Use your kart to push the ball into the " "goal." @@ -2900,7 +2641,6 @@ msgstr "" #. I18N: ./data/gui/options_audio.stkgui #. I18N: In the audio options screen -#: data/po/gui_strings.h:291 msgid "Sound Effects" msgstr "" @@ -2909,7 +2649,6 @@ msgstr "" #. I18N: ./data/gui/easter_egg.stkgui #. I18N: track group #: src/states_screens/grand_prix_editor_screen.cpp:334 -#: data/po/gui_strings.h:1287 data/po/gui_strings.h:1323 msgid "Standard" msgstr "" @@ -2926,7 +2665,6 @@ msgstr "" #. I18N: In the track info screen #. I18N: ./data/gui/gp_info.stkgui #. I18N: In the grand prix info screen -#: data/po/gui_strings.h:93 data/po/gui_strings.h:217 msgid "Start Race" msgstr "" @@ -2946,12 +2684,10 @@ msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: Main menu button -#: data/po/gui_strings.h:153 msgid "Story Mode" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:8 msgid "Strike!" msgstr "" @@ -2959,23 +2695,19 @@ msgstr "" #. I18N: In the change password dialog #. I18N: ./data/gui/online/recovery_input.stkgui #. I18N: In the recovery dialog -#: data/po/gui_strings.h:505 data/po/gui_strings.h:528 msgid "Submit" msgstr "" #. I18N: ../stk-assets/tracks/subsea/track.xml -#: data/po/gui_strings.h:1402 msgid "Subsea" msgstr "" #. I18N: ./data/gui/racesetup.stkgui #. I18N: Difficulty -#: data/po/gui_strings.h:814 msgid "SuperTux" msgstr "" #. I18N: ./data/gui/addons_screen.stkgui -#: data/po/gui_strings.h:1334 msgid "SuperTuxKart Addons" msgstr "" @@ -2983,8 +2715,6 @@ msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: ./data/gui/help4.stkgui #. I18N: ./data/gui/help3.stkgui -#: data/po/gui_strings.h:374 data/po/gui_strings.h:948 -#: data/po/gui_strings.h:994 data/po/gui_strings.h:1027 msgid "SuperTuxKart Help" msgstr "" @@ -2995,20 +2725,14 @@ msgstr "" #. I18N: ./data/gui/options_ui.stkgui #. I18N: ./data/gui/options_device.stkgui #. I18N: ./data/gui/options_video.stkgui -#: data/po/gui_strings.h:224 data/po/gui_strings.h:271 -#: data/po/gui_strings.h:356 data/po/gui_strings.h:1101 -#: data/po/gui_strings.h:1119 data/po/gui_strings.h:1202 -#: data/po/gui_strings.h:1247 msgid "SuperTuxKart Options" msgstr "" #. I18N: ./data/gui/help4.stkgui -#: data/po/gui_strings.h:1013 msgid "SuperTuxKart can be played in multiplayer mode on the same computer" msgstr "" #. I18N: ./data/gui/help3.stkgui -#: data/po/gui_strings.h:1046 msgid "SuperTuxKart features several game modes" msgstr "" @@ -3022,19 +2746,16 @@ msgid "" msgstr "" #. I18N: ../stk-assets/karts/suzanne/kart.xml -#: data/po/gui_strings.h:1513 msgid "Suzanne" msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:415 msgid "" "Swapper - gift boxes are transformed into bananas and vice versa for a short " "time." msgstr "" #. I18N: ./data/gui/help2.stkgui -#: data/po/gui_strings.h:421 msgid "Swatter - will squash karts close by, slowing them down." msgstr "" @@ -3044,7 +2765,6 @@ msgid "System Language" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:65 msgid "" "Take your opponents for mosquitos! With the swatter, squash at least 5 of " "them in a race." @@ -3052,30 +2772,25 @@ msgstr "" #. I18N: ./data/gui/online/registration_terms.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:621 msgid "Terms and Agreement" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:872 msgid "Texture compression" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:884 msgid "Texture filtering" msgstr "" #. I18N: ./data/gui/help1.stkgui #. I18N: in the help menu -#: data/po/gui_strings.h:987 msgid "The 'skidding' key allows you to skid in sharp turns and get a boost." msgstr "" #. I18N: ../stk-assets/tracks/stadium/track.xml -#: data/po/gui_strings.h:1468 msgid "The Stadium" msgstr "" @@ -3100,7 +2815,6 @@ msgstr "" #. I18N: ./data/gui/help3.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1054 msgid "Time Trial: Contains no powerups, so only your driving skills matter!" msgstr "" @@ -3118,12 +2832,10 @@ msgstr "" #. I18N: ./data/gui/help2.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:394 msgid "To help you win, there are some powerups you can collect :" msgstr "" #. I18N: ./data/grandprix/3_tothemoonandback.grandprix -#: data/po/gui_strings.h:1366 msgid "To the moon and back" msgstr "" @@ -3156,7 +2868,6 @@ msgstr "" #. I18N: ./data/gui/gp_info.stkgui #. I18N: In the grand prix info screen -#: data/po/gui_strings.h:213 msgid "Track group" msgstr "" @@ -3164,7 +2875,6 @@ msgstr "" #. I18N: In the grand prix info screen #. I18N: ./data/gui/addons_screen.stkgui #. I18N: In the addons screen -#: data/po/gui_strings.h:209 data/po/gui_strings.h:1350 msgid "Tracks" msgstr "" @@ -3179,24 +2889,21 @@ msgstr "" #. I18N: ./data/gui/main.stkgui #. I18N: In the main screen -#: src/states_screens/race_gui_overworld.cpp:455 data/po/gui_strings.h:181 +#: src/states_screens/race_gui_overworld.cpp:455 msgid "Tutorial" msgstr "" #. I18N: ./data/gui/tutorial.stkgui #. I18N: Title for tutorials screen -#: data/po/gui_strings.h:303 msgid "Tutorial : Selection Room" msgstr "" #. I18N: ../stk-assets/karts/tux/kart.xml -#: data/po/gui_strings.h:1483 msgid "Tux" msgstr "" #. I18N: ./data/gui/select_challenge.stkgui #. I18N: Type of race, in a challenge -#: data/po/gui_strings.h:436 msgid "Type :" msgstr "" @@ -3212,12 +2919,10 @@ msgstr "" #. I18N: ./data/gui/addons_loading.stkgui #. I18N: Add-on screen action -#: data/po/gui_strings.h:1307 msgid "Uninstall" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:44 msgid "Unstoppable" msgstr "" @@ -3232,7 +2937,6 @@ msgstr "" #. I18N: ./data/gui/addons_screen.stkgui #. I18N: In addons screen, in the filtering bar, to enable a filter that will show only recently updated items -#: data/po/gui_strings.h:1338 msgid "Updated" msgstr "" @@ -3241,19 +2945,16 @@ msgid "Updated date" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:41 msgid "Use 10 or more powerups in a race." msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:876 msgid "Use high definition textures" msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui #. I18N: In soccer setup screen -#: data/po/gui_strings.h:116 msgid "Use left/right to choose your team and press fire" msgstr "" @@ -3264,7 +2965,6 @@ msgstr "" #. I18N: ./data/gui/online/user_info_dialog.stkgui #. I18N: User info dialog' dialog -#: data/po/gui_strings.h:542 msgid "User Info" msgstr "" @@ -3274,7 +2974,7 @@ msgstr "" #: src/states_screens/options_screen_input2.cpp:87 #: src/states_screens/options_screen_audio.cpp:65 #: src/states_screens/options_screen_video.cpp:136 -#: src/states_screens/options_screen_input.cpp:140 data/po/gui_strings.h:1123 +#: src/states_screens/options_screen_input.cpp:140 msgid "User Interface" msgstr "" @@ -3283,7 +2983,6 @@ msgid "User defined" msgstr "" #. I18N: ./data/gui/online/user_search.stkgui -#: data/po/gui_strings.h:451 msgid "User search" msgstr "" @@ -3294,8 +2993,7 @@ msgstr "" #. I18N: ./data/gui/online/recovery_input.stkgui #. I18N: In the recovery dialog #: src/states_screens/online_user_search.cpp:74 -#: src/states_screens/online_profile_friends.cpp:67 data/po/gui_strings.h:240 -#: data/po/gui_strings.h:329 data/po/gui_strings.h:520 +#: src/states_screens/online_profile_friends.cpp:67 msgid "Username" msgstr "" @@ -3304,7 +3002,6 @@ msgid "Username and/or email address invalid." msgstr "" #. I18N: ./data/gui/soccer_setup.stkgui -#: data/po/gui_strings.h:119 msgid "VS" msgstr "" @@ -3321,7 +3018,6 @@ msgstr "" #. I18N: ./data/gui/options_video.stkgui #. I18N: In the video settings -#: data/po/gui_strings.h:1263 msgid "Vertical Sync (requires restart)" msgstr "" @@ -3329,19 +3025,16 @@ msgstr "" #. I18N: User info dialog #. I18N: ./data/gui/online/notification_dialog.stkgui #. I18N: User info dialog -#: data/po/gui_strings.h:566 data/po/gui_strings.h:729 msgid "View" msgstr "" #. I18N: ./data/gui/options_audio.stkgui #. I18N: In the audio options screen -#: data/po/gui_strings.h:287 data/po/gui_strings.h:299 msgid "Volume" msgstr "" #. I18N: ./data/gui/online/vote_dialog.stkgui #. I18N: In the vote dialog -#: data/po/gui_strings.h:637 msgid "Vote" msgstr "" @@ -3372,14 +3065,11 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help3.stkgui #. I18N: Tab in help menu -#: data/po/gui_strings.h:382 data/po/gui_strings.h:956 -#: data/po/gui_strings.h:1002 data/po/gui_strings.h:1035 msgid "Weapons" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -#: data/po/gui_strings.h:864 msgid "Weather Effects" msgstr "" @@ -3391,13 +3081,11 @@ msgstr "" #. I18N: Cutscene subtitle from ../stk-assets/tracks/introcutscene2/scene.xml #. I18N: ../stk-assets/tracks/introcutscene2/scene.xml -#: data/po/gui_strings.h:1423 data/po/gui_strings.h:1426 msgid "What's wrong, little hippies? Your great gnu leader is missing?" msgstr "" #. I18N: ./data/gui/help4.stkgui #. I18N: In the help menu -#: data/po/gui_strings.h:1024 msgid "" "When input devices are configured, you are ready to play. Select the " "'multiplayer race' icon in the main menu. When it is time to choose a kart, " @@ -3413,33 +3101,28 @@ msgid "White" msgstr "" #. I18N: ../stk-assets/karts/wilber/kart.xml -#: data/po/gui_strings.h:1501 msgid "Wilber" msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:47 msgid "Win 5 single races in a row." msgstr "" #. I18N: ./data/achievements.xml -#: data/po/gui_strings.h:35 msgid "Win in all single player modes, against at least 3 opponents." msgstr "" #. I18N: ../stk-assets/tracks/xr591/track.xml -#: data/po/gui_strings.h:1384 msgid "XR591" msgstr "" #. I18N: ../stk-assets/karts/20_xue/kart.xml -#: data/po/gui_strings.h:1507 msgid "Xue" msgstr "" #. I18N: ./data/gui/confirm_dialog.stkgui #. I18N: In a 'are you sure?' dialog -#: src/states_screens/edit_gp_screen.cpp:253 data/po/gui_strings.h:145 +#: src/states_screens/edit_gp_screen.cpp:253 msgid "Yes" msgstr "" @@ -3448,7 +3131,6 @@ msgid "You are now ready to race. Good luck!" msgstr "" #. I18N: ./data/gui/options_players.stkgui -#: data/po/gui_strings.h:363 msgid "You are playing as" msgstr "" @@ -3472,7 +3154,6 @@ msgstr "" #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog -#: data/po/gui_strings.h:701 msgid "" "You can play without creating an online account by selecting an offline " "account. Though then you can not connect to friends, vote for addons etc. " @@ -3554,7 +3235,6 @@ msgstr "" #. I18N: ./data/gui/online/recovery_info.stkgui #. I18N: In the recovery dialog -#: data/po/gui_strings.h:781 msgid "" "You will receive an email with further instructions on how to reset your " "password. Please be patient and be sure to check your spam folder." @@ -3597,7 +3277,6 @@ msgid "Your profile" msgstr "" #. I18N: ../stk-assets/tracks/zengarden/track.xml -#: data/po/gui_strings.h:1390 msgid "Zen Garden" msgstr "" @@ -3608,7 +3287,6 @@ msgstr "" #. I18N: ./data/gui/online/lobby.stkgui #. I18N: In networking lobby -#: data/po/gui_strings.h:582 msgid "actions" msgstr "" @@ -3635,7 +3313,6 @@ msgid "low" msgstr "" #. I18N: ../stk-assets/karts/20_sara_the_racer/kart.xml -#: data/po/gui_strings.h:1495 msgid "sara the racer" msgstr "" diff --git a/data/po/update_pot.sh b/data/po/update_pot.sh index d62874692..848ee1ede 100755 --- a/data/po/update_pot.sh +++ b/data/po/update_pot.sh @@ -36,18 +36,20 @@ python ./data/po/extract_strings_from_XML.py $XML_FILE_LIST echo "---------------------------" echo " Generating .pot file..." +# XML Files +xgettext -d supertuxkart -s --keyword=_ --add-comments="I18N:" \ + -p ./data/po -o supertuxkart.pot \ + --no-location --from-code=UTF-8 ./data/po/gui_strings.h \ + --package-name=supertuxkart + # C++ Files -xgettext -d supertuxkart -s --keyword=_ --keyword=N_ --keyword=_LTR \ +xgettext -j -d supertuxkart -s --keyword=_ --keyword=N_ --keyword=_LTR \ --keyword=_C:1c,2 --keyword=_P:1,2 \ --keyword=_CP:1c,2,3 --add-comments="I18N:" \ -p ./data/po -o supertuxkart.pot $CPP_FILE_LIST \ --package-name=supertuxkart -# XML Files -xgettext -j -d supertuxkart -s --keyword=_ --add-comments="I18N:" \ - -p ./data/po -o supertuxkart.pot \ - --from-code=UTF-8 ./data/po/gui_strings.h \ - --package-name=supertuxkart + echo " Done" echo "---------------------------" From d5cbf4fc6b168c629a804178011fe56d5030fbdd Mon Sep 17 00:00:00 2001 From: Flakebi Date: Mon, 9 Feb 2015 01:15:04 +0100 Subject: [PATCH 023/117] Math works better with brackets --- src/karts/kart.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index ea446fe6e..ea25f3308 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2522,8 +2522,8 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz, { // fabs(speed) is important, otherwise the negative number will // become a huge unsigned number in the particle scene node! - float f = fabsf(getSpeed())/m_kart_properties->getMaxSpeed() * - m_difficulty->getMaxSpeed(); + float f = fabsf(getSpeed())/(m_kart_properties->getMaxSpeed() * + m_difficulty->getMaxSpeed()); // The speed of the kart can be higher (due to powerups) than // the normal maximum speed of the kart. if(f>1.0f) f = 1.0f; From 4abe3ce3273cce1eb44379e10e9b378dbf2216b3 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Mon, 9 Feb 2015 01:15:48 +0100 Subject: [PATCH 024/117] Make music and sound work with fast starts, fixes #1291 --- src/main.cpp | 4 ++-- src/modes/world_status.cpp | 25 +++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 5acfb4f71..5c913415e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1052,7 +1052,7 @@ void initRest() Online::RequestManager::get()->startNetworkThread(); NewsManager::get(); // this will create the news manager - music_manager = new MusicManager(); + music_manager = new MusicManager(); SFXManager::create(); // The order here can be important, e.g. KartPropertiesManager needs // defaultKartProperties, which are defined in stk_config. @@ -1238,7 +1238,7 @@ int main(int argc, char *argv[] ) material_manager->addSharedMaterial(materials_file); } Referee::init(); - powerup_manager -> loadAllPowerups (); + powerup_manager->loadAllPowerups(); ItemManager::loadDefaultItemMeshes(); GUIEngine::addLoadingIcon( irr_driver->getTexture(FileManager::GUI, diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index c4d43de0e..19d482be4 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -56,7 +56,22 @@ void WorldStatus::reset() m_auxiliary_timer = 0.0f; // Using SETUP_PHASE will play the track into sfx first, and has no // other side effects. - m_phase = UserConfigParams::m_race_now ? RACE_PHASE : SETUP_PHASE; + m_phase = UserConfigParams::m_race_now ? MUSIC_PHASE : SETUP_PHASE; + + if (UserConfigParams::m_race_now) + { + // Setup music and sound + if (World::getWorld()->getWeather() != NULL) + World::getWorld()->getWeather()->playSound(); + + // Starting the music here doesn't work so it's done in the MUSIC_PHASE + World::getWorld()->getTrack()->startMusic(); + + // Start engines + for (unsigned int i = 0; i < World::getWorld()->getNumKarts(); i++) + World::getWorld()->getKart(i)->startEngineSFX(); + } + m_previous_phase = UNDEFINED_PHASE; // Just in case that the game is reset during the intro phase m_track_intro_sound->stop(); @@ -140,7 +155,7 @@ void WorldStatus::update(const float dt) if (World::getWorld()->getWeather() != NULL) { - World::getWorld()->getWeather()->playSound(); + World::getWorld()->getWeather()->playSound(); } return; @@ -254,6 +269,12 @@ void WorldStatus::update(const float dt) break; case MUSIC_PHASE: + // Start the music here when starting fast + if (UserConfigParams::m_race_now) + { + music_manager->startMusic(music_manager->getCurrentMusic()); + UserConfigParams::m_race_now = false; + } // how long to display the 'music' message if (m_auxiliary_timer>stk_config->m_music_credit_time) { From f86a92286c5a31498e087877bd33214ae898b76e Mon Sep 17 00:00:00 2001 From: Flakebi Date: Mon, 9 Feb 2015 01:56:56 +0100 Subject: [PATCH 025/117] Set the music before updating to fix -N too --- src/modes/world_status.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 19d482be4..60fd52985 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -58,15 +58,13 @@ void WorldStatus::reset() // other side effects. m_phase = UserConfigParams::m_race_now ? MUSIC_PHASE : SETUP_PHASE; + // Parts of the initialisation-phase are skipped so do it here if (UserConfigParams::m_race_now) { // Setup music and sound if (World::getWorld()->getWeather() != NULL) World::getWorld()->getWeather()->playSound(); - // Starting the music here doesn't work so it's done in the MUSIC_PHASE - World::getWorld()->getTrack()->startMusic(); - // Start engines for (unsigned int i = 0; i < World::getWorld()->getNumKarts(); i++) World::getWorld()->getKart(i)->startEngineSFX(); @@ -80,6 +78,9 @@ void WorldStatus::reset() if (device->getTimer()->isStopped()) device->getTimer()->start(); + + // Set the right music + World::getWorld()->getTrack()->startMusic(); } // reset //----------------------------------------------------------------------------- @@ -228,8 +229,6 @@ void WorldStatus::update(const float dt) m_start_sound->play(); } - World::getWorld()->getTrack()->startMusic(); - // event onGo(); } From 683f42527e186daf0f022dd75766874f6d4a6d3c Mon Sep 17 00:00:00 2001 From: Flakebi Date: Mon, 9 Feb 2015 15:18:53 +0100 Subject: [PATCH 026/117] Add light effect when skidding --- src/karts/kart.cpp | 39 ++++++++++++++++++++++++++++++++++++++- src/karts/kart.hpp | 12 ++++++++++-- src/karts/skidding.cpp | 3 +++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index ea446fe6e..a5c31e3f1 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -132,6 +132,8 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, m_fire_clicked = 0; m_wrongway_counter = 0; m_nitro_light = NULL; + m_skidding_light_1 = NULL; + m_skidding_light_2 = NULL; m_type = RaceManager::KT_AI; m_view_blocked_by_plunger = 0; @@ -2393,11 +2395,24 @@ void Kart::loadData(RaceManager::KartType type, bool is_animated_model) bool always_animated = (type == RaceManager::KT_PLAYER && race_manager->getNumPlayers() == 1); m_node = m_kart_model->attachModel(is_animated_model, always_animated); + // Create nitro light m_nitro_light = irr_driver->addLight(core::vector3df(0.0f, 0.5f, m_kart_model->getLength()*-0.5f - 0.05f), - 0.6f /* force */, 5.0f /* radius */, 0.0f, 0.4f, 1.0f, false, m_node); + 0.4f /* force */, 5.0f /* radius */, 0.0f, 0.4f, 1.0f, false, m_node); m_nitro_light->setVisible(false); m_nitro_light->setName( ("nitro emitter (" + getIdent() + ")").c_str() ); + // Create skidding lights + // For the first skidding level + m_skidding_light_1 = irr_driver->addLight(core::vector3df(0.0f, 0.1f, m_kart_model->getLength()*-0.5f - 0.05f), + 0.3f /* force */, 3.0f /* radius */, 1.0f, 0.6f, 0.0f, false, m_node); + m_skidding_light_1->setVisible(false); + m_skidding_light_1->setName( ("skidding emitter 1 (" + getIdent() + ")").c_str() ); + // For the second skidding level + m_skidding_light_2 = irr_driver->addLight(core::vector3df(0.0f, 0.1f, m_kart_model->getLength()*-0.5f - 0.05f), + 0.4f /* force */, 4.0f /* radius */, 1.0f, 0.0f, 0.0f, false, m_node); + m_skidding_light_2->setVisible(false); + m_skidding_light_2->setName( ("skidding emitter 2 (" + getIdent() + ")").c_str() ); + #ifdef DEBUG m_node->setName( (getIdent()+"(lod-node)").c_str() ); #endif @@ -2739,4 +2754,26 @@ void Kart::setOnScreenText(const wchar_t *text) // when the parent is deleted. } // setOnScreenText +void Kart::activateSkidLight(unsigned int level) +{ + if (level > 0) + { + if (level == 1) + { + m_skidding_light_1->setVisible(true); + m_skidding_light_2->setVisible(false); + } + else + { + m_skidding_light_2->setVisible(true); + m_skidding_light_1->setVisible(false); + } + } + else + { + m_skidding_light_1->setVisible(false); + m_skidding_light_2->setVisible(false); + } +} + /* EOF */ diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 7c685d66d..5342d9043 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -222,8 +222,14 @@ private: /** To prevent using nitro in too short bursts */ float m_min_nitro_time; + /** A light that's shown when the kart uses nitro. */ scene::ISceneNode* m_nitro_light; + /** Lights that are shown when the kart is skidding. */ + scene::ISceneNode* m_skidding_light_1; + /** A light that's shown on the second skid-level with another color. */ + scene::ISceneNode* m_skidding_light_2; + void updatePhysics(float dt); void handleMaterialSFX(const Material *material); void handleMaterialGFX(); @@ -250,8 +256,8 @@ public: virtual bool isInRest () const; virtual void applyEngineForce (float force); - virtual void flyUp(); - virtual void flyDown(); + virtual void flyUp(); + virtual void flyDown(); virtual void startEngineSFX (); virtual void adjustSpeed (float f); @@ -448,6 +454,8 @@ public: /** Counter which is used for displaying wrong way message after a delay */ float getWrongwayCounter() { return m_wrongway_counter; } void setWrongwayCounter(float counter) { m_wrongway_counter = counter; } + + void activateSkidLight(unsigned int level); }; // Kart diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index d3e02d413..a1014adff 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -77,6 +77,7 @@ void Skidding::reset() m_jump_speed = 0.0f; m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0); m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0); + m_kart->activateSkidLight(0); m_kart->getControls().m_skid = KartControl::SC_NONE; btVector3 rot(0, 0, 0); @@ -392,6 +393,7 @@ void Skidding::update(float dt, bool is_on_ground, { m_skid_bonus_ready = true; m_kart->getKartGFX()->setSkidLevel(level); + m_kart->activateSkidLight(level); } // If player stops skidding, trigger bonus, and change state to // SKID_SHOW_GFX_* @@ -445,6 +447,7 @@ void Skidding::update(float dt, bool is_on_ground, ->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0); m_kart->getKartGFX() ->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0); + m_kart->activateSkidLight(0); m_skid_state = SKID_NONE; } } // switch From 604e712b8145609b469bc95b3dcc6e695c38befc Mon Sep 17 00:00:00 2001 From: Flakebi Date: Mon, 9 Feb 2015 22:28:47 +0100 Subject: [PATCH 027/117] Apply konstin's idea which makes it much shorter --- src/karts/kart.cpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index a5c31e3f1..ba8ff652f 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2756,24 +2756,8 @@ void Kart::setOnScreenText(const wchar_t *text) void Kart::activateSkidLight(unsigned int level) { - if (level > 0) - { - if (level == 1) - { - m_skidding_light_1->setVisible(true); - m_skidding_light_2->setVisible(false); - } - else - { - m_skidding_light_2->setVisible(true); - m_skidding_light_1->setVisible(false); - } - } - else - { - m_skidding_light_1->setVisible(false); - m_skidding_light_2->setVisible(false); - } + m_skidding_light_1->setVisible(level == 1); + m_skidding_light_2->setVisible(level > 1); } /* EOF */ From 58f0ce412e799a50e25878f6134cb7d60427c61e Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 10 Feb 2015 08:32:05 +1100 Subject: [PATCH 028/117] Removed unnecessary m_adjusted_gain variable and resetTempVolume function. Fixed that music volume in race could not be changed. --- src/audio/music.hpp | 2 +- src/audio/music_dummy.hpp | 2 +- src/audio/music_information.cpp | 26 ++++++++++++-------------- src/audio/music_information.hpp | 8 ++------ src/audio/music_manager.cpp | 6 +++--- src/audio/music_ogg.cpp | 11 ++++++----- src/audio/music_ogg.hpp | 2 +- src/audio/sfx_manager.cpp | 18 +++++++----------- src/audio/sfx_manager.hpp | 3 +-- src/audio/sfx_openal.cpp | 30 +++++++++++++++--------------- src/audio/sfx_openal.hpp | 10 +++++----- 11 files changed, 54 insertions(+), 64 deletions(-) diff --git a/src/audio/music.hpp b/src/audio/music.hpp index 824f4d3de..1389bc853 100644 --- a/src/audio/music.hpp +++ b/src/audio/music.hpp @@ -34,7 +34,7 @@ public: virtual bool stopMusic () = 0; virtual bool pauseMusic () = 0; virtual bool resumeMusic () = 0; - virtual void volumeMusic (float gain) = 0; + virtual void setVolume (float volume) = 0; virtual void updateFading(float percent) = 0; virtual void updateFaster(float percent, float pitch) = 0; virtual void update () = 0; diff --git a/src/audio/music_dummy.hpp b/src/audio/music_dummy.hpp index 977c4a5b7..ef9e4a700 100644 --- a/src/audio/music_dummy.hpp +++ b/src/audio/music_dummy.hpp @@ -34,7 +34,7 @@ public: virtual bool stopMusic () { return true; } virtual bool pauseMusic () { return true; } virtual bool resumeMusic () { return true; } - virtual void volumeMusic (float gain) {} + virtual void setVolume (float volume) {} virtual void updateFading(float percent) {} virtual void updateFaster(float percent, float pitch) {} virtual void update () {} diff --git a/src/audio/music_information.cpp b/src/audio/music_information.cpp index 7f07c2b38..fde25fc8f 100644 --- a/src/audio/music_information.cpp +++ b/src/audio/music_information.cpp @@ -85,7 +85,6 @@ MusicInformation::MusicInformation(const XMLNode *root, m_faster_time = 1.0f; m_max_pitch = 0.1f; m_gain = 1.0f; - m_adjusted_gain = 1.0f; // Otherwise read config file @@ -101,8 +100,6 @@ MusicInformation::MusicInformation(const XMLNode *root, root->get("fast", &m_enable_fast ); root->get("fast-filename", &m_fast_filename ); - m_adjusted_gain = m_gain; - // Get the path from the filename and add it to the ogg filename std::string path = StringUtils::getPath(filename); m_normal_filename = path + "/" + m_normal_filename; @@ -172,7 +169,7 @@ void MusicInformation::startMusic() m_normal_filename.c_str()); return; } - m_normal_music->volumeMusic(m_adjusted_gain); + m_normal_music->setVolume(m_gain); m_normal_music->playMusic(); // Then (if available) load the music for the last track @@ -206,7 +203,7 @@ void MusicInformation::startMusic() "supported or not found.\n", m_fast_filename.c_str()); return; } - m_fast_music->volumeMusic(m_adjusted_gain); + m_fast_music->setVolume(m_gain); } // startMusic //----------------------------------------------------------------------------- @@ -298,21 +295,22 @@ void MusicInformation::resumeMusic() } // resumeMusic //----------------------------------------------------------------------------- -void MusicInformation::volumeMusic(float gain) +void MusicInformation::setDefaultVolume() { - m_adjusted_gain = m_gain * gain; if (m_normal_music && m_normal_music->isPlaying()) - m_normal_music->volumeMusic(m_adjusted_gain); + m_normal_music->setVolume(m_gain); if (m_fast_music && m_fast_music->isPlaying()) - m_fast_music->volumeMusic(m_adjusted_gain); -} // volumeMusic + m_fast_music->setVolume(m_gain); +} // setVolume //----------------------------------------------------------------------------- - -void MusicInformation::setTemporaryVolume(float gain) +/** Overwrites the current volume with a temporary value (used e.g. to fade + * from normal music to last lap music). + */ +void MusicInformation::setTemporaryVolume(float volume) { - if (m_normal_music != NULL) m_normal_music->volumeMusic(gain); - if (m_fast_music != NULL) m_fast_music->volumeMusic(gain); + if (m_normal_music != NULL) m_normal_music->setVolume(volume); + if (m_fast_music != NULL) m_fast_music->setVolume(volume); } //----------------------------------------------------------------------------- diff --git a/src/audio/music_information.hpp b/src/audio/music_information.hpp index 21b622d30..7203451bc 100644 --- a/src/audio/music_information.hpp +++ b/src/audio/music_information.hpp @@ -57,7 +57,6 @@ private: bool m_enable_fast; float m_gain; - float m_adjusted_gain; /** Either time for fading faster music in, or time to change pitch. */ float m_faster_time; @@ -87,12 +86,9 @@ private: void stopMusic(); void pauseMusic(); void resumeMusic(); - void volumeMusic(float gain); + void setDefaultVolume(); void switchToFastMusic(); - void setTemporaryVolume(float gain); - // ------------------------------------------------------------------------ - /** Resets a temporary volume change. */ - void resetTemporaryVolume() { volumeMusic(m_adjusted_gain); } + void setTemporaryVolume(float volume); // ------------------------------------------------------------------------ /** Sets the music to be waiting, i.e. startMusic still needs to be * called. Used to pre-load track music during track loading time. */ diff --git a/src/audio/music_manager.cpp b/src/audio/music_manager.cpp index 12ddb70d2..46fbeaa14 100644 --- a/src/audio/music_manager.cpp +++ b/src/audio/music_manager.cpp @@ -252,7 +252,7 @@ void MusicManager::setTemporaryVolume(float gain) void MusicManager::resetTemporaryVolume() { if (m_current_music) - SFXManager::get()->queue(SFXManager::SFX_MUSIC_RESET_TMP_VOLUME, + SFXManager::get()->queue(SFXManager::SFX_MUSIC_DEFAULT_VOLUME, m_current_music); } // resetTemporaryVolume @@ -271,7 +271,7 @@ void MusicManager::setMasterMusicVolume(float gain) if (m_current_music) { // Sets the music volume to m_master_gain - SFXManager::get()->queue(SFXManager::SFX_MUSIC_VOLUME, + SFXManager::get()->queue(SFXManager::SFX_MUSIC_DEFAULT_VOLUME, m_current_music); } @@ -296,7 +296,7 @@ MusicInformation* MusicManager::getMusicInformation(const std::string& filename) MusicInformation *mi = MusicInformation::create(filename); if(mi) { - SFXManager::get()->queue(SFXManager::SFX_MUSIC_VOLUME, mi); + SFXManager::get()->queue(SFXManager::SFX_MUSIC_DEFAULT_VOLUME, mi); m_all_music[basename] = mi; } return mi; diff --git a/src/audio/music_ogg.cpp b/src/audio/music_ogg.cpp index 68678a6cf..e49a293b9 100644 --- a/src/audio/music_ogg.cpp +++ b/src/audio/music_ogg.cpp @@ -247,14 +247,15 @@ bool MusicOggStream::resumeMusic() } // resumeMusic //----------------------------------------------------------------------------- -void MusicOggStream::volumeMusic(float gain) +void MusicOggStream::setVolume(float volume) { - if (gain > 1.0f) gain = 1.0f; - if (gain < 0.0f) gain = 0.0f; + volume *= music_manager->getMasterMusicVolume(); + if (volume > 1.0f) volume = 1.0f; + if (volume < 0.0f) volume = 0.0f; - alSourcef(m_soundSource, AL_GAIN, gain); + alSourcef(m_soundSource, AL_GAIN, volume); check("volume music"); // clear errors -} // volumeMusic +} // setVolume //----------------------------------------------------------------------------- void MusicOggStream::updateFading(float percent) diff --git a/src/audio/music_ogg.hpp b/src/audio/music_ogg.hpp index 96ed7b62d..c8f2ec63e 100644 --- a/src/audio/music_ogg.hpp +++ b/src/audio/music_ogg.hpp @@ -60,7 +60,7 @@ public: virtual bool stopMusic(); virtual bool pauseMusic(); virtual bool resumeMusic(); - virtual void volumeMusic (float gain); + virtual void setVolume(float volume); virtual bool isPlaying(); protected: diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index edb5fd05b..5f9910e54 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -326,8 +326,7 @@ void* SFXManager::mainLoop(void *obj) case SFX_UPDATE: me->reallyUpdateNow(current); break; case SFX_MUSIC_START: { - float gain = music_manager->getMasterMusicVolume(); - current->m_music_information->volumeMusic(gain); + current->m_music_information->setDefaultVolume(); current->m_music_information->startMusic(); break; } case SFX_MUSIC_STOP: @@ -335,7 +334,10 @@ void* SFXManager::mainLoop(void *obj) case SFX_MUSIC_PAUSE: current->m_music_information->pauseMusic(); break; case SFX_MUSIC_RESUME: - current->m_music_information->resumeMusic(); break; + current->m_music_information->resumeMusic(); + // This might be necessasary if the volume was changed + // in the in-game menu + current->m_music_information->setDefaultVolume(); break; case SFX_MUSIC_SWITCH_FAST: current->m_music_information->switchToFastMusic(); break; case SFX_MUSIC_SET_TMP_VOLUME: @@ -343,17 +345,11 @@ void* SFXManager::mainLoop(void *obj) MusicInformation *mi = current->m_music_information; mi->setTemporaryVolume(current->m_parameter.getX()); break; } - case SFX_MUSIC_RESET_TMP_VOLUME: - { - MusicInformation *mi = current->m_music_information; - mi->resetTemporaryVolume(); break; - } case SFX_MUSIC_WAITING: current->m_music_information->setMusicWaiting(); break; - case SFX_MUSIC_VOLUME: + case SFX_MUSIC_DEFAULT_VOLUME: { - float gain = music_manager->getMasterMusicVolume(); - current->m_music_information->volumeMusic(gain); + current->m_music_information->setDefaultVolume(); } default: assert("Not yet supported."); } diff --git a/src/audio/sfx_manager.hpp b/src/audio/sfx_manager.hpp index 5f99a3aeb..3bd8788df 100644 --- a/src/audio/sfx_manager.hpp +++ b/src/audio/sfx_manager.hpp @@ -83,9 +83,8 @@ public: SFX_MUSIC_RESUME, SFX_MUSIC_SWITCH_FAST, SFX_MUSIC_SET_TMP_VOLUME, - SFX_MUSIC_RESET_TMP_VOLUME, SFX_MUSIC_WAITING, - SFX_MUSIC_VOLUME, + SFX_MUSIC_DEFAULT_VOLUME, SFX_EXIT, }; // SFXCommands diff --git a/src/audio/sfx_openal.cpp b/src/audio/sfx_openal.cpp index 8f86afb9d..1207f51b2 100644 --- a/src/audio/sfx_openal.cpp +++ b/src/audio/sfx_openal.cpp @@ -38,7 +38,7 @@ #include #include -SFXOpenAL::SFXOpenAL(SFXBuffer* buffer, bool positional, float gain, +SFXOpenAL::SFXOpenAL(SFXBuffer* buffer, bool positional, float volume, bool owns_buffer) : SFXBase() { @@ -46,7 +46,7 @@ SFXOpenAL::SFXOpenAL(SFXBuffer* buffer, bool positional, float gain, m_sound_source = 0; m_status = SFX_NOT_INITIALISED; m_positional = positional; - m_default_gain = gain; + m_default_gain = volume; m_loop = false; m_gain = -1.0f; m_master_gain = 1.0f; @@ -179,22 +179,22 @@ void SFXOpenAL::reallySetSpeed(float factor) //----------------------------------------------------------------------------- /** Changes the volume of a sound effect. - * \param gain Volume adjustment between 0.0 (mute) and 1.0 (full volume). + * \param volume Volume adjustment between 0.0 (mute) and 1.0 (full volume). */ -void SFXOpenAL::setVolume(float gain) +void SFXOpenAL::setVolume(float volume) { if(m_status==SFX_UNKNOWN || !SFXManager::get()->sfxAllowed()) return; - assert(!isnan(gain)) ; - SFXManager::get()->queue(SFXManager::SFX_VOLUME, this, gain); + assert(!isnan(volume)) ; + SFXManager::get()->queue(SFXManager::SFX_VOLUME, this, volume); } // setVolume //----------------------------------------------------------------------------- /** Changes the volume of a sound effect. - * \param gain Volume adjustment between 0.0 (mute) and 1.0 (full volume). + * \param volume Volume adjustment between 0.0 (mute) and 1.0 (full volume). */ -void SFXOpenAL::reallySetVolume(float gain) +void SFXOpenAL::reallySetVolume(float volume) { - m_gain = m_default_gain * gain; + m_gain = m_default_gain * volume; if(m_status==SFX_UNKNOWN) return; @@ -210,23 +210,23 @@ void SFXOpenAL::reallySetVolume(float gain) //----------------------------------------------------------------------------- /** Schedules setting of the master volume. - * \param gain Gain value. + * \param volume Volume value. */ -void SFXOpenAL::setMasterVolume(float gain) +void SFXOpenAL::setMasterVolume(float volume) { // This needs to be called even if sfx are disabled atm, so only exit // in case that the sfx could not be loaded in the first place. if(m_status==SFX_UNKNOWN) return; - SFXManager::get()->queue(SFXManager::SFX_MASTER_VOLUME, this, gain); + SFXManager::get()->queue(SFXManager::SFX_MASTER_VOLUME, this, volume); } // setMasterVolume //----------------------------------------------------------------------------- /** Sets the master volume. - * \param gain Master volume. + * \param volume Master volume. */ -void SFXOpenAL::reallySetMasterVolumeNow(float gain) +void SFXOpenAL::reallySetMasterVolumeNow(float volume) { - m_master_gain = gain; + m_master_gain = volume; if(m_status==SFX_UNKNOWN || m_status == SFX_NOT_INITIALISED) return; diff --git a/src/audio/sfx_openal.hpp b/src/audio/sfx_openal.hpp index 87a3c7d3b..1642970b5 100644 --- a/src/audio/sfx_openal.hpp +++ b/src/audio/sfx_openal.hpp @@ -76,7 +76,7 @@ private: float m_play_time; public: - SFXOpenAL(SFXBuffer* buffer, bool positional, float gain, + SFXOpenAL(SFXBuffer* buffer, bool positional, float volume, bool owns_buffer = false); virtual ~SFXOpenAL(); @@ -97,10 +97,10 @@ public: virtual void reallySetSpeed(float factor); virtual void setPosition(const Vec3 &position); virtual void reallySetPosition(const Vec3 &p); - virtual void setVolume(float gain); - virtual void reallySetVolume(float gain); - virtual void setMasterVolume(float gain); - virtual void reallySetMasterVolumeNow(float gain); + virtual void setVolume(float volume); + virtual void reallySetVolume(float volume); + virtual void setMasterVolume(float volume); + virtual void reallySetMasterVolumeNow(float volue); virtual void onSoundEnabledBack(); virtual void setRolloff(float rolloff); // ------------------------------------------------------------------------ From 95ecd0eed1b72d3178d696d2b13cedb794fd3641 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 10 Feb 2015 09:22:23 +1100 Subject: [PATCH 029/117] Let the audio thread run 'all the time' by sending itself an update package after the queue is empty. --- src/audio/sfx_manager.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 5f9910e54..b8bf46585 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -355,6 +355,15 @@ void* SFXManager::mainLoop(void *obj) } delete current; current = NULL; + // We access the size without lock, doesn't matter if we + // should get an incorrect value because of concurrent read/writes + if (me->m_sfx_commands.getData().size() == 0) + { + // Wait some time to let other threads run, then queue an + // update event to keep music playing. + StkTime::sleep(1); + me->queue(SFX_UPDATE, (SFXBase*)NULL, 0.01f); + } me->m_sfx_commands.lock(); } // while From ec598d6f41205c04288f72e512d6ebbd6645584e Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 10 Feb 2015 17:07:46 +1100 Subject: [PATCH 030/117] Start music properly in thread, avoid starting track music more than once. --- src/modes/world_status.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 60fd52985..c19ae0cd3 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -246,9 +246,10 @@ void WorldStatus::update(const float dt) return; case GO_PHASE : - if (m_auxiliary_timer>2.5f && music_manager->getCurrentMusic()) + if (m_auxiliary_timer>2.5f && music_manager->getCurrentMusic() && + !music_manager->getCurrentMusic()->isPlaying()) { - music_manager->startMusic(music_manager->getCurrentMusic()); + music_manager->startMusic(); } if (m_auxiliary_timer > 3.0f) // how long to display the 'go' message @@ -271,7 +272,7 @@ void WorldStatus::update(const float dt) // Start the music here when starting fast if (UserConfigParams::m_race_now) { - music_manager->startMusic(music_manager->getCurrentMusic()); + music_manager->startMusic(); UserConfigParams::m_race_now = false; } // how long to display the 'music' message From 7d0875878b20242157d40e6a4dadd8e620862db7 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 10 Feb 2015 17:27:07 +1100 Subject: [PATCH 031/117] Simplified startMusic call. --- src/states_screens/options_screen_audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/states_screens/options_screen_audio.cpp b/src/states_screens/options_screen_audio.cpp index 3049e40dd..e5bbaf8b3 100644 --- a/src/states_screens/options_screen_audio.cpp +++ b/src/states_screens/options_screen_audio.cpp @@ -156,7 +156,7 @@ void OptionsScreenAudio::eventCallback(Widget* widget, const std::string& name, if(w->getState() == false) music_manager->stopMusic(); else - music_manager->startMusic(music_manager->getCurrentMusic(), true); + music_manager->startMusic(); } else if(name == "sfx_enabled") { From 84a84b67767116fc8dde297d7b825bb91103da0b Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 10 Feb 2015 17:31:35 +1100 Subject: [PATCH 032/117] Try to fix playing without music. --- src/audio/music_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio/music_manager.cpp b/src/audio/music_manager.cpp index 46fbeaa14..db3cb3b9a 100644 --- a/src/audio/music_manager.cpp +++ b/src/audio/music_manager.cpp @@ -165,7 +165,7 @@ void MusicManager::addMusicToTracks() */ void MusicManager::startMusic() { - if (m_current_music) + if (m_current_music && UserConfigParams::m_music) SFXManager::get()->queue(SFXManager::SFX_MUSIC_START, m_current_music); } // startMusic From 91d042da31d1f43a9991cd5d3674317135296f9a Mon Sep 17 00:00:00 2001 From: Flakebi Date: Tue, 10 Feb 2015 13:55:12 +0100 Subject: [PATCH 033/117] Set correct volume for last lap music --- src/audio/music.hpp | 1 - src/audio/music_dummy.hpp | 1 - src/audio/music_information.cpp | 5 +++-- src/audio/music_ogg.cpp | 7 ------- src/audio/music_ogg.hpp | 1 - src/audio/sfx_manager.cpp | 3 +-- 6 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/audio/music.hpp b/src/audio/music.hpp index 1389bc853..8908d9e1e 100644 --- a/src/audio/music.hpp +++ b/src/audio/music.hpp @@ -35,7 +35,6 @@ public: virtual bool pauseMusic () = 0; virtual bool resumeMusic () = 0; virtual void setVolume (float volume) = 0; - virtual void updateFading(float percent) = 0; virtual void updateFaster(float percent, float pitch) = 0; virtual void update () = 0; virtual bool isPlaying () = 0; diff --git a/src/audio/music_dummy.hpp b/src/audio/music_dummy.hpp index ef9e4a700..b88202847 100644 --- a/src/audio/music_dummy.hpp +++ b/src/audio/music_dummy.hpp @@ -35,7 +35,6 @@ public: virtual bool pauseMusic () { return true; } virtual bool resumeMusic () { return true; } virtual void setVolume (float volume) {} - virtual void updateFading(float percent) {} virtual void updateFaster(float percent, float pitch) {} virtual void update () {} virtual bool isPlaying () { return false; } diff --git a/src/audio/music_information.cpp b/src/audio/music_information.cpp index fde25fc8f..f35702495 100644 --- a/src/audio/music_information.cpp +++ b/src/audio/music_information.cpp @@ -223,8 +223,8 @@ void MusicInformation::update(float dt) return; } float fraction=m_time_since_faster/m_faster_time; - m_normal_music->updateFading(1-fraction); - m_fast_music->updateFading(fraction); + m_normal_music->setVolume(1-fraction); + m_fast_music->setVolume(fraction); break; } case SOUND_FASTER: { @@ -322,6 +322,7 @@ void MusicInformation::switchToFastMusic() { m_mode = SOUND_FADING; m_fast_music->playMusic(); + m_fast_music->setVolume(0); } else { diff --git a/src/audio/music_ogg.cpp b/src/audio/music_ogg.cpp index e49a293b9..d4458980c 100644 --- a/src/audio/music_ogg.cpp +++ b/src/audio/music_ogg.cpp @@ -257,13 +257,6 @@ void MusicOggStream::setVolume(float volume) check("volume music"); // clear errors } // setVolume -//----------------------------------------------------------------------------- -void MusicOggStream::updateFading(float percent) -{ - alSourcef(m_soundSource,AL_GAIN,percent); - update(); -} // updateFading - //----------------------------------------------------------------------------- void MusicOggStream::updateFaster(float percent, float max_pitch) { diff --git a/src/audio/music_ogg.hpp b/src/audio/music_ogg.hpp index c8f2ec63e..55127790c 100644 --- a/src/audio/music_ogg.hpp +++ b/src/audio/music_ogg.hpp @@ -51,7 +51,6 @@ public: virtual ~MusicOggStream(); virtual void update(); - virtual void updateFading(float percent); virtual void updateFaster(float percent, float max_pitch); virtual bool load(const std::string& filename); diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index b8bf46585..500bfed31 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -873,7 +873,7 @@ void SFXManager::positionListener(const Vec3 &position, const Vec3 &front, void SFXManager::reallyPositionListenerNow() { #if HAVE_OGGVORBIS - if (!UserConfigParams::m_sfx || !m_initialized) return; + if (!sfxAllowed()) return; m_listener_position.lock(); { @@ -926,4 +926,3 @@ SFXBase* SFXManager::quickSound(const std::string &sound_type) } } // quickSound - From a598b0ed1a225011ca6e191f208cbbb018297429 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 11 Feb 2015 08:06:28 +1100 Subject: [PATCH 034/117] Fix after-race sfx which wasn't playing anymore (caused by quick sound effects not having update() called, which is necessary to detect if they have stopped playing). --- src/audio/sfx_manager.cpp | 55 +++++++++++++++++++++++++++------------ src/audio/sfx_manager.hpp | 5 ++-- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index b8bf46585..97882c0a6 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -135,16 +135,19 @@ SFXManager::~SFXManager() } m_all_sfx.getData().clear(); m_all_sfx.unlock(); + + m_quick_sounds.lock(); // ---- clear m_quick_sounds { - std::map::iterator i = m_quick_sounds.begin(); - for (; i != m_quick_sounds.end(); i++) + std::map::iterator i = m_quick_sounds.getData().begin(); + for (; i != m_quick_sounds.getData().end(); i++) { SFXBase* snd = (*i).second; delete snd; } } - m_quick_sounds.clear(); + m_quick_sounds.getData().clear(); + m_quick_sounds.unlock(); // ---- clear m_all_sfx_types { @@ -614,7 +617,7 @@ SFXBase* SFXManager::createSoundSource(SFXBuffer* buffer, //---------------------------------------------------------------------------- SFXBase* SFXManager::createSoundSource(const std::string &name, - const bool addToSFXList) + const bool add_to_SFXList) { std::map::iterator i = m_all_sfx_types.find(name); if ( i == m_all_sfx_types.end() ) @@ -625,7 +628,7 @@ SFXBase* SFXManager::createSoundSource(const std::string &name, return NULL; } - return createSoundSource( i->second, addToSFXList ); + return createSoundSource( i->second, add_to_SFXList ); } // createSoundSource //---------------------------------------------------------------------------- @@ -690,6 +693,17 @@ void SFXManager::reallyUpdateNow(SFXCommand *current) } // for i in m_all_sfx m_all_sfx.unlock(); + // We need to lock the quick sounds during update, since adding more + // quick sounds by another thread could invalidate the iterator. + m_quick_sounds.lock(); + std::map::iterator i = m_quick_sounds.getData().begin(); + for (; i != m_quick_sounds.getData().end(); i++) + { + if (i->second->getStatus() == SFXBase::SFX_PLAYING) + i->second->updatePlayingSFX(dt); + } // for i in m_all_sfx + m_quick_sounds.unlock(); + } // reallyUpdateNow //---------------------------------------------------------------------------- @@ -820,11 +834,13 @@ void SFXManager::setMasterSFXVolume(float gain) // quick SFX { - std::map::iterator i = m_quick_sounds.begin(); - for (; i != m_quick_sounds.end(); i++) + m_quick_sounds.lock(); + std::map::iterator i = m_quick_sounds.getData().begin(); + for (; i != m_quick_sounds.getData().end(); i++) { (*i).second->setMasterVolume(m_master_gain); } + m_quick_sounds.unlock(); } } // setMasterSFXVolume @@ -908,22 +924,29 @@ void SFXManager::reallyPositionListenerNow() SFXBase* SFXManager::quickSound(const std::string &sound_type) { if (!sfxAllowed()) return NULL; - std::map::iterator sound = m_quick_sounds.find(sound_type); - if (sound == m_quick_sounds.end()) + m_quick_sounds.lock(); + std::map::iterator sound = + m_quick_sounds.getData().find(sound_type); + + if (sound == m_quick_sounds.getData().end()) { // sound not yet in our local list of quick sounds - SFXBase* newSound = SFXManager::get()->createSoundSource(sound_type, false); - if (newSound == NULL) return NULL; - newSound->play(); - m_quick_sounds[sound_type] = newSound; - return newSound; + SFXBase* new_sound = createSoundSource(sound_type, false); + if (new_sound == NULL) return NULL; + new_sound->play(); + m_quick_sounds.getData()[sound_type] = new_sound; + m_quick_sounds.unlock(); + return new_sound; } else { - (*sound).second->play(); - return (*sound).second; + SFXBase *base_sound = sound->second; + base_sound->play(); + m_quick_sounds.unlock(); + return base_sound; } + } // quickSound diff --git a/src/audio/sfx_manager.hpp b/src/audio/sfx_manager.hpp index 3bd8788df..08e939179 100644 --- a/src/audio/sfx_manager.hpp +++ b/src/audio/sfx_manager.hpp @@ -187,8 +187,9 @@ private: /** The list of sound effects to be played in the next update. */ Synchronised< std::vector > m_sfx_commands; - /** To play non-positional sounds without having to create a new object for each */ - std::map m_quick_sounds; + /** To play non-positional sounds without having to create a + * new object for each. */ + Synchronised > m_quick_sounds; /** If the sfx manager has been initialised. */ bool m_initialized; From 401187dcf0f9b3cdacff7c913589dfb4c4339cef Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Tue, 10 Feb 2015 02:04:44 +0100 Subject: [PATCH 035/117] Try to fix 1937 --- src/graphics/post_processing.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index 93e4aba6e..483f4ac0b 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -510,10 +510,11 @@ void PostProcessing::renderSSAO() DrawFullScreenEffect(irr_driver->getSSAORadius(), irr_driver->getSSAOK(), irr_driver->getSSAOSigma()); } -void PostProcessing::renderMotionBlur(unsigned cam, FrameBuffer &in_fbo, FrameBuffer &out_fbo) +void PostProcessing::renderMotionBlur(unsigned , FrameBuffer &in_fbo, FrameBuffer &out_fbo) { MotionBlurProvider * const cb = (MotionBlurProvider *)irr_driver-> getCallback(ES_MOTIONBLUR); + unsigned cam = Camera::getActiveCamera()->getIndex(); scene::ICameraSceneNode * const camnode = Camera::getCamera(cam)->getCameraSceneNode(); @@ -540,8 +541,8 @@ void PostProcessing::renderMotionBlur(unsigned cam, FrameBuffer &in_fbo, FrameBu DrawFullScreenEffect( // Todo : use a previousPVMatrix per cam, not global irr_driver->getPreviousPVMatrix(), - cb->getCenter(cam), - cb->getBoostTime(0) * 10, // Todo : should be framerate dependent + core::vector2df(0.5, 0.5), + cb->getBoostTime(Camera::getActiveCamera()->getIndex()) * 10, // Todo : should be framerate dependent 0.15f); } @@ -800,7 +801,7 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo MotionBlurProvider * const cb = (MotionBlurProvider *)irr_driver-> getCallback(ES_MOTIONBLUR); - if (isRace && UserConfigParams::m_motionblur && World::getWorld() != NULL && cb->getBoostTime(0) > 0.) // motion blur + if (isRace && UserConfigParams::m_motionblur && World::getWorld() != NULL && cb->getBoostTime(Camera::getActiveCamera()->getIndex()) > 0.) // motion blur { renderMotionBlur(0, *in_fbo, *out_fbo); std::swap(in_fbo, out_fbo); From f57ad3c1ed7ea1be63cb92e09228001da9689670 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 11 Feb 2015 09:16:48 +1100 Subject: [PATCH 036/117] Try to fix #1972 (things went wrong when stk tried to delete uninitialised sound sources). --- src/audio/music_information.cpp | 2 +- src/audio/sfx_openal.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio/music_information.cpp b/src/audio/music_information.cpp index fde25fc8f..03f15a184 100644 --- a/src/audio/music_information.cpp +++ b/src/audio/music_information.cpp @@ -165,7 +165,7 @@ void MusicInformation::startMusic() delete m_normal_music; m_normal_music = NULL; Log::warn("MusicInformation", "Unable to load music %s, " - "not supported or not found.\n", + "not supported or not found.", m_normal_filename.c_str()); return; } diff --git a/src/audio/sfx_openal.cpp b/src/audio/sfx_openal.cpp index 1207f51b2..2368185e5 100644 --- a/src/audio/sfx_openal.cpp +++ b/src/audio/sfx_openal.cpp @@ -67,7 +67,7 @@ SFXOpenAL::SFXOpenAL(SFXBuffer* buffer, bool positional, float volume, * buffer. */ SFXOpenAL::~SFXOpenAL() { - if (m_status!=SFX_UNKNOWN) + if (m_status!=SFX_UNKNOWN && m_status!=SFX_NOT_INITIALISED) { alDeleteSources(1, &m_sound_source); } From d4f982d7ddbdae6df686d53f92760ae6121c62db Mon Sep 17 00:00:00 2001 From: Flakebi Date: Wed, 11 Feb 2015 20:52:05 +0100 Subject: [PATCH 037/117] Fix crash with --nographics --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 5c913415e..1d308d2b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1296,7 +1296,7 @@ int main(int argc, char *argv[] ) exit(0); } - if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_DRIVER_RECENT_ENOUGH)) + if (!ProfileWorld::isNoGraphics() && GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_DRIVER_RECENT_ENOUGH)) { MessageDialog *dialog = new MessageDialog(_("Your driver version is too old. Please install " From 33f87e28f744e86f305e6eb960cc9e8755229d95 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 12 Feb 2015 07:57:25 +1100 Subject: [PATCH 038/117] Prevent flooding of terminal with "music not playing" messages, added debug error check. --- src/audio/music_ogg.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/audio/music_ogg.cpp b/src/audio/music_ogg.cpp index e49a293b9..f37f1935e 100644 --- a/src/audio/music_ogg.cpp +++ b/src/audio/music_ogg.cpp @@ -45,7 +45,7 @@ MusicOggStream::MusicOggStream() MusicOggStream::~MusicOggStream() { if(stopMusic() == false) - Log::warn("MusicOgg", "problems while stopping music.\n"); + Log::warn("MusicOgg", "problems while stopping music."); } // ~MusicOggStream //----------------------------------------------------------------------------- @@ -307,13 +307,19 @@ void MusicOggStream::update() if (active) { + // For debugging + SFXManager::checkError("before source state"); // we have data, so we should be playing... ALenum state; alGetSourcei(m_soundSource, AL_SOURCE_STATE, &state); if (state != AL_PLAYING) { - Log::warn("MusicOgg", "Music not playing when it should be. " - "Source state: %d\n", state); + // Prevent flooding + static int count = 0; + count++; + if (count<10) + Log::warn("MusicOgg", "Music not playing when it should be. " + "Source state: %d", state); alGetSourcei(m_soundSource, AL_BUFFERS_PROCESSED, &processed); alSourcePlay(m_soundSource); } @@ -321,7 +327,7 @@ void MusicOggStream::update() else { Log::warn("MusicOgg", "Attempt to stream music into buffer failed " - "twice in a row.\n"); + "twice in a row."); } } // update From 72ce6c8bf6a51e94af4e801962e5be5e5dfa111e Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 12 Feb 2015 07:57:51 +1100 Subject: [PATCH 039/117] Fixed race over music not playing when sfx are disabled. --- src/states_screens/race_result_gui.cpp | 21 ++++++++++++++++----- src/states_screens/race_result_gui.hpp | 16 +++++++++++----- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 53afd1455..80f5cbbbc 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -62,6 +62,10 @@ DEFINE_SCREEN_SINGLETON( RaceResultGUI ); RaceResultGUI::RaceResultGUI() : Screen("race_result.stkgui", /*pause race*/ false) { + std::string path = file_manager->getAsset(FileManager::MUSIC, + "race_summary.music"); + m_race_over_music = music_manager->getMusicInformation(path); + } // RaceResultGUI //----------------------------------------------------------------------------- @@ -83,6 +87,13 @@ void RaceResultGUI::init() music_manager->stopMusic(); m_finish_sound = SFXManager::get()->quickSound("race_finish"); + if (!m_finish_sound) + { + // If there is no finish sound (because sfx are disabled), start + // the race over music here (since the race over music is only started + // when the finish sound has been played). + music_manager->startMusic(m_race_over_music); + } // Calculate how many track screenshots can fit into the "result-table" widget GUIEngine::Widget* result_table = getWidget("result-table"); @@ -600,14 +611,14 @@ void RaceResultGUI::onUpdate(float dt) { renderGlobal(dt); - if (m_finish_sound != NULL && - m_finish_sound->getStatus() != SFXBase::SFX_PLAYING) + // When the finish sound has been played, start the race over music. + if(m_finish_sound && m_finish_sound->getStatus() != SFXBase::SFX_PLAYING) { try { - std::string path = file_manager->getAsset(FileManager::MUSIC, - "race_summary.music"); - music_manager->startMusic(music_manager->getMusicInformation(path)); + // This call is done once each frame, but startMusic() is cheap + // if the music is already playing. + music_manager->startMusic(m_race_over_music); } catch (std::exception& e) { diff --git a/src/states_screens/race_result_gui.hpp b/src/states_screens/race_result_gui.hpp index 3019461ab..4e365b114 100644 --- a/src/states_screens/race_result_gui.hpp +++ b/src/states_screens/race_result_gui.hpp @@ -19,14 +19,15 @@ #ifndef HEADER_RACE_RESULT_GUI_HPP #define HEADER_RACE_RESULT_GUI_HPP + +#include "guiengine/screen.hpp" +#include "states_screens/dialogs/message_dialog.hpp" #include "states_screens/race_gui_base.hpp" +#include "states_screens/state_manager.hpp" #include #include -#include "guiengine/screen.hpp" -#include "states_screens/dialogs/message_dialog.hpp" -#include "states_screens/state_manager.hpp" namespace irr { @@ -36,6 +37,7 @@ namespace irr } } +class MusicInformation; class SFXBase; /** @@ -162,13 +164,17 @@ private: /** The previous monospace state of the font. */ bool m_was_monospace; - SFXBase* m_finish_sound; + /** Sound effect at end of race. */ + SFXBase *m_finish_sound; + + /** Music to be played after race ended. */ + MusicInformation *m_race_over_music; /** For highscores */ std::string m_highscore_who; /** For highscores */ - StateManager::ActivePlayer* m_highscore_player; + StateManager::ActivePlayer *m_highscore_player; /** For highscores */ int m_highscore_rank; From c675b6f38ccafa405641b17468b838cc5bed3657 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 12 Feb 2015 13:00:12 +1100 Subject: [PATCH 040/117] Avoid warning at runtime about missing texture. --- src/guiengine/widgets/icon_button_widget.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/guiengine/widgets/icon_button_widget.cpp b/src/guiengine/widgets/icon_button_widget.cpp index f550bcd79..7ea478475 100644 --- a/src/guiengine/widgets/icon_button_widget.cpp +++ b/src/guiengine/widgets/icon_button_widget.cpp @@ -68,8 +68,13 @@ void IconButtonWidget::add() } else if (m_icon_path_type == ICON_PATH_TYPE_RELATIVE) { - std::string file = file_manager->getAsset(m_properties[PROP_ICON]); - setTexture(irr_driver->getTexture(file)); + // Avoid warning about missing texture in case of e.g. + // screenshot widget + if(m_properties[PROP_ICON] != "") + { + std::string file = file_manager->getAsset(m_properties[PROP_ICON]); + setTexture(irr_driver->getTexture(file)); + } } } From f589b2063ecb4dd680bc9b12cc18d99f7886ed2c Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 12 Feb 2015 13:19:58 +1100 Subject: [PATCH 041/117] Fix crash when going story mode while music is disabled. --- src/audio/sfx_manager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 9d6dd1004..f729c0f39 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -683,7 +683,8 @@ void SFXManager::reallyUpdateNow(SFXCommand *current) { assert(current->m_command==SFX_UPDATE); float dt = current->m_parameter.getX(); - music_manager->getCurrentMusic()->update(dt); + if (music_manager->getCurrentMusic()) + music_manager->getCurrentMusic()->update(dt); m_all_sfx.lock(); for (std::vector::iterator i = m_all_sfx.getData().begin(); i != m_all_sfx.getData().end(); i++) From 93c5012f57e909cedd11b6d876d873634554fef8 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 12 Feb 2015 16:49:16 +1100 Subject: [PATCH 042/117] Fixed incorrect label 'debug' for verbose messages and vice versa. --- src/utils/log.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/log.cpp b/src/utils/log.cpp index da4a0fbca..345a914d9 100644 --- a/src/utils/log.cpp +++ b/src/utils/log.cpp @@ -154,7 +154,7 @@ void Log::printMessage(int level, const char *component, const char *format, } __android_log_vprint(alp, "SuperTuxKart", format, args); #else - static const char *names[] = {"verbose", "debug ", "info ", + static const char *names[] = {"debug", "verbose ", "info ", "warn ", "error ", "fatal "}; // Using a va_list twice produces undefined results, ie crash. From 4e88cb6753af4704d12450950fdea8c57cd3ee6f Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 12 Feb 2015 16:56:25 +1100 Subject: [PATCH 043/117] Fixed #1978 (by removing unnecessary stopMusic in World, which actually stops the menu music, not the tutorial music. It's unnecessary since startMusic (main menu) later will first stop currently running music. --- src/modes/world.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index b2583f2b6..7a76ea5df 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -404,7 +404,6 @@ World::~World() if(m_physics) delete m_physics; - music_manager->stopMusic(); m_world = NULL; irr_driver->getSceneManager()->clear(); From cc4903737c36de4c776dc8db2efc5206957df813 Mon Sep 17 00:00:00 2001 From: Daniel Butum Date: Thu, 12 Feb 2015 10:43:55 +0200 Subject: [PATCH 044/117] Add QtCreator project file to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 23a5fa5f6..a98ad5a4e 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,9 @@ src/html *~ *.swp +# Ignore QtCreator project file +CMakeLists.txt.user + packets_log.txt history.dat README.dependencies From 769eaf4742bd613fbe4cb2cb8c9c16c60ae9a65d Mon Sep 17 00:00:00 2001 From: Daniel Butum Date: Thu, 12 Feb 2015 10:47:14 +0200 Subject: [PATCH 045/117] Log API requests that do not return success --- src/online/xml_request.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/online/xml_request.cpp b/src/online/xml_request.cpp index 3bb742c84..5d98c4a66 100644 --- a/src/online/xml_request.cpp +++ b/src/online/xml_request.cpp @@ -71,6 +71,11 @@ namespace Online { m_success = (rec_success == "yes"); m_xml_data->get("info", &m_info); + + if (!m_success) + { + Log::debug("XMLRequest::afterOperation", "Request returned error: %ls", m_info.c_str()); + } } else { From f6542194d85d9643317d9a1e45ab356af40bdf13 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 13 Feb 2015 08:16:38 +1100 Subject: [PATCH 046/117] Fixed line length. --- src/online/xml_request.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/online/xml_request.cpp b/src/online/xml_request.cpp index 5d98c4a66..4154ff1f7 100644 --- a/src/online/xml_request.cpp +++ b/src/online/xml_request.cpp @@ -74,7 +74,8 @@ namespace Online if (!m_success) { - Log::debug("XMLRequest::afterOperation", "Request returned error: %ls", m_info.c_str()); + Log::debug("XMLRequest::afterOperation", + "Request returned error: %ls", m_info.c_str()); } } else From 11f233938d733d3db0327a5247b81cd1926b1dfa Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 13 Feb 2015 08:26:25 +1100 Subject: [PATCH 047/117] Cherry picked pull request #1917. --- src/input/wiimote_manager.cpp | 8 ++------ src/online/online_player_profile.cpp | 5 +++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/input/wiimote_manager.cpp b/src/input/wiimote_manager.cpp index 041416e69..f3fc3868f 100644 --- a/src/input/wiimote_manager.cpp +++ b/src/input/wiimote_manager.cpp @@ -410,12 +410,8 @@ void WiimoteManager::WiimoteDialogListener::onConfirm() int nb_wiimotes = wiimote_manager->getNumberOfWiimotes(); if(nb_wiimotes > 0) { - core::stringw msg = StringUtils::insertValues( - _("Found %d wiimote(s)"), - core::stringw(nb_wiimotes)); - - new MessageDialog( msg ); - + new MessageDialog(_P("Found %d wiimote", "Found %d wiimotes", + nb_wiimotes)); } else { diff --git a/src/online/online_player_profile.cpp b/src/online/online_player_profile.cpp index 48f6f8523..dbe9b7219 100644 --- a/src/online/online_player_profile.cpp +++ b/src/online/online_player_profile.cpp @@ -441,8 +441,9 @@ namespace Online core::stringw message(""); if (friend_request_count > 1) { - message = _("You have %d new friend requests!", - friend_request_count); + message = _P("You have %d new friend request!", + "You have %d new friend requests!", + friend_request_count); } else { From f437781dcceea8f68b1ee7d0bd5aa921f5fdd097 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 13 Feb 2015 08:41:19 +1100 Subject: [PATCH 048/117] Fix #1980. --- src/states_screens/user_screen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/states_screens/user_screen.cpp b/src/states_screens/user_screen.cpp index 7d2b17220..a480ea3fa 100644 --- a/src/states_screens/user_screen.cpp +++ b/src/states_screens/user_screen.cpp @@ -360,6 +360,7 @@ void BaseUserScreen::eventCallback(Widget* widget, PlayerProfile *cp = getSelectedPlayer(); RegisterScreen::getInstance()->setRename(cp); RegisterScreen::getInstance()->push(); + RegisterScreen::getInstance()->setParent(this); m_new_registered_data = false; m_auto_login = false; // Init will automatically be called, which From cdb35b0804f176048a11f1571d7c0333c191d82b Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 13 Feb 2015 08:42:09 +1100 Subject: [PATCH 049/117] Added missing initialisation, fixed line endings. --- src/states_screens/register_screen.cpp | 3 ++- src/states_screens/register_screen.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/states_screens/register_screen.cpp b/src/states_screens/register_screen.cpp index 7d2d7889e..383e60360 100644 --- a/src/states_screens/register_screen.cpp +++ b/src/states_screens/register_screen.cpp @@ -45,7 +45,8 @@ DEFINE_SCREEN_SINGLETON( RegisterScreen ); RegisterScreen::RegisterScreen() : Screen("online/register.stkgui") { m_existing_player = NULL; - m_account_mode = ACCOUNT_OFFLINE; + m_account_mode = ACCOUNT_OFFLINE; + m_parent_screen = NULL; } // RegisterScreen // ----------------------------------------------------------------------------- diff --git a/src/states_screens/register_screen.hpp b/src/states_screens/register_screen.hpp index 1ba2d770c..6c73b7acc 100644 --- a/src/states_screens/register_screen.hpp +++ b/src/states_screens/register_screen.hpp @@ -78,7 +78,7 @@ public: virtual void loadedFromFile() OVERRIDE {}; virtual void onUpdate(float dt) OVERRIDE; virtual bool onEscapePressed() OVERRIDE; - virtual void onDialogClose() OVERRIDE; + virtual void onDialogClose() OVERRIDE; virtual void onFocusChanged(GUIEngine::Widget *previous, GUIEngine::Widget *focus, int playerID); From 6e8f8a15bd78eb944bf84f597c36a8d1a2cc576e Mon Sep 17 00:00:00 2001 From: Flakebi Date: Wed, 4 Feb 2015 16:45:10 +0100 Subject: [PATCH 050/117] Sort resolutions by size --- .../widgets/dynamic_ribbon_widget.cpp | 1 + .../widgets/dynamic_ribbon_widget.hpp | 9 +++++++++ src/states_screens/options_screen_video.cpp | 20 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/guiengine/widgets/dynamic_ribbon_widget.cpp b/src/guiengine/widgets/dynamic_ribbon_widget.cpp index 23224fa59..f233a5d46 100644 --- a/src/guiengine/widgets/dynamic_ribbon_widget.cpp +++ b/src/guiengine/widgets/dynamic_ribbon_widget.cpp @@ -518,6 +518,7 @@ void DynamicRibbonWidget::clearItems() m_scroll_offset = 0; m_max_label_width = 0; } + // ----------------------------------------------------------------------------- void DynamicRibbonWidget::elementRemoved() { diff --git a/src/guiengine/widgets/dynamic_ribbon_widget.hpp b/src/guiengine/widgets/dynamic_ribbon_widget.hpp index d2e842cf8..520bde76e 100644 --- a/src/guiengine/widgets/dynamic_ribbon_widget.hpp +++ b/src/guiengine/widgets/dynamic_ribbon_widget.hpp @@ -22,6 +22,8 @@ #include +#include + #include "guiengine/widget.hpp" #include "guiengine/widgets/ribbon_widget.hpp" #include "utils/leak_check.hpp" @@ -237,6 +239,13 @@ namespace GUIEngine 'updateItemDisplay' to update the display. */ void clearItems(); + /** Sort the list of items with a given comparator. */ + template + void sortItems(Compare comp) + { + std::sort(m_items.begin(), m_items.end(), comp); + } + /** * \brief Register a listener to be notified of selection changes within the ribbon. * \note The ribbon takes ownership of this listener and will delete it. diff --git a/src/states_screens/options_screen_video.cpp b/src/states_screens/options_screen_video.cpp index e875daa1c..26b9308a9 100644 --- a/src/states_screens/options_screen_video.cpp +++ b/src/states_screens/options_screen_video.cpp @@ -126,6 +126,23 @@ void OptionsScreenVideo::loadedFromFile() // ---------------------------------------------------------------------------- +struct SortResolutions +{ + bool operator()(GUIEngine::ItemDescription i1, GUIEngine::ItemDescription i2) + { + int w1 = -1, w2 = -1, h1 = -1, h2 = -1; + if (sscanf(i1.m_code_name.c_str(), "%ix%i", &w1, &h1) != 2 || w1 == -1 || h1 == -1 || + sscanf(i2.m_code_name.c_str(), "%ix%i", &w2, &h2) != 2 || w2 == -1 || h2 == -1) + { + Log::error("OptionsScreenVideo", "Failed to decode resolution %s or %s", + i1.m_code_name.c_str(), i2.m_code_name.c_str()); + return false; + } + return w1 < w2 || (w1 == w2 && h1 < h2); + } +} sortResolutions; +// ---------------------------------------------------------------------------- + void OptionsScreenVideo::init() { Screen::init(); @@ -307,6 +324,9 @@ void OptionsScreenVideo::init() res->addItem(L"1024\u00D7768", "1024x768", "/gui/screen43.png"); } + // Sort resolutions by size + res->sortItems(sortResolutions); + } // end if not inited res->updateItemDisplay(); From 92f78aae299e93483688ea5db062845f3663218f Mon Sep 17 00:00:00 2001 From: Flakebi Date: Fri, 13 Feb 2015 11:46:36 +0100 Subject: [PATCH 051/117] Improve sorting resolutions --- src/states_screens/options_screen_video.cpp | 201 ++++++++++---------- 1 file changed, 101 insertions(+), 100 deletions(-) diff --git a/src/states_screens/options_screen_video.cpp b/src/states_screens/options_screen_video.cpp index 26b9308a9..958506182 100644 --- a/src/states_screens/options_screen_video.cpp +++ b/src/states_screens/options_screen_video.cpp @@ -103,6 +103,33 @@ static GFXPreset GFX_PRESETS[] = static const int GFX_LEVEL_AMOUNT = 5; +struct Resolution +{ + int width, height; + + Resolution() + { + width = 0; + height = 0; + } + + Resolution(int w, int h) + { + width = w; + height = h; + } + + bool operator< (Resolution r) const + { + return width < r.width || (width == r.width && height < r.height); + } + + float getRatio() const + { + return (float) width / height; + } +}; + // ---------------------------------------------------------------------------- OptionsScreenVideo::OptionsScreenVideo() : Screen("options_video.stkgui") @@ -126,23 +153,6 @@ void OptionsScreenVideo::loadedFromFile() // ---------------------------------------------------------------------------- -struct SortResolutions -{ - bool operator()(GUIEngine::ItemDescription i1, GUIEngine::ItemDescription i2) - { - int w1 = -1, w2 = -1, h1 = -1, h2 = -1; - if (sscanf(i1.m_code_name.c_str(), "%ix%i", &w1, &h1) != 2 || w1 == -1 || h1 == -1 || - sscanf(i2.m_code_name.c_str(), "%ix%i", &w2, &h2) != 2 || w2 == -1 || h2 == -1) - { - Log::error("OptionsScreenVideo", "Failed to decode resolution %s or %s", - i1.m_code_name.c_str(), i2.m_code_name.c_str()); - return false; - } - return w1 < w2 || (w1 == w2 && h1 < h2); - } -} sortResolutions; -// ---------------------------------------------------------------------------- - void OptionsScreenVideo::init() { Screen::init(); @@ -193,6 +203,9 @@ void OptionsScreenVideo::init() irr_driver->getVideoModes(); const int amount = (int)modes.size(); + std::vector resolutions; + Resolution r; + bool found_config_res = false; // for some odd reason, irrlicht sometimes fails to report the good @@ -207,40 +220,95 @@ void OptionsScreenVideo::init() for (int n=0; n::iterator it = resolutions.begin(); + it != resolutions.end(); it++) + { + const float ratio = it->getRatio(); char name[32]; - sprintf( name, "%ix%i", w, h ); + sprintf(name, "%ix%i", it->width, it->height); core::stringw label; - label += w; + label += it->width; label += L"\u00D7"; - label += h; + label += it->height; #define ABOUT_EQUAL(a , b) (fabsf( a - b ) < 0.01) @@ -259,74 +327,7 @@ void OptionsScreenVideo::init() else res->addItem(label, name, "/gui/screen_other.png"); #undef ABOUT_EQUAL - } // next resolution - - if (!found_config_res) - { - const int w = UserConfigParams::m_width; - const int h = UserConfigParams::m_height; - const float ratio = (float)w / h; - - if (w == 800 && h == 600) - { -#ifdef DEBUG - found_800_600 = true; -#endif - } - else if (w == 1024 && h == 640) - { - found_1024_640 = true; - } - else if (w == 1024 && h == 768) - { - found_1024_768 = true; - } - - char name[32]; - sprintf( name, "%ix%i", w, h ); - - core::stringw label; - label += w; - label += L"\u00D7"; - label += h; - -#define ABOUT_EQUAL(a , b) (fabsf( a - b ) < 0.01) - - if (ABOUT_EQUAL( ratio, (5.0f/4.0f) )) - res->addItem(label, name, "/gui/screen54.png"); - else if (ABOUT_EQUAL( ratio, (4.0f/3.0f) )) - res->addItem(label, name, "/gui/screen43.png"); - else if (ABOUT_EQUAL( ratio, (16.0f/10.0f) )) - res->addItem(label, name, "/gui/screen1610.png"); - else if (ABOUT_EQUAL( ratio, (5.0f/3.0f) )) - res->addItem(label, name, "/gui/screen53.png"); - else if (ABOUT_EQUAL( ratio, (3.0f/2.0f) )) - res->addItem(label, name, "/gui/screen32.png"); - else if (ABOUT_EQUAL( ratio, (16.0f/9.0f) )) - res->addItem(label, name, "/gui/screen169.png"); - else - res->addItem(label, name, "/gui/screen_other.png"); -#undef ABOUT_EQUAL - } - -#ifdef DEBUG - if (!found_800_600) - { - res->addItem(L"800\u00D7600", "800x600", "/gui/screen43.png"); - } -#endif - if (!found_1024_640) - { - res->addItem(L"1024\u00D7640", "1024x640", "/gui/screen1610.png"); - } - if (!found_1024_768) - { - res->addItem(L"1024\u00D7768", "1024x768", "/gui/screen43.png"); - } - - // Sort resolutions by size - res->sortItems(sortResolutions); - + } // add next resolution } // end if not inited res->updateItemDisplay(); From e6aea6e0b676956d92e62aabfe0a728271020542 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sat, 14 Feb 2015 18:30:23 -0500 Subject: [PATCH 052/117] Improve tools and usage of XML encoding, fixes #1982 --- src/addons/addon.cpp | 10 +++++----- src/audio/music_information.cpp | 4 ++-- src/config/player_manager.cpp | 4 ++-- src/config/player_profile.cpp | 4 ++-- src/io/xml_node.cpp | 11 +++++++++++ src/io/xml_node.hpp | 1 + src/online/server.cpp | 2 +- src/tracks/track.cpp | 2 +- src/utils/string_utils.cpp | 29 ++++++++++++----------------- src/utils/string_utils.hpp | 4 ++-- 10 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/addons/addon.cpp b/src/addons/addon.cpp index 052d532a1..6f17829a9 100644 --- a/src/addons/addon.cpp +++ b/src/addons/addon.cpp @@ -66,7 +66,7 @@ Addon::Addon(const XMLNode &xml) std::string designer; xml.get("name", &name ); - m_name = StringUtils::decodeFromHtmlEntities(name); + m_name = StringUtils::xmlDecode(name); m_dir_name = StringUtils::toLowerCase(name); xml.get("id", &m_dir_name ); m_id = createAddonId(m_dir_name); @@ -83,8 +83,8 @@ Addon::Addon(const XMLNode &xml) xml.get("file", &m_zip_file ); xml.get("description", &description ); - m_description = StringUtils::decodeFromHtmlEntities(description); - m_designer = StringUtils::decodeFromHtmlEntities(designer); + m_description = StringUtils::xmlDecode(description); + m_designer = StringUtils::xmlDecode(designer); // resolve XML entities //m_description = StringUtils::replace(m_description, " ", "\n"); @@ -144,10 +144,10 @@ void Addon::writeXML(std::ofstream *out_stream) // We write m_dir_name as 'id' to stay backwards compatible (*out_stream) << " <" << m_type << " name=\"" - << StringUtils::encodeToHtmlEntities(m_name) + << StringUtils::xmlEncode(m_name) << "\" id=\"" << m_dir_name << "\" designer=\"" - << StringUtils::encodeToHtmlEntities(m_designer) + << StringUtils::xmlEncode(m_designer) << "\" status=\"" << m_status << "\" date=\"" << m_date << "\" installed=\"" diff --git a/src/audio/music_information.cpp b/src/audio/music_information.cpp index 096e85c96..c18a8ba01 100644 --- a/src/audio/music_information.cpp +++ b/src/audio/music_information.cpp @@ -91,9 +91,9 @@ MusicInformation::MusicInformation(const XMLNode *root, // -------------------------- std::string s; root->get("title", &s ); - m_title = StringUtils::decodeFromHtmlEntities(s); + m_title = StringUtils::xmlDecode(s); root->get("composer", &s ); - m_composer = StringUtils::decodeFromHtmlEntities(s); + m_composer = StringUtils::xmlDecode(s); root->get("file", &m_normal_filename); root->get("gain", &m_gain ); root->get("tracks", &m_all_tracks ); diff --git a/src/config/player_manager.cpp b/src/config/player_manager.cpp index c7025ad83..f1b84a52c 100644 --- a/src/config/player_manager.cpp +++ b/src/config/player_manager.cpp @@ -214,7 +214,7 @@ void PlayerManager::load() if(current) { stringw name; - current->get("player", &name); + current->getAndDecode("player", &name); m_current_player = getPlayer(name); } @@ -269,7 +269,7 @@ void PlayerManager::save() if(m_current_player) { players_file << L" getName() << L"\"/>\n"; + << StringUtils::xmlEncode(m_current_player->getName()) << L"\"/>\n"; } // Save all non-guest players diff --git a/src/config/player_profile.cpp b/src/config/player_profile.cpp index dca58f8ae..e5df0c1ba 100644 --- a/src/config/player_profile.cpp +++ b/src/config/player_profile.cpp @@ -78,7 +78,7 @@ PlayerProfile::PlayerProfile(const XMLNode* node) m_achievements_status = NULL; m_icon_filename = ""; - node->get("name", &m_local_name ); + node->getAndDecode("name", &m_local_name); node->get("guest", &m_is_guest_account ); node->get("use-frequency", &m_use_frequency ); node->get("unique-id", &m_unique_id ); @@ -196,7 +196,7 @@ const std::string PlayerProfile::getIconFilename() const */ void PlayerProfile::save(UTFWriter &out) { - out << L" ::const_iterator o; + o = m_attributes.find(attribute); + if (o == m_attributes.end()) return 0; + std::string raw_value = core::stringc(o->second).c_str(); + *value = StringUtils::xmlDecode(raw_value); + return 1; +} // get +// ---------------------------------------------------------------------------- int XMLNode::get(const std::string &attribute, core::vector2df *value) const { std::string s = ""; diff --git a/src/io/xml_node.hpp b/src/io/xml_node.hpp index fb1bbaf47..675e68b0c 100644 --- a/src/io/xml_node.hpp +++ b/src/io/xml_node.hpp @@ -73,6 +73,7 @@ public: unsigned int getNumNodes() const {return (unsigned int) m_nodes.size(); } int get(const std::string &attribute, std::string *value) const; int get(const std::string &attribute, core::stringw *value) const; + int getAndDecode(const std::string &attribute, core::stringw *value) const; int get(const std::string &attribute, int32_t *value) const; int get(const std::string &attribute, uint16_t *value) const; int get(const std::string &attribute, uint32_t *value) const; diff --git a/src/online/server.cpp b/src/online/server.cpp index db72dad4c..f716da58a 100644 --- a/src/online/server.cpp +++ b/src/online/server.cpp @@ -39,7 +39,7 @@ namespace Online m_max_players = 0; xml.get("name", &m_lower_case_name); - m_name = StringUtils::decodeFromHtmlEntities(m_lower_case_name); + m_name = StringUtils::xmlDecode(m_lower_case_name); m_lower_case_name = StringUtils::toLowerCase(m_lower_case_name); xml.get("id", &m_server_id); diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index ed96aa286..36f567a00 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -480,7 +480,7 @@ void Track::loadTrackInfo() std::string designer; root->get("designer", &designer); - m_designer = StringUtils::decodeFromHtmlEntities(designer); + m_designer = StringUtils::xmlDecode(designer); root->get("version", &m_version); std::vector filenames; diff --git a/src/utils/string_utils.cpp b/src/utils/string_utils.cpp index 8d1966308..3aa6f2167 100644 --- a/src/utils/string_utils.cpp +++ b/src/utils/string_utils.cpp @@ -574,11 +574,11 @@ namespace StringUtils } // ------------------------------------------------------------------------ - /** Converts ASCII text with HTML entities (e.g. &xE9;) to unicode strings + /** Converts ASCII text with XML entities (e.g. &x00;) to unicode strings * \param input The input string which should be decoded. * \return A irrlicht wide string with unicode characters. */ - irr::core::stringw decodeFromHtmlEntities(const std::string& input) + irr::core::stringw xmlDecode(const std::string& input) { irr::core::stringw output; std::string entity; @@ -659,35 +659,30 @@ namespace StringUtils } return output; - } // decodeFromHtmlEntities + } // xmlDecode // ------------------------------------------------------------------------ - /** Converts a unicode string to plain ASCII using html-like & codes. + /** Converts a unicode string to plain ASCII using XML entites (e.g. &x00;) * \param s The input string which should be encoded. * \return A std:;string with ASCII characters. */ - std::string encodeToHtmlEntities(const irr::core::stringw &s) + std::string xmlEncode(const irr::core::stringw &s) { std::ostringstream output; for(unsigned int i=0; i= 128 || s[i] == '&' || s[i] == '<' || s[i] == '>' || s[i] == '\"') + { + output << "&#x" << std::hex << std::uppercase << s[i] << ";"; + } else { - if(s[i]<128) - { - irr::c8 c=(char)(s[i]); - output< splitPath(const std::string& path); std::string replace(const std::string& other, const std::string& from, const std::string& to); - irr::core::stringw decodeFromHtmlEntities(const std::string& input); + irr::core::stringw xmlDecode(const std::string& input); - std::string encodeToHtmlEntities(const irr::core::stringw &output); + std::string xmlEncode(const irr::core::stringw &output); // ------------------------------------------------------------------------ template From 975aa7628b097b8bb8d9d5f70145e56ab404ee83 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sat, 14 Feb 2015 18:36:03 -0500 Subject: [PATCH 053/117] Fix #1981 --- src/states_screens/online_user_search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/states_screens/online_user_search.cpp b/src/states_screens/online_user_search.cpp index 099de902b..872f7fcb3 100644 --- a/src/states_screens/online_user_search.cpp +++ b/src/states_screens/online_user_search.cpp @@ -227,7 +227,8 @@ void OnlineUserSearch::eventCallback(GUIEngine::Widget* widget, else if (name == m_user_list_widget->m_properties[GUIEngine::PROP_ID]) { m_selected_index = m_user_list_widget->getSelectionID(); - new UserInfoDialog(m_users[m_selected_index]); + if (m_selected_index != -1) + new UserInfoDialog(m_users[m_selected_index]); } else if (name == m_search_button_widget->m_properties[GUIEngine::PROP_ID]) { From 1a94788d2d149471a844038c71d8da5bdb6e6da8 Mon Sep 17 00:00:00 2001 From: hiker Date: Sun, 15 Feb 2015 21:32:06 +1100 Subject: [PATCH 054/117] Tried VERY crude work-around for #1938 (crash when exiting a race on osx). --- lib/irrlicht/include/SMaterialLayer.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/irrlicht/include/SMaterialLayer.h b/lib/irrlicht/include/SMaterialLayer.h index 068c03094..fd2397f60 100644 --- a/lib/irrlicht/include/SMaterialLayer.h +++ b/lib/irrlicht/include/SMaterialLayer.h @@ -72,8 +72,15 @@ namespace video //! Destructor ~SMaterialLayer() { - MatrixAllocator.destruct(TextureMatrix); - MatrixAllocator.deallocate(TextureMatrix); +#ifdef __APPLE__ + // Avoid a crash on osx, where one of the ReadOnlyMaterial + // Texture matrices is -1 - for atm unknown reasons - see #1938 + if(TextureMatrix != (core::matrix4*)0xffffffff) +#endif + { + MatrixAllocator.destruct(TextureMatrix); + MatrixAllocator.deallocate(TextureMatrix); + } } //! Assignment operator @@ -219,6 +226,7 @@ namespace video //! Texture Matrix /** Do not access this element directly as the internal ressource management has to cope with Null pointers etc. */ +public: core::matrix4* TextureMatrix; }; From aae307c8b603071de9d553907c8237cc1c733d7e Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 16 Feb 2015 09:35:49 +1100 Subject: [PATCH 055/117] Fixed #1985 (improve message for per-player handicaps). --- data/gui/options_ui.stkgui | 2 +- src/guiengine/widgets/player_kart_widget.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data/gui/options_ui.stkgui b/data/gui/options_ui.stkgui index 1e7273744..2c654be7b 100644 --- a/data/gui/options_ui.stkgui +++ b/data/gui/options_ui.stkgui @@ -58,7 +58,7 @@
-
diff --git a/src/guiengine/widgets/player_kart_widget.cpp b/src/guiengine/widgets/player_kart_widget.cpp index f603b859e..e9218761f 100644 --- a/src/guiengine/widgets/player_kart_widget.cpp +++ b/src/guiengine/widgets/player_kart_widget.cpp @@ -355,7 +355,8 @@ void PlayerKartWidget::add() m_player_ident_spinner->addLabel(label); if (UserConfigParams::m_per_player_difficulty) { - // The second player is the same, but with handicap + // I18N: 'handicapped' indicates that per-player handicaps are + // activated for this kart (i.e. it will drive slower) label = _("%s (handicapped)", label); m_player_ident_spinner->addLabel(label); } From 12e583dde0de240687dbbfc97cc1ef3aa7ca7a7f Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 15 Feb 2015 20:45:52 -0500 Subject: [PATCH 056/117] Add scripts to translate from Transifex --- data/po/.gitignore | 2 ++ data/po/gen-mo.sh | 31 ------------------------------- data/po/pull_from_transifex.sh | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 31 deletions(-) create mode 100644 data/po/.gitignore delete mode 100755 data/po/gen-mo.sh create mode 100644 data/po/pull_from_transifex.sh diff --git a/data/po/.gitignore b/data/po/.gitignore new file mode 100644 index 000000000..55f1e5080 --- /dev/null +++ b/data/po/.gitignore @@ -0,0 +1,2 @@ +transifex +tx.exe \ No newline at end of file diff --git a/data/po/gen-mo.sh b/data/po/gen-mo.sh deleted file mode 100755 index 1295922b7..000000000 --- a/data/po/gen-mo.sh +++ /dev/null @@ -1,31 +0,0 @@ -msgfmt ca.po -o ca/LC_MESSAGES/supertuxkart.mo -msgfmt cs.po -o cs/LC_MESSAGES/supertuxkart.mo -msgfmt da.po -o da/LC_MESSAGES/supertuxkart.mo -msgfmt de.po -o de/LC_MESSAGES/supertuxkart.mo -msgfmt es.po -o es/LC_MESSAGES/supertuxkart.mo -msgfmt fi.po -o fi/LC_MESSAGES/supertuxkart.mo -msgfmt fr_CA.po -o fr_CA/LC_MESSAGES/supertuxkart.mo -msgfmt fr.po -o fr/LC_MESSAGES/supertuxkart.mo -msgfmt ga.po -o ga/LC_MESSAGES/supertuxkart.mo -msgfmt gl.po -o gl/LC_MESSAGES/supertuxkart.mo -msgfmt it.po -o it/LC_MESSAGES/supertuxkart.mo -msgfmt nb.po -o nb/LC_MESSAGES/supertuxkart.mo -msgfmt nl.po -o nl/LC_MESSAGES/supertuxkart.mo -msgfmt pl.po -o pl/LC_MESSAGES/supertuxkart.mo -msgfmt pt_BR.po -o pt_BR/LC_MESSAGES/supertuxkart.mo -msgfmt ro.po -o ro/LC_MESSAGES/supertuxkart.mo -msgfmt ru.po -o ru/LC_MESSAGES/supertuxkart.mo -msgfmt sl.po -o sl/LC_MESSAGES/supertuxkart.mo -msgfmt sv.po -o sv/LC_MESSAGES/supertuxkart.mo -msgfmt uk.po -o uk/LC_MESSAGES/supertuxkart.mo -msgfmt zh_CN.po -o zh_CN/LC_MESSAGES/supertuxkart.mo -msgfmt he.po -o he/LC_MESSAGES/supertuxkart.mo -msgfmt ko.po -o ko/LC_MESSAGES/supertuxkart.mo -msgfmt sk.po -o sk/LC_MESSAGES/supertuxkart.mo -msgfmt zh_TW.po -o zh_TW/LC_MESSAGES/supertuxkart.mo -msgfmt tr.po -o tr/LC_MESSAGES/supertuxkart.mo -msgfmt pt.po -o pt/LC_MESSAGES/supertuxkart.mo -msgfmt eu.po -o eu/LC_MESSAGES/supertuxkart.mo -msgfmt is.po -o is/LC_MESSAGES/supertuxkart.mo -msgfmt lt.po -o lt/LC_MESSAGES/supertuxkart.mo -msgfmt hr.po -o hr/LC_MESSAGES/supertuxkart.mo diff --git a/data/po/pull_from_transifex.sh b/data/po/pull_from_transifex.sh new file mode 100644 index 000000000..e198f7bb5 --- /dev/null +++ b/data/po/pull_from_transifex.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# Run this script from the root directory to get the latest translations from transifex : +# ./data/po/pull_from_transifex.sh + +cd ./data/po +export PATH=$PATH:`pwd` + +if [ -d "transifex/translations/supertuxkart.supertuxkartpot" ]; then + cd transifex +else + echo "==== Performing initial checkout ====" + mkdir -p transifex + cd transifex + tx init + tx set --auto-remote http://www.transifex.net/projects/p/supertuxkart/ +fi + +echo "==== Pulling all translations ====" +tx pull --all + +echo "==== Copying files ====" +ls ./translations/supertuxkart.supertuxkartpot/*.po +cp -R ./translations/supertuxkart.supertuxkartpot/*.po .. + +echo "==== Done ====" \ No newline at end of file From 93c5d7428f3f1e4f5172d2b6aa77e1bbe375de82 Mon Sep 17 00:00:00 2001 From: deve Date: Mon, 16 Feb 2015 09:32:56 +0100 Subject: [PATCH 057/117] Update .gitignore file. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a98ad5a4e..f2da11a90 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ stk-editor/ .config/ supertuxkart-64 +make*.bat data/editor data/karts @@ -16,6 +17,7 @@ data/music data/sfx data/textures data/tracks +data/wip-tracks data/.svn # Ignore doxygen output From 93fa0754712106e6e89683da57a812e51b21c4d6 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 17 Feb 2015 00:14:26 +1100 Subject: [PATCH 058/117] Hopefully proper fix for #1938 (crash on osx when exiting race). --- lib/irrlicht/include/SMaterialLayer.h | 7 ------- src/karts/kart_model.cpp | 11 ++++++++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/irrlicht/include/SMaterialLayer.h b/lib/irrlicht/include/SMaterialLayer.h index fd2397f60..a4d09988b 100644 --- a/lib/irrlicht/include/SMaterialLayer.h +++ b/lib/irrlicht/include/SMaterialLayer.h @@ -72,15 +72,8 @@ namespace video //! Destructor ~SMaterialLayer() { -#ifdef __APPLE__ - // Avoid a crash on osx, where one of the ReadOnlyMaterial - // Texture matrices is -1 - for atm unknown reasons - see #1938 - if(TextureMatrix != (core::matrix4*)0xffffffff) -#endif - { MatrixAllocator.destruct(TextureMatrix); MatrixAllocator.deallocate(TextureMatrix); - } } //! Assignment operator diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 1928a66ab..7637c1b92 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -658,8 +658,11 @@ void KartModel::reset() // Stop any animations currently being played. setAnimation(KartModel::AF_DEFAULT); - // Don't force any LOD - ((LODNode*)m_kart->getNode())->forceLevelOfDetail(-1); + + // Don't force any LOD. Non-animated karts are not LOD nodes. + LODNode *lod = dynamic_cast(m_kart->getNode()); + if (lod) + lod->forceLevelOfDetail(-1); } // reset // ---------------------------------------------------------------------------- @@ -670,7 +673,9 @@ void KartModel::reset() void KartModel::finishedRace() { // Force the animated model, independent of actual camera distance. - ((LODNode*)m_kart->getNode())->forceLevelOfDetail(0); + LODNode *lod = dynamic_cast(m_kart->getNode()); + if (lod) + lod->forceLevelOfDetail(0); } // finishedRace // ---------------------------------------------------------------------------- From f9bb17384cf36b39e0c6f6d54150b9751f0b4a5f Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 17 Feb 2015 08:14:31 +1100 Subject: [PATCH 059/117] Try to balande the different kart physics better. --- data/stk_config.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 32753f9eb..1e88c4151 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -686,8 +686,8 @@ time-full-steer ="0:0.15 0.5:0.15 0.5:0.25 1.0:0.25" time-reset-steer="0.1"/> - + @@ -712,7 +712,7 @@ time-full-steer ="0:0.17 0.5:0.17 0.5:0.28 1.0:0.28" time-reset-steer="0.1"/> - @@ -741,7 +741,7 @@ time-full-steer ="0:0.23 0.5:0.23 0.5:0.41 1.0:0.41" time-reset-steer="0.1"/> - From 198d6470d71870f0ab0f16aba97849cf69e3dd5b Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 17 Feb 2015 08:33:32 +1100 Subject: [PATCH 060/117] Let medium kart accelerate a bit less. --- data/stk_config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 1e88c4151..80b558770 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -712,7 +712,7 @@ time-full-steer ="0:0.17 0.5:0.17 0.5:0.28 1.0:0.28" time-reset-steer="0.1"/> - From ada5a204458f8797363605b32ff0cd5e436209ba Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 17 Feb 2015 17:14:53 +1100 Subject: [PATCH 061/117] Added some (commented out) debug lines that are useful when comparing different kart physics. --- src/karts/kart.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index f7c567b70..a730252d1 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1093,6 +1093,19 @@ void Kart::eliminate() */ void Kart::update(float dt) { +#ifdef DEBUG_TO_COMPARE_KART_PHYSICS + // This information is useful when comparing kart physics, e.g. to + // see top speed, acceleration (i.e. time to top speed) etc. + Log::verbose("physics", "%s t %f xyz %f %f %f %f v %f %f %f %f maxv %f", + getIdent().c_str(), + World::getWorld()->getTime(), + getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(), + getXYZ().length(), + getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(), + getVelocity().length(), + m_max_speed->getCurrentMaxSpeed()); +#endif + if ( UserConfigParams::m_graphical_effects ) { // update star effect (call will do nothing if stars are not activated) From 7f90d151c466aca38ededdb3cdbef1164dd577ba Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 17 Feb 2015 17:15:25 +1100 Subject: [PATCH 062/117] Use "Local Name" to fix #1977. --- data/gui/online/register.stkgui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/gui/online/register.stkgui b/data/gui/online/register.stkgui index 3a64972c6..fb6803e7f 100644 --- a/data/gui/online/register.stkgui +++ b/data/gui/online/register.stkgui @@ -18,7 +18,7 @@
From 0ef21814486b9ccf1d989ea16ca8335084d5de02 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Tue, 17 Feb 2015 19:07:56 -0500 Subject: [PATCH 063/117] Minor update to git ignore file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f2da11a90..1ba98ce23 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ bld*/ build*/ cmake_build/ +cmake_build*/ dependencies/ CMakeFiles/ stk-editor/ From 0652780040fad7f71e969ba96e34689ef31c8bd2 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Tue, 17 Feb 2015 19:13:13 -0500 Subject: [PATCH 064/117] [OSX] brutally exit supertuxkart when pressing the window's close button. Fixes #1220. --- lib/irrlicht/source/Irrlicht/MacOSX/AppDelegate.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/irrlicht/source/Irrlicht/MacOSX/AppDelegate.mm b/lib/irrlicht/source/Irrlicht/MacOSX/AppDelegate.mm index 78a3b67ee..35915d565 100644 --- a/lib/irrlicht/source/Irrlicht/MacOSX/AppDelegate.mm +++ b/lib/irrlicht/source/Irrlicht/MacOSX/AppDelegate.mm @@ -50,6 +50,7 @@ - (void)windowWillClose:(id)sender { _quit = TRUE; + exit(0); // FIXME: hackish workaround, irrlicht crashes if we attempt clean exit } - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize From bb682ca5fb63f88432d7aafbe38f896c4bdc1d80 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Tue, 17 Feb 2015 19:44:20 -0500 Subject: [PATCH 065/117] Apply workaround to fix #1869 --- src/graphics/particle_emitter.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/graphics/particle_emitter.cpp b/src/graphics/particle_emitter.cpp index 5ba8d0342..4a5e9f126 100644 --- a/src/graphics/particle_emitter.cpp +++ b/src/graphics/particle_emitter.cpp @@ -510,6 +510,14 @@ void ParticleEmitter::setParticleType(const ParticleKind* type) m_node->setMaterialTexture(0, material->getTexture()); mat0.ZWriteEnable = !material->isTransparent(); // disable z-buffer writes if material is transparent + + // fallback for old render engine + if (material->getShaderType() == Material::SHADERTYPE_ADDITIVE) + mat0.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; + else if (material->getShaderType() == Material::SHADERTYPE_ALPHA_BLEND) + mat0.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + else if (material->getShaderType() == Material::SHADERTYPE_ALPHA_TEST) + mat0.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; } else { From 5f9e4086aff568d222309c79e8b1ec157624d70f Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 19 Feb 2015 09:24:23 +1100 Subject: [PATCH 066/117] Fixed #1476 (well, better a work around intel bugs). --- data/graphical_restrictions.xml | 1 + src/graphics/graphics_restrictions.cpp | 3 ++- src/graphics/graphics_restrictions.hpp | 1 + src/graphics/render.cpp | 13 +++++++++---- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/data/graphical_restrictions.xml b/data/graphical_restrictions.xml index 625858991..1890b1a8f 100644 --- a/data/graphical_restrictions.xml +++ b/data/graphical_restrictions.xml @@ -2,6 +2,7 @@ + diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index faa24438d..24fb7dae2 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -57,7 +57,8 @@ namespace GraphicsRestrictions "AMDVertexShaderLayer", "DriverRecentEnough", "HighDefinitionTextures", - "AdvancedPipeline" + "AdvancedPipeline", + "FramebufferSRGBWorking", }; } // namespace Private using namespace Private; diff --git a/src/graphics/graphics_restrictions.hpp b/src/graphics/graphics_restrictions.hpp index 171fa10fe..2636e1c64 100644 --- a/src/graphics/graphics_restrictions.hpp +++ b/src/graphics/graphics_restrictions.hpp @@ -52,6 +52,7 @@ namespace GraphicsRestrictions GR_DRIVER_RECENT_ENOUGH, GR_HIGHDEFINITION_TEXTURES, GR_ADVANCED_PIPELINE, + GR_FRAMEBUFFER_SRGB_WORKING, GR_COUNT /** MUST be last entry. */ } ; diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 40e3986b9..5acdfcaba 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -22,6 +22,7 @@ #include "graphics/callbacks.hpp" #include "central_settings.hpp" #include "graphics/glwrap.hpp" +#include "graphics/graphics_restrictions.hpp" #include "graphics/lod_node.hpp" #include "graphics/post_processing.hpp" #include "graphics/referee.hpp" @@ -380,10 +381,14 @@ void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned po } else { - // We need a cleared depth buffer for some effect (eg particles depth blending) - m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind(); - glClear(GL_DEPTH_BUFFER_BIT); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + // We need a cleared depth buffer for some effect (eg particles depth blending) + if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_WORKING)) + glDisable(GL_FRAMEBUFFER_SRGB); + m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind(); + glClear(GL_DEPTH_BUFFER_BIT); + if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_WORKING)) + glEnable(GL_FRAMEBUFFER_SRGB); + glBindFramebuffer(GL_FRAMEBUFFER, 0); } PROFILER_POP_CPU_MARKER(); From 1a861c742c3070673f6273fcaed51d29f22b7e3f Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Wed, 18 Feb 2015 23:42:42 +0100 Subject: [PATCH 067/117] Add a GR for GI in central_settings --- src/graphics/central_settings.cpp | 8 +++++++- src/graphics/central_settings.hpp | 1 + src/graphics/graphics_restrictions.cpp | 1 + src/graphics/graphics_restrictions.hpp | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/graphics/central_settings.cpp b/src/graphics/central_settings.cpp index 0cecaaae5..864cd710d 100644 --- a/src/graphics/central_settings.cpp +++ b/src/graphics/central_settings.cpp @@ -27,6 +27,7 @@ void CentralVideoSettings::init() hasTextureCompression = false; hasUBO = false; hasGS = false; + m_GI_has_artifact = false; m_need_rh_workaround = false; m_need_srgb_workaround = false; @@ -140,6 +141,11 @@ void CentralVideoSettings::init() UserConfigParams::m_high_definition_textures = 0x00; } + if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_GI)) + { + m_GI_has_artifact = true; + } + // Specific disablement if (strstr((const char *)glGetString(GL_VENDOR), "NVIDIA") != NULL) { @@ -263,7 +269,7 @@ bool CentralVideoSettings::supportsShadows() const bool CentralVideoSettings::supportsGlobalIllumination() const { - return isARBGeometryShader4Usable() && isARBUniformBufferObjectUsable(); + return isARBGeometryShader4Usable() && isARBUniformBufferObjectUsable() && !m_GI_has_artifact; } bool CentralVideoSettings::supportsIndirectInstancingRendering() const diff --git a/src/graphics/central_settings.hpp b/src/graphics/central_settings.hpp index 980c0a666..b7b774541 100644 --- a/src/graphics/central_settings.hpp +++ b/src/graphics/central_settings.hpp @@ -26,6 +26,7 @@ private: bool m_need_rh_workaround; bool m_need_srgb_workaround; + bool m_GI_has_artifact; public: void init(); bool isGLSL() const; diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index 24fb7dae2..e088d935b 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -59,6 +59,7 @@ namespace GraphicsRestrictions "HighDefinitionTextures", "AdvancedPipeline", "FramebufferSRGBWorking", + "GI", }; } // namespace Private using namespace Private; diff --git a/src/graphics/graphics_restrictions.hpp b/src/graphics/graphics_restrictions.hpp index 2636e1c64..908a0e03f 100644 --- a/src/graphics/graphics_restrictions.hpp +++ b/src/graphics/graphics_restrictions.hpp @@ -53,6 +53,7 @@ namespace GraphicsRestrictions GR_HIGHDEFINITION_TEXTURES, GR_ADVANCED_PIPELINE, GR_FRAMEBUFFER_SRGB_WORKING, + GR_GI, GR_COUNT /** MUST be last entry. */ } ; From a74c98ec76749db3924310edefb61bee4f8f59ca Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Wed, 18 Feb 2015 23:45:57 +0100 Subject: [PATCH 068/117] Fix for GR GI --- src/graphics/central_settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/central_settings.cpp b/src/graphics/central_settings.cpp index 864cd710d..3b793a3f0 100644 --- a/src/graphics/central_settings.cpp +++ b/src/graphics/central_settings.cpp @@ -141,7 +141,7 @@ void CentralVideoSettings::init() UserConfigParams::m_high_definition_textures = 0x00; } - if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_GI)) + if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_GI)) { m_GI_has_artifact = true; } From 96413801716b4db8d2bd3dd21707019306acc48b Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Thu, 19 Feb 2015 00:44:21 +0100 Subject: [PATCH 069/117] Fix billboard transparency --- data/shaders/billboard.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/shaders/billboard.frag b/data/shaders/billboard.frag index 16b3f742b..7b7af00a8 100644 --- a/data/shaders/billboard.frag +++ b/data/shaders/billboard.frag @@ -6,5 +6,5 @@ out vec4 FragColor; void main(void) { vec4 color = texture(tex, uv); - FragColor = vec4(color.rgb, color.a); + FragColor = vec4(color.a * color.rgb, color.a); } From 626ae83f433313510de7f1b934f29a3e98dda956 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 19 Feb 2015 16:21:00 +1100 Subject: [PATCH 070/117] Added unit test for #1793. --- src/graphics/graphics_restrictions.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index e088d935b..d656267c1 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -413,6 +413,11 @@ void unitTesting() assert(Version("3.3 NVIDIA-10.0.19 310.90.10.05b1", "NVIDIA GeForce GTX 680MX OpenGL Engine") == Version("310.90.10.5") ); + + assert(Version("4.1 NVIDIA-10.0.43 310.41.05f01", + "NVIDIA GeForce GTX 780M OpenGL Engine") + == Version("310.41.05")); + assert(Version("3.1 (Core Profile) Mesa 10.3.0", "Mesa DRI Mobile Intel\u00ae GM45 Express Chipset") == Version("10.3.0") ); From f56fc91d42db916c52dd15c6f16fe57082a65842 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 19 Feb 2015 16:21:37 +1100 Subject: [PATCH 071/117] Fix #1793 by disabling global illumination on osx with intel graphics cards. --- data/graphical_restrictions.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/graphical_restrictions.xml b/data/graphical_restrictions.xml index 1890b1a8f..1e1a0b5ca 100644 --- a/data/graphical_restrictions.xml +++ b/data/graphical_restrictions.xml @@ -3,6 +3,7 @@ + From 2e19c3a34e126c584a7d3696af1e6c6ae8f011fb Mon Sep 17 00:00:00 2001 From: Daniel Butum Date: Fri, 20 Feb 2015 11:40:22 +0200 Subject: [PATCH 072/117] Fix font_tool build for Linux --- tools/font_tool/CMakeLists.txt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tools/font_tool/CMakeLists.txt b/tools/font_tool/CMakeLists.txt index 1073b9185..e67e6fbce 100644 --- a/tools/font_tool/CMakeLists.txt +++ b/tools/font_tool/CMakeLists.txt @@ -1,24 +1,33 @@ option(FONT_TOOL "Compile font tool (only useful for developers)" OFF) mark_as_advanced(FONT_TOOL) - if(FONT_TOOL) if(MSVC) add_executable(font_tool CFontTool.cpp main.cpp) target_link_libraries(font_tool stkirrlicht) else() find_package(Freetype) - find_package(X11) find_library(FONTCONFIG_LIBRARY fontconfig) + if(UNIX AND NOT APPLE) + find_package(Xrandr REQUIRED) + find_package(X11 REQUIRED) + if(NOT XRANDR_FOUND) + message(STATUS "XRANDR not found.") + endif() + endif() if(FREETYPE_FOUND) include_directories(${FREETYPE_INCLUDE_DIRS}) add_executable(font_tool CFontTool.cpp main.cpp) + target_link_libraries(font_tool stkirrlicht) target_link_libraries(font_tool ${FREETYPE_LIBRARIES}) - target_link_libraries(font_tool ${X11_Xft_LIB} Xxf86vm) target_link_libraries(font_tool ${OPENGL_LIBRARIES}) target_link_libraries(font_tool ${FONTCONFIG_LIBRARY}) + if(UNIX AND NOT APPLE) + target_link_libraries(font_tool ${XRANDR_LIBRARIES}) + target_link_libraries(font_tool ${X11_Xft_LIB} Xxf86vm) + endif() else() message(STATUS "Freetype was not found, the font tool won't be built (only useful for developers)") endif() From 359582d0e6551ae5a38bd177784137242456d60a Mon Sep 17 00:00:00 2001 From: hiker Date: Sat, 21 Feb 2015 11:19:15 +1100 Subject: [PATCH 073/117] Added support for "Easter Bunny Ears" easter egg. --- src/config/user_config.hpp | 5 ++ src/main.cpp | 100 ++++++++++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index 8de4232bd..0891e60a2 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -606,6 +606,11 @@ namespace UserConfigParams PARAM_DEFAULT( IntUserConfigParam(0, "christmas-mode", &m_graphics_quality, "Christmas hats: 0 use current date, 1 always on, 2 always off") ); + // This saves the actual user preference. + PARAM_PREFIX IntUserConfigParam m_easter_ear_mode + PARAM_DEFAULT(IntUserConfigParam(0, "easter-ear-mode", + &m_graphics_quality, "Easter Bunny Ears: 0 use current date, 1 always on, 2 always off")); + PARAM_PREFIX BoolUserConfigParam m_weather_effects PARAM_DEFAULT( BoolUserConfigParam(true, "weather_gfx", &m_graphics_quality, "Weather effects") ); diff --git a/src/main.cpp b/src/main.cpp index 1d308d2b4..3ee1bdb09 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -206,6 +206,7 @@ static void cleanSuperTuxKart(); static void cleanUserConfig(); +void runUnitTests(); // ============================================================================ // gamepad visualisation screen @@ -388,6 +389,66 @@ void handleXmasMode() if(xmas) kart_properties_manager->setHatMeshName("christmas_hat.b3d"); } // handleXmasMode +// ============================================================================ +/** Determines if Easter Ears should be used + * m_easter_ear_mode (0: use current date, 1: always on, 2: always off). + */ +bool isEasterMode(int day, int month, int year, int before_after_days) +{ + bool ears = false; + switch (UserConfigParams::m_easter_ear_mode) + { + case 0: + { + // Compute Easter date, based on wikipedia formula + // http://en.wikipedia.org/wiki/Computus + int a = year % 19; + int b = year >> 2; + int c = int(floor(b / 25)) + 1; + int d = (c * 3) >> 2; + int e = ((a * 19) - int(floor((c * 8 + 5) / 25)) + d + 15) % 30; + e += (29578 - a - e * 32) >> 10; + e -= ((year % 7) + b - d + e + 2) % 7; + d = e >> 5; + int easter_day = e - d * 31; + int easter_month = d + 3; + + int easter_start_day = easter_day - before_after_days; + int easter_start_month = easter_month; + if (easter_start_day < 1) + { + easter_start_day += 31; // Month is April, going into March + easter_start_month--; + } + int easter_end_day = easter_day + before_after_days; + int easter_end_month = easter_month; + int month_length = easter_end_month == 3 ? 31 : 30; + if (easter_end_day > month_length) + { + easter_end_day -= month_length; + easter_end_month++; + } + return (month > easter_start_month || (month == easter_start_month && day >= easter_start_day)) && + (month < easter_end_month || (month == easter_end_month && day <= easter_end_day)); + break; + } + case 1: return true; break; + default: return false; break; + } // switch m_xmas_mode + +} // isEasterMode(day, month, year, before_after_days) + +// ============================================================================ +/** Wrapper around handleEasterEarMode(day, month, year, before_after_days). + */ +void handleEasterEarMode() +{ + int day, month, year; + StkTime::getDate(&day, &month, &year); + if (isEasterMode(day, month, year, /*before_after_days*/5)) + kart_properties_manager->setHatMeshName("easter_ears.b3d"); +} // handleEasterMode + // ============================================================================ /** This function sets up all data structure for an immediate race start. * It is used when the -N or -R command line options are used. @@ -637,6 +698,8 @@ int handleCmdLinePreliminary() int n; if(CommandLine::has("--xmas", &n)) UserConfigParams::m_xmas_mode = n; + if (CommandLine::has("--easter", &n)) + UserConfigParams::m_easter_ear_mode = n; if(CommandLine::has("--log", &n)) Log::setLogLevel(n); @@ -1206,6 +1269,7 @@ int main(int argc, char *argv[] ) "options_video.png")); kart_properties_manager -> loadAllKarts (); handleXmasMode(); + handleEasterEarMode(); // Needs the kart and track directories to load potential challenges // in those dirs, so it can only be created after reading tracks @@ -1292,7 +1356,7 @@ int main(int argc, char *argv[] ) if(UserConfigParams::m_unit_testing) { - GraphicsRestrictions::unitTesting(); + runUnitTests(); exit(0); } @@ -1554,3 +1618,37 @@ static void cleanUserConfig() if(irr_driver) delete irr_driver; } // cleanUserConfig +//============================================================================= +void runUnitTests() +{ + GraphicsRestrictions::unitTesting(); + // Test easter mode: in 2015 Easter is 5th of April - check with 0 days + // before and after + int saved_easter_mode = UserConfigParams::m_easter_ear_mode; + UserConfigParams::m_easter_ear_mode = 0; // disable always on or off mode + assert( isEasterMode( 5, 4, 2015, 0)); + assert(!isEasterMode( 4, 4, 2015, 0)); + assert(!isEasterMode( 6, 4, 2015, 0)); + + assert( isEasterMode( 1, 4, 2018, 0)); + assert( isEasterMode(27, 3, 2016, 0)); + + // Check days before/after + assert( isEasterMode( 2, 4, 2015, 3)); + assert(!isEasterMode( 1, 4, 2015, 3)); + assert( isEasterMode( 8, 4, 2015, 3)); + assert(!isEasterMode( 9, 4, 2015, 3)); + + // Check if going to previous month is handled correctly: 1/4/2018 + assert( isEasterMode(27, 3, 2018, 5)); + assert(!isEasterMode(26, 3, 2018, 5)); + assert( isEasterMode( 6, 4, 2018, 5)); + assert(!isEasterMode( 7, 4, 2018, 5)); + + // Check if going to previous month is handled correctly: 1/4/2018 + assert( isEasterMode( 1, 4, 2016, 5)); + assert(!isEasterMode( 2, 4, 2016, 5)); + assert( isEasterMode(22, 3, 2016, 5)); + assert(!isEasterMode(21, 3, 2016, 5)); + UserConfigParams::m_easter_ear_mode = saved_easter_mode; +} // unitTesting \ No newline at end of file From a371338c141b2124ad1261077b28c5ff7a508ee2 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Sat, 21 Feb 2015 18:06:27 +0100 Subject: [PATCH 074/117] Fix transparency of fallback characters --- data/shaders/colortexturedquad.frag | 2 +- src/guiengine/scalable_font.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/shaders/colortexturedquad.frag b/data/shaders/colortexturedquad.frag index 7fbabed16..45aa0bf58 100644 --- a/data/shaders/colortexturedquad.frag +++ b/data/shaders/colortexturedquad.frag @@ -7,5 +7,5 @@ out vec4 FragColor; void main() { vec4 res = texture(tex, uv); - FragColor = vec4(res.xyz * col.xyz, res.a); + FragColor = res * col; } diff --git a/src/guiengine/scalable_font.cpp b/src/guiengine/scalable_font.cpp index c9ad661e7..980fcd836 100644 --- a/src/guiengine/scalable_font.cpp +++ b/src/guiengine/scalable_font.cpp @@ -668,9 +668,9 @@ void ScalableFont::doDraw(const core::stringw& text, if (fallback[n]) { // TODO: don't hardcode colors? - static video::SColor orange(color.getAlpha(), 255, 100, 0); - static video::SColor yellow(color.getAlpha(), 255, 220, 15); - video::SColor title_colors[] = {yellow, orange, orange, yellow}; + video::SColor orange(color.getAlpha(), 255, 100, 0); + video::SColor yellow(color.getAlpha(), 255, 220, 15); + video::SColor title_colors[] = {orange, yellow, orange, yellow}; if (charCollector != NULL) { From 0f56a4f6d344d6db8357e614cca0bd04fe93cf2b Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 22 Feb 2015 10:01:46 +0100 Subject: [PATCH 075/117] Fixed compilation with cmake 3.2 --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f4e7c9de..7a153e23d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,6 +184,9 @@ find_package(OpenGL REQUIRED) include_directories(${OPENGL_INCLUDE_DIR}) if(UNIX AND NOT APPLE) + find_package(X11 REQUIRED) + include_directories(${X11_INCLUDE_DIR}) + if(USE_XRANDR) find_package(Xrandr REQUIRED) if(NOT XRANDR_FOUND) @@ -321,6 +324,7 @@ target_link_libraries(supertuxkart ) if(UNIX AND NOT APPLE) + target_link_libraries(supertuxkart ${X11_LIBRARIES}) if(USE_XRANDR) target_link_libraries(supertuxkart ${XRANDR_LIBRARIES}) else() From 13a2f9f5971cd97714b5ba8c868183b9c00a2601 Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Sun, 22 Feb 2015 20:31:14 +0100 Subject: [PATCH 076/117] First part of #1988 fix --- data/shaders/degraded_ibl.frag | 18 ++++++++++++++++++ src/config/user_config.hpp | 4 ++++ src/graphics/post_processing.cpp | 19 +++++++++++++++---- src/graphics/shaders.cpp | 13 +++++++++++++ src/graphics/shaders.hpp | 6 ++++++ 5 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 data/shaders/degraded_ibl.frag diff --git a/data/shaders/degraded_ibl.frag b/data/shaders/degraded_ibl.frag new file mode 100644 index 000000000..06722b5c4 --- /dev/null +++ b/data/shaders/degraded_ibl.frag @@ -0,0 +1,18 @@ +uniform sampler2D ntex; + +out vec4 Diff; +out vec4 Spec; + +vec3 DecodeNormal(vec2 n); +vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix); +vec3 DiffuseIBL(vec3 normal); +vec3 SpecularIBL(vec3 normal, vec3 V, float roughness); + +void main(void) +{ + vec2 uv = gl_FragCoord.xy / screen; + vec3 normal = normalize(DecodeNormal(2. * texture(ntex, uv).xy - 1.)); + + Diff = vec4(0.25 * DiffuseIBL(normal), 1.); + Spec = vec4(0., 0., 0., 1.); +} diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index 0891e60a2..9ef1df30f 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -654,6 +654,10 @@ namespace UserConfigParams PARAM_DEFAULT( IntUserConfigParam(0, "shadows_resoltion", &m_graphics_quality, "Shadow resolution (0 = disabled") ); + PARAM_PREFIX BoolUserConfigParam m_degraded_IBL + PARAM_DEFAULT(BoolUserConfigParam(false, + "Degraded_IBL", &m_graphics_quality, + "Disable specular IBL")); // ---- Misc PARAM_PREFIX BoolUserConfigParam m_cache_overworld diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index 483f4ac0b..f860df6bb 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -220,11 +220,22 @@ void PostProcessing::renderEnvMap(const float *bSHCoeff, const float *gSHCoeff, glBlendEquation(GL_FUNC_ADD); glBlendFunc(GL_ONE, GL_ONE); - glUseProgram(FullScreenShader::IBLShader::getInstance()->Program); - glBindVertexArray(SharedObject::FullScreenQuadVAO); + if (UserConfigParams::m_degraded_IBL) + { + glUseProgram(FullScreenShader::DegradedIBLShader::getInstance()->Program); + glBindVertexArray(SharedObject::FullScreenQuadVAO); - FullScreenShader::IBLShader::getInstance()->SetTextureUnits(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture(), skybox); - FullScreenShader::IBLShader::getInstance()->setUniforms(); + FullScreenShader::DegradedIBLShader::getInstance()->SetTextureUnits(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH)); + FullScreenShader::DegradedIBLShader::getInstance()->setUniforms(); + } + else + { + glUseProgram(FullScreenShader::IBLShader::getInstance()->Program); + glBindVertexArray(SharedObject::FullScreenQuadVAO); + + FullScreenShader::IBLShader::getInstance()->SetTextureUnits(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture(), skybox); + FullScreenShader::IBLShader::getInstance()->setUniforms(); + } glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0); diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index 6cec2fb5b..f4a5168d3 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -1641,6 +1641,19 @@ namespace FullScreenShader AssignSamplerNames(Program, 0, "ntex", 1, "dtex", 2, "probe"); } + DegradedIBLShader::DegradedIBLShader() + { + Program = LoadProgram(OBJECT, + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/decodeNormal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/DiffuseIBL.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/SpecularIBL.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/degraded_ibl.frag").c_str()); + AssignUniforms(); + AssignSamplerNames(Program, 0, "ntex"); + } + ShadowedSunLightShaderPCF::ShadowedSunLightShaderPCF() { Program = LoadProgram(OBJECT, diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index cda475f79..44005f700 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -426,6 +426,12 @@ public: IBLShader(); }; +class DegradedIBLShader : public ShaderHelperSingleton, public TextureRead +{ +public: + DegradedIBLShader(); +}; + class ShadowedSunLightShaderPCF : public ShaderHelperSingleton, public TextureRead { public: From b338a68eb41425fdf20b0874d9b97a7c8cc3873f Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 23 Feb 2015 08:32:16 +1100 Subject: [PATCH 077/117] Fix #1984 (countries in lower case were not when compared with upper case countries). --- src/tinygettext/language.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tinygettext/language.cpp b/src/tinygettext/language.cpp index 5a470bbe9..e6303cd9d 100644 --- a/src/tinygettext/language.cpp +++ b/src/tinygettext/language.cpp @@ -17,6 +17,8 @@ #include "language.hpp" +#include "utils/string_utils.hpp" + #include #include #include @@ -430,7 +432,9 @@ Language::from_env(const std::string& env) if (ln != std::string::npos && ln+1 < env.size()) // _ { - country = env.substr(ln+1, (std::min(dt, at) == std::string::npos) ? std::string::npos : std::min(dt, at) - (ln+1)); + country = env.substr(ln+1, (std::min(dt, at) == std::string::npos) + ? std::string::npos : std::min(dt, at) - (ln+1)); + country = StringUtils::toUpperCase(country); } if (dt != std::string::npos && dt+1 < env.size()) // . From e3c408fc0e1b409b1cfce6287d3017a3311d0b86 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 22 Feb 2015 18:25:59 -0500 Subject: [PATCH 078/117] Implement interface to allow disabling IBL --- data/gui/custom_video_settings.stkgui | 17 ++++++++++++++--- .../dialogs/custom_video_settings.cpp | 6 ++++++ src/states_screens/options_screen_video.cpp | 15 +++++++++------ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/data/gui/custom_video_settings.stkgui b/data/gui/custom_video_settings.stkgui index 2ae3c8eed..35039a75a 100644 --- a/data/gui/custom_video_settings.stkgui +++ b/data/gui/custom_video_settings.stkgui @@ -25,9 +25,20 @@
-
diff --git a/src/states_screens/dialogs/custom_video_settings.cpp b/src/states_screens/dialogs/custom_video_settings.cpp index 8c37e9e47..5a35cfd70 100644 --- a/src/states_screens/dialogs/custom_video_settings.cpp +++ b/src/states_screens/dialogs/custom_video_settings.cpp @@ -92,6 +92,7 @@ void CustomVideoSettingsDialog::beforeAddingWidgets() shadows->setValue(0); getWidget("dynamiclight")->setState(UserConfigParams::m_dynamic_lights); getWidget("lightshaft")->setState(UserConfigParams::m_light_shaft); + getWidget("ibl")->setState(!UserConfigParams::m_degraded_IBL); getWidget("global_illumination")->setState(UserConfigParams::m_gi); getWidget("motionblur")->setState(UserConfigParams::m_motionblur); getWidget("mlaa")->setState(UserConfigParams::m_mlaa); @@ -150,6 +151,9 @@ GUIEngine::EventPropagation CustomVideoSettingsDialog::processEvent(const std::s UserConfigParams::m_light_shaft = advanced_pipeline && getWidget("lightshaft")->getState(); + UserConfigParams::m_degraded_IBL = + !getWidget("ibl")->getState(); + UserConfigParams::m_gi = advanced_pipeline && CVS->supportsGlobalIllumination() && getWidget("global_illumination")->getState(); @@ -231,6 +235,7 @@ void CustomVideoSettingsDialog::updateActivation() getWidget("mlaa")->setActivated(); getWidget("ssao")->setActivated(); getWidget("lightshaft")->setActivated(); + getWidget("ibl")->setActivated(); getWidget("global_illumination")->setActivated(); getWidget("glow")->setActivated(); getWidget("bloom")->setActivated(); @@ -243,6 +248,7 @@ void CustomVideoSettingsDialog::updateActivation() getWidget("mlaa")->setDeactivated(); getWidget("ssao")->setDeactivated(); getWidget("lightshaft")->setDeactivated(); + getWidget("ibl")->setDeactivated(); getWidget("global_illumination")->setDeactivated(); getWidget("glow")->setDeactivated(); getWidget("bloom")->setDeactivated(); diff --git a/src/states_screens/options_screen_video.cpp b/src/states_screens/options_screen_video.cpp index 958506182..2e144d697 100644 --- a/src/states_screens/options_screen_video.cpp +++ b/src/states_screens/options_screen_video.cpp @@ -61,6 +61,7 @@ struct GFXPreset /** Depth of field */ bool dof; bool global_illumination; + bool degraded_ibl; }; static GFXPreset GFX_PRESETS[] = @@ -69,35 +70,35 @@ static GFXPreset GFX_PRESETS[] = false /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */, false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, false /* weather */, false /* animatedScenery */, 0 /* animatedCharacters */, 0 /* anisotropy */, - false /* depth of field */, false /* global illumination */ + false /* depth of field */, false /* global illumination */, true /* degraded IBL */ }, { false /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */, false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, false /* weather */, true /* animatedScenery */, 1 /* animatedCharacters */, 4 /* anisotropy */, - false /* depth of field */, false /* global illumination */ + false /* depth of field */, false /* global illumination */, true /* degraded IBL */ }, { true /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */, false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, true /* weather */, true /* animatedScenery */, 1 /* animatedCharacters */, 4 /* anisotropy */, - false /* depth of field */, false /* global illumination */ + false /* depth of field */, false /* global illumination */, true /* degraded IBL */ }, { true /* light */, 0 /* shadow */, false /* bloom */, true /* motionblur */, true /* lightshaft */, true /* glow */, true /* mlaa */, false /* ssao */, true /* weather */, true /* animatedScenery */, 1 /* animatedCharacters */, 8 /* anisotropy */, - false /* depth of field */, false /* global illumination */ + false /* depth of field */, false /* global illumination */, false /* degraded IBL */ }, { true /* light */, 1024 /* shadow */, true /* bloom */, true /* motionblur */, true /* lightshaft */, true /* glow */, true /* mlaa */, true /* ssao */, true /* weather */, true /* animatedScenery */, 2 /* animatedCharacters */, 16 /* anisotropy */, - true /* depth of field */, true /* global illumination */ + true /* depth of field */, true /* global illumination */, false /* degraded IBL */ } }; @@ -395,7 +396,8 @@ void OptionsScreenVideo::updateGfxSlider() GFX_PRESETS[l].ssao == UserConfigParams::m_ssao && GFX_PRESETS[l].weather == UserConfigParams::m_weather_effects && GFX_PRESETS[l].dof == UserConfigParams::m_dof && - GFX_PRESETS[l].global_illumination == UserConfigParams::m_gi) + GFX_PRESETS[l].global_illumination == UserConfigParams::m_gi && + GFX_PRESETS[l].degraded_ibl == UserConfigParams::m_degraded_IBL) { gfx->setValue(l + 1); found = true; @@ -567,6 +569,7 @@ void OptionsScreenVideo::eventCallback(Widget* widget, const std::string& name, UserConfigParams::m_weather_effects = GFX_PRESETS[level].weather; UserConfigParams::m_dof = GFX_PRESETS[level].dof; UserConfigParams::m_gi = GFX_PRESETS[level].global_illumination; + UserConfigParams::m_degraded_IBL = GFX_PRESETS[level].degraded_ibl; updateGfxSlider(); } From 8a4c240f8b2b4bcdece610262ecbba24e8388fea Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 22 Feb 2015 20:08:19 -0500 Subject: [PATCH 079/117] Tweak subsea challenge, fixes #1997 --- data/challenges/subsea.challenge | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/challenges/subsea.challenge b/data/challenges/subsea.challenge index 3fa34185a..e82e06550 100644 --- a/data/challenges/subsea.challenge +++ b/data/challenges/subsea.challenge @@ -10,10 +10,10 @@ - + - + From 2c1b80b939d6e855a556221a4006e70c14bdf3fb Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 23 Feb 2015 22:44:35 +1100 Subject: [PATCH 080/117] Added separate achievement and friend message boxes for Ocean ... which atm is just the peach one (though imho looks better than the previous normal box). --- data/skins/Ocean.stkskin | 10 +++++----- data/skins/ocean/achievement.png | Bin 0 -> 28721 bytes data/skins/ocean/friend.png | Bin 0 -> 25829 bytes 3 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 data/skins/ocean/achievement.png create mode 100644 data/skins/ocean/friend.png diff --git a/data/skins/Ocean.stkskin b/data/skins/Ocean.stkskin index 4318a6083..2fabdb4f1 100644 --- a/data/skins/Ocean.stkskin +++ b/data/skins/Ocean.stkskin @@ -68,12 +68,12 @@ when the border that intersect at this corner are enabled. - + - 6Np~$scZW1cEU<*IBHi8Z`25cQ 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 literal 0 HcmV?d00001 diff --git a/data/skins/ocean/friend.png b/data/skins/ocean/friend.png new file mode 100644 index 0000000000000000000000000000000000000000..9238c8add12881f2fd06fe297d04c8f396def1d5 GIT binary patch literal 25829 zcmX_o1z1#FwD!;qLk}Sx0}PD_N_R>l-Kl^yLw9$Vk|GL7igc#}4u}XyH%QHZbp6Ng z{`bOR;F$-`S$pr5@4FW9+Rv4V@M-ZuAP|wNih?c(gcb(;9EOVxyrS2469C_^ZPb+& zKo9>uOS&pEfp_rSRgArWznJ~^g(e3rgadElc≨wih!rHvoS%8^8?W$L~K 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 literal 0 HcmV?d00001 From e56b4e33bc5ad3d81bf5130036e35d39eec96db1 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 23 Feb 2015 23:06:29 +1100 Subject: [PATCH 081/117] Show login screen with error message if automatic login did not work. If user has left main menu screen, an error notification message is displayed. For #1778. --- src/guiengine/message_queue.cpp | 4 +++- src/guiengine/message_queue.hpp | 2 +- src/online/online_player_profile.cpp | 24 +++++++++++++++++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/guiengine/message_queue.cpp b/src/guiengine/message_queue.cpp index dbea7a2d5..747e9b403 100755 --- a/src/guiengine/message_queue.cpp +++ b/src/guiengine/message_queue.cpp @@ -52,6 +52,8 @@ public: m_message = message; if(mt==MessageQueue::MT_ACHIEVEMENT) m_render_type = "achievement-message::neutral"; + else if (mt==MessageQueue::MT_ERROR) + m_render_type = "achievement-message::neutral"; else m_render_type = "friend-message::neutral"; } // Message @@ -77,7 +79,7 @@ class CompareMessages public: /** Used to sort messages by priority in the priority queue. Achievement * messages (1) need to have a higher priority than friend messages - * (value 0). */ + * (value 0), and errors (3) the highest priority. */ bool operator() (const Message *a, const Message *b) const { return a->getMessageType() < b->getMessageType(); diff --git a/src/guiengine/message_queue.hpp b/src/guiengine/message_queue.hpp index 0bb3a5e0a..8a06d41f0 100644 --- a/src/guiengine/message_queue.hpp +++ b/src/guiengine/message_queue.hpp @@ -34,7 +34,7 @@ namespace MessageQueue * different look. This type is used to sort the messages, so it is * important that messages that need to be shown as early as possible * will be listed last (i.e. have highest priority). */ - enum MessageType {MT_FRIEND, MT_ACHIEVEMENT}; + enum MessageType { MT_FRIEND, MT_ACHIEVEMENT, MT_ERROR}; void add(MessageType mt, const core::stringw &message); void updatePosition(); diff --git a/src/online/online_player_profile.cpp b/src/online/online_player_profile.cpp index dbe9b7219..f2a6d6472 100644 --- a/src/online/online_player_profile.cpp +++ b/src/online/online_player_profile.cpp @@ -26,6 +26,7 @@ #include "online/online_profile.hpp" #include "online/profile_manager.hpp" #include "online/servers_manager.hpp" +#include "states_screens/main_menu_screen.hpp" #include "states_screens/online_profile_friends.hpp" #include "states_screens/user_screen.hpp" #include "states_screens/dialogs/change_password_dialog.hpp" @@ -141,7 +142,6 @@ namespace Online { PlayerManager::getCurrentPlayer()->signIn(isSuccess(), getXMLData()); GUIEngine::Screen *screen = GUIEngine::getCurrentScreen(); - BaseUserScreen *login = dynamic_cast(screen); // If the login is successful, reset any saved session of other // local players using the same online account (which are now invalid) @@ -160,6 +160,8 @@ namespace Online } } + // Test if failure while showing user login screen + BaseUserScreen *login = dynamic_cast(screen); if (login) { if(isSuccess()) @@ -167,6 +169,26 @@ namespace Online else login->loginError(getInfo()); } // if dialog + + // Check if failure happened during automatic (saved) signin. + else if (!isSuccess()) + { + if (GUIEngine::getCurrentScreen() != MainMenuScreen::getInstance()) + { + // User has already opened another menu, so use message queue + // to inform user that login failed. + MessageQueue::add(MessageQueue::MT_ERROR, getInfo()); + return; + } + + // User still at main menu screen, push user screen. Note that + // this function is called from the main thread, so we can + // push screens without synchronisations. + UserScreen::getInstance()->push(); + UserScreen::getInstance()->loginError(getInfo()); + } + + } // SignInRequest::callback // ------------------------------------------------------------------------ From b70ce0c71419aca69a14cf372c9811bd58ae5108 Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Mon, 23 Feb 2015 19:10:38 +0100 Subject: [PATCH 082/117] Remove lens flare, it's yielding too much aliasing now --- src/graphics/post_processing.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index f860df6bb..92dd53cea 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -752,22 +752,11 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo // Downsample FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_BLOOM_256), GL_COLOR_BUFFER_BIT, GL_LINEAR); FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_BLOOM_128), GL_COLOR_BUFFER_BIT, GL_LINEAR); - - // Copy for lens flare - FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_LENS_512), GL_COLOR_BUFFER_BIT, GL_LINEAR); - FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_LENS_256), GL_COLOR_BUFFER_BIT, GL_LINEAR); - FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_LENS_128), GL_COLOR_BUFFER_BIT, GL_LINEAR); - // Blur - renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_TMP_512), 1., 1.); renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_TMP_256), 1., 1.); renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_TMP_128), 1., 1.); - - renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_512), irr_driver->getFBO(FBO_TMP_512)); - renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_256), irr_driver->getFBO(FBO_TMP_256)); - renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_128), irr_driver->getFBO(FBO_TMP_128)); // Additively blend on top of tmp1 in_fbo->Bind(); @@ -777,12 +766,6 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo FullScreenShader::BloomBlendShader::getInstance()->SetTextureUnits( irr_driver->getRenderTargetTexture(RTT_BLOOM_128), irr_driver->getRenderTargetTexture(RTT_BLOOM_256), irr_driver->getRenderTargetTexture(RTT_BLOOM_512)); DrawFullScreenEffect(); - - - FullScreenShader::LensBlendShader::getInstance()->SetTextureUnits( - irr_driver->getRenderTargetTexture(RTT_LENS_128), irr_driver->getRenderTargetTexture(RTT_LENS_256), irr_driver->getRenderTargetTexture(RTT_LENS_512)); - DrawFullScreenEffect(); - glDisable(GL_BLEND); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } // end if bloom From 65cb55add42d53060df7dfe8a98582dbe45c6dfd Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Mon, 23 Feb 2015 00:05:17 +0100 Subject: [PATCH 083/117] Make bloom less pixelated using Intel doc --- data/shaders/Lightspaceboundingbox.comp | 2 +- data/shaders/bilateralH.comp | 7 +++--- data/shaders/bilateralV.comp | 7 +++--- data/shaders/bloom.frag | 2 +- data/shaders/gaussian6h.comp | 7 +++--- data/shaders/gaussian6v.comp | 7 +++--- src/graphics/irr_driver.hpp | 2 ++ src/graphics/post_processing.cpp | 29 ++++++++++++++++++++----- src/graphics/rtts.cpp | 4 ++++ 9 files changed, 48 insertions(+), 19 deletions(-) diff --git a/data/shaders/Lightspaceboundingbox.comp b/data/shaders/Lightspaceboundingbox.comp index e55e69c68..602da12e8 100644 --- a/data/shaders/Lightspaceboundingbox.comp +++ b/data/shaders/Lightspaceboundingbox.comp @@ -49,7 +49,7 @@ void main() ivec3 lmax3 = ivec3(-1000); ivec3 lmin3 = ivec3(1000); - vec2 start_xy = gl_LocalInvocationID.xy + gl_WorkGroupID.xy * gl_WorkGroupSize.xy * 8; + vec2 start_xy = gl_LocalInvocationID.xy + gl_WorkGroupID.xy * gl_WorkGroupSize.xy * 8 + .5; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { diff --git a/data/shaders/bilateralH.comp b/data/shaders/bilateralH.comp index 5ecad97c9..a249cd321 100644 --- a/data/shaders/bilateralH.comp +++ b/data/shaders/bilateralH.comp @@ -15,9 +15,10 @@ void main() { int x = int(gl_LocalInvocationID.x), y = int(gl_LocalInvocationID.y); ivec2 iuv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); - vec2 uv_m = (iuv - ivec2(8, 0)) * pixel; - vec2 uv = iuv * pixel; - vec2 uv_p = (iuv + ivec2(8, 0)) * pixel; + vec2 guv = gl_GlobalInvocationID.xy + .5; + vec2 uv_m = (guv - vec2(8, 0)) * pixel; + vec2 uv = guv * pixel; + vec2 uv_p = (guv + vec2(8, 0)) * pixel; local_src[x][y] = texture(source, uv_m).x; local_depth[x][y] = texture(depth, uv_m).x; diff --git a/data/shaders/bilateralV.comp b/data/shaders/bilateralV.comp index 720566821..caa9d80d6 100644 --- a/data/shaders/bilateralV.comp +++ b/data/shaders/bilateralV.comp @@ -15,9 +15,10 @@ void main() { int x = int(gl_LocalInvocationID.x), y = int(gl_LocalInvocationID.y); ivec2 iuv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); - vec2 uv_m = (iuv - ivec2(0, 8)) * pixel; - vec2 uv = iuv * pixel; - vec2 uv_p = (iuv + ivec2(0, 8)) * pixel; + vec2 guv = gl_GlobalInvocationID.xy + .5; + vec2 uv_m = (guv - vec2(0, 8)) * pixel; + vec2 uv = guv * pixel; + vec2 uv_p = (guv + vec2(0, 8)) * pixel; local_src[x][y] = texture(source, uv_m).x; local_depth[x][y] = texture(depth, uv_m).x; diff --git a/data/shaders/bloom.frag b/data/shaders/bloom.frag index 099922bab..90fc1eac4 100644 --- a/data/shaders/bloom.frag +++ b/data/shaders/bloom.frag @@ -7,7 +7,7 @@ vec3 getRGBFromCIEXxy(vec3 YxyColor); void main() { - vec2 uv = gl_FragCoord.xy / 512; + vec2 uv = gl_FragCoord.xy / 1024; vec3 col = texture(tex, uv).xyz; vec3 Yxy = getCIEYxy(col); vec3 WhiteYxy = getCIEYxy(vec3(1.)); diff --git a/data/shaders/gaussian6h.comp b/data/shaders/gaussian6h.comp index f2df38fe0..c58b02d1d 100644 --- a/data/shaders/gaussian6h.comp +++ b/data/shaders/gaussian6h.comp @@ -13,9 +13,10 @@ void main() { int x = int(gl_LocalInvocationID.x), y = int(gl_LocalInvocationID.y); ivec2 iuv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); - vec2 uv_m = (iuv - ivec2(6, 0)) * pixel; - vec2 uv = iuv * pixel; - vec2 uv_p = (iuv + ivec2(6, 0)) * pixel; + vec2 guv = gl_GlobalInvocationID.xy + .5; + vec2 uv_m = (guv - vec2(6, 0)) * pixel; + vec2 uv = guv * pixel; + vec2 uv_p = (guv + vec2(6, 0)) * pixel; local_src[x][y] = texture(source, uv_m); local_src[x + 6][y] = texture(source, uv); diff --git a/data/shaders/gaussian6v.comp b/data/shaders/gaussian6v.comp index 7c3a0979a..6924f897a 100644 --- a/data/shaders/gaussian6v.comp +++ b/data/shaders/gaussian6v.comp @@ -13,9 +13,10 @@ void main() { int x = int(gl_LocalInvocationID.x), y = int(gl_LocalInvocationID.y); ivec2 iuv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); - vec2 uv_m = (iuv - ivec2(0, 6)) * pixel; - vec2 uv = iuv * pixel; - vec2 uv_p = (iuv + ivec2(0, 6)) * pixel; + vec2 guv = gl_GlobalInvocationID.xy + .5; + vec2 uv_m = (guv - vec2(0, 6)) * pixel; + vec2 uv = guv * pixel; + vec2 uv_p = (guv + vec2(0, 6)) * pixel; local_src[x][y] = texture(source, uv_m); local_src[x][y + 6] = texture(source, uv); diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 34c8977a7..9e6775283 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -100,6 +100,7 @@ enum TypeFBO FBO_DISPLACE, FBO_BLOOM_1024, FBO_SCALAR_1024, + FBO_TMP_1024, FBO_BLOOM_512, FBO_TMP_512, FBO_LENS_512, @@ -162,6 +163,7 @@ enum TypeRTT RTT_BLOOM_1024, RTT_SCALAR_1024, + RTT_TMP_1024, RTT_BLOOM_512, RTT_TMP_512, RTT_LENS_512, diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index 92dd53cea..794d95d40 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -744,19 +744,37 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo glClear(GL_STENCIL_BUFFER_BIT); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); - FrameBuffer::Blit(*in_fbo, irr_driver->getFBO(FBO_BLOOM_1024), GL_COLOR_BUFFER_BIT, GL_LINEAR); + FrameBuffer::Blit(*in_fbo, irr_driver->getFBO(FBO_TMP_1024), GL_COLOR_BUFFER_BIT, GL_LINEAR); - irr_driver->getFBO(FBO_BLOOM_512).Bind(); - renderBloom(irr_driver->getRenderTargetTexture(RTT_BLOOM_1024)); + irr_driver->getFBO(FBO_BLOOM_1024).Bind(); + renderBloom(irr_driver->getRenderTargetTexture(RTT_TMP_1024)); // Downsample + FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_1024), irr_driver->getFBO(FBO_BLOOM_512), GL_COLOR_BUFFER_BIT, GL_LINEAR); FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_BLOOM_256), GL_COLOR_BUFFER_BIT, GL_LINEAR); FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_BLOOM_128), GL_COLOR_BUFFER_BIT, GL_LINEAR); // Blur - renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_TMP_512), 1., 1.); - renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_TMP_256), 1., 1.); + // The fbo sum chain is from https://software.intel.com/en-us/articles/compute-shader-hdr-and-bloom + glBlendFunc(GL_ONE, GL_ONE); + glBlendEquation(GL_FUNC_ADD); + renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_TMP_128), 1., 1.); + glEnable(GL_BLEND); + irr_driver->getFBO(FBO_BLOOM_256).Bind(); + renderPassThrough(irr_driver->getFBO(FBO_BLOOM_128).getRTT()[0]); + glDisable(GL_BLEND); + renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_TMP_256), 2., 2.); + glEnable(GL_BLEND); + irr_driver->getFBO(FBO_BLOOM_512).Bind(); + renderPassThrough(irr_driver->getFBO(FBO_BLOOM_256).getRTT()[0]); + glDisable(GL_BLEND); + renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_TMP_512), 2., 2.); + glEnable(GL_BLEND); + irr_driver->getFBO(FBO_BLOOM_1024).Bind(); + renderPassThrough(irr_driver->getFBO(FBO_BLOOM_512).getRTT()[0]); + glDisable(GL_BLEND); + renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_1024), irr_driver->getFBO(FBO_TMP_1024), 2., 2.); // Additively blend on top of tmp1 in_fbo->Bind(); @@ -766,6 +784,7 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo FullScreenShader::BloomBlendShader::getInstance()->SetTextureUnits( irr_driver->getRenderTargetTexture(RTT_BLOOM_128), irr_driver->getRenderTargetTexture(RTT_BLOOM_256), irr_driver->getRenderTargetTexture(RTT_BLOOM_512)); DrawFullScreenEffect(); + glDisable(GL_BLEND); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } // end if bloom diff --git a/src/graphics/rtts.cpp b/src/graphics/rtts.cpp index 7bd11e9c7..c26a2279d 100644 --- a/src/graphics/rtts.cpp +++ b/src/graphics/rtts.cpp @@ -105,6 +105,7 @@ RTT::RTT(size_t width, size_t height) RenderTargetTextures[RTT_BLOOM_1024] = generateRTT(shadowsize0, GL_RGBA16F, GL_BGR, GL_FLOAT); RenderTargetTextures[RTT_SCALAR_1024] = generateRTT(shadowsize0, GL_R32F, GL_RED, GL_FLOAT); RenderTargetTextures[RTT_BLOOM_512] = generateRTT(shadowsize1, GL_RGBA16F, GL_BGR, GL_FLOAT); + RenderTargetTextures[RTT_TMP_1024] = generateRTT(shadowsize0, GL_RGBA16F, GL_BGR, GL_FLOAT); RenderTargetTextures[RTT_TMP_512] = generateRTT(shadowsize1, GL_RGBA16F, GL_BGR, GL_FLOAT); RenderTargetTextures[RTT_LENS_512] = generateRTT(shadowsize1, GL_RGBA16F, GL_BGR, GL_FLOAT); @@ -192,6 +193,9 @@ RTT::RTT(size_t width, size_t height) somevector.push_back(RenderTargetTextures[RTT_SCALAR_1024]); FrameBuffers.push_back(new FrameBuffer(somevector, 1024, 1024)); somevector.clear(); + somevector.push_back(RenderTargetTextures[RTT_TMP_1024]); + FrameBuffers.push_back(new FrameBuffer(somevector, 1024, 1024)); + somevector.clear(); somevector.push_back(RenderTargetTextures[RTT_BLOOM_512]); FrameBuffers.push_back(new FrameBuffer(somevector, 512, 512)); somevector.clear(); From 06f0188a5b0f63a9a4d4c9302cbb512bac282401 Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Mon, 23 Feb 2015 22:02:32 +0100 Subject: [PATCH 084/117] Fix passthrough shader It was not using half coordinate center --- data/shaders/passthrough.frag | 11 +++++++++++ src/graphics/post_processing.cpp | 19 +++++++------------ src/graphics/post_processing.hpp | 2 +- src/graphics/render.cpp | 6 +++--- src/graphics/render_geometry.cpp | 2 +- src/graphics/render_lighting.cpp | 2 +- src/graphics/shaders.cpp | 5 ++--- src/graphics/shaders.hpp | 4 +--- 8 files changed, 27 insertions(+), 24 deletions(-) create mode 100644 data/shaders/passthrough.frag diff --git a/data/shaders/passthrough.frag b/data/shaders/passthrough.frag new file mode 100644 index 000000000..5ffb2d22c --- /dev/null +++ b/data/shaders/passthrough.frag @@ -0,0 +1,11 @@ +uniform sampler2D tex; +uniform int width; +uniform int height; + +out vec4 FragColor; + +void main() +{ + vec2 uv = gl_FragCoord.xy / vec2(width, height); + FragColor = texture(tex, uv); +} \ No newline at end of file diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index 794d95d40..ca7b3e421 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -468,15 +468,10 @@ void PostProcessing::renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &a glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT); } -void PostProcessing::renderPassThrough(GLuint tex) +void PostProcessing::renderPassThrough(GLuint tex, unsigned width, unsigned height) { - glUseProgram(FullScreenShader::PassThroughShader::getInstance()->Program); - glBindVertexArray(FullScreenShader::PassThroughShader::getInstance()->vao); - FullScreenShader::PassThroughShader::getInstance()->SetTextureUnits(tex); - FullScreenShader::PassThroughShader::getInstance()->setUniforms(); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + DrawFullScreenEffect(width, height); } void PostProcessing::renderTextureLayer(unsigned tex, unsigned layer) @@ -728,7 +723,7 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo glBlendEquation(GL_FUNC_ADD); in_fbo->Bind(); - renderPassThrough(irr_driver->getRenderTargetTexture(RTT_QUARTER2)); + renderPassThrough(irr_driver->getRenderTargetTexture(RTT_QUARTER2), in_fbo->getWidth(), in_fbo->getHeight()); glDisable(GL_BLEND); } PROFILER_POP_CPU_MARKER(); @@ -762,17 +757,17 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_TMP_128), 1., 1.); glEnable(GL_BLEND); irr_driver->getFBO(FBO_BLOOM_256).Bind(); - renderPassThrough(irr_driver->getFBO(FBO_BLOOM_128).getRTT()[0]); + renderPassThrough(irr_driver->getFBO(FBO_BLOOM_128).getRTT()[0], 256, 256); glDisable(GL_BLEND); renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_TMP_256), 2., 2.); glEnable(GL_BLEND); irr_driver->getFBO(FBO_BLOOM_512).Bind(); - renderPassThrough(irr_driver->getFBO(FBO_BLOOM_256).getRTT()[0]); + renderPassThrough(irr_driver->getFBO(FBO_BLOOM_256).getRTT()[0], 512, 512); glDisable(GL_BLEND); renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_TMP_512), 2., 2.); glEnable(GL_BLEND); irr_driver->getFBO(FBO_BLOOM_1024).Bind(); - renderPassThrough(irr_driver->getFBO(FBO_BLOOM_512).getRTT()[0]); + renderPassThrough(irr_driver->getFBO(FBO_BLOOM_512).getRTT()[0], 1024, 1024); glDisable(GL_BLEND); renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_1024), irr_driver->getFBO(FBO_TMP_1024), 2., 2.); @@ -828,7 +823,7 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo glEnable(GL_FRAMEBUFFER_SRGB); irr_driver->getFBO(FBO_MLAA_COLORS).Bind(); - renderPassThrough(in_fbo->getRTT()[0]); + renderPassThrough(in_fbo->getRTT()[0], irr_driver->getFBO(FBO_MLAA_COLORS).getWidth(), irr_driver->getFBO(FBO_MLAA_COLORS).getHeight()); out_fbo = &irr_driver->getFBO(FBO_MLAA_COLORS); if (UserConfigParams::m_mlaa) // MLAA. Must be the last pp filter. diff --git a/src/graphics/post_processing.hpp b/src/graphics/post_processing.hpp index 8f373d36c..8933ae552 100644 --- a/src/graphics/post_processing.hpp +++ b/src/graphics/post_processing.hpp @@ -91,7 +91,7 @@ public: void renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &auxiliary); /** Render tex. Used for blit/texture resize */ - void renderPassThrough(unsigned tex); + void renderPassThrough(unsigned tex, unsigned width, unsigned height); void renderTextureLayer(unsigned tex, unsigned layer); void applyMLAA(); diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 5acdfcaba..3df4ffb43 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -254,13 +254,13 @@ void IrrDriver::renderGLSL(float dt) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); - m_post_processing->renderPassThrough(m_rtts->getFBO(FBO_HALF1_R).getRTT()[0]); + m_post_processing->renderPassThrough(m_rtts->getFBO(FBO_HALF1_R).getRTT()[0], viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); } else if (irr_driver->getRSM()) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); - m_post_processing->renderPassThrough(m_rtts->getRSM().getRTT()[0]); + m_post_processing->renderPassThrough(m_rtts->getRSM().getRTT()[0], viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); } else if (irr_driver->getShadowViz()) { @@ -272,7 +272,7 @@ void IrrDriver::renderGLSL(float dt) glBindFramebuffer(GL_FRAMEBUFFER, 0); if (CVS->isDefferedEnabled()) camera->activate(); - m_post_processing->renderPassThrough(fbo->getRTT()[0]); + m_post_processing->renderPassThrough(fbo->getRTT()[0], viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); glDisable(GL_FRAMEBUFFER_SRGB); } } diff --git a/src/graphics/render_geometry.cpp b/src/graphics/render_geometry.cpp index 3a822a7e1..727c251c3 100644 --- a/src/graphics/render_geometry.cpp +++ b/src/graphics/render_geometry.cpp @@ -883,7 +883,7 @@ void IrrDriver::renderTransparent() irr_driver->getFBO(FBO_COLORS).Bind(); glStencilFunc(GL_EQUAL, 1, 0xFF); - m_post_processing->renderPassThrough(m_rtts->getRenderTarget(RTT_DISPLACE)); + m_post_processing->renderPassThrough(m_rtts->getRenderTarget(RTT_DISPLACE), irr_driver->getFBO(FBO_COLORS).getWidth(), irr_driver->getFBO(FBO_COLORS).getHeight()); glDisable(GL_STENCIL_TEST); } diff --git a/src/graphics/render_lighting.cpp b/src/graphics/render_lighting.cpp index 0996d9867..cf74eea07 100644 --- a/src/graphics/render_lighting.cpp +++ b/src/graphics/render_lighting.cpp @@ -289,5 +289,5 @@ void IrrDriver::renderLightsScatter(unsigned pointlightcount) glDisable(GL_DEPTH_TEST); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); getFBO(FBO_COLORS).Bind(); - m_post_processing->renderPassThrough(getRenderTargetTexture(RTT_HALF1)); + m_post_processing->renderPassThrough(getRenderTargetTexture(RTT_HALF1), getFBO(FBO_COLORS).getWidth(), getFBO(FBO_COLORS).getHeight()); } diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index f4a5168d3..a5c5e6b57 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -1875,11 +1875,10 @@ namespace FullScreenShader { Program = LoadProgram(OBJECT, GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), - GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/texturedquad.frag").c_str()); + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/passthrough.frag").c_str()); - AssignUniforms(); + AssignUniforms("width", "height"); AssignSamplerNames(Program, 0, "tex"); - vao = createVAO(Program); } LayerPassThroughShader::LayerPassThroughShader() diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 44005f700..231a74ae5 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -557,11 +557,9 @@ public: Gaussian3VBlurShader(); }; -class PassThroughShader : public ShaderHelperSingleton, public TextureRead +class PassThroughShader : public ShaderHelperSingleton, public TextureRead { public: - GLuint vao; - PassThroughShader(); }; From 2206f8ad5c89bae00a54703d91d7e16215b58bb8 Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Mon, 23 Feb 2015 22:11:57 +0100 Subject: [PATCH 085/117] Clean bloomblending since it's not used anymore --- data/shaders/bloomblend.frag | 14 -------------- src/graphics/post_processing.cpp | 9 +++------ src/graphics/shaders.cpp | 10 ---------- src/graphics/shaders.hpp | 6 ------ 4 files changed, 3 insertions(+), 36 deletions(-) delete mode 100644 data/shaders/bloomblend.frag diff --git a/data/shaders/bloomblend.frag b/data/shaders/bloomblend.frag deleted file mode 100644 index 503c9dcb9..000000000 --- a/data/shaders/bloomblend.frag +++ /dev/null @@ -1,14 +0,0 @@ -uniform sampler2D tex_128; -uniform sampler2D tex_256; -uniform sampler2D tex_512; - -out vec4 FragColor; - -void main() -{ - vec2 uv = gl_FragCoord.xy / screen; - vec4 col = .125 * texture(tex_128, uv); - col += .25 * texture(tex_256, uv); - col += .5 * texture(tex_512, uv); - FragColor = vec4(col.xyz, 1.); -} diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index ca7b3e421..c6f922cfd 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -754,7 +754,7 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); - renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_TMP_128), 1., 1.); + renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_TMP_128), 2., 2.); glEnable(GL_BLEND); irr_driver->getFBO(FBO_BLOOM_256).Bind(); renderPassThrough(irr_driver->getFBO(FBO_BLOOM_128).getRTT()[0], 256, 256); @@ -771,15 +771,12 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo glDisable(GL_BLEND); renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_1024), irr_driver->getFBO(FBO_TMP_1024), 2., 2.); - // Additively blend on top of tmp1 + // Additively blend in_fbo->Bind(); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); - FullScreenShader::BloomBlendShader::getInstance()->SetTextureUnits( - irr_driver->getRenderTargetTexture(RTT_BLOOM_128), irr_driver->getRenderTargetTexture(RTT_BLOOM_256), irr_driver->getRenderTargetTexture(RTT_BLOOM_512)); - DrawFullScreenEffect(); - + renderPassThrough(irr_driver->getRenderTargetTexture(RTT_BLOOM_1024), in_fbo->getWidth(), in_fbo->getHeight()); glDisable(GL_BLEND); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } // end if bloom diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index a5c5e6b57..768213795 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -1570,16 +1570,6 @@ namespace FullScreenShader AssignSamplerNames(Program, 0, "tex"); } - - BloomBlendShader::BloomBlendShader() - { - Program = LoadProgram(OBJECT, - GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), - GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/bloomblend.frag").c_str()); - AssignUniforms(); - - AssignSamplerNames(Program, 0, "tex_128", 1, "tex_256", 2, "tex_512"); - } LensBlendShader::LensBlendShader() { diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 231a74ae5..df8624f04 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -388,12 +388,6 @@ public: BloomShader(); }; -class BloomBlendShader : public ShaderHelperSingleton, public TextureRead -{ -public: - BloomBlendShader(); -}; - class LensBlendShader : public ShaderHelperSingleton, public TextureRead { public: From 33c0dfe7c2b049570dafa68676c79d2c9b6829f1 Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Fri, 20 Feb 2015 23:40:19 +0100 Subject: [PATCH 086/117] Remove shadow blob --- src/graphics/shadow.cpp | 87 ----------------------------------------- src/graphics/shadow.hpp | 59 ---------------------------- src/karts/kart.cpp | 12 ------ src/karts/kart.hpp | 3 -- 4 files changed, 161 deletions(-) delete mode 100644 src/graphics/shadow.cpp delete mode 100644 src/graphics/shadow.hpp diff --git a/src/graphics/shadow.cpp b/src/graphics/shadow.cpp deleted file mode 100644 index 07948219e..000000000 --- a/src/graphics/shadow.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2013 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "graphics/shadow.hpp" -#include "graphics/irr_driver.hpp" - -#include -#include -#include - -Shadow::Shadow(video::ITexture *texture, scene::ISceneNode *node, - float scale = 1.0, float x_offset = 0.0, float y_offset = 0.0, - float z_offset = 0.0) -{ - video::SMaterial m; - m.setTexture(0, texture); - m.BackfaceCulling = false; - m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - m.setFlag(video::EMF_ZWRITE_ENABLE , false); - m_mesh = irr_driver->createQuadMesh(&m, /*create_one_quad*/true); - scene::IMeshBuffer *buffer = m_mesh->getMeshBuffer(0); - irr::video::S3DVertex* v=(video::S3DVertex*)buffer->getVertices(); - v[0].Pos.X = -scale+x_offset; v[0].Pos.Z = scale+z_offset; v[0].Pos.Y = 0.01f-y_offset; - v[1].Pos.X = scale+x_offset; v[1].Pos.Z = scale+z_offset; v[1].Pos.Y = 0.01f-y_offset; - v[2].Pos.X = scale+x_offset; v[2].Pos.Z = -scale+z_offset; v[2].Pos.Y = 0.01f-y_offset; - v[3].Pos.X = -scale+x_offset; v[3].Pos.Z = -scale+z_offset; v[3].Pos.Y = 0.01f-y_offset; - v[0].TCoords = core::vector2df(0,0); - v[1].TCoords = core::vector2df(1,0); - v[2].TCoords = core::vector2df(1,1); - v[3].TCoords = core::vector2df(0,1); - core::vector3df normal(0, 0, 1.0f); - v[0].Normal = normal; - v[1].Normal = normal; - v[2].Normal = normal; - v[3].Normal = normal; - buffer->recalculateBoundingBox(); - - m_node = irr_driver->addMesh(m_mesh, "shadow"); -#ifdef DEBUG - m_node->setName("shadow"); -#endif - - m_mesh->drop(); // the node grabs the mesh, so we can drop this reference - m_node->setAutomaticCulling(scene::EAC_OFF); - m_parent_kart_node = node; - m_parent_kart_node->addChild(m_node); -} // Shadow - -// ---------------------------------------------------------------------------- -Shadow::~Shadow() -{ - // Note: the mesh was not loaded from disk, so it is not cached, - // and does not need to be removed. It's clean up when removing the node - m_parent_kart_node->removeChild(m_node); -} // ~Shadow - -// ---------------------------------------------------------------------------- -/** Removes the shadow, used for the simplified shadow when the kart is in - * the air. - */ -void Shadow::disableShadow() -{ - m_node->setVisible(false); -} -// ---------------------------------------------------------------------------- -/** Enables the shadow again, after it was disabled with disableShadow(). - */ -void Shadow::enableShadow() -{ - m_node->setVisible(true); -} -// ---------------------------------------------------------------------------- diff --git a/src/graphics/shadow.hpp b/src/graphics/shadow.hpp deleted file mode 100644 index e234e716b..000000000 --- a/src/graphics/shadow.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2013 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_SHADOW_HPP -#define HEADER_SHADOW_HPP - -#include "utils/no_copy.hpp" - -#include - -namespace irr -{ - namespace scene { class ISceneNode; class IMesh; } - namespace video { class ITexture; } -} -using namespace irr; - -/** - * \brief This class is used to enable a shadow for a kart. - * For now it uses a simple texture to simulate the shadow, real time shadows might - * be added later. - * \ingroup graphics - */ -class Shadow : public NoCopy -{ -private: - /** The scene node for the shadow. */ - scene::ISceneNode *m_node; - /** The mesh of the shadow. */ - scene::IMesh *m_mesh; - /** The scene node of the kart to which this shadow belongs. */ - scene::ISceneNode *m_parent_kart_node; -public: - Shadow(video::ITexture *texture, scene::ISceneNode *node, - float scale, float x_offset, float y_offset,float z_offset); - ~Shadow(); - void enableShadow(); - void disableShadow(); -}; // Shadow -#endif - -/* EOF */ - - diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index a730252d1..b4a396570 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -32,7 +32,6 @@ #include "graphics/particle_emitter.hpp" #include "graphics/particle_kind.hpp" #include "graphics/particle_kind_manager.hpp" -#include "graphics/shadow.hpp" #include "graphics/skid_marks.hpp" #include "graphics/slip_stream.hpp" #include "graphics/stk_text_billboard.hpp" @@ -117,7 +116,6 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, m_squash_time = 0.0f; m_shadow_enabled = false; - m_shadow = NULL; m_wheel_box = NULL; m_collision_particles = NULL; m_slipstream = NULL; @@ -268,7 +266,6 @@ Kart::~Kart() if(m_attachment) delete m_attachment; if(m_stars_effect) delete m_stars_effect; - delete m_shadow; if (m_wheel_box) m_wheel_box->remove(); if(m_skidmarks) delete m_skidmarks ; @@ -1407,11 +1404,9 @@ void Kart::update(float dt) if((!isOnGround() || emergency) && m_shadow_enabled) { m_shadow_enabled = false; - m_shadow->disableShadow(); } if(!m_shadow_enabled && isOnGround() && !emergency) { - m_shadow->enableShadow(); m_shadow_enabled = true; } } // update @@ -2469,13 +2464,6 @@ void Kart::loadData(RaceManager::KartType type, bool is_animated_model) ->isFogEnabled() ); } - m_shadow = new Shadow(m_kart_properties->getShadowTexture(), - m_node, - m_kart_properties->getShadowScale(), - m_kart_properties->getShadowXOffset(), - m_kart_properties->getGraphicalYOffset(), - m_kart_properties->getShadowZOffset()); - World::getWorld()->kartAdded(this, m_node); } // loadData diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 5342d9043..5c0b2d5ae 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -170,9 +170,6 @@ private: /** Is time flying activated */ bool m_is_jumping; - /** The shadow of a kart. */ - Shadow *m_shadow; - /** If a kart is flying, the shadow is disabled (since it is * stuck to the kart, i.e. the shadow would be flying, too). */ bool m_shadow_enabled; From bcffedc186b27883de160266bcdb0ceaf605a85a Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Fri, 20 Feb 2015 18:08:08 +0100 Subject: [PATCH 087/117] Start working on proper SDSM --- data/shaders/shadowmatrixgeneration.comp | 52 ++++++ src/graphics/shaders.cpp | 11 ++ src/graphics/shaders.hpp | 6 + src/graphics/shadow_matrixes.cpp | 217 ++++++++--------------- 4 files changed, 140 insertions(+), 146 deletions(-) create mode 100644 data/shaders/shadowmatrixgeneration.comp diff --git a/data/shaders/shadowmatrixgeneration.comp b/data/shaders/shadowmatrixgeneration.comp new file mode 100644 index 000000000..98afb0242 --- /dev/null +++ b/data/shaders/shadowmatrixgeneration.comp @@ -0,0 +1,52 @@ +uniform mat4 SunCamMatrix; + +layout (local_size_x = 4) in; + +struct CascadeBoundingBox +{ + int xmin; + int xmax; + int ymin; + int ymax; + int zmin; + int zmax; +}; + +layout (std430) buffer BoundingBoxes +{ + CascadeBoundingBox BB[4]; +}; + +layout (std140) buffer NewMatrixData +{ + mat4 ShadowMatrixes[4]; +}; + +mat4 buildProjectionMatrixOrthoLH(float left, float right, float up, float down, float zNear, float zFar) +{ + mat4 M; + M[0] = vec4(2. / (right - left), 0., 0., 0.); + + M[1] = vec4(0., 2. / (up - down), 0., 0.); + + M[2] = vec4(0., 0., 1. / (zFar - zNear), 0.); + + M[3].x = - (right + left) / (right - left); + M[3].y = - (up + down) / (up - down); + M[3].z = zNear / (zNear - zFar); + M[3].w = 1.; + + return M; +} + +void main() +{ + if (gl_LocalInvocationIndex > 3) + return; + + ShadowMatrixes[gl_LocalInvocationIndex] = buildProjectionMatrixOrthoLH( + BB[gl_LocalInvocationIndex].xmin / 4. - 1., BB[gl_LocalInvocationIndex].xmax / 4. + 1., + BB[gl_LocalInvocationIndex].ymax / 4. + 1., BB[gl_LocalInvocationIndex].ymin / 4. - 1., + BB[gl_LocalInvocationIndex].zmin / 4. - 100., BB[gl_LocalInvocationIndex].zmax / 4. + 1.) * SunCamMatrix; +} + diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index 768213795..a6712472c 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -1904,6 +1904,17 @@ namespace FullScreenShader glShaderStorageBlockBinding(Program, block_idx, 2); } + ShadowMatrixesGenerationShader::ShadowMatrixesGenerationShader() + { + Program = LoadProgram(OBJECT, + GL_COMPUTE_SHADER, file_manager->getAsset("shaders/shadowmatrixgeneration.comp").c_str()); + AssignUniforms("SunCamMatrix"); + GLuint block_idx = glGetProgramResourceIndex(Program, GL_SHADER_STORAGE_BLOCK, "BoundingBoxes"); + glShaderStorageBlockBinding(Program, block_idx, 2); + block_idx = glGetProgramResourceIndex(Program, GL_SHADER_STORAGE_BLOCK, "NewMatrixData"); + glShaderStorageBlockBinding(Program, block_idx, 1); + } + DepthHistogramShader::DepthHistogramShader() { Program = LoadProgram(OBJECT, diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index df8624f04..1a34ea362 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -578,6 +578,12 @@ public: LightspaceBoundingBoxShader(); }; +class ShadowMatrixesGenerationShader : public ShaderHelperSingleton +{ +public: + ShadowMatrixesGenerationShader(); +}; + class DepthHistogramShader : public ShaderHelperSingleton, public TextureRead { public: diff --git a/src/graphics/shadow_matrixes.cpp b/src/graphics/shadow_matrixes.cpp index 74ede406b..c9683e6e3 100644 --- a/src/graphics/shadow_matrixes.cpp +++ b/src/graphics/shadow_matrixes.cpp @@ -108,109 +108,46 @@ struct Histogram void IrrDriver::UpdateSplitAndLightcoordRangeFromComputeShaders(size_t width, size_t height) { // Value that should be kept between multiple calls - static GLuint ssbo[2]; - static Histogram *Hist[2]; - static GLsync LightcoordBBFence = 0; - static size_t currentHist = 0; - static GLuint ssboSplit[2]; - static float tmpshadowSplit[5] = { 1., 5., 20., 50., 150. }; + static bool ssboInit = false; + static GLuint CBBssbo, tempShadowMatssbo; + CascadeBoundingBox InitialCBB[4]; - if (!LightcoordBBFence) - { - glGenBuffers(2, ssbo); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[0]); - glBufferStorage(GL_SHADER_STORAGE_BUFFER, 4 * sizeof(CascadeBoundingBox), 0, GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); - CBB[0] = (CascadeBoundingBox *)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 4 * sizeof(CascadeBoundingBox), GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[1]); - glBufferStorage(GL_SHADER_STORAGE_BUFFER, 4 * sizeof(CascadeBoundingBox), 0, GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); - CBB[1] = (CascadeBoundingBox *)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 4 * sizeof(CascadeBoundingBox), GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); - - /* glGenBuffers(2, ssboSplit); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboSplit[0]); - glBufferStorage(GL_SHADER_STORAGE_BUFFER, sizeof(Histogram), 0, GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); - Hist[0] = (Histogram *)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(Histogram), GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboSplit[1]); - glBufferStorage(GL_SHADER_STORAGE_BUFFER, sizeof(Histogram), 0, GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); - Hist[1] = (Histogram *)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(Histogram), GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);*/ - } - - // Use bounding boxes from last frame - if (LightcoordBBFence) - { - while (glClientWaitSync(LightcoordBBFence, GL_SYNC_FLUSH_COMMANDS_BIT, 0) != GL_ALREADY_SIGNALED); - glDeleteSync(LightcoordBBFence); - } - - /* { - memcpy(shadowSplit, tmpshadowSplit, 5 * sizeof(float)); - unsigned numpix = Hist[currentHist]->count; - unsigned split = 0; - unsigned i; - for (i = 0; i < 1022; i++) - { - split += Hist[currentHist]->bin[i]; - if (split > numpix / 2) - break; - } - tmpshadowSplit[1] = (float)++i / 4.; - - for (; i < 1023; i++) - { - split += Hist[currentHist]->bin[i]; - if (split > 3 * numpix / 4) - break; - } - tmpshadowSplit[2] = (float)++i / 4.; - - for (; i < 1024; i++) - { - split += Hist[currentHist]->bin[i]; - if (split > 7 * numpix / 8) - break; - } - tmpshadowSplit[3] = (float)++i / 4.; - - for (; i < 1024; i++) - { - split += Hist[currentHist]->bin[i]; - } - - tmpshadowSplit[0] = (float)(Hist[currentHist]->bin[1024] - 1) / 4.; - tmpshadowSplit[4] = (float)(Hist[currentHist]->bin[1025] + 1) / 4.; - printf("numpix is %d\n", numpix); - printf("total : %d\n", split); - printf("split 0 : %f\n", tmpshadowSplit[1]); - printf("split 1 : %f\n", tmpshadowSplit[2]); - printf("split 2 : %f\n", tmpshadowSplit[3]); - printf("min %f max %f\n", tmpshadowSplit[0], tmpshadowSplit[4]); - currentHist = (currentHist + 1) % 2; - }*/ - - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, ssbo[currentCBB]); - // glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssboSplit[currentHist]); for (unsigned i = 0; i < 4; i++) { - CBB[currentCBB][i].xmin = CBB[currentCBB][i].ymin = CBB[currentCBB][i].zmin = 1000; - CBB[currentCBB][i].xmax = CBB[currentCBB][i].ymax = CBB[currentCBB][i].zmax = -1000; + InitialCBB[i].xmin = InitialCBB[i].ymin = InitialCBB[i].zmin = 1000; + InitialCBB[i].xmax = InitialCBB[i].ymax = InitialCBB[i].zmax = -1000; } - // memset(Hist[currentHist], 0, sizeof(Histogram)); - // Hist[currentHist]->mindepth = 3000; - glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); + + if (!ssboInit) + { + glGenBuffers(1, &CBBssbo); + glGenBuffers(1, &tempShadowMatssbo); + ssboInit = true; + } + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, CBBssbo); + glBufferData(GL_SHADER_STORAGE_BUFFER, 4 * sizeof(CascadeBoundingBox), InitialCBB, GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, CBBssbo); + glUseProgram(FullScreenShader::LightspaceBoundingBoxShader::getInstance()->Program); FullScreenShader::LightspaceBoundingBoxShader::getInstance()->SetTextureUnits(getDepthStencilTexture()); - FullScreenShader::LightspaceBoundingBoxShader::getInstance()->setUniforms(m_suncam->getViewMatrix(), tmpshadowSplit[1], tmpshadowSplit[2], tmpshadowSplit[3], tmpshadowSplit[4]); + FullScreenShader::LightspaceBoundingBoxShader::getInstance()->setUniforms(m_suncam->getViewMatrix(), shadowSplit[1], shadowSplit[2], shadowSplit[3], shadowSplit[4]); glDispatchCompute((int)width / 64, (int)height / 64, 1); - /* glUseProgram(FullScreenShader::DepthHistogramShader::getInstance()->Program); - FullScreenShader::DepthHistogramShader::getInstance()->SetTextureUnits(getDepthStencilTexture()); - FullScreenShader::DepthHistogramShader::getInstance()->setUniforms(); - glDispatchCompute((int)width / 32, (int)height / 32, 1);*/ + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, tempShadowMatssbo); + glBufferData(GL_SHADER_STORAGE_BUFFER, 4 * 16 * sizeof(float), 0, GL_STATIC_COPY); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, tempShadowMatssbo); + + glUseProgram(FullScreenShader::ShadowMatrixesGenerationShader::getInstance()->Program); + FullScreenShader::ShadowMatrixesGenerationShader::getInstance()->setUniforms(m_suncam->getViewMatrix()); + glDispatchCompute(4, 1, 1); glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); - LightcoordBBFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - - currentCBB = (currentCBB + 1) % 2; - + glBindBuffer(GL_COPY_READ_BUFFER, tempShadowMatssbo); + glBindBuffer(GL_COPY_WRITE_BUFFER, SharedObject::ViewProjectionMatrixesUBO); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 80 * sizeof(float), 4 * 16 * sizeof(float)); } /** Generate View, Projection, Inverse View, Inverse Projection, ViewProjection and InverseProjection matrixes @@ -281,60 +218,42 @@ void IrrDriver::computeMatrixesAndCameras(scene::ICameraSceneNode * const camnod for (unsigned i = 0; i < 4; i++) { core::matrix4 tmp_matrix; - if (!CVS->isSDSMEnabled()) - { - camnode->setFarValue(FarValues[i]); - camnode->setNearValue(NearValues[i]); - camnode->render(); - const scene::SViewFrustum *frustrum = camnode->getViewFrustum(); - float tmp[24] = { - frustrum->getFarLeftDown().X, - frustrum->getFarLeftDown().Y, - frustrum->getFarLeftDown().Z, - frustrum->getFarLeftUp().X, - frustrum->getFarLeftUp().Y, - frustrum->getFarLeftUp().Z, - frustrum->getFarRightDown().X, - frustrum->getFarRightDown().Y, - frustrum->getFarRightDown().Z, - frustrum->getFarRightUp().X, - frustrum->getFarRightUp().Y, - frustrum->getFarRightUp().Z, - frustrum->getNearLeftDown().X, - frustrum->getNearLeftDown().Y, - frustrum->getNearLeftDown().Z, - frustrum->getNearLeftUp().X, - frustrum->getNearLeftUp().Y, - frustrum->getNearLeftUp().Z, - frustrum->getNearRightDown().X, - frustrum->getNearRightDown().Y, - frustrum->getNearRightDown().Z, - frustrum->getNearRightUp().X, - frustrum->getNearRightUp().Y, - frustrum->getNearRightUp().Z, - }; - memcpy(m_shadows_cam[i], tmp, 24 * sizeof(float)); - std::vector vectors = getFrustrumVertex(*frustrum); - tmp_matrix = getTighestFitOrthoProj(SunCamViewMatrix, vectors, m_shadow_scales[i]); - } - else - { - float left = float(CBB[currentCBB][i].xmin / 4 - 2); - float right = float(CBB[currentCBB][i].xmax / 4 + 2); - float up = float(CBB[currentCBB][i].ymin / 4 - 2); - float down = float(CBB[currentCBB][i].ymax / 4 + 2); + camnode->setFarValue(FarValues[i]); + camnode->setNearValue(NearValues[i]); + camnode->render(); + const scene::SViewFrustum *frustrum = camnode->getViewFrustum(); + float tmp[24] = { + frustrum->getFarLeftDown().X, + frustrum->getFarLeftDown().Y, + frustrum->getFarLeftDown().Z, + frustrum->getFarLeftUp().X, + frustrum->getFarLeftUp().Y, + frustrum->getFarLeftUp().Z, + frustrum->getFarRightDown().X, + frustrum->getFarRightDown().Y, + frustrum->getFarRightDown().Z, + frustrum->getFarRightUp().X, + frustrum->getFarRightUp().Y, + frustrum->getFarRightUp().Z, + frustrum->getNearLeftDown().X, + frustrum->getNearLeftDown().Y, + frustrum->getNearLeftDown().Z, + frustrum->getNearLeftUp().X, + frustrum->getNearLeftUp().Y, + frustrum->getNearLeftUp().Z, + frustrum->getNearRightDown().X, + frustrum->getNearRightDown().Y, + frustrum->getNearRightDown().Z, + frustrum->getNearRightUp().X, + frustrum->getNearRightUp().Y, + frustrum->getNearRightUp().Z, + }; + memcpy(m_shadows_cam[i], tmp, 24 * sizeof(float)); + + std::vector vectors = getFrustrumVertex(*frustrum); + tmp_matrix = getTighestFitOrthoProj(SunCamViewMatrix, vectors, m_shadow_scales[i]); - // Prevent Matrix without extend - if (left != right && up != down) - { - tmp_matrix.buildProjectionMatrixOrthoLH(left, right, - down, up, - float(CBB[currentCBB][i].zmin / 4 - 100), - float(CBB[currentCBB][i].zmax / 4 + 2)); - m_shadow_scales[i] = std::make_pair(right - left, down - up); - } - } m_shadow_camnodes[i]->setProjectionMatrix(tmp_matrix, true); m_shadow_camnodes[i]->render(); @@ -382,7 +301,13 @@ void IrrDriver::computeMatrixesAndCameras(scene::ICameraSceneNode * const camnod tmp[144] = float(width); tmp[145] = float(height); glBindBuffer(GL_UNIFORM_BUFFER, SharedObject::ViewProjectionMatrixesUBO); - glBufferSubData(GL_UNIFORM_BUFFER, 0, (16 * 9 + 2) * sizeof(float), tmp); + if (CVS->isSDSMEnabled()) + { + glBufferSubData(GL_UNIFORM_BUFFER, 0, (16 * 5) * sizeof(float), tmp); + glBufferSubData(GL_UNIFORM_BUFFER, (16 * 9) * sizeof(float), 2 * sizeof(float), &tmp[144]); + } + else + glBufferSubData(GL_UNIFORM_BUFFER, 0, (16 * 9 + 2) * sizeof(float), tmp); } From af265f8345ef3d1bf3dba0709380bb6df5e9462d Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Mon, 23 Feb 2015 22:43:11 +0100 Subject: [PATCH 088/117] Use 30 fps in menu --- src/main_loop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 730b663a6..cc1d4e37a 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -77,7 +77,7 @@ float MainLoop::getLimitedDt() // Throttle fps if more than maximum, which can reduce // the noise the fan on a graphics card makes. // When in menus, reduce FPS much, it's not necessary to push to the maximum for plain menus - const int max_fps = (StateManager::get()->throttleFPS() ? 35 : UserConfigParams::m_max_fps); + const int max_fps = (StateManager::get()->throttleFPS() ? 30 : UserConfigParams::m_max_fps); const int current_fps = (int)(1000.0f/dt); if (m_throttle_fps && current_fps > max_fps && !ProfileWorld::isProfileMode()) { From 419b96c068d1129133a9b247e3541ccd60bdb77e Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Tue, 24 Feb 2015 22:08:49 +0100 Subject: [PATCH 089/117] Do not use extra uv for mlaa --- data/shaders/mlaa_blend2.frag | 3 +-- data/shaders/mlaa_color1.frag | 17 ++++++++++------- data/shaders/mlaa_neigh3.frag | 21 ++++++++++++--------- src/graphics/post_processing.cpp | 15 +++------------ src/graphics/shaders.cpp | 7 ++----- src/graphics/shaders.hpp | 6 ------ 6 files changed, 28 insertions(+), 41 deletions(-) diff --git a/data/shaders/mlaa_blend2.frag b/data/shaders/mlaa_blend2.frag index 1e12621f9..40ea9cfa1 100644 --- a/data/shaders/mlaa_blend2.frag +++ b/data/shaders/mlaa_blend2.frag @@ -4,8 +4,6 @@ uniform sampler2D areaMap; #define MAX_SEARCH_STEPS 8.0 #define MAX_DISTANCE 33.0 -in vec2 uv; - out vec4 FragColor; /** @@ -75,6 +73,7 @@ vec2 Area(vec2 distance, float e1, float e2) { void main() { vec4 areas = vec4(0.0); + vec2 uv = gl_FragCoord.xy / screen; vec2 e = texture(edgesMap, uv).rg; diff --git a/data/shaders/mlaa_color1.frag b/data/shaders/mlaa_color1.frag index 81d4642b5..dfb8598a5 100644 --- a/data/shaders/mlaa_color1.frag +++ b/data/shaders/mlaa_color1.frag @@ -1,8 +1,5 @@ uniform sampler2D colorMapG; -in vec4 offset[2]; -in vec2 uv; - const float threshold = 0.1; out vec4 FragColor; @@ -10,14 +7,20 @@ out vec4 FragColor; void main() { vec3 weights = vec3(0.2126,0.7152, 0.0722); // ITU-R BT. 709 + vec2 uv = gl_FragCoord.xy / screen; + vec2 uv_left = uv + vec2(-1., 0.) / screen; + vec2 uv_top = uv + vec2(0., 1.) / screen; + vec2 uv_right = uv + vec2(1., 0.) / screen; + vec2 uv_bottom = uv + vec2(0., -1.) / screen; + /** * Luma calculation requires gamma-corrected colors: */ float L = dot(texture(colorMapG, uv).rgb, weights); - float Lleft = dot(texture(colorMapG, offset[0].xy).rgb, weights); - float Ltop = dot(texture(colorMapG, offset[0].zw).rgb, weights); - float Lright = dot(texture(colorMapG, offset[1].xy).rgb, weights); - float Lbottom = dot(texture(colorMapG, offset[1].zw).rgb, weights); + float Lleft = dot(texture(colorMapG, uv_left).rgb, weights); + float Ltop = dot(texture(colorMapG, uv_top).rgb, weights); + float Lright = dot(texture(colorMapG, uv_right).rgb, weights); + float Lbottom = dot(texture(colorMapG, uv_bottom).rgb, weights); vec4 delta = abs(vec4(L) - vec4(Lleft, Ltop, Lright, Lbottom)); vec4 edges = step(vec4(threshold), delta); diff --git a/data/shaders/mlaa_neigh3.frag b/data/shaders/mlaa_neigh3.frag index 3e8d03033..47a452466 100644 --- a/data/shaders/mlaa_neigh3.frag +++ b/data/shaders/mlaa_neigh3.frag @@ -1,16 +1,19 @@ uniform sampler2D blendMap; uniform sampler2D colorMap; -in vec4 offset[2]; -in vec2 uv; - out vec4 FragColor; void main() { + vec2 uv = gl_FragCoord.xy / screen; + vec2 uv_left = uv + vec2(-1., 0.) / screen; + vec2 uv_top = uv + vec2(0., 1.) / screen; + vec2 uv_right = uv + vec2(1., 0.) / screen; + vec2 uv_bottom = uv + vec2(0., -1.) / screen; + // Fetch the blending weights for current pixel: vec4 topLeft = texture(blendMap, uv); - float bottom = texture(blendMap, offset[1].zw).g; - float right = texture(blendMap, offset[1].xy).a; + float bottom = texture(blendMap, uv_bottom).g; + float right = texture(blendMap, uv_right).a; vec4 a = vec4(topLeft.r, bottom, topLeft.b, right); // Up to 4 lines can be crossing a pixel (one in each edge). So, we perform @@ -27,10 +30,10 @@ void main() { // Add the contributions of the possible 4 lines that can cross this pixel: vec4 C = texture(colorMap, uv); - vec4 Cleft = texture(colorMap, offset[0].xy); - vec4 Ctop = texture(colorMap, offset[0].zw); - vec4 Cright = texture(colorMap, offset[1].xy); - vec4 Cbottom = texture(colorMap, offset[1].zw); + vec4 Cleft = texture(colorMap, uv_left); + vec4 Ctop = texture(colorMap, uv_top); + vec4 Cright = texture(colorMap, uv_right); + vec4 Cbottom = texture(colorMap, uv_bottom); color = mix(C, Ctop, a.r) * w.r + color; color = mix(C, Cbottom, a.g) * w.g + color; color = mix(C, Cleft, a.b) * w.b + color; diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index c6f922cfd..82f470236 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -600,10 +600,7 @@ void PostProcessing::applyMLAA() // Pass 1: color edge detection glUseProgram(FullScreenShader::MLAAColorEdgeDetectionSHader::getInstance()->Program); FullScreenShader::MLAAColorEdgeDetectionSHader::getInstance()->SetTextureUnits(irr_driver->getRenderTargetTexture(RTT_MLAA_COLORS)); - FullScreenShader::MLAAColorEdgeDetectionSHader::getInstance()->setUniforms(PIXEL_SIZE); - - glBindVertexArray(FullScreenShader::MLAAColorEdgeDetectionSHader::getInstance()->vao); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + DrawFullScreenEffect(PIXEL_SIZE); glStencilFunc(GL_EQUAL, 1, ~0); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); @@ -614,10 +611,7 @@ void PostProcessing::applyMLAA() glUseProgram(FullScreenShader::MLAABlendWeightSHader::getInstance()->Program); FullScreenShader::MLAABlendWeightSHader::getInstance()->SetTextureUnits(irr_driver->getRenderTargetTexture(RTT_MLAA_TMP), getTextureGLuint(m_areamap)); - FullScreenShader::MLAABlendWeightSHader::getInstance()->setUniforms(PIXEL_SIZE); - - glBindVertexArray(FullScreenShader::MLAABlendWeightSHader::getInstance()->vao); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + DrawFullScreenEffect(PIXEL_SIZE); // Blit in to tmp1 FrameBuffer::Blit(irr_driver->getFBO(FBO_MLAA_COLORS), irr_driver->getFBO(FBO_MLAA_TMP)); @@ -627,10 +621,7 @@ void PostProcessing::applyMLAA() glUseProgram(FullScreenShader::MLAAGatherSHader::getInstance()->Program); FullScreenShader::MLAAGatherSHader::getInstance()->SetTextureUnits(irr_driver->getRenderTargetTexture(RTT_MLAA_BLEND), irr_driver->getRenderTargetTexture(RTT_MLAA_TMP)); - FullScreenShader::MLAAGatherSHader::getInstance()->setUniforms(PIXEL_SIZE); - - glBindVertexArray(FullScreenShader::MLAAGatherSHader::getInstance()->vao); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + DrawFullScreenEffect(PIXEL_SIZE); // Done. glDisable(GL_STENCIL_TEST); diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index a6712472c..d9c58e27d 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -1995,12 +1995,11 @@ namespace FullScreenShader MLAAColorEdgeDetectionSHader::MLAAColorEdgeDetectionSHader() { Program = LoadProgram(OBJECT, - GL_VERTEX_SHADER, file_manager->getAsset("shaders/mlaa_offset.vert").c_str(), + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/mlaa_color1.frag").c_str()); AssignUniforms("PIXEL_SIZE"); AssignSamplerNames(Program, 0, "colorMapG"); - vao = createVAO(Program); } MLAABlendWeightSHader::MLAABlendWeightSHader() @@ -2011,18 +2010,16 @@ namespace FullScreenShader AssignUniforms("PIXEL_SIZE"); AssignSamplerNames(Program, 0, "edgesMap", 1, "areaMap"); - vao = createVAO(Program); } MLAAGatherSHader::MLAAGatherSHader() { Program = LoadProgram(OBJECT, - GL_VERTEX_SHADER, file_manager->getAsset("shaders/mlaa_offset.vert").c_str(), + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/mlaa_neigh3.frag").c_str()); AssignUniforms("PIXEL_SIZE"); AssignSamplerNames(Program, 0, "blendMap", 1, "colorMap"); - vao = createVAO(Program); } } diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 1a34ea362..d815ae86f 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -635,24 +635,18 @@ public: class MLAAColorEdgeDetectionSHader : public ShaderHelperSingleton, public TextureRead { public: - GLuint vao; - MLAAColorEdgeDetectionSHader(); }; class MLAABlendWeightSHader : public ShaderHelperSingleton, public TextureRead { public: - GLuint vao; - MLAABlendWeightSHader(); }; class MLAAGatherSHader : public ShaderHelperSingleton, public TextureRead { public: - GLuint vao; - MLAAGatherSHader(); }; From 26b38a3afe3e8b9e9c580e7c677e7b7f68b17d70 Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Tue, 24 Feb 2015 22:29:31 +0100 Subject: [PATCH 090/117] GodRay use correct screencoord --- data/shaders/godfade.frag | 3 ++- data/shaders/godray.frag | 2 +- data/shaders/mlaa_offset.vert | 15 --------------- src/graphics/post_processing.cpp | 12 ++---------- src/graphics/shaders.cpp | 2 -- src/graphics/shaders.hpp | 4 ---- 6 files changed, 5 insertions(+), 33 deletions(-) delete mode 100644 data/shaders/mlaa_offset.vert diff --git a/data/shaders/godfade.frag b/data/shaders/godfade.frag index 7380d6e05..051565b00 100644 --- a/data/shaders/godfade.frag +++ b/data/shaders/godfade.frag @@ -1,11 +1,12 @@ uniform sampler2D tex; uniform vec3 col; -in vec2 uv; out vec4 FragColor; void main() { + // Use quarter resolution + vec2 uv = 4. * gl_FragCoord.xy / screen; vec4 res = texture(tex, uv); // Keep the sun fully bright, but fade the sky diff --git a/data/shaders/godray.frag b/data/shaders/godray.frag index 4a0a9e1c7..7cb7d2e12 100644 --- a/data/shaders/godray.frag +++ b/data/shaders/godray.frag @@ -5,11 +5,11 @@ uniform vec2 sunpos; const float decaystep = 0.88; -in vec2 uv; out vec4 FragColor; void main() { + vec2 uv = 4. * gl_FragCoord.xy / screen; vec2 texc = uv; vec2 tosun = sunpos - texc; diff --git a/data/shaders/mlaa_offset.vert b/data/shaders/mlaa_offset.vert deleted file mode 100644 index 1a7a01c21..000000000 --- a/data/shaders/mlaa_offset.vert +++ /dev/null @@ -1,15 +0,0 @@ -in vec2 Position; -in vec2 Texcoord; - -out vec4 offset[2]; -out vec2 uv; - -void main() { - gl_Position = vec4(Position, 0., 1.); - vec4 invy = vec4(Texcoord, Texcoord); -// invy.y = 1.0 - invy.y; - uv = invy.st; - - offset[0] = invy.xyxy + vec4(-1.0, 0.0, 0.0, 1.0) / screen.xyxy; - offset[1] = invy.xyxy + vec4( 1.0, 0.0, 0.0, -1.0) / screen.xyxy; -} diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index 82f470236..6b43506de 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -554,22 +554,14 @@ void PostProcessing::renderMotionBlur(unsigned , FrameBuffer &in_fbo, FrameBuffe static void renderGodFade(GLuint tex, const SColor &col) { - glUseProgram(FullScreenShader::GodFadeShader::getInstance()->Program); - glBindVertexArray(FullScreenShader::GodFadeShader::getInstance()->vao); FullScreenShader::GodFadeShader::getInstance()->SetTextureUnits(tex); - FullScreenShader::GodFadeShader::getInstance()->setUniforms(col); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + DrawFullScreenEffect(col); } static void renderGodRay(GLuint tex, const core::vector2df &sunpos) { - glUseProgram(FullScreenShader::GodRayShader::getInstance()->Program); - glBindVertexArray(FullScreenShader::GodRayShader::getInstance()->vao); FullScreenShader::GodRayShader::getInstance()->SetTextureUnits(tex); - FullScreenShader::GodRayShader::getInstance()->setUniforms(sunpos); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + DrawFullScreenEffect(sunpos); } static void toneMap(FrameBuffer &fbo, GLuint rtt, float vignette_weight) diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index d9c58e27d..532708a67 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -1978,7 +1978,6 @@ namespace FullScreenShader AssignUniforms("col"); AssignSamplerNames(Program, 0, "tex"); - vao = createVAO(Program); } GodRayShader::GodRayShader() @@ -1989,7 +1988,6 @@ namespace FullScreenShader AssignUniforms("sunpos"); AssignSamplerNames(Program, 0, "tex"); - vao = createVAO(Program); } MLAAColorEdgeDetectionSHader::MLAAColorEdgeDetectionSHader() diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index d815ae86f..50a48a759 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -619,16 +619,12 @@ public: class GodFadeShader : public ShaderHelperSingleton, public TextureRead { public: - GLuint vao; - GodFadeShader(); }; class GodRayShader : public ShaderHelperSingleton, public TextureRead { public: - GLuint vao; - GodRayShader(); }; From 58d9853542bd57ea74602bc5f5edbd68eed2c0a8 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 26 Feb 2015 09:36:08 +1100 Subject: [PATCH 091/117] Added debug name to tires dropped in battle mode. --- src/modes/three_strikes_battle.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 24f8d5087..abdc9c4e6 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -1,5 +1,3 @@ - - // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2006-2013 SuperTuxKart-Team // @@ -342,6 +340,10 @@ void ThreeStrikesBattle::update(float dt) TrackObjectPresentationMesh* tire_presentation = new TrackObjectPresentationMesh(tire, tire_xyz, tire_hpr, tire_scale); +#ifdef DEBUG + tire_presentation->getNode()->setName("Tire on ground"); +#endif + TrackObject* tire_obj = new TrackObject(tire_xyz, tire_hpr, tire_scale, "movable", tire_presentation, true /* is_dynamic */, From 832aefeceb7e821ebd77d82973c0087ff3fec16e Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 26 Feb 2015 16:29:10 +1100 Subject: [PATCH 092/117] Bugfix: A user that was not online previously could not enter an online username if a user with an online user name was shown before. --- src/states_screens/user_screen.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/states_screens/user_screen.cpp b/src/states_screens/user_screen.cpp index a480ea3fa..b72f00259 100644 --- a/src/states_screens/user_screen.cpp +++ b/src/states_screens/user_screen.cpp @@ -280,6 +280,10 @@ void BaseUserScreen::makeEntryFieldsVisible() { getWidget("label_password")->setVisible(online); m_password_tb->setVisible(online); + // Is user has no online name, make sure the user can enter one + if (player->getLastOnlineName().empty()) + m_username_tb->setActivated(); + } } // makeEntryFieldsVisible From 988f5ea933c925fae1d4653bc89c1da4c99c2715 Mon Sep 17 00:00:00 2001 From: Deve Date: Thu, 26 Feb 2015 20:16:39 +0100 Subject: [PATCH 093/117] Hopefully proper fix for #2007 --- data/shaders/Lightspaceboundingbox.comp | 2 +- data/shaders/bilateralH.comp | 2 +- data/shaders/bilateralV.comp | 2 +- data/shaders/gaussian6h.comp | 2 +- data/shaders/gaussian6v.comp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/shaders/Lightspaceboundingbox.comp b/data/shaders/Lightspaceboundingbox.comp index 602da12e8..933517e71 100644 --- a/data/shaders/Lightspaceboundingbox.comp +++ b/data/shaders/Lightspaceboundingbox.comp @@ -49,7 +49,7 @@ void main() ivec3 lmax3 = ivec3(-1000); ivec3 lmin3 = ivec3(1000); - vec2 start_xy = gl_LocalInvocationID.xy + gl_WorkGroupID.xy * gl_WorkGroupSize.xy * 8 + .5; + vec2 start_xy = gl_LocalInvocationID.xy + gl_WorkGroupID.xy * gl_WorkGroupSize.xy * 8 + vec2(0.5); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { diff --git a/data/shaders/bilateralH.comp b/data/shaders/bilateralH.comp index a249cd321..76da31d9d 100644 --- a/data/shaders/bilateralH.comp +++ b/data/shaders/bilateralH.comp @@ -15,7 +15,7 @@ void main() { int x = int(gl_LocalInvocationID.x), y = int(gl_LocalInvocationID.y); ivec2 iuv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); - vec2 guv = gl_GlobalInvocationID.xy + .5; + vec2 guv = gl_GlobalInvocationID.xy + vec2(0.5); vec2 uv_m = (guv - vec2(8, 0)) * pixel; vec2 uv = guv * pixel; vec2 uv_p = (guv + vec2(8, 0)) * pixel; diff --git a/data/shaders/bilateralV.comp b/data/shaders/bilateralV.comp index caa9d80d6..af2d7c3db 100644 --- a/data/shaders/bilateralV.comp +++ b/data/shaders/bilateralV.comp @@ -15,7 +15,7 @@ void main() { int x = int(gl_LocalInvocationID.x), y = int(gl_LocalInvocationID.y); ivec2 iuv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); - vec2 guv = gl_GlobalInvocationID.xy + .5; + vec2 guv = gl_GlobalInvocationID.xy + vec2(0.5); vec2 uv_m = (guv - vec2(0, 8)) * pixel; vec2 uv = guv * pixel; vec2 uv_p = (guv + vec2(0, 8)) * pixel; diff --git a/data/shaders/gaussian6h.comp b/data/shaders/gaussian6h.comp index c58b02d1d..62f54f821 100644 --- a/data/shaders/gaussian6h.comp +++ b/data/shaders/gaussian6h.comp @@ -13,7 +13,7 @@ void main() { int x = int(gl_LocalInvocationID.x), y = int(gl_LocalInvocationID.y); ivec2 iuv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); - vec2 guv = gl_GlobalInvocationID.xy + .5; + vec2 guv = gl_GlobalInvocationID.xy + vec2(0.5); vec2 uv_m = (guv - vec2(6, 0)) * pixel; vec2 uv = guv * pixel; vec2 uv_p = (guv + vec2(6, 0)) * pixel; diff --git a/data/shaders/gaussian6v.comp b/data/shaders/gaussian6v.comp index 6924f897a..0878e0822 100644 --- a/data/shaders/gaussian6v.comp +++ b/data/shaders/gaussian6v.comp @@ -13,7 +13,7 @@ void main() { int x = int(gl_LocalInvocationID.x), y = int(gl_LocalInvocationID.y); ivec2 iuv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); - vec2 guv = gl_GlobalInvocationID.xy + .5; + vec2 guv = gl_GlobalInvocationID.xy + vec2(0.5); vec2 uv_m = (guv - vec2(0, 6)) * pixel; vec2 uv = guv * pixel; vec2 uv_p = (guv + vec2(0, 6)) * pixel; From f542aea53d4e32ba0a97a216f9dbfc870c96fc3d Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 27 Feb 2015 22:33:03 +1100 Subject: [PATCH 094/117] Fixed line endings style. --- src/main.cpp | 20 ++++++++++---------- src/states_screens/user_screen.cpp | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3ee1bdb09..d9557221b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -402,16 +402,16 @@ bool isEasterMode(int day, int month, int year, int before_after_days) { // Compute Easter date, based on wikipedia formula // http://en.wikipedia.org/wiki/Computus - int a = year % 19; - int b = year >> 2; - int c = int(floor(b / 25)) + 1; - int d = (c * 3) >> 2; - int e = ((a * 19) - int(floor((c * 8 + 5) / 25)) + d + 15) % 30; - e += (29578 - a - e * 32) >> 10; - e -= ((year % 7) + b - d + e + 2) % 7; - d = e >> 5; - int easter_day = e - d * 31; - int easter_month = d + 3; + int a = year % 19; + int b = year >> 2; + int c = int(floor(b / 25)) + 1; + int d = (c * 3) >> 2; + int e = ((a * 19) - int(floor((c * 8 + 5) / 25)) + d + 15) % 30; + e += (29578 - a - e * 32) >> 10; + e -= ((year % 7) + b - d + e + 2) % 7; + d = e >> 5; + int easter_day = e - d * 31; + int easter_month = d + 3; int easter_start_day = easter_day - before_after_days; int easter_start_month = easter_month; diff --git a/src/states_screens/user_screen.cpp b/src/states_screens/user_screen.cpp index b72f00259..2f12a5c99 100644 --- a/src/states_screens/user_screen.cpp +++ b/src/states_screens/user_screen.cpp @@ -281,8 +281,8 @@ void BaseUserScreen::makeEntryFieldsVisible() getWidget("label_password")->setVisible(online); m_password_tb->setVisible(online); // Is user has no online name, make sure the user can enter one - if (player->getLastOnlineName().empty()) - m_username_tb->setActivated(); + if (player->getLastOnlineName().empty()) + m_username_tb->setActivated(); } } // makeEntryFieldsVisible From 27b80d2c4d48bcac7f78c52f87c26216b6b06128 Mon Sep 17 00:00:00 2001 From: hiker Date: Sat, 28 Feb 2015 00:40:14 +1100 Subject: [PATCH 095/117] Fix #1999 (split screen not workong in hd3000). --- src/graphics/render.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 3df4ffb43..dbdd32fba 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -385,6 +385,12 @@ void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned po if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_WORKING)) glDisable(GL_FRAMEBUFFER_SRGB); m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind(); + // Bind() modifies the viewport. In order not to affect anything else, + // the viewport is just reset here and not removed in Bind(). + const core::recti &vp = Camera::getActiveCamera()->getViewport(); + glViewport(vp.UpperLeftCorner.X, vp.UpperLeftCorner.Y, + vp.LowerRightCorner.X - vp.UpperLeftCorner.X, + vp.LowerRightCorner.Y - vp.UpperLeftCorner.Y); glClear(GL_DEPTH_BUFFER_BIT); if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_WORKING)) glEnable(GL_FRAMEBUFFER_SRGB); From d6466f82cd6062381913f6b08d187d46824ee155 Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 28 Feb 2015 22:38:38 +0100 Subject: [PATCH 096/117] Fixed #2008 --- src/graphics/render.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index dbdd32fba..e08ba70a1 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -254,13 +254,13 @@ void IrrDriver::renderGLSL(float dt) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); - m_post_processing->renderPassThrough(m_rtts->getFBO(FBO_HALF1_R).getRTT()[0], viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); + m_post_processing->renderPassThrough(m_rtts->getFBO(FBO_HALF1_R).getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); } else if (irr_driver->getRSM()) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); - m_post_processing->renderPassThrough(m_rtts->getRSM().getRTT()[0], viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); + m_post_processing->renderPassThrough(m_rtts->getRSM().getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); } else if (irr_driver->getShadowViz()) { @@ -272,7 +272,7 @@ void IrrDriver::renderGLSL(float dt) glBindFramebuffer(GL_FRAMEBUFFER, 0); if (CVS->isDefferedEnabled()) camera->activate(); - m_post_processing->renderPassThrough(fbo->getRTT()[0], viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); + m_post_processing->renderPassThrough(fbo->getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); glDisable(GL_FRAMEBUFFER_SRGB); } } From c205aa533b142a3abf684d2f3fff47a5b99424a7 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sat, 28 Feb 2015 20:41:12 -0500 Subject: [PATCH 097/117] Add outline on font for instructions in the overworld, fixing cases where the text is hard to read --- src/guiengine/engine.cpp | 6 ++++++ src/guiengine/engine.hpp | 3 +++ src/states_screens/race_gui_overworld.cpp | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/guiengine/engine.cpp b/src/guiengine/engine.cpp index 2a367198c..37406b954 100644 --- a/src/guiengine/engine.cpp +++ b/src/guiengine/engine.cpp @@ -689,6 +689,7 @@ namespace GUIEngine IGUIEnvironment* g_env; Skin* g_skin = NULL; ScalableFont *g_font; + ScalableFont *g_outline_font; ScalableFont *g_large_font; ScalableFont *g_title_font; ScalableFont *g_small_font; @@ -974,6 +975,8 @@ namespace GUIEngine g_large_font = NULL; g_digit_font->drop(); g_digit_font = NULL; + g_outline_font->drop(); + g_outline_font = NULL; // nothing else to delete for now AFAIK, irrlicht will automatically // kill everything along the device @@ -1080,6 +1083,9 @@ namespace GUIEngine sfont_larger->setKerningHeight(-5); g_large_font = sfont_larger; + g_outline_font = sfont->getHollowCopy(); + g_outline_font->m_black_border = true; + Private::large_font_height = g_large_font->getDimension( L"X" ).Height; ScalableFont* sfont_smaller = sfont->getHollowCopy(); diff --git a/src/guiengine/engine.hpp b/src/guiengine/engine.hpp index 9669503c7..a1c869bd3 100644 --- a/src/guiengine/engine.hpp +++ b/src/guiengine/engine.hpp @@ -83,6 +83,7 @@ namespace GUIEngine extern Skin* g_skin; extern irr::gui::ScalableFont* g_small_font; extern irr::gui::ScalableFont* g_font; + extern irr::gui::ScalableFont* g_outline_font; extern irr::gui::ScalableFont* g_large_font; extern irr::gui::ScalableFont* g_title_font; extern irr::gui::ScalableFont* g_digit_font; @@ -136,6 +137,8 @@ namespace GUIEngine */ inline irr::gui::ScalableFont* getFont() { return Private::g_font; } + inline irr::gui::ScalableFont* getOutlineFont() { return Private::g_outline_font; } + /** * \return the "large" font (useful for text) */ diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index c0cc5a82d..6b88045d7 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -459,7 +459,7 @@ void RaceGUIOverworld::drawGlobalMiniMap() UserConfigParams::m_height - GUIEngine::getFontHeight()*2, UserConfigParams::m_width, UserConfigParams::m_height); - GUIEngine::getFont()->draw(_("Press fire to play the tutorial"), pos2, + GUIEngine::getOutlineFont()->draw(_("Press fire to play the tutorial"), pos2, video::SColor(255,255,150,60), true, true /* vcenter */, NULL); continue; @@ -529,7 +529,7 @@ void RaceGUIOverworld::drawGlobalMiniMap() UserConfigParams::m_height - GUIEngine::getFontHeight()*2, UserConfigParams::m_width, UserConfigParams::m_height); - GUIEngine::getFont()->draw(_("Press fire to start the challenge"), pos2, + GUIEngine::getOutlineFont()->draw(_("Press fire to start the challenge"), pos2, video::SColor(255,255,150,60), true, true /* vcenter */, NULL); } From b7fb159de4ca2992de663648cc43a5bf1cd63bc6 Mon Sep 17 00:00:00 2001 From: hiker Date: Sun, 1 Mar 2015 19:41:28 +1100 Subject: [PATCH 098/117] Fix #2000 (missing variable initialisation). --- src/tracks/track_object_presentation.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 89cd36f8b..7f634f095 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -300,6 +300,7 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh( TrackObjectPresentationSceneNode(xyz, hpr, scale) { m_is_looped = false; + m_is_in_skybox = false; m_mesh = NULL; m_node = NULL; @@ -315,6 +316,7 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh( m_is_looped = false; m_mesh = NULL; m_node = NULL; + m_is_in_skybox = false; bool animated = (UserConfigParams::m_graphical_effects || World::getWorld()->getIdent() == IDENT_CUTSCENE); From cab6149193ddd80e9c0d0d18fb496bf696825568 Mon Sep 17 00:00:00 2001 From: hiker Date: Sun, 1 Mar 2015 22:46:03 +1100 Subject: [PATCH 099/117] Cosmetic change for coding style (removed 2 unnecessary constructors). --- src/states_screens/grand_prix_lose.cpp | 8 +- src/states_screens/grand_prix_win.cpp | 8 +- src/tracks/track_object_presentation.cpp | 389 ++++++++++++----------- src/tracks/track_object_presentation.hpp | 253 ++++++++------- 4 files changed, 351 insertions(+), 307 deletions(-) diff --git a/src/states_screens/grand_prix_lose.cpp b/src/states_screens/grand_prix_lose.cpp index e5c316fb0..4b63c630f 100644 --- a/src/states_screens/grand_prix_lose.cpp +++ b/src/states_screens/grand_prix_lose.cpp @@ -199,9 +199,11 @@ void GrandPrixLose::setKarts(std::vector ident_arg) core::vector3df kart_rot(0, 90.0f, 0); core::vector3df kart_scale(KART_SCALE, KART_SCALE, KART_SCALE); - //FIXME: it's not ideal that both the track object and the presentation know the initial coordinates of the object - TrackObjectPresentationSceneNode* presentation = new TrackObjectPresentationSceneNode( - kart_main_node, kart_pos, kart_rot, kart_scale); + //FIXME: it's not ideal that both the track object and the + // presentation know the initial coordinates of the object + TrackObjectPresentationSceneNode* presentation = + new TrackObjectPresentationSceneNode(kart_pos, kart_rot, + kart_scale, kart_main_node); TrackObject* tobj = new TrackObject(kart_pos, kart_rot, kart_scale, "ghost", presentation, false /* isDynamic */, NULL /* physics settings */); tobjman->insertObject(tobj); diff --git a/src/states_screens/grand_prix_win.cpp b/src/states_screens/grand_prix_win.cpp index 8e900c2bf..b48626b88 100644 --- a/src/states_screens/grand_prix_win.cpp +++ b/src/states_screens/grand_prix_win.cpp @@ -313,9 +313,11 @@ void GrandPrixWin::setKarts(const std::string idents_arg[3]) core::vector3df kart_rot(0, 0, 0); core::vector3df kart_scale(1.0f, 1.0f, 1.0f); - //FIXME: it's not ideal that both the track object and the presentation know the initial coordinates of the object - TrackObjectPresentationSceneNode* presentation = new TrackObjectPresentationSceneNode( - kart_main_node, kart_pos, kart_rot, kart_scale); + //FIXME: it's not ideal that both the track object and the presentation + // know the initial coordinates of the object + TrackObjectPresentationSceneNode* presentation = + new TrackObjectPresentationSceneNode(kart_pos, kart_rot, kart_scale, + kart_main_node); TrackObject* tobj = new TrackObject(kart_pos, kart_rot, kart_scale, "ghost", presentation, false /* isDynamic */, NULL /* physics settings */); tobjman->insertObject(tobj); diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 7f634f095..8e324cba9 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -35,78 +35,79 @@ #include "input/input_manager.hpp" #include "items/item_manager.hpp" #include "modes/world.hpp" +#include "scriptengine/script_engine.hpp" #include "states_screens/dialogs/race_paused_dialog.hpp" #include "states_screens/dialogs/tutorial_message_dialog.hpp" #include "tracks/model_definition_loader.hpp" #include "tracks/track.hpp" #include "tracks/track_object_manager.hpp" -#include "scriptengine/script_engine.hpp" -#include -#include -#include + #include -#include +#include #include #include +#include +#include +#include // ---------------------------------------------------------------------------- - TrackObjectPresentation::TrackObjectPresentation(const XMLNode& xml_node) { m_init_xyz = core::vector3df(0,0,0); m_init_hpr = core::vector3df(0,0,0); m_init_scale = core::vector3df(1,1,1); - - if (!xml_node.get("xyz", &m_init_xyz )) + if (!xml_node.get("xyz", &m_init_xyz )) { // support for old deprecated syntax xml_node.getXYZ(&m_init_xyz); } - xml_node.get("hpr", &m_init_hpr ); + xml_node.get("hpr", &m_init_hpr ); xml_node.get("scale", &m_init_scale); -} - +} // TrackObjectPresentation // ---------------------------------------------------------------------------- - const core::vector3df& TrackObjectPresentationSceneNode::getPosition() const { if (m_node == NULL) return m_init_xyz; return m_node->getPosition(); -} +} // getPosition +// ---------------------------------------------------------------------------- const core::vector3df TrackObjectPresentationSceneNode::getAbsolutePosition() const { if (m_node == NULL) return m_init_xyz; m_node->updateAbsolutePosition(); return m_node->getAbsolutePosition(); -} +} // getAbsolutePosition - +// ---------------------------------------------------------------------------- const core::vector3df& TrackObjectPresentationSceneNode::getRotation() const { if (m_node == NULL) return m_init_hpr; return m_node->getRotation(); -} +} // getRotation +// ---------------------------------------------------------------------------- const core::vector3df& TrackObjectPresentationSceneNode::getScale() const { if (m_node == NULL) return m_init_scale; return m_node->getScale(); -} +} // getScale - -void TrackObjectPresentationSceneNode::move(const core::vector3df& xyz, const core::vector3df& hpr, - const core::vector3df& scale) +// ---------------------------------------------------------------------------- +void TrackObjectPresentationSceneNode::move(const core::vector3df& xyz, + const core::vector3df& hpr, + const core::vector3df& scale) { if (m_node == NULL) return; if (m_node->getParent() != NULL) { scene::ISceneNode* parent = m_node->getParent(); - m_node->setPosition((xyz - parent->getAbsolutePosition()) / parent->getScale()); + m_node->setPosition((xyz - parent->getAbsolutePosition()) + / parent->getScale()); } else { @@ -115,14 +116,16 @@ void TrackObjectPresentationSceneNode::move(const core::vector3df& xyz, const co m_node->setRotation(hpr); m_node->setScale(scale); m_node->updateAbsolutePosition(); -} +} // move +// ---------------------------------------------------------------------------- void TrackObjectPresentationSceneNode::setEnable(bool enabled) { if (m_node != NULL) m_node->setVisible(enabled); -} +} // setEnable +// ---------------------------------------------------------------------------- void TrackObjectPresentationSceneNode::reset() { if (m_node == NULL) return; @@ -130,30 +133,29 @@ void TrackObjectPresentationSceneNode::reset() m_node->setPosition(m_init_xyz); m_node->setRotation(m_init_hpr); m_node->setScale(m_init_scale); -} +} // reset // ---------------------------------------------------------------------------- - -TrackObjectPresentationEmpty::TrackObjectPresentationEmpty(const XMLNode& xml_node) : - TrackObjectPresentationSceneNode(xml_node) +TrackObjectPresentationEmpty::TrackObjectPresentationEmpty(const XMLNode& xml_node) + : TrackObjectPresentationSceneNode(xml_node) { m_node = irr_driver->getSceneManager()->addEmptySceneNode(); m_node->setPosition(m_init_xyz); m_node->setRotation(m_init_hpr); m_node->setScale(m_init_scale); -} +} // TrackObjectPresentationEmpty +// ---------------------------------------------------------------------------- TrackObjectPresentationEmpty::~TrackObjectPresentationEmpty() { irr_driver->removeNode(m_node); -} +} // ~TrackObjectPresentationEmpty // ---------------------------------------------------------------------------- - TrackObjectPresentationLibraryNode::TrackObjectPresentationLibraryNode( - const XMLNode& xml_node, - ModelDefinitionLoader& model_def_loader) : -TrackObjectPresentationSceneNode(xml_node) + const XMLNode& xml_node, + ModelDefinitionLoader& model_def_loader) + : TrackObjectPresentationSceneNode(xml_node) { std::string name; xml_node.get("name", &name); @@ -174,7 +176,8 @@ TrackObjectPresentationSceneNode(xml_node) libroot = file_manager->createXMLTree(lib_node_path); if (libroot == NULL) { - Log::error("TrackObjectPresentationLibraryNode", "Cannot find library '%s'", lib_node_path.c_str()); + Log::error("TrackObjectPresentationLibraryNode", + "Cannot find library '%s'", lib_node_path.c_str()); return; } @@ -201,7 +204,8 @@ TrackObjectPresentationSceneNode(xml_node) { libroot = model_def_loader.getLibraryNodes()[name]; assert(libroot != NULL); - create_lod_definitions = false; // LOD definitions are already created, don't create them again + // LOD definitions are already created, don't create them again + create_lod_definitions = false; } m_node->setPosition(m_init_xyz); @@ -212,37 +216,40 @@ TrackObjectPresentationSceneNode(xml_node) assert(libroot != NULL); World::getWorld()->getTrack()->loadObjects(libroot, lib_path, model_def_loader, create_lod_definitions, m_node); -} +} // TrackObjectPresentationLibraryNode +// ---------------------------------------------------------------------------- TrackObjectPresentationLibraryNode::~TrackObjectPresentationLibraryNode() { irr_driver->removeNode(m_node); -} +} // TrackObjectPresentationLibraryNode // ---------------------------------------------------------------------------- - TrackObjectPresentationLOD::TrackObjectPresentationLOD(const XMLNode& xml_node, - scene::ISceneNode* parent, ModelDefinitionLoader& model_def_loader) : - TrackObjectPresentationSceneNode(xml_node) + scene::ISceneNode* parent, + ModelDefinitionLoader& model_def_loader) + : TrackObjectPresentationSceneNode(xml_node) { m_node = model_def_loader.instanciateAsLOD(&xml_node, parent); if (m_node == NULL) throw std::runtime_error("Cannot load LOD node"); m_node->setPosition(m_init_xyz); m_node->setRotation(m_init_hpr); m_node->setScale(m_init_scale); -} +} // TrackObjectPresentationLOD +// ---------------------------------------------------------------------------- TrackObjectPresentationLOD::~TrackObjectPresentationLOD() { if (m_node) irr_driver->removeNode(m_node); -} +} // TrackObjectPresentationLOD // ---------------------------------------------------------------------------- - -TrackObjectPresentationMesh::TrackObjectPresentationMesh(const XMLNode& xml_node, - bool enabled, scene::ISceneNode* parent) : - TrackObjectPresentationSceneNode(xml_node) +TrackObjectPresentationMesh::TrackObjectPresentationMesh( + const XMLNode& xml_node, + bool enabled, + scene::ISceneNode* parent) + : TrackObjectPresentationSceneNode(xml_node) { m_is_looped = false; m_mesh = NULL; @@ -257,7 +264,8 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh(const XMLNode& xml_node std::string render_pass; xml_node.get("renderpass", &render_pass); - bool skeletal_animation = true; // for backwards compatibility, if unspecified assume there is + // for backwards compatibility, if unspecified assume there is + bool skeletal_animation = true; xml_node.get("skeletal-animation", &skeletal_animation); if (render_pass == "skybox") @@ -265,9 +273,6 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh(const XMLNode& xml_node m_is_in_skybox = true; } - //std::string full_path = - // World::getWorld()->getTrack()->getTrackFile(model_name); - bool animated = skeletal_animation && (UserConfigParams::m_graphical_effects || World::getWorld()->getIdent() == IDENT_CUTSCENE); bool displacing = false; @@ -275,13 +280,9 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh(const XMLNode& xml_node animated &= !displacing; if (animated) - { m_mesh = irr_driver->getAnimatedMesh(model_name); - } else - { m_mesh = irr_driver->getMesh(model_name); - } if (!m_mesh) { @@ -289,50 +290,52 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh(const XMLNode& xml_node } if (!animated) - m_mesh = MeshTools::createMeshWithTangents(m_mesh, &MeshTools::isNormalMap); - + { + m_mesh = MeshTools::createMeshWithTangents(m_mesh, + &MeshTools::isNormalMap); + } init(&xml_node, parent, enabled); -} +} // TrackObjectPresentationMesh +// ---------------------------------------------------------------------------- TrackObjectPresentationMesh::TrackObjectPresentationMesh( - scene::IAnimatedMesh* model, const core::vector3df& xyz, - const core::vector3df& hpr, const core::vector3df& scale) : - TrackObjectPresentationSceneNode(xyz, hpr, scale) + scene::IAnimatedMesh* model, + const core::vector3df& xyz, + const core::vector3df& hpr, + const core::vector3df& scale) + : TrackObjectPresentationSceneNode(xyz, hpr, scale) { - m_is_looped = false; + m_is_looped = false; m_is_in_skybox = false; - m_mesh = NULL; - m_node = NULL; - - m_mesh = model; + m_mesh = NULL; + m_node = NULL; + m_mesh = model; init(NULL, NULL, true); -} +} // TrackObjectPresentationMesh +// ---------------------------------------------------------------------------- TrackObjectPresentationMesh::TrackObjectPresentationMesh( - const std::string& model_file, const core::vector3df& xyz, - const core::vector3df& hpr, const core::vector3df& scale) : - TrackObjectPresentationSceneNode(xyz, hpr, scale) + const std::string& model_file, + const core::vector3df& xyz, + const core::vector3df& hpr, + const core::vector3df& scale) + : TrackObjectPresentationSceneNode(xyz, hpr, scale) { - m_is_looped = false; - m_mesh = NULL; - m_node = NULL; + m_is_looped = false; + m_mesh = NULL; + m_node = NULL; m_is_in_skybox = false; - - bool animated = (UserConfigParams::m_graphical_effects || - World::getWorld()->getIdent() == IDENT_CUTSCENE); + bool animated = (UserConfigParams::m_graphical_effects || + World::getWorld()->getIdent() == IDENT_CUTSCENE); m_model_file = model_file; if (file_manager->fileExists(model_file)) { if (animated) - { m_mesh = irr_driver->getAnimatedMesh(model_file); - } else - { m_mesh = irr_driver->getMesh(model_file); - } } if (!m_mesh) @@ -341,11 +344,14 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh( } init(NULL, NULL, true); -} +} // TrackObjectPresentationMesh -void TrackObjectPresentationMesh::init(const XMLNode* xml_node, scene::ISceneNode* parent, bool enabled) +// ---------------------------------------------------------------------------- +void TrackObjectPresentationMesh::init(const XMLNode* xml_node, + scene::ISceneNode* parent, bool enabled) { - bool skeletal_animation = true; // for backwards compatibility, if unspecified assume there is + // for backwards compatibility, if unspecified assume there is + bool skeletal_animation = true; if(xml_node) xml_node->get("skeletal-animation", &skeletal_animation); @@ -374,7 +380,8 @@ void TrackObjectPresentationMesh::init(const XMLNode* xml_node, scene::ISceneNod else if (animated) { scene::IAnimatedMeshSceneNode *node = - irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_mesh, m_model_file, parent); + irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_mesh, + m_model_file, parent); m_node = node; m_frame_start = node->getStartFrame(); @@ -400,13 +407,10 @@ void TrackObjectPresentationMesh::init(const XMLNode* xml_node, scene::ISceneNod m_frame_start = 0; m_frame_end = 0; - if (World::getWorld() != NULL && World::getWorld()->getTrack() != NULL && xml_node != NULL) - World::getWorld()->getTrack()->handleAnimatedTextures(m_node, *xml_node); + if (World::getWorld() && World::getWorld()->getTrack() && xml_node) + World::getWorld()->getTrack() + ->handleAnimatedTextures(m_node, *xml_node); } -//#ifdef DEBUG -// std::string debug_name = model_name+" (track-object)"; -// m_node->setName(debug_name.c_str()); -//#endif if(!enabled) m_node->setVisible(false); @@ -414,8 +418,9 @@ void TrackObjectPresentationMesh::init(const XMLNode* xml_node, scene::ISceneNod m_node->setPosition(m_init_xyz); m_node->setRotation(m_init_hpr); m_node->setScale(m_init_scale); -} +} // init +// ---------------------------------------------------------------------------- TrackObjectPresentationMesh::~TrackObjectPresentationMesh() { if (m_node) @@ -428,8 +433,9 @@ TrackObjectPresentationMesh::~TrackObjectPresentationMesh() if(m_mesh->getReferenceCount()==1) irr_driver->removeMeshFromCache(m_mesh); } -} +} // ~TrackObjectPresentationMesh +// ---------------------------------------------------------------------------- void TrackObjectPresentationMesh::reset() { if (m_node->getType()==scene::ESNT_ANIMATED_MESH) @@ -451,8 +457,9 @@ void TrackObjectPresentationMesh::reset() // last frame, even if looping is disabled a_node->setFrameLoop(m_frame_start, m_frame_end); } -} +} // reset +// ---------------------------------------------------------------------------- int TrackObjectPresentationMesh::getCurrentFrame() { if (m_node->getType() == scene::ESNT_ANIMATED_MESH) @@ -463,8 +470,9 @@ int TrackObjectPresentationMesh::getCurrentFrame() return (int)a_node->getFrameNr(); } return -1; //Not a skeletal animation -} +} // getCurrentFrame +// ---------------------------------------------------------------------------- void TrackObjectPresentationMesh::setCurrentFrame(int frame) { if (m_node->getType() == scene::ESNT_ANIMATED_MESH) @@ -474,8 +482,13 @@ void TrackObjectPresentationMesh::setCurrentFrame(int frame) a_node->setCurrentFrame((f32)frame); } -} +} // setCurrentFrame +// ---------------------------------------------------------------------------- +/** Set custom loops, as well as pause by scripts. + * \param start Start frame. + * \param end End frame. + */ void TrackObjectPresentationMesh::setLoop(int start, int end) { if (m_node->getType() == scene::ESNT_ANIMATED_MESH) @@ -487,18 +500,18 @@ void TrackObjectPresentationMesh::setLoop(int start, int end) // last frame, even if looping is disabled a_node->setFrameLoop(start, end); } -} +} // setLoop + // ---------------------------------------------------------------------------- - - -TrackObjectPresentationSound::TrackObjectPresentationSound(const XMLNode& xml_node, - scene::ISceneNode* parent) +TrackObjectPresentationSound::TrackObjectPresentationSound( + const XMLNode& xml_node, + scene::ISceneNode* parent) : TrackObjectPresentation(xml_node) { // TODO: respect 'parent' if any m_sound = NULL; - m_xyz = m_init_xyz; + m_xyz = m_init_xyz; std::string sound; xml_node.get("sound", &sound); @@ -558,9 +571,9 @@ void TrackObjectPresentationSound::update(float dt) { if (m_sound != NULL) { - // muting when too far is implemented manually since not supported by OpenAL - // so need to call this every frame to update the muting state if listener - // moved + // muting when too far is implemented manually since not supported by + // OpenAL so need to call this every frame to update the muting state + // if listener moved m_sound->setPosition(m_xyz); } } // update @@ -609,9 +622,9 @@ void TrackObjectPresentationSound::move(const core::vector3df& xyz, } // move // ---------------------------------------------------------------------------- - -TrackObjectPresentationBillboard::TrackObjectPresentationBillboard(const XMLNode& xml_node, - scene::ISceneNode* parent) +TrackObjectPresentationBillboard::TrackObjectPresentationBillboard( + const XMLNode& xml_node, + scene::ISceneNode* parent) : TrackObjectPresentationSceneNode(xml_node) { std::string texture_name; @@ -637,22 +650,26 @@ TrackObjectPresentationBillboard::TrackObjectPresentationBillboard(const XMLNode irr_driver->getTexture(file_manager->searchTexture(texture_name)); if (texture == NULL) { - Log::warn("TrackObjectPresentation", "Billboard texture '%s' not found", texture_name.c_str()); + Log::warn("TrackObjectPresentation", "Billboard texture '%s' not found", + texture_name.c_str()); } - m_node = irr_driver->addBillboard(core::dimension2df(width, height), texture, parent); + m_node = irr_driver->addBillboard(core::dimension2df(width, height), + texture, parent); Material *stk_material = material_manager->getMaterial(texture_name); stk_material->setMaterialProperties(&(m_node->getMaterial(0)), NULL); m_node->setPosition(m_init_xyz); -} +} // TrackObjectPresentationBillboard // ---------------------------------------------------------------------------- void TrackObjectPresentationBillboard::update(float dt) { if (m_fade_out_when_close) { - scene::ICameraSceneNode* curr_cam = irr_driver->getSceneManager()->getActiveCamera(); - const float dist = m_node->getAbsolutePosition().getDistanceFrom( curr_cam->getPosition() ); + scene::ICameraSceneNode* curr_cam = irr_driver->getSceneManager() + ->getActiveCamera(); + const float dist = m_node->getAbsolutePosition() + .getDistanceFrom( curr_cam->getPosition() ); scene::IBillboardSceneNode* node = (scene::IBillboardSceneNode*)m_node; @@ -666,23 +683,25 @@ void TrackObjectPresentationBillboard::update(float dt) } else { - int a = (int)(255*(dist - m_fade_out_start) / (m_fade_out_end - m_fade_out_start)); + int a = (int)(255*(dist - m_fade_out_start) + / (m_fade_out_end - m_fade_out_start)); node->setColor(video::SColor(a, 255, 255, 255)); } - } -} + } // m_fade_out_when_close +} // update // ---------------------------------------------------------------------------- TrackObjectPresentationBillboard::~TrackObjectPresentationBillboard() { if (m_node) irr_driver->removeNode(m_node); -} +} // ~TrackObjectPresentationBillboard // ---------------------------------------------------------------------------- - -TrackObjectPresentationParticles::TrackObjectPresentationParticles(const XMLNode& xml_node, scene::ISceneNode* parent) : - TrackObjectPresentationSceneNode(xml_node) +TrackObjectPresentationParticles::TrackObjectPresentationParticles( + const XMLNode& xml_node, + scene::ISceneNode* parent) + : TrackObjectPresentationSceneNode(xml_node) { m_emitter = NULL; m_lod_emitter_node = NULL; @@ -692,12 +711,11 @@ TrackObjectPresentationParticles::TrackObjectPresentationParticles(const XMLNode int clip_distance = -1; xml_node.get("clip_distance", &clip_distance); - - xml_node.get("conditions", &m_trigger_condition); + xml_node.get("conditions", &m_trigger_condition); try { - ParticleKind* kind = ParticleKindManager::get()->getParticles( path.c_str() ); + ParticleKind* kind = ParticleKindManager::get()->getParticles(path); if (kind == NULL) { throw std::runtime_error(path + " could not be loaded"); @@ -709,7 +727,7 @@ TrackObjectPresentationParticles::TrackObjectPresentationParticles(const XMLNode { scene::ISceneManager* sm = irr_driver->getSceneManager(); scene::ISceneNode* sroot = sm->getRootSceneNode(); - LODNode* lod = new LODNode("particles", parent == NULL ? sroot : parent, sm); + LODNode* lod = new LODNode("particles", !parent ? sroot : parent, sm); lod->add(clip_distance, (scene::ISceneNode*)emitter->getNode(), true); m_node = lod; m_lod_emitter_node = lod; @@ -728,9 +746,10 @@ TrackObjectPresentationParticles::TrackObjectPresentationParticles(const XMLNode } catch (std::runtime_error& e) { - Log::warn ("Track", "Could not load particles '%s'; cause :\n %s", path.c_str(), e.what()); + Log::warn ("Track", "Could not load particles '%s'; cause :\n %s", + path.c_str(), e.what()); } -} +} // TrackObjectPresentationParticles // ---------------------------------------------------------------------------- TrackObjectPresentationParticles::~TrackObjectPresentationParticles() @@ -744,7 +763,7 @@ TrackObjectPresentationParticles::~TrackObjectPresentationParticles() } delete m_emitter; // this will also delete m_node } -} +} // ~TrackObjectPresentationParticles // ---------------------------------------------------------------------------- void TrackObjectPresentationParticles::update(float dt) @@ -753,7 +772,7 @@ void TrackObjectPresentationParticles::update(float dt) { m_emitter->update(dt); } -} +} // update // ---------------------------------------------------------------------------- void TrackObjectPresentationParticles::triggerParticles() @@ -763,12 +782,13 @@ void TrackObjectPresentationParticles::triggerParticles() m_emitter->setCreationRateAbsolute(1.0f); m_emitter->setParticleType(m_emitter->getParticlesInfo()); } -} +} // triggerParticles // ---------------------------------------------------------------------------- - -TrackObjectPresentationLight::TrackObjectPresentationLight(const XMLNode& xml_node, scene::ISceneNode* parent) : - TrackObjectPresentationSceneNode(xml_node) +TrackObjectPresentationLight::TrackObjectPresentationLight( + const XMLNode& xml_node, + scene::ISceneNode* parent) + : TrackObjectPresentationSceneNode(xml_node) { xml_node.get("color", &m_color); const video::SColorf colorf(m_color); @@ -781,32 +801,29 @@ TrackObjectPresentationLight::TrackObjectPresentationLight(const XMLNode& xml_no if (CVS->isGLSL()) { - m_node = irr_driver->addLight(m_init_xyz, m_energy, m_distance, colorf.r, colorf.g, colorf.b, false, parent); + m_node = irr_driver->addLight(m_init_xyz, m_energy, m_distance, + colorf.r, colorf.g, colorf.b, false, + parent); } else { m_node = NULL; // lights require shaders to work - //scene::ILightSceneNode* node = irr_driver->getSceneManager()->addLightSceneNode(NULL, m_init_xyz, m_color, m_distance); - //node->setLightType(video::ELT_POINT); - //node->enableCastShadow(true); - //m_node = node; } -} +} // TrackObjectPresentationLight // ---------------------------------------------------------------------------- TrackObjectPresentationLight::~TrackObjectPresentationLight() { -} +} // ~TrackObjectPresentationLight // ---------------------------------------------------------------------------- - -TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger(const XMLNode& xml_node) : - TrackObjectPresentation(xml_node) +TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger( + const XMLNode& xml_node) + : TrackObjectPresentation(xml_node) { float trigger_distance = 1.0f; xml_node.get("distance", &trigger_distance); - - xml_node.get("action", &m_action); + xml_node.get("action", &m_action ); m_action_active = true; @@ -814,25 +831,25 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger(const Log::warn("TrackObject", "Action-trigger has no action defined."); ItemManager::get()->newItem(m_init_xyz, trigger_distance, this); -} +} // TrackObjectPresentationActionTrigger // ---------------------------------------------------------------------------- - -TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger -(const core::vector3df& xyz,std::string script_name, float distance) -:TrackObjectPresentation(xyz) +TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger( + const core::vector3df& xyz, + const std::string& script_name, + float distance) + : TrackObjectPresentation(xyz) { - m_init_xyz = xyz; - m_init_hpr = core::vector3df(0, 0, 0); - m_init_scale = core::vector3df(1, 1, 1); + m_init_xyz = xyz; + m_init_hpr = core::vector3df(0, 0, 0); + m_init_scale = core::vector3df(1, 1, 1); float trigger_distance = distance; - m_action = script_name; - m_action_active = true; - - + m_action = script_name; + m_action_active = true; ItemManager::get()->newItem(m_init_xyz, trigger_distance, this); -} +} // TrackObjectPresentationActionTrigger +// ---------------------------------------------------------------------------- void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) { if (!m_action_active) return; @@ -846,7 +863,8 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) new RacePausedDialog(0.8f, 0.6f); //dynamic_cast(World::getWorld())->scheduleSelectKart(); } - //action trigger near big doors in the overword to notify players that they'll open once they finish all the challenges + //action trigger near big doors in the overword to notify players that + // they'll open once they finish all the challenges else if (m_action == "big_door") { m_action_active = false; @@ -858,7 +876,8 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) // allow ONE unsolved challenge : the last one if (unlocked_challenges < m_challenges.size() - 1) { - new TutorialMessageDialog(_("Complete all challenges to unlock the big door!"), true); + new TutorialMessageDialog( + _("Complete all challenges to unlock the big door!"), true); } } else if (m_action == "tutorial_drive") @@ -868,13 +887,15 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) m_action_active = false; //World::getWorld()->getRaceGUI()->clearAllMessages(); - InputDevice* device = input_manager->getDeviceManager()->getLatestUsedDevice(); + InputDevice* device = input_manager->getDeviceManager() + ->getLatestUsedDevice(); DeviceConfig* config = device->getConfiguration(); irr::core::stringw accel = config->getBindingAsString(PA_ACCEL); irr::core::stringw left = config->getBindingAsString(PA_STEER_LEFT); irr::core::stringw right = config->getBindingAsString(PA_STEER_RIGHT); - new TutorialMessageDialog(_("Accelerate with <%s> and steer with <%s> and <%s>", accel, left, right), + new TutorialMessageDialog(_("Accelerate with <%s> and steer with " + "<%s> and <%s>", accel, left, right), false); } } @@ -887,17 +908,20 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) else if (m_action == "tutorial_giftboxes") { m_action_active = false; - InputDevice* device = input_manager->getDeviceManager()->getLatestUsedDevice(); + InputDevice* device = input_manager->getDeviceManager() + ->getLatestUsedDevice(); DeviceConfig* config = device->getConfiguration(); irr::core::stringw fire = config->getBindingAsString(PA_FIRE); - new TutorialMessageDialog(_("Collect gift boxes, and fire the weapon with <%s> to blow away these boxes!", fire), - true); + new TutorialMessageDialog(_("Collect gift boxes, and fire the weapon " + "with <%s> to blow away these boxes!", + fire),true); } else if (m_action == "tutorial_backgiftboxes") { m_action_active = false; - InputDevice* device = input_manager->getDeviceManager()->getLatestUsedDevice(); + InputDevice* device = input_manager->getDeviceManager() + ->getLatestUsedDevice(); DeviceConfig* config = device->getConfiguration(); irr::core::stringw fire = config->getBindingAsString(PA_FIRE); irr::core::stringw back = config->getBindingAsString(PA_LOOK_BACK); @@ -911,40 +935,47 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) { m_action_active = false; - new TutorialMessageDialog(_("Collect nitro bottles (we will use them after the curve)"), - true); + new TutorialMessageDialog(_("Collect nitro bottles (we will use them " + "after the curve)"), true); } else if (m_action == "tutorial_nitro_use") { m_action_active = false; - InputDevice* device = input_manager->getDeviceManager()->getLatestUsedDevice(); + InputDevice* device = input_manager->getDeviceManager() + ->getLatestUsedDevice(); DeviceConfig* config = device->getConfiguration(); irr::core::stringw nitro = config->getBindingAsString(PA_NITRO); - new TutorialMessageDialog(_("Use the nitro you collected by pressing <%s>!", nitro), - true); + new TutorialMessageDialog(_("Use the nitro you collected by " + "pressing <%s>!", nitro), true); } else if (m_action == "tutorial_rescue") { m_action_active = false; - InputDevice* device = input_manager->getDeviceManager()->getLatestUsedDevice(); + InputDevice* device = input_manager->getDeviceManager() + ->getLatestUsedDevice(); DeviceConfig* config = device->getConfiguration(); irr::core::stringw rescue = config->getBindingAsString(PA_RESCUE); - new TutorialMessageDialog(_("Oops! When you're in trouble, press <%s> to be rescued", rescue), - false); + new TutorialMessageDialog(_("Oops! When you're in trouble, press <%s> " + "to be rescued", rescue), + false); } else if (m_action == "tutorial_skidding") { m_action_active = false; //World::getWorld()->getRaceGUI()->clearAllMessages(); - InputDevice* device = input_manager->getDeviceManager()->getLatestUsedDevice(); + InputDevice* device = input_manager->getDeviceManager() + ->getLatestUsedDevice(); DeviceConfig* config = device->getConfiguration(); irr::core::stringw skid = config->getBindingAsString(PA_DRIFT); - new TutorialMessageDialog(_("Accelerate and press the <%s> key while turning to skid. Skidding for a short while can help you turn faster to take sharp turns.", skid), + new TutorialMessageDialog(_("Accelerate and press the <%s> key while " + "turning to skid. Skidding for a short " + "while can help you turn faster to take " + "sharp turns.", skid), true); } else if (m_action == "tutorial_skidding2") @@ -952,7 +983,9 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) m_action_active = false; World::getWorld()->getRaceGUI()->clearAllMessages(); - new TutorialMessageDialog(_("Note that if you manage to skid for several seconds, you will receive a bonus speedup as a reward!"), + new TutorialMessageDialog(_("Note that if you manage to skid for " + "several seconds, you will receive a bonus " + "speedup as a reward!"), true); } else if (m_action == "tutorial_endmessage") @@ -971,7 +1004,8 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) else { //TODO move all above functions into scripts and remove the ifs - Scripting::ScriptEngine* m_script_engine = World::getWorld()->getScriptEngine(); + Scripting::ScriptEngine* m_script_engine = + World::getWorld()->getScriptEngine(); m_action_active = false; m_script_engine->runScript(m_action); @@ -982,7 +1016,4 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) */ } -} - - - +} // onTriggerItemApproached diff --git a/src/tracks/track_object_presentation.hpp b/src/tracks/track_object_presentation.hpp index 50f3884ea..d717082f7 100644 --- a/src/tracks/track_object_presentation.hpp +++ b/src/tracks/track_object_presentation.hpp @@ -20,32 +20,33 @@ #ifndef HEADER_TRACK_OBJECT_PRESENTATION_HPP #define HEADER_TRACK_OBJECT_PRESENTATION_HPP -#include -#include -namespace irr -{ - namespace scene { class IAnimatedMesh; class IMeshSceneNode; class ISceneNode; } -} -using namespace irr; - #include "graphics/lod_node.hpp" #include "items/item.hpp" #include "utils/cpp2011.hpp" #include "utils/no_copy.hpp" #include "utils/vec3.hpp" + +#include +#include + #include -class XMLNode; class SFXBase; class ParticleEmitter; class PhysicalObject; class ThreeDAnimation; class ModelDefinitionLoader; class STKInstancedSceneNode; +class XMLNode; -/** - * \ingroup tracks - * Base class for all track object presentation classes +namespace irr +{ + namespace scene { class IAnimatedMesh; class IMeshSceneNode; class ISceneNode; } +} +using namespace irr; + +/** \ingroup tracks + * Base class for all track object presentation classes. */ class TrackObjectPresentation { @@ -64,21 +65,18 @@ public: TrackObjectPresentation(const XMLNode& xml_node); - TrackObjectPresentation( - const core::vector3df& xyz, - const core::vector3df& hpr, - const core::vector3df& scale) + TrackObjectPresentation(const core::vector3df& xyz, + const core::vector3df& hpr = core::vector3df(0,0,0), + const core::vector3df& scale = core::vector3df(0,0,0)) { m_init_xyz = xyz; m_init_hpr = hpr; m_init_scale = scale; - } - TrackObjectPresentation(const core::vector3df& xyz) - { - m_init_xyz = xyz; - } + } // TrackObjectPresentation + // ------------------------------------------------------------------------ virtual ~TrackObjectPresentation() {} + // ------------------------------------------------------------------------ virtual void reset() {} virtual void setEnable(bool enabled) {} @@ -86,50 +84,56 @@ public: virtual void move(const core::vector3df& xyz, const core::vector3df& hpr, const core::vector3df& scale) {} + // ------------------------------------------------------------------------ + /** Returns the position of this TrackObjectPresentation. */ virtual const core::vector3df& getPosition() const { return m_init_xyz; } - virtual const core::vector3df getAbsolutePosition() const { return m_init_xyz; } + // ------------------------------------------------------------------------ + /** Returns a copy of the initial position. Note this function does not + * return a const reference, since some classes overwrite it this way. */ + virtual const core::vector3df getAbsolutePosition() const + { + return m_init_xyz; + } // getAbsolutePosition + // ------------------------------------------------------------------------ + /** Returns the initial rotation. */ virtual const core::vector3df& getRotation() const { return m_init_hpr; } + // ------------------------------------------------------------------------ + /** Returns the initial scale. */ virtual const core::vector3df& getScale() const { return m_init_scale; } LEAK_CHECK() }; -/** - * \ingroup tracks - * Base class for all track object presentation classes using a scene node - * as presentation +// ============================================================================ +/** \ingroup tracks + * Base class for all track object presentation classes using a scene node + * as presentation */ class TrackObjectPresentationSceneNode : public TrackObjectPresentation { protected: + /** A pointer to the scene node of this object. */ scene::ISceneNode* m_node; public: + /** Constructor based on data from xml. */ TrackObjectPresentationSceneNode(const XMLNode& xml_node) : TrackObjectPresentation(xml_node) { m_node = NULL; - } - - TrackObjectPresentationSceneNode( - const core::vector3df& xyz, - const core::vector3df& hpr, - const core::vector3df& scale) : - TrackObjectPresentation(xyz, hpr, scale) - { - m_node = NULL; - } - - TrackObjectPresentationSceneNode( - scene::ISceneNode* node, - const core::vector3df& xyz, - const core::vector3df& hpr, - const core::vector3df& scale) : + } // TrackObjectPresentationSceneNode + // ------------------------------------------------------------------------ + /** Constructor based on a transform. */ + TrackObjectPresentationSceneNode(const core::vector3df& xyz, + const core::vector3df& hpr, + const core::vector3df& scale, + scene::ISceneNode* node = NULL) : TrackObjectPresentation(xyz, hpr, scale) { m_node = node; - } + } // TrackObjectPresentationSceneNode + // ------------------------------------------------------------------------ virtual const core::vector3df& getPosition() const OVERRIDE; virtual const core::vector3df getAbsolutePosition() const OVERRIDE; virtual const core::vector3df& getRotation() const OVERRIDE; @@ -139,39 +143,41 @@ public: virtual void setEnable(bool enabled) OVERRIDE; virtual void reset() OVERRIDE; + // ------------------------------------------------------------------------ + /** Returns a pointer to the scene node. */ scene::ISceneNode* getNode() { return m_node; } + // ------------------------------------------------------------------------ + /** Returns a pointer to the scene node, const version. */ const scene::ISceneNode* getNode() const { return m_node; } -}; +}; // class TrackObjectPresentationSceneNode -/** - * \ingroup tracks - * A track object representation that is invisible and only consists of a - * location, rotation and scale. +// ============================================================================ +/** \ingroup tracks + * A track object representation that is invisible and only consists of a + * location, rotation and scale. */ class TrackObjectPresentationEmpty : public TrackObjectPresentationSceneNode { public: - TrackObjectPresentationEmpty(const XMLNode& xml_node); virtual ~TrackObjectPresentationEmpty(); -}; +}; // class TrackObjectPresentationEmpty -/** -* \ingroup tracks -* A track object representation that is a library node -*/ +// ============================================================================ +/** \ingroup tracks + * A track object representation that is a library node + */ class TrackObjectPresentationLibraryNode : public TrackObjectPresentationSceneNode { public: - TrackObjectPresentationLibraryNode(const XMLNode& xml_node, ModelDefinitionLoader& model_def_loader); virtual ~TrackObjectPresentationLibraryNode(); -}; +}; // TrackObjectPresentationLibraryNode -/** - * \ingroup tracks - * A track object representation that consists of a level-of-detail scene node +// ============================================================================ +/** \ingroup tracks + * A track object representation that consists of a level-of-detail scene node */ class TrackObjectPresentationLOD : public TrackObjectPresentationSceneNode { @@ -183,9 +189,9 @@ public: virtual ~TrackObjectPresentationLOD(); }; -/** - * \ingroup tracks - * A track object representation that consists of a mesh scene node. +// ============================================================================ +/** \ingroup tracks + * A track object representation that consists of a mesh scene node. */ class TrackObjectPresentationMesh : public TrackObjectPresentationSceneNode { @@ -211,38 +217,38 @@ private: void init(const XMLNode* xml_node, scene::ISceneNode* parent, bool enabled); public: - TrackObjectPresentationMesh(const XMLNode& xml_node, bool enabled, scene::ISceneNode* parent); - - TrackObjectPresentationMesh( - const std::string& model_file, const core::vector3df& xyz, - const core::vector3df& hpr, const core::vector3df& scale); - TrackObjectPresentationMesh( - scene::IAnimatedMesh* mesh, const core::vector3df& xyz, - const core::vector3df& hpr, const core::vector3df& scale); - - void setLoop(int start, int end); //set custom loops, as well as pause by scripts - - void setCurrentFrame(int frame); - - int getCurrentFrame(); + TrackObjectPresentationMesh(const XMLNode& xml_node, bool enabled, + scene::ISceneNode* parent); + TrackObjectPresentationMesh(const std::string& model_file, + const core::vector3df& xyz, + const core::vector3df& hpr, + const core::vector3df& scale); + TrackObjectPresentationMesh(scene::IAnimatedMesh* mesh, + const core::vector3df& xyz, + const core::vector3df& hpr, + const core::vector3df& scale); virtual ~TrackObjectPresentationMesh(); - + void setLoop(int start, int end); + void setCurrentFrame(int frame); + int getCurrentFrame(); virtual void reset() OVERRIDE; - + // ------------------------------------------------------------------------ + /** Returns the mode file name. */ const std::string& getModelFile() const { return m_model_file; } -}; +}; // class TrackObjectPresentationMesh -/** - * \ingroup tracks - * A track object representation that consists of a sound emitter +// ============================================================================ +/** \ingroup tracks + * A track object representation that consists of a sound emitter */ class TrackObjectPresentationSound : public TrackObjectPresentation, public TriggerItemListener { private: - /** If a sound is attached to this object and/or this is a sound emitter object */ + /** If a sound is attached to this object and/or this is a sound emitter + * object */ SFXBase* m_sound; /** Currently used for sound effects only, in cutscenes only atm */ @@ -252,43 +258,45 @@ private: public: - TrackObjectPresentationSound(const XMLNode& xml_node, scene::ISceneNode* parent); + TrackObjectPresentationSound(const XMLNode& xml_node, + scene::ISceneNode* parent); virtual ~TrackObjectPresentationSound(); virtual void onTriggerItemApproached(Item* who) OVERRIDE; virtual void update(float dt) OVERRIDE; + virtual void move(const core::vector3df& xyz, const core::vector3df& hpr, + const core::vector3df& scale) OVERRIDE; void triggerSound(bool loop); void stopSound(); + // ------------------------------------------------------------------------ /** Currently used for sound effects only, in cutscenes only atm */ const std::string& getTriggerCondition() const { return m_trigger_condition; } +}; // TrackObjectPresentationSound - virtual void move(const core::vector3df& xyz, const core::vector3df& hpr, - const core::vector3df& scale) OVERRIDE; -}; - -/** - * \ingroup tracks - * A track object representation that consists of a billboard scene node. +// ============================================================================ +/** \ingroup tracks + * A track object representation that consists of a billboard scene node. */ class TrackObjectPresentationBillboard : public TrackObjectPresentationSceneNode { - /** To make the billboard disappear when close to the camera. Useful for light halos : - * instead of "colliding" with the camera and suddenly disappearing when clipped by - * frustum culling, it will gently fade out. + /** To make the billboard disappear when close to the camera. Useful for + * light halos: instead of "colliding" with the camera and suddenly + * disappearing when clipped by frustum culling, it will gently fade out. */ bool m_fade_out_when_close; float m_fade_out_start; float m_fade_out_end; public: - TrackObjectPresentationBillboard(const XMLNode& xml_node, scene::ISceneNode* parent); + TrackObjectPresentationBillboard(const XMLNode& xml_node, + scene::ISceneNode* parent); virtual ~TrackObjectPresentationBillboard(); virtual void update(float dt) OVERRIDE; -}; +}; // TrackObjectPresentationBillboard -/** - * \ingroup tracks - * A track object representation that consists of a particle emitter +// ============================================================================ +/** \ingroup tracks + * A track object representation that consists of a particle emitter */ class TrackObjectPresentationParticles : public TrackObjectPresentationSceneNode { @@ -298,20 +306,21 @@ private: std::string m_trigger_condition; public: - TrackObjectPresentationParticles(const XMLNode& xml_node, scene::ISceneNode* parent); + TrackObjectPresentationParticles(const XMLNode& xml_node, + scene::ISceneNode* parent); virtual ~TrackObjectPresentationParticles(); virtual void update(float dt) OVERRIDE; - - std::string& getTriggerCondition() { return m_trigger_condition; } - void triggerParticles(); -}; + // ------------------------------------------------------------------------ + /** Returns the trigger condition for this object. */ + std::string& getTriggerCondition() { return m_trigger_condition; } +}; // TrackObjectPresentationParticles -/** -* \ingroup tracks -* A track object representation that consists of a light emitter -*/ +// ============================================================================ +/** \ingroup tracks + * A track object representation that consists of a light emitter + */ class TrackObjectPresentationLight : public TrackObjectPresentationSceneNode { private: @@ -320,40 +329,40 @@ private: float m_energy; public: - TrackObjectPresentationLight(const XMLNode& xml_node, scene::ISceneNode* parent); + TrackObjectPresentationLight(const XMLNode& xml_node, + scene::ISceneNode* parent); virtual ~TrackObjectPresentationLight(); -}; +}; // TrackObjectPresentationLight - -/** - * \ingroup tracks - * A track object representation that consists of an action trigger +// ============================================================================ +/** \ingroup tracks + * A track object representation that consists of an action trigger */ class TrackObjectPresentationActionTrigger : public TrackObjectPresentation, public TriggerItemListener { private: - /** For action trigger objects */ std::string m_action; bool m_action_active; - public: - - TrackObjectPresentationActionTrigger(const XMLNode& xml_node); - TrackObjectPresentationActionTrigger(const core::vector3df& xyz,std::string scriptname, float distance); + TrackObjectPresentationActionTrigger(const core::vector3df& xyz, + const std::string& scriptname, + float distance); virtual ~TrackObjectPresentationActionTrigger() {} virtual void onTriggerItemApproached(Item* who) OVERRIDE; - + // ------------------------------------------------------------------------ + /** Reset the trigger (i.e. sets it to active again). */ virtual void reset() OVERRIDE { m_action_active = true; } - + // ------------------------------------------------------------------------ + /** Sets the trigger to be enabled or disabled. */ virtual void setEnable(bool status) OVERRIDE{ m_action_active = status; } -}; +}; // class TrackObjectPresentationActionTrigger #endif // TRACKOBJECTPRESENTATION_HPP From a5d800d8d64727bad6446fcd41ecf61750cba147 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 2 Mar 2015 07:55:39 +1100 Subject: [PATCH 100/117] Made terms&conditions translatable (but hard-coded the URL). --- src/states_screens/dialogs/registration_dialog.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/states_screens/dialogs/registration_dialog.cpp b/src/states_screens/dialogs/registration_dialog.cpp index 653a8586d..83b39567f 100644 --- a/src/states_screens/dialogs/registration_dialog.cpp +++ b/src/states_screens/dialogs/registration_dialog.cpp @@ -39,11 +39,14 @@ RegistrationDialog::RegistrationDialog() : loadFromFile("online/registration_terms.stkgui"); LabelWidget* terms_widget = getWidget("terms"); - core::stringw terms = core::stringw(L"You must agree to these terms in order to register an account for STK." - L"Still needs actual content. Preferably in an XML document which can then be parsed to be put here." - L"By checking the box below, you are confirming that you understand these terms." - L"If you have any questions or comments regarding these terms," - L"one of the members of the development team would gladly assist you."); + core::stringw terms = _(L"Please read the terms and conditions " + L"for SuperTuxKart at '%s'. You must agree " + L"to these terms in order to register an account for STK. " + L"By checking the box below, you are confirming that you understand " + L"these terms. If you have any questions or comments regarding these " + L"terms, one of the members of the development team would gladly " + L"assist you.", + L"http://supertuxkart.net/terms"); terms_widget->setText(terms, false); // showRegistrationTerms(); From 53a830be0b64e80486b58a427f2b1ccdca76959f Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 2 Mar 2015 08:45:56 +1100 Subject: [PATCH 101/117] Check the translation of the terms to make sure that they include our official url. If it is not contained, use the original English terms. --- .../dialogs/registration_dialog.cpp | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/states_screens/dialogs/registration_dialog.cpp b/src/states_screens/dialogs/registration_dialog.cpp index 83b39567f..f2fd44325 100644 --- a/src/states_screens/dialogs/registration_dialog.cpp +++ b/src/states_screens/dialogs/registration_dialog.cpp @@ -39,15 +39,27 @@ RegistrationDialog::RegistrationDialog() : loadFromFile("online/registration_terms.stkgui"); LabelWidget* terms_widget = getWidget("terms"); - core::stringw terms = _(L"Please read the terms and conditions " + core::stringw orig_terms = L"Please read the terms and conditions " L"for SuperTuxKart at '%s'. You must agree " L"to these terms in order to register an account for STK. " L"By checking the box below, you are confirming that you understand " L"these terms. If you have any questions or comments regarding these " L"terms, one of the members of the development team would gladly " - L"assist you.", - L"http://supertuxkart.net/terms"); - terms_widget->setText(terms, false); + L"assist you."; + + core::stringw url = L"http://supertuxkart.net/terms"; + core::stringw translation = _(orig_terms.c_str(), url.c_str()); + + // Make sure the translation contains the right URL. If not, this + // translation is really messed up, and we better show the original. + if (translation.find(url.c_str()) == -1) + { + translation = StringUtils::insertValues(orig_terms, url); + Log::warn("Terms", "Translated terms do not contain right URL, " + "using English terms"); + } + + terms_widget->setText(translation, false); // showRegistrationTerms(); } From c4c85257ecae50145baf8647df519f7596b16d19 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 1 Mar 2015 20:44:09 -0500 Subject: [PATCH 102/117] Restore lens flare effect --- src/graphics/post_processing.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index 6b43506de..5a9ea07ea 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -731,11 +731,22 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_1024), irr_driver->getFBO(FBO_BLOOM_512), GL_COLOR_BUFFER_BIT, GL_LINEAR); FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_BLOOM_256), GL_COLOR_BUFFER_BIT, GL_LINEAR); FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_BLOOM_128), GL_COLOR_BUFFER_BIT, GL_LINEAR); + + // Copy for lens flare + FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_LENS_512), GL_COLOR_BUFFER_BIT, GL_LINEAR); + FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_LENS_256), GL_COLOR_BUFFER_BIT, GL_LINEAR); + FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_LENS_128), GL_COLOR_BUFFER_BIT, GL_LINEAR); + // Blur + // The fbo sum chain is from https://software.intel.com/en-us/articles/compute-shader-hdr-and-bloom glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); + + renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_512), irr_driver->getFBO(FBO_TMP_512)); + renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_256), irr_driver->getFBO(FBO_TMP_256)); + renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_128), irr_driver->getFBO(FBO_TMP_128)); renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_TMP_128), 2., 2.); glEnable(GL_BLEND); @@ -760,6 +771,12 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); renderPassThrough(irr_driver->getRenderTargetTexture(RTT_BLOOM_1024), in_fbo->getWidth(), in_fbo->getHeight()); + + + FullScreenShader::LensBlendShader::getInstance()->SetTextureUnits( + irr_driver->getRenderTargetTexture(RTT_LENS_128), irr_driver->getRenderTargetTexture(RTT_LENS_256), irr_driver->getRenderTargetTexture(RTT_LENS_512)); + DrawFullScreenEffect(); + glDisable(GL_BLEND); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } // end if bloom From d5736f3a5ef6fe9af69138ce5ce047ca3f784307 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 1 Mar 2015 22:04:20 -0500 Subject: [PATCH 103/117] Fix #1954, part 1 --- src/graphics/2dutils.cpp | 14 +++-- src/graphics/irr_driver.cpp | 3 ++ src/graphics/irr_driver.hpp | 2 + src/graphics/post_processing.cpp | 6 +-- src/graphics/render.cpp | 8 +-- src/graphics/rtts.cpp | 4 +- src/guiengine/screen.cpp | 7 ++- src/guiengine/skin.cpp | 10 ++-- src/states_screens/race_gui.cpp | 24 +++++---- src/states_screens/race_gui_base.cpp | 62 +++++++++++------------ src/states_screens/race_gui_overworld.cpp | 36 ++++++------- 11 files changed, 90 insertions(+), 86 deletions(-) diff --git a/src/graphics/2dutils.cpp b/src/graphics/2dutils.cpp index 55421cc22..537ff43b8 100644 --- a/src/graphics/2dutils.cpp +++ b/src/graphics/2dutils.cpp @@ -65,10 +65,9 @@ float &tex_width, float &tex_height, float &tex_center_pos_x, float &tex_center_pos_y ) { - core::dimension2d frame_size = - irr_driver->getVideoDriver()->getCurrentRenderTargetSize(); + core::dimension2d frame_size = irr_driver->getActualScreenSize(); const int screen_w = frame_size.Width; - const int screen_h = frame_size.Height; + const int screen_h = frame_size.Height; center_pos_x = float(destRect.UpperLeftCorner.X + destRect.LowerRightCorner.X); center_pos_x /= screen_w; center_pos_x -= 1.; @@ -137,7 +136,7 @@ void draw2DImage(const video::ITexture* texture, const core::rect& destRect return; glEnable(GL_SCISSOR_TEST); - const core::dimension2d& renderTargetSize = irr_driver->getVideoDriver()->getCurrentRenderTargetSize(); + const core::dimension2d& renderTargetSize = irr_driver->getActualScreenSize(); glScissor(clipRect->UpperLeftCorner.X, renderTargetSize.Height - clipRect->LowerRightCorner.Y, clipRect->getWidth(), clipRect->getHeight()); } @@ -226,7 +225,7 @@ void draw2DImage(const video::ITexture* texture, const core::rect& destRect return; glEnable(GL_SCISSOR_TEST); - const core::dimension2d& renderTargetSize = irr_driver->getVideoDriver()->getCurrentRenderTargetSize(); + const core::dimension2d& renderTargetSize = irr_driver->getActualScreenSize(); glScissor(clipRect->UpperLeftCorner.X, renderTargetSize.Height - clipRect->LowerRightCorner.Y, clipRect->getWidth(), clipRect->getHeight()); } @@ -288,8 +287,7 @@ void GL32_draw2DRectangle(video::SColor color, const core::rect& position, return; } - core::dimension2d frame_size = - irr_driver->getVideoDriver()->getCurrentRenderTargetSize(); + core::dimension2d frame_size = irr_driver->getActualScreenSize(); const int screen_w = frame_size.Width; const int screen_h = frame_size.Height; float center_pos_x = float(position.UpperLeftCorner.X + position.LowerRightCorner.X); @@ -319,7 +317,7 @@ void GL32_draw2DRectangle(video::SColor color, const core::rect& position, return; glEnable(GL_SCISSOR_TEST); - const core::dimension2d& renderTargetSize = irr_driver->getVideoDriver()->getCurrentRenderTargetSize(); + const core::dimension2d& renderTargetSize = irr_driver->getActualScreenSize(); glScissor(clip->UpperLeftCorner.X, renderTargetSize.Height - clip->LowerRightCorner.Y, clip->getWidth(), clip->getHeight()); } diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index a209d551c..c2995d097 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -478,6 +478,9 @@ void IrrDriver::initDevice() m_gui_env = m_device->getGUIEnvironment(); m_video_driver = m_device->getVideoDriver(); m_sync = 0; + + m_actual_screen_size = m_video_driver->getCurrentRenderTargetSize(); + CVS->init(); diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 9e6775283..5b8245773 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -211,6 +211,7 @@ private: bool m_rsm_matrix_initialized; bool m_rsm_map_available; core::vector2df m_current_screen_size; + core::dimension2du m_actual_screen_size; /** Additional details to be shown in case that a texture is not found. * This is used to specify details like: "while loading kart '...'" */ @@ -684,6 +685,7 @@ public: const core::matrix4 &getProjViewMatrix() const { return m_ProjViewMatrix; } const core::matrix4 &getInvProjViewMatrix() const { return m_InvProjViewMatrix; } const core::vector2df &getCurrentScreenSize() const { return m_current_screen_size; } + const core::dimension2du getActualScreenSize() const { return m_actual_screen_size; } // ------------------------------------------------------------------------ float getSSAORadius() const { diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index 5a9ea07ea..80266a818 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -98,11 +98,11 @@ void PostProcessing::reset() const core::recti &vp = Camera::getCamera(i)->getViewport(); // Map viewport to [-1,1] x [-1,1]. First define the coordinates // left, right, top, bottom: - float right = vp.LowerRightCorner.X < UserConfigParams::m_width + float right = vp.LowerRightCorner.X < irr_driver->getActualScreenSize().Width ? 0.0f : 1.0f; float left = vp.UpperLeftCorner.X > 0.0f ? 0.0f : -1.0f; float top = vp.UpperLeftCorner.Y > 0.0f ? 0.0f : 1.0f; - float bottom = vp.LowerRightCorner.Y < UserConfigParams::m_height + float bottom = vp.LowerRightCorner.Y < irr_driver->getActualScreenSize().Height ? 0.0f : -1.0f; // Use left etc to define 4 vertices on which the rendered screen @@ -672,7 +672,7 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo // Fade to quarter irr_driver->getFBO(FBO_QUARTER1).Bind(); - glViewport(0, 0, UserConfigParams::m_width / 4, UserConfigParams::m_height / 4); + glViewport(0, 0, irr_driver->getActualScreenSize().Width / 4, irr_driver->getActualScreenSize().Height / 4); renderGodFade(out_fbo->getRTT()[0], col); // Blur diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index e08ba70a1..e1fab4cf7 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -282,8 +282,8 @@ void IrrDriver::renderGLSL(float dt) // Use full screen size float tmp[2]; - tmp[0] = float(UserConfigParams::m_width); - tmp[1] = float(UserConfigParams::m_height); + tmp[0] = float(m_actual_screen_size.Width); + tmp[1] = float(m_actual_screen_size.Height); glBindBuffer(GL_UNIFORM_BUFFER, SharedObject::ViewProjectionMatrixesUBO); glBufferSubData(GL_UNIFORM_BUFFER, (16 * 9) * sizeof(float), 2 * sizeof(float), tmp); @@ -294,8 +294,8 @@ void IrrDriver::renderGLSL(float dt) // Set the viewport back to the full screen for race gui m_video_driver->setViewPort(core::recti(0, 0, - UserConfigParams::m_width, - UserConfigParams::m_height)); + irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height)); for(unsigned int i=0; igetPostProcessing()->render(camera, false); // reset - glViewport(0, 0, UserConfigParams::m_width, UserConfigParams::m_height); + glViewport(0, 0, + irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height); irr_driver->setRTT(NULL); glBindFramebuffer(GL_FRAMEBUFFER, 0); diff --git a/src/guiengine/screen.cpp b/src/guiengine/screen.cpp index 29706ee0e..7f4064b84 100644 --- a/src/guiengine/screen.cpp +++ b/src/guiengine/screen.cpp @@ -19,6 +19,7 @@ #include "guiengine/screen.hpp" #include "io/file_manager.hpp" +#include "graphics/irr_driver.hpp" #include "guiengine/engine.hpp" #include "guiengine/layout_manager.hpp" #include "guiengine/modaldialog.hpp" @@ -234,16 +235,14 @@ void Screen::manualRemoveWidget(Widget* w) /** \brief Implementing method from AbstractTopLevelContainer */ int Screen::getWidth() { - core::dimension2d frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize(); - return frame_size.Width; + return irr_driver->getActualScreenSize().Width; } // ----------------------------------------------------------------------------- /** \brief Implementing method from AbstractTopLevelContainer */ int Screen::getHeight() { - core::dimension2d frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize(); - return frame_size.Height; + return irr_driver->getActualScreenSize().Height; } // ----------------------------------------------------------------------------- diff --git a/src/guiengine/skin.cpp b/src/guiengine/skin.cpp index ad77c23b8..f948ea28d 100644 --- a/src/guiengine/skin.cpp +++ b/src/guiengine/skin.cpp @@ -346,8 +346,7 @@ void Skin::drawBgImage() source_area = core::recti(0, 0, texture_w, texture_h); - core::dimension2d frame_size = - GUIEngine::getDriver()->getCurrentRenderTargetSize(); + core::dimension2d frame_size = irr_driver->getActualScreenSize(); const int screen_w = frame_size.Width; const int screen_h = frame_size.Height; @@ -971,7 +970,7 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget, // automatically guess from position on-screen if tabs go up or down const bool vertical_flip = (unsigned int)rect.UpperLeftCorner.Y < - GUIEngine::getDriver()->getCurrentRenderTargetSize().Height/2; + irr_driver->getActualScreenSize().Height / 2; params->m_vertical_flip = vertical_flip; core::recti rect2 = rect; @@ -2254,9 +2253,8 @@ void Skin::drawBGFadeColor() SColor color = SkinConfig::m_colors["dialog_background::neutral"]; if (m_dialog_size < 1.0f) color.setAlpha( (unsigned int)(color.getAlpha()*m_dialog_size )); - GL32_draw2DRectangle(color, - core::recti(position2d< s32 >(0,0), - GUIEngine::getDriver()->getCurrentRenderTargetSize()) ); + GL32_draw2DRectangle(color, core::recti(position2d< s32 >(0,0), + irr_driver->getActualScreenSize())); } // drawBGFadeColor // ----------------------------------------------------------------------------- diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index 1784a36bd..ad5143cd7 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -80,7 +80,7 @@ RaceGUI::RaceGUI() // special case : when 3 players play, use available 4th space for such things if (race_manager->getNumLocalPlayers() == 3) { - m_map_left = UserConfigParams::m_width - m_map_width; + m_map_left = irr_driver->getActualScreenSize().Width - m_map_width; } m_is_tutorial = (race_manager->getTrackName() == "tutorial"); @@ -148,10 +148,10 @@ void RaceGUI::renderGlobal(float dt) { static video::SColor black = video::SColor(255,0,0,0); GL32_draw2DRectangle(black, - core::rect(UserConfigParams::m_width/2, - UserConfigParams::m_height/2, - UserConfigParams::m_width, - UserConfigParams::m_height)); + core::rect(irr_driver->getActualScreenSize().Width/2, + irr_driver->getActualScreenSize().Height/2, + irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height)); } World *world = World::getWorld(); @@ -319,13 +319,13 @@ void RaceGUI::drawGlobalTimer() } } - core::rect pos(UserConfigParams::m_width - dist_from_right, 10, - UserConfigParams::m_width , 50); + core::rect pos(irr_driver->getActualScreenSize().Width - dist_from_right, 10, + irr_driver->getActualScreenSize().Width , 50); // special case : when 3 players play, use available 4th space for such things if (race_manager->getNumLocalPlayers() == 3) { - pos += core::vector2d(0, UserConfigParams::m_height/2); + pos += core::vector2d(0, irr_driver->getActualScreenSize().Height/2); } gui::ScalableFont* font = (use_digit_font ? GUIEngine::getHighresDigitFont() : GUIEngine::getFont()); @@ -348,8 +348,8 @@ void RaceGUI::drawGlobalMiniMap() const video::ITexture *old_rtt_mini_map = world->getTrack()->getOldRttMiniMap(); const FrameBuffer* new_rtt_mini_map = world->getTrack()->getNewRttMiniMap(); - int upper_y = UserConfigParams::m_height - m_map_bottom - m_map_height; - int lower_y = UserConfigParams::m_height - m_map_bottom; + int upper_y = irr_driver->getActualScreenSize().Height - m_map_bottom - m_map_height; + int lower_y = irr_driver->getActualScreenSize().Height - m_map_bottom; core::rect dest(m_map_left, upper_y, m_map_left + m_map_width, lower_y); @@ -723,6 +723,8 @@ void RaceGUI::drawSpeedEnergyRank(const AbstractKart* kart, meter_texture->getOriginalSize()); draw2DImage(meter_texture, meter_pos, meter_texture_coords, NULL, NULL, true); + // TODO: temporary workaround, shouldn't have to use + // draw2DVertexPrimitiveList to render a simple rectangle const float speed = kart->getSpeed(); @@ -830,7 +832,7 @@ void RaceGUI::drawLap(const AbstractKart* kart, // move the lap/rank display down a little bit so that it is // displayed under the time. if(viewport.UpperLeftCorner.Y==0 && - viewport.LowerRightCorner.X==UserConfigParams::m_width && + viewport.LowerRightCorner.X==irr_driver->getActualScreenSize().Width && race_manager->getNumPlayers()!=3) pos.UpperLeftCorner.Y += m_font_height; pos.LowerRightCorner.Y = viewport.LowerRightCorner.Y+20; diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index f8d7f2108..db54885ae 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -542,24 +542,24 @@ void RaceGUIBase::drawGlobalMusicDescription() thetext_composer += mi->getComposer().c_str(); textWidth2 = font->getDimension(thetext_composer.c_str()).Width; } - const int max_text_size = (int)(UserConfigParams::m_width*2.0f/3.0f); + const int max_text_size = (int)(irr_driver->getActualScreenSize().Width*2.0f/3.0f); if (textWidth > max_text_size) textWidth = max_text_size; if (textWidth2 > max_text_size) textWidth2 = max_text_size; const int ICON_SIZE = 64; - const int y = UserConfigParams::m_height - 80; + const int y = irr_driver->getActualScreenSize().Height - 80; // the 20 is an arbitrary space left between the note icon and the text - const int noteX = (UserConfigParams::m_width / 2) + const int noteX = (irr_driver->getActualScreenSize().Width / 2) - std::max(textWidth, textWidth2)/2 - ICON_SIZE/2 - 20; const int noteY = y; // the 20 is an arbitrary space left between the note icon and the text - const int textXFrom = (UserConfigParams::m_width / 2) + const int textXFrom = (irr_driver->getActualScreenSize().Width / 2) - std::max(textWidth, textWidth2)/2 + 20; - const int textXTo = (UserConfigParams::m_width / 2) + const int textXTo = (irr_driver->getActualScreenSize().Width / 2) + std::max(textWidth, textWidth2)/2 + 20; // ---- Draw "by" text - const int text_y = (int)(UserConfigParams::m_height - 80*(resize3) + const int text_y = (int)(irr_driver->getActualScreenSize().Height - 80*(resize3) + 40*(1-resize)); static const video::SColor white = video::SColor(255, 255, 255, 255); @@ -598,10 +598,10 @@ void RaceGUIBase::drawGlobalMusicDescription() void RaceGUIBase::drawGlobalGoal() { static video::SColor color = video::SColor(255, 255, 255, 255); - core::rect pos(UserConfigParams::m_width>>1, - UserConfigParams::m_height>>1, - UserConfigParams::m_width>>1, - UserConfigParams::m_height>>1); + core::rect pos(irr_driver->getActualScreenSize().Width>>1, + irr_driver->getActualScreenSize().Height>>1, + irr_driver->getActualScreenSize().Width>>1, + irr_driver->getActualScreenSize().Height>>1); gui::IGUIFont* font = GUIEngine::getTitleFont(); font->draw(m_string_goal.c_str(), pos, color, true, true); } @@ -615,10 +615,10 @@ void RaceGUIBase::drawGlobalReadySetGo() case WorldStatus::READY_PHASE: { static video::SColor color = video::SColor(255, 255, 255, 255); - core::rect pos(UserConfigParams::m_width>>1, - UserConfigParams::m_height>>1, - UserConfigParams::m_width>>1, - UserConfigParams::m_height>>1); + core::rect pos(irr_driver->getActualScreenSize().Width>>1, + irr_driver->getActualScreenSize().Height>>1, + irr_driver->getActualScreenSize().Width>>1, + irr_driver->getActualScreenSize().Height>>1); gui::IGUIFont* font = GUIEngine::getTitleFont(); font->draw(m_string_ready.c_str(), pos, color, true, true); } @@ -626,10 +626,10 @@ void RaceGUIBase::drawGlobalReadySetGo() case WorldStatus::SET_PHASE: { static video::SColor color = video::SColor(255, 255, 255, 255); - core::rect pos(UserConfigParams::m_width>>1, - UserConfigParams::m_height>>1, - UserConfigParams::m_width>>1, - UserConfigParams::m_height>>1); + core::rect pos(irr_driver->getActualScreenSize().Width>>1, + irr_driver->getActualScreenSize().Height>>1, + irr_driver->getActualScreenSize().Width>>1, + irr_driver->getActualScreenSize().Height>>1); gui::IGUIFont* font = GUIEngine::getTitleFont(); font->draw(m_string_set.c_str(), pos, color, true, true); } @@ -637,10 +637,10 @@ void RaceGUIBase::drawGlobalReadySetGo() case WorldStatus::GO_PHASE: { static video::SColor color = video::SColor(255, 255, 255, 255); - core::rect pos(UserConfigParams::m_width>>1, - UserConfigParams::m_height>>1, - UserConfigParams::m_width>>1, - UserConfigParams::m_height>>1); + core::rect pos(irr_driver->getActualScreenSize().Width>>1, + irr_driver->getActualScreenSize().Height>>1, + irr_driver->getActualScreenSize().Width>>1, + irr_driver->getActualScreenSize().Height>>1); //gui::IGUIFont* font = irr_driver->getRaceFont(); gui::IGUIFont* font = GUIEngine::getTitleFont(); @@ -670,20 +670,20 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) int x_base = 10; int y_base = 20; - unsigned int y_space = UserConfigParams::m_height - bottom_margin - y_base; + unsigned int y_space = irr_driver->getActualScreenSize().Height - bottom_margin - y_base; // Special case : when 3 players play, use 4th window to display such stuff if (race_manager->getNumLocalPlayers() == 3) { - x_base = UserConfigParams::m_width/2 + x_base; - y_base = UserConfigParams::m_height/2 + y_base; - y_space = UserConfigParams::m_height - y_base; + x_base = irr_driver->getActualScreenSize().Width/2 + x_base; + y_base = irr_driver->getActualScreenSize().Height/2 + y_base; + y_space = irr_driver->getActualScreenSize().Height - y_base; } // -2 because that's the spacing further on int ICON_PLAYER_WIDTH = y_space / race_manager->getNumberOfKarts() - 2; - int icon_width_max = (int)(50*(UserConfigParams::m_width/800.0f)); - int icon_width_min = (int)(35*(UserConfigParams::m_height/600.0f)); + int icon_width_max = (int)(50*(irr_driver->getActualScreenSize().Width/800.0f)); + int icon_width_min = (int)(35*(irr_driver->getActualScreenSize().Height/600.0f)); if (icon_width_min > icon_width_max) { int icon_width_tmp = icon_width_max; @@ -696,7 +696,7 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) if (ICON_PLAYER_WIDTH < icon_width_min) ICON_PLAYER_WIDTH = icon_width_min; // TODO: Is this absolute treshold necessary? - if(UserConfigParams::m_height<600) + if(irr_driver->getActualScreenSize().Height<600) { ICON_PLAYER_WIDTH = 35; } @@ -730,9 +730,9 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) const unsigned int kart_amount = world->getNumKarts(); //where is the limit to hide last icons - int y_icons_limit=UserConfigParams::m_height-bottom_margin-ICON_PLAYER_WIDTH; + int y_icons_limit=irr_driver->getActualScreenSize().Height-bottom_margin-ICON_PLAYER_WIDTH; if (race_manager->getNumLocalPlayers() == 3) - y_icons_limit=UserConfigParams::m_height-ICON_WIDTH; + y_icons_limit=irr_driver->getActualScreenSize().Height-ICON_WIDTH; world->getKartsDisplayInfo(&m_kart_display_infos); diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index 6b88045d7..a77839be4 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -84,7 +84,7 @@ RaceGUIOverworld::RaceGUIOverworld() m_map_height = (int)(250.0f * scaling); m_map_left = 20; - m_map_bottom = UserConfigParams::m_height-10; + m_map_bottom = irr_driver->getActualScreenSize().Height-10; // Minimap is also rendered bigger via OpenGL, so find power-of-two again const int map_texture = 2 << ((int) ceil(1.0 + log(128.0 * scaling))); @@ -95,7 +95,7 @@ RaceGUIOverworld::RaceGUIOverworld() // special case : when 3 players play, use available 4th space for such things if (race_manager->getNumLocalPlayers() == 3) { - m_map_left = UserConfigParams::m_width - m_map_width; + m_map_left = irr_driver->getActualScreenSize().Width - m_map_width; } m_speed_meter_icon = material_manager->getMaterial("speedback.png"); @@ -146,10 +146,10 @@ void RaceGUIOverworld::renderGlobal(float dt) { static video::SColor black = video::SColor(255,0,0,0); GL32_draw2DRectangle(black, - core::rect(UserConfigParams::m_width/2, - UserConfigParams::m_height/2, - UserConfigParams::m_width, - UserConfigParams::m_height)); + core::rect(irr_driver->getActualScreenSize().Width/2, + irr_driver->getActualScreenSize().Height/2, + irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height)); } World *world = World::getWorld(); @@ -216,14 +216,14 @@ void RaceGUIOverworld::drawTrophyPoints() int dist_from_right = 10 + m_trophy_points_width; - core::rect pos(UserConfigParams::m_width - dist_from_right, 10, - UserConfigParams::m_width , 50); + core::rect pos(irr_driver->getActualScreenSize().Width - dist_from_right, 10, + irr_driver->getActualScreenSize().Width , 50); gui::ScalableFont* font = GUIEngine::getFont(); bool vcenter = true; - const int size = UserConfigParams::m_width/20; + const int size = irr_driver->getActualScreenSize().Width/20; core::rect dest(size, pos.UpperLeftCorner.Y, size*2, pos.UpperLeftCorner.Y + size); core::rect source(core::position2di(0, 0), m_trophy3->getSize()); @@ -435,7 +435,7 @@ void RaceGUIOverworld::drawGlobalMiniMap() // ---- Draw nearby challenge if any core::rect pos(15, 10, - 15 + UserConfigParams::m_width/2, + 15 + irr_driver->getActualScreenSize().Width/2, 10 + GUIEngine::getTitleFontHeight()); m_close_to_a_challenge = false; @@ -456,9 +456,9 @@ void RaceGUIOverworld::drawGlobalMiniMap() false, true /* vcenter */, NULL); core::rect pos2(0, - UserConfigParams::m_height - GUIEngine::getFontHeight()*2, - UserConfigParams::m_width, - UserConfigParams::m_height); + irr_driver->getActualScreenSize().Height - GUIEngine::getFontHeight()*2, + irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height); GUIEngine::getOutlineFont()->draw(_("Press fire to play the tutorial"), pos2, video::SColor(255,255,150,60), true, true /* vcenter */, NULL); @@ -495,7 +495,7 @@ void RaceGUIOverworld::drawGlobalMiniMap() core::rect pos(15, 20 + GUIEngine::getTitleFontHeight(), - 15 + UserConfigParams::m_width/2, + 15 + irr_driver->getActualScreenSize().Width/2, 20 + 2*GUIEngine::getTitleFontHeight()); //just below GP name @@ -520,15 +520,15 @@ void RaceGUIOverworld::drawGlobalMiniMap() } pos.UpperLeftCorner.Y += GUIEngine::getTitleFontHeight(); - pos.LowerRightCorner.Y = UserConfigParams::m_height; + pos.LowerRightCorner.Y = irr_driver->getActualScreenSize().Height; GUIEngine::getFont()->draw(challenge->getChallengeDescription().c_str(), pos, video::SColor(255,255,255,255), false, false /* vcenter */, NULL); core::rect pos2(0, - UserConfigParams::m_height - GUIEngine::getFontHeight()*2, - UserConfigParams::m_width, - UserConfigParams::m_height); + irr_driver->getActualScreenSize().Height - GUIEngine::getFontHeight()*2, + irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height); GUIEngine::getOutlineFont()->draw(_("Press fire to start the challenge"), pos2, video::SColor(255,255,150,60), true, true /* vcenter */, NULL); From 48d5567b3f87c9cbc0c62ff62f1c44cce1e20517 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sun, 1 Mar 2015 21:50:54 -0500 Subject: [PATCH 104/117] Fix #1954, part 2 --- src/graphics/camera.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp index 06d477186..465950bc1 100644 --- a/src/graphics/camera.cpp +++ b/src/graphics/camera.cpp @@ -164,21 +164,21 @@ void Camera::readEndCamera(const XMLNode &root) */ void Camera::setupCamera() { - m_aspect = (float)(UserConfigParams::m_width)/UserConfigParams::m_height; + m_aspect = (float)(irr_driver->getActualScreenSize().Width)/irr_driver->getActualScreenSize().Height; switch(race_manager->getNumLocalPlayers()) { case 1: m_viewport = core::recti(0, 0, - UserConfigParams::m_width, - UserConfigParams::m_height); + irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height); m_scaling = core::vector2df(1.0f, 1.0f); m_fov = DEGREE_TO_RAD*75.0f; break; case 2: m_viewport = core::recti(0, m_index==0 ? 0 - : UserConfigParams::m_height>>1, - UserConfigParams::m_width, - m_index==0 ? UserConfigParams::m_height>>1 - : UserConfigParams::m_height); + : irr_driver->getActualScreenSize().Height>>1, + irr_driver->getActualScreenSize().Width, + m_index==0 ? irr_driver->getActualScreenSize().Height>>1 + : irr_driver->getActualScreenSize().Height); m_scaling = core::vector2df(1.0f, 0.5f); m_aspect *= 2.0f; m_fov = DEGREE_TO_RAD*65.0f; @@ -188,19 +188,19 @@ void Camera::setupCamera() if(m_index<2) { m_viewport = core::recti(m_index==0 ? 0 - : UserConfigParams::m_width>>1, + : irr_driver->getActualScreenSize().Width>>1, 0, - m_index==0 ? UserConfigParams::m_width>>1 - : UserConfigParams::m_width, - UserConfigParams::m_height>>1); + m_index==0 ? irr_driver->getActualScreenSize().Width>>1 + : irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height>>1); m_scaling = core::vector2df(0.5f, 0.5f); m_fov = DEGREE_TO_RAD*50.0f; } else { - m_viewport = core::recti(0, UserConfigParams::m_height>>1, - UserConfigParams::m_width, - UserConfigParams::m_height); + m_viewport = core::recti(0, irr_driver->getActualScreenSize().Height>>1, + irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height); m_scaling = core::vector2df(1.0f, 0.5f); m_fov = DEGREE_TO_RAD*65.0f; m_aspect *= 2.0f; @@ -208,10 +208,10 @@ void Camera::setupCamera() break;*/ case 4: { // g++ 4.3 whines about the variables in switch/case if not {}-wrapped (???) - const int x1 = (m_index%2==0 ? 0 : UserConfigParams::m_width>>1); - const int y1 = (m_index<2 ? 0 : UserConfigParams::m_height>>1); - const int x2 = (m_index%2==0 ? UserConfigParams::m_width>>1 : UserConfigParams::m_width); - const int y2 = (m_index<2 ? UserConfigParams::m_height>>1 : UserConfigParams::m_height); + const int x1 = (m_index%2==0 ? 0 : irr_driver->getActualScreenSize().Width>>1); + const int y1 = (m_index<2 ? 0 : irr_driver->getActualScreenSize().Height>>1); + const int x2 = (m_index%2==0 ? irr_driver->getActualScreenSize().Width>>1 : irr_driver->getActualScreenSize().Width); + const int y2 = (m_index<2 ? irr_driver->getActualScreenSize().Height>>1 : irr_driver->getActualScreenSize().Height); m_viewport = core::recti(x1, y1, x2, y2); m_scaling = core::vector2df(0.5f, 0.5f); m_fov = DEGREE_TO_RAD*50.0f; @@ -222,8 +222,8 @@ void Camera::setupCamera() Log::warn("Camera", "Incorrect number of players: '%d' - assuming 1.", race_manager->getNumLocalPlayers()); m_viewport = core::recti(0, 0, - UserConfigParams::m_width, - UserConfigParams::m_height); + irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height); m_scaling = core::vector2df(1.0f, 1.0f); m_fov = DEGREE_TO_RAD*75.0f; break; From 04ebd528bf48a571026c74ee1078221d3db4e180 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 2 Mar 2015 18:26:21 +1100 Subject: [PATCH 105/117] Revert "Check the translation of the terms to make sure that they include" This reverts commit 53a830be0b64e80486b58a427f2b1ccdca76959f. --- .../dialogs/registration_dialog.cpp | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/states_screens/dialogs/registration_dialog.cpp b/src/states_screens/dialogs/registration_dialog.cpp index f2fd44325..83b39567f 100644 --- a/src/states_screens/dialogs/registration_dialog.cpp +++ b/src/states_screens/dialogs/registration_dialog.cpp @@ -39,27 +39,15 @@ RegistrationDialog::RegistrationDialog() : loadFromFile("online/registration_terms.stkgui"); LabelWidget* terms_widget = getWidget("terms"); - core::stringw orig_terms = L"Please read the terms and conditions " + core::stringw terms = _(L"Please read the terms and conditions " L"for SuperTuxKart at '%s'. You must agree " L"to these terms in order to register an account for STK. " L"By checking the box below, you are confirming that you understand " L"these terms. If you have any questions or comments regarding these " L"terms, one of the members of the development team would gladly " - L"assist you."; - - core::stringw url = L"http://supertuxkart.net/terms"; - core::stringw translation = _(orig_terms.c_str(), url.c_str()); - - // Make sure the translation contains the right URL. If not, this - // translation is really messed up, and we better show the original. - if (translation.find(url.c_str()) == -1) - { - translation = StringUtils::insertValues(orig_terms, url); - Log::warn("Terms", "Translated terms do not contain right URL, " - "using English terms"); - } - - terms_widget->setText(translation, false); + L"assist you.", + L"http://supertuxkart.net/terms"); + terms_widget->setText(terms, false); // showRegistrationTerms(); } From b21dafc12bece236296653aedd9e569772c781df Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 2 Mar 2015 18:51:50 +1100 Subject: [PATCH 106/117] Added error message type for skins, which is atm only used to display automatic login failures. Added Arthur's new icons for errors, and friend+achievement icons for Ocean skin. --- data/skins/Ocean.stkskin | 4 ++++ data/skins/Peach.stkskin | 4 ++++ data/skins/ocean/achievement.png | Bin 28721 -> 27900 bytes data/skins/ocean/error.png | Bin 0 -> 26678 bytes data/skins/ocean/friend.png | Bin 25829 -> 24407 bytes data/skins/peach/error.png | Bin 0 -> 26708 bytes src/guiengine/message_queue.cpp | 2 +- 7 files changed, 9 insertions(+), 1 deletion(-) create mode 100755 data/skins/ocean/error.png create mode 100755 data/skins/peach/error.png diff --git a/data/skins/Ocean.stkskin b/data/skins/Ocean.stkskin index 2fabdb4f1..d4d7196de 100644 --- a/data/skins/Ocean.stkskin +++ b/data/skins/Ocean.stkskin @@ -76,6 +76,10 @@ when the border that intersect at this corner are enabled. left_border="128" right_border="13" top_border="13" bottom_border="13" preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/> + + diff --git a/data/skins/Peach.stkskin b/data/skins/Peach.stkskin index e9594353f..bca6fab82 100644 --- a/data/skins/Peach.stkskin +++ b/data/skins/Peach.stkskin @@ -76,6 +76,10 @@ when the border that intersect at this corner are enabled. left_border="128" right_border="13" top_border="13" bottom_border="13" preserve_h_aspect_ratios="true" hborder_out_portion="0" vborder_out_portion="0"/> + + diff --git a/data/skins/ocean/achievement.png b/data/skins/ocean/achievement.png index b2d4585a843bf189f1f21ff5d27764ec066a8153..f5044fb7a81e518b0c90f1a31696ca78663359fe 100644 GIT binary patch literal 27900 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 diff --git a/data/skins/ocean/error.png b/data/skins/ocean/error.png new file mode 100755 index 0000000000000000000000000000000000000000..689c0224c6f7711d30dacb76c015381834c7b7cc GIT binary patch literal 26678 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 kN#7X08Jkq6CO!8gfOL1) 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 literal 25829 zcmX_o1z1#FwD!;qLk}Sx0}PD_N_R>l-Kl^yLw9$Vk|GL7igc#}4u}XyH%QHZbp6Ng z{`bOR;F$-`S$pr5@4FW9+Rv4V@M-ZuAP|wNih?c(gcb(;9EOVxyrS2469C_^ZPb+& zKo9>uOS&pEfp_rSRgArWznJ~^g(e3rgadElc≨wih!rHvoS%8^8?W$L~K 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 diff --git a/data/skins/peach/error.png b/data/skins/peach/error.png new file mode 100755 index 0000000000000000000000000000000000000000..2597e9c7573f9a6e10bc33c2bec0597c1fb18ce6 GIT binary patch literal 26708 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 Date: Mon, 2 Mar 2015 19:01:21 +1100 Subject: [PATCH 107/117] Fixed typo in error message. --- src/tracks/track.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 1054fa1de..635d913f6 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -518,7 +518,7 @@ public: btTransform getStartTransform (unsigned int index) const { if (index >= m_start_transforms.size()) - Log::fatal("Tracj", "No start position for kart %i.", index); + Log::fatal("Track", "No start position for kart %i.", index); return m_start_transforms[index]; } // ------------------------------------------------------------------------ From cb7eab4f0344e6d3d4b0ecade26664ef05014060 Mon Sep 17 00:00:00 2001 From: deve Date: Mon, 2 Mar 2015 13:56:48 +0100 Subject: [PATCH 108/117] MinGW: Support for make install --- CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a153e23d..7cc9cb17e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -379,6 +379,15 @@ if(MSVC OR MINGW) add_custom_target(stkshaders SOURCES ${STK_SHADERS}) endif() +if(MINGW) + find_library(LIBGCC NAMES libgcc_s_dw2-1.dll libgcc_s_sjlj-1.dll) + find_library(LIBSTDCPP NAMES libstdc++-6.dll) + if(OPENMP_FOUND) + find_library(LIBOPENMP NAMES libgomp-1.dll) + endif() + file(COPY ${LIBGCC} ${LIBSTDCPP} ${PTHREAD_LIBRARY} ${LIBOPENMP} DESTINATION ${CMAKE_BINARY_DIR}/bin/) +endif() + # Optional tools add_subdirectory(tools/font_tool) @@ -441,3 +450,8 @@ install(FILES data/supertuxkart_32.png DESTINATION share/icons/hicolor/32x32/app install(FILES data/supertuxkart_128.png DESTINATION share/icons/hicolor/128x128/apps RENAME supertuxkart.png) install(FILES data/supertuxkart_32.png data/supertuxkart_128.png DESTINATION share/pixmaps) install(FILES data/supertuxkart.appdata.xml DESTINATION share/appdata) + +if(MINGW) + install(DIRECTORY ${CMAKE_BINARY_DIR}/bin/ DESTINATION ${STK_INSTALL_BINARY_DIR} + FILES_MATCHING PATTERN "*.dll") +endif() From 89d5cee4c1bccffeeae233a7e5ab0dc2fd183cb9 Mon Sep 17 00:00:00 2001 From: Deve Date: Mon, 2 Mar 2015 19:30:26 +0100 Subject: [PATCH 109/117] MinGW: Better finding libraries --- CMakeLists.txt | 22 ++++++++++++++++------ cmake/Toolchain-mingw.cmake | 6 +++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cc9cb17e..b193ff600 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,7 +278,7 @@ else() if(MSVC) set(PTHREAD_NAMES pthreadVC2) elseif(MINGW) - set(PTHREAD_NAMES "winpthread-1" pthreadGC2) + set(PTHREAD_NAMES "winpthread-1" "pthreadGC2" "libwinpthread-1") endif() find_library(PTHREAD_LIBRARY NAMES pthread ${PTHREAD_NAMES} PATHS ${PROJECT_SOURCE_DIR}/dependencies/lib) mark_as_advanced(PTHREAD_LIBRARY) @@ -380,12 +380,22 @@ if(MSVC OR MINGW) endif() if(MINGW) - find_library(LIBGCC NAMES libgcc_s_dw2-1.dll libgcc_s_sjlj-1.dll) - find_library(LIBSTDCPP NAMES libstdc++-6.dll) - if(OPENMP_FOUND) - find_library(LIBOPENMP NAMES libgomp-1.dll) + find_library(LIBGCC NAMES "libgcc_s_dw2-1.dll" "libgcc_s_sjlj-1.dll" PATHS ${CMAKE_FIND_ROOT_PATH}) + if(LIBGCC) + file(COPY ${LIBGCC} DESTINATION ${CMAKE_BINARY_DIR}/bin/) + endif() + find_library(LIBSTDCPP NAMES "libstdc++-6.dll" PATHS ${CMAKE_FIND_ROOT_PATH}) + if(LIBSTDCPP) + file(COPY ${LIBSTDCPP} DESTINATION ${CMAKE_BINARY_DIR}/bin/) + endif() + find_library(LIBOPENMP NAMES "libgomp-1.dll" PATHS ${CMAKE_FIND_ROOT_PATH}) + if(LIBOPENMP) + file(COPY ${LIBOPENMP} DESTINATION ${CMAKE_BINARY_DIR}/bin/) + endif() + find_library(LIBPTHREAD NAMES "winpthread-1.dll" "pthreadGC2.dll" "libwinpthread-1.dll" PATHS ${CMAKE_FIND_ROOT_PATH}) + if(LIBPTHREAD) + file(COPY ${LIBPTHREAD} DESTINATION ${CMAKE_BINARY_DIR}/bin/) endif() - file(COPY ${LIBGCC} ${LIBSTDCPP} ${PTHREAD_LIBRARY} ${LIBOPENMP} DESTINATION ${CMAKE_BINARY_DIR}/bin/) endif() # Optional tools diff --git a/cmake/Toolchain-mingw.cmake b/cmake/Toolchain-mingw.cmake index 1ecb47ac2..56908e12e 100644 --- a/cmake/Toolchain-mingw.cmake +++ b/cmake/Toolchain-mingw.cmake @@ -7,11 +7,11 @@ SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++-posix) SET(CMAKE_RC_COMPILER i686-w64-mingw32-windres) # here is the target environment located -SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32 ${PROJECT_SOURCE_DIR}/dependencies) +SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32 /usr/lib/gcc/i686-w64-mingw32/4.9-posix ${PROJECT_SOURCE_DIR}/dependencies) # adjust the default behaviour of the FIND_XXX() commands: # search headers and libraries in the target environment, search # programs in the host environment set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) \ No newline at end of file +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ALWAYS) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) From 08b1fc01f0659ff53ac93b0ce246e2254fc1573c Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 3 Mar 2015 16:03:38 +1100 Subject: [PATCH 110/117] Fix error if files are downloaded using https (as happened because of a server misconfiguration). Add details about the certificate if the URL contains https (and not only if it's not a downloaded file, i.e. it is a request with an xml answers that is read from memory). --- src/online/http_request.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/online/http_request.cpp b/src/online/http_request.cpp index 2262d40ef..c3d2a340f 100644 --- a/src/online/http_request.cpp +++ b/src/online/http_request.cpp @@ -171,7 +171,7 @@ namespace Online curl_easy_setopt(m_curl_session, CURLOPT_LOW_SPEED_LIMIT, 10); curl_easy_setopt(m_curl_session, CURLOPT_LOW_SPEED_TIME, 20); //curl_easy_setopt(m_curl_session, CURLOPT_VERBOSE, 1L); - if (m_filename.size() == 0) + if (m_url.substr(0, 8) == "https://") { // https, load certificate info struct curl_slist *chunk = NULL; From 26054a1c60f8e6e186a8ecfa32518f6970281fcf Mon Sep 17 00:00:00 2001 From: deve Date: Tue, 3 Mar 2015 07:17:48 +0100 Subject: [PATCH 111/117] MinGW: Better pthread names order --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b193ff600..55b30e1ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,7 +278,7 @@ else() if(MSVC) set(PTHREAD_NAMES pthreadVC2) elseif(MINGW) - set(PTHREAD_NAMES "winpthread-1" "pthreadGC2" "libwinpthread-1") + set(PTHREAD_NAMES "winpthread-1" "libwinpthread-1" "pthreadGC2") endif() find_library(PTHREAD_LIBRARY NAMES pthread ${PTHREAD_NAMES} PATHS ${PROJECT_SOURCE_DIR}/dependencies/lib) mark_as_advanced(PTHREAD_LIBRARY) @@ -392,7 +392,7 @@ if(MINGW) if(LIBOPENMP) file(COPY ${LIBOPENMP} DESTINATION ${CMAKE_BINARY_DIR}/bin/) endif() - find_library(LIBPTHREAD NAMES "winpthread-1.dll" "pthreadGC2.dll" "libwinpthread-1.dll" PATHS ${CMAKE_FIND_ROOT_PATH}) + find_library(LIBPTHREAD NAMES "winpthread-1.dll" "libwinpthread-1.dll" "pthreadGC2.dll" PATHS ${CMAKE_FIND_ROOT_PATH}) if(LIBPTHREAD) file(COPY ${LIBPTHREAD} DESTINATION ${CMAKE_BINARY_DIR}/bin/) endif() From 3da52bcbfbdd5ea9bb945fbb61387d9b91ea8689 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 4 Mar 2015 07:49:13 +1100 Subject: [PATCH 112/117] Cosmetic changes. --- src/items/item.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index 06f4cad71..8ada22f57 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -19,9 +19,6 @@ #include "items/item.hpp" -#include -#include - #include "graphics/irr_driver.hpp" #include "graphics/lod_node.hpp" #include "karts/abstract_kart.hpp" @@ -32,6 +29,9 @@ #include "utils/constants.hpp" #include "utils/vec3.hpp" +#include +#include + Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, scene::IMesh* mesh, scene::IMesh* lowres_mesh) { @@ -48,12 +48,15 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, LODNode* lodnode = new LODNode("item", irr_driver->getSceneManager()->getRootSceneNode(), irr_driver->getSceneManager()); - scene::IMeshSceneNode* meshnode = irr_driver->addMesh(mesh, StringUtils::insertValues("item_%i", (int)type)); + scene::IMeshSceneNode* meshnode = + irr_driver->addMesh(mesh, StringUtils::insertValues("item_%i", (int)type)); if (lowres_mesh != NULL) { lodnode->add(35, meshnode, true); - scene::IMeshSceneNode* meshnode = irr_driver->addMesh(lowres_mesh, StringUtils::insertValues("item_lo_%i", (int)type)); + scene::IMeshSceneNode* meshnode = + irr_driver->addMesh(lowres_mesh, + StringUtils::insertValues("item_lo_%i", (int)type)); lodnode->add(100, meshnode, true); } else From f347cd0b10a5206c7c5f743d938a082408b5cc58 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 5 Mar 2015 11:26:16 +1100 Subject: [PATCH 113/117] Updated license information for new notification elements. --- data/skins/ocean/License.txt | 4 ++++ data/skins/peach/License.txt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/data/skins/ocean/License.txt b/data/skins/ocean/License.txt index 3f38dc24f..4f64d57db 100644 --- a/data/skins/ocean/License.txt +++ b/data/skins/ocean/License.txt @@ -1,3 +1,7 @@ +friend.png, error.png, achievement.png - Licensed under CC-BY-SA 3.0 by Magne Djupvik (notification_backgrounds.xcf), + based on cup_gold.png licensed under CC-BY-SA 3+ from Open Game Art (art by onyum.com, comissionned by Bart Kelsey) + glass_section.png licensed under CC-BY-SA 3.0 Unported and main_about.png licensed under Creative-Commons BY-SA 3, By yeKcim (Anthony CarrĂ©) + This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. diff --git a/data/skins/peach/License.txt b/data/skins/peach/License.txt index 3f38dc24f..4f64d57db 100644 --- a/data/skins/peach/License.txt +++ b/data/skins/peach/License.txt @@ -1,3 +1,7 @@ +friend.png, error.png, achievement.png - Licensed under CC-BY-SA 3.0 by Magne Djupvik (notification_backgrounds.xcf), + based on cup_gold.png licensed under CC-BY-SA 3+ from Open Game Art (art by onyum.com, comissionned by Bart Kelsey) + glass_section.png licensed under CC-BY-SA 3.0 Unported and main_about.png licensed under Creative-Commons BY-SA 3, By yeKcim (Anthony CarrĂ©) + This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. From fb4f4aeb2e1b18ce9a1d4065988411c9d9f900fa Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Wed, 4 Mar 2015 19:39:12 -0500 Subject: [PATCH 114/117] Always create movables at scene manager root --- src/tracks/track_object.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index 8351c7858..44b93e4ef 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -27,7 +27,7 @@ #include "physics/physical_object.hpp" #include "race/race_manager.hpp" #include "utils/helpers.hpp" - +#include /** A track object: any additional object on the track. This object implements * a graphics-only representation, i.e. there is no physical representation. @@ -191,7 +191,21 @@ void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent, m_presentation = new TrackObjectPresentationMesh(xml_node, m_enabled, parent); - glownode = ((TrackObjectPresentationMesh *) m_presentation)->getNode(); + scene::ISceneNode* node = ((TrackObjectPresentationMesh *)m_presentation)->getNode(); + if (type == "movable" && parent != NULL) + { + // HACK: unparent movables from their parent library object if any, + // because bullet provides absolute transforms, not transforms relative + // to the parent object + node->updateAbsolutePosition(); + core::matrix4 absTransform = node->getAbsoluteTransformation(); + node->setParent(irr_driver->getSceneManager()->getRootSceneNode()); + node->setPosition(absTransform.getTranslation()); + node->setRotation(absTransform.getRotationDegrees()); + node->setScale(absTransform.getScale()); + } + + glownode = node; } std::string render_pass; From 0722df79da9e273bf1a83aef7446045ea9cd24ae Mon Sep 17 00:00:00 2001 From: Deve Date: Thu, 5 Mar 2015 19:58:40 +0100 Subject: [PATCH 115/117] Fixed getScale() result when matrix contains very low negative values. See #1548. --- lib/irrlicht/include/matrix4.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/irrlicht/include/matrix4.h b/lib/irrlicht/include/matrix4.h index f4af53da8..690180184 100644 --- a/lib/irrlicht/include/matrix4.h +++ b/lib/irrlicht/include/matrix4.h @@ -865,9 +865,9 @@ namespace core // Deal with the 0 rotation case first // Prior to Irrlicht 1.6, we always returned this value. - if(core::iszero(M[1]) && core::iszero(M[2]) && - core::iszero(M[4]) && core::iszero(M[6]) && - core::iszero(M[8]) && core::iszero(M[9])) + if (M[1] == 0 && M[2] == 0 && + M[4] == 0 && M[6] == 0 && + M[8] == 0 && M[9] == 0) return vector3d(M[0], M[5], M[10]); // We have to do the full calculation. From 8bd33dba2344a95ab1e2d5309b1b13fab21dc585 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Thu, 5 Mar 2015 20:14:33 -0500 Subject: [PATCH 116/117] Improved handling of scaled physical objects --- src/physics/physical_object.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/physics/physical_object.cpp b/src/physics/physical_object.cpp index d3287f877..4e8ec6418 100644 --- a/src/physics/physical_object.cpp +++ b/src/physics/physical_object.cpp @@ -246,6 +246,10 @@ void PhysicalObject::init() { Log::fatal("PhysicalObject", "Unknown node type"); } + + max = max * Vec3(m_init_scale); + min = min * Vec3(m_init_scale); + Vec3 extend = max-min; // Adjust the mesth of the graphical object so that its center is where it // is in bullet (usually at (0,0,0)). It can be changed in the case clause @@ -503,10 +507,8 @@ void PhysicalObject::update(float dt) hpr.setHPR(t.getRotation()); //m_node->setRotation(hpr.toIrrHPR()); - core::vector3df scale(1,1,1); m_object->move(xyz.toIrrVector(), hpr.toIrrVector()*RAD_TO_DEGREE, - scale, false); - return; + m_init_scale, false); } // update // ---------------------------------------------------------------------------- From 33feb60d7acf3e7dba9ea18365f5b8b63e5b7809 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sat, 7 Mar 2015 18:46:42 -0500 Subject: [PATCH 117/117] Do not complain about icon-not-found when no icon was ever specified, silences a warning mentionned in #2021 --- src/guiengine/widgets/icon_button_widget.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/guiengine/widgets/icon_button_widget.cpp b/src/guiengine/widgets/icon_button_widget.cpp index 7ea478475..9f155a3f7 100644 --- a/src/guiengine/widgets/icon_button_widget.cpp +++ b/src/guiengine/widgets/icon_button_widget.cpp @@ -80,9 +80,12 @@ void IconButtonWidget::add() if (m_texture == NULL) { - Log::error("icon_button", - "add() : error, cannot find texture '%s'.", - m_properties[PROP_ICON].c_str()); + if (m_properties[PROP_ICON].size() > 0) + { + Log::error("icon_button", + "add() : error, cannot find texture '%s' in iconbutton '%s'.", + m_properties[PROP_ICON].c_str(), m_properties[PROP_ID].c_str()); + } std::string file = file_manager->getAsset(FileManager::GUI,"main_help.png"); setTexture(irr_driver->getTexture(file)); if(!m_texture)