From 708ad6ac7f00ed30e0e73d193d05439c74a7e8ac Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 6 Aug 2012 07:34:37 +0000 Subject: [PATCH] - music player uses URI's instead of enum from music base - #1045 and #1046 should be fixed - moved pregame backgrounds to config/mainmenu - animation can be overriden with .json multiple times --- AI/VCAI/VCAI.cpp | 2 +- Global.h | 8 +- client/BattleInterface/CBattleInterface.cpp | 12 +- .../CBattleInterfaceClasses.cpp | 8 +- client/CAdvmapInterface.cpp | 4 +- client/CAnimation.cpp | 12 +- client/CBitmapHandler.cpp | 13 +- client/CCastleInterface.cpp | 2 +- client/CMusicBase.h | 90 ---------- client/CMusicHandler.cpp | 164 +++++++++--------- client/CMusicHandler.h | 42 ++--- client/CPlayerInterface.cpp | 2 +- client/CPreGame.cpp | 8 +- client/CPreGame.h | 4 +- client/GUIClasses.cpp | 25 +-- client/GUIClasses.h | 6 +- client/Graphics.cpp | 3 +- client/UIFramework/CIntObjectClasses.cpp | 1 - client/UIFramework/SDL_Extensions.cpp | 2 +- config/filesystem.json | 1 + config/mainmenu.json | 4 +- lib/CGeneralTextHandler.cpp | 3 +- lib/Filesystem/CResourceLoader.cpp | 46 +++-- lib/Filesystem/CResourceLoader.h | 6 +- 24 files changed, 212 insertions(+), 256 deletions(-) delete mode 100644 client/CMusicBase.h diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index baee62ba0..5a453047a 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -2576,7 +2576,7 @@ int3 whereToExplore(HeroPtr h) } catch(cannotFulfillGoalException &e) { - std::vector > tiles; //tiles[distance_to_fow], metryka taksówkowa + std::vector > tiles; //tiles[distance_to_fow] try { return ai->explorationNewPoint(radius, h, tiles); diff --git a/Global.h b/Global.h index f9def202d..a34a282c1 100644 --- a/Global.h +++ b/Global.h @@ -268,16 +268,16 @@ namespace vstd //checks if a is between b and c template - bool isbetween(const t1 &a, const t2 &b, const t3 &c) + bool isbetween(const t1 &value, const t2 &min, const t3 &max) { - return a > b && a < c; + return value > min && value < max; } //checks if a is within b and c template - bool iswithin(const t1 &a, const t2 &b, const t3 &c) + bool iswithin(const t1 &value, const t2 &min, const t3 &max) { - return a >= b && a <= c; + return value >= min && value <= max; } template diff --git a/client/BattleInterface/CBattleInterface.cpp b/client/BattleInterface/CBattleInterface.cpp index 6a75a230f..3a648c4c6 100644 --- a/client/BattleInterface/CBattleInterface.cpp +++ b/client/BattleInterface/CBattleInterface.cpp @@ -371,8 +371,14 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe CCS->musich->stopMusic(); int channel = CCS->soundh->playSoundFromSet(CCS->soundh->battleIntroSounds); - CCS->soundh->setCallback(channel, boost::bind(&CMusicHandler::playMusicFromSet, CCS->musich, CCS->musich->battleMusics, -1)); - memset(stackCountOutsideHexes, 1, GameConstants::BFIELD_SIZE * sizeof(bool)); //initialize array with trues + auto onIntroPlayed = []() + { + if (LOCPLINT->battleInt) + CCS->musich->playMusicFromSet("battle", true); + }; + + CCS->soundh->setCallback(channel, onIntroPlayed); + memset(stackCountOutsideHexes, 1, GameConstants::BFIELD_SIZE * sizeof(bool)); //initialize array with trues currentAction = INVALID; selectedAction = INVALID; @@ -446,7 +452,7 @@ CBattleInterface::~CBattleInterface() if(adventureInt && adventureInt->selection) { int terrain = LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->tertype; - CCS->musich->playMusic(CCS->musich->terrainMusics[terrain], -1); + CCS->musich->playMusicFromSet("terrain", terrain, true); } } diff --git a/client/BattleInterface/CBattleInterfaceClasses.cpp b/client/BattleInterface/CBattleInterfaceClasses.cpp index 345d0216c..583599c14 100644 --- a/client/BattleInterface/CBattleInterfaceClasses.cpp +++ b/client/BattleInterface/CBattleInterfaceClasses.cpp @@ -417,7 +417,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect case 2: text = 302; break; } - CCS->musich->playMusic(musicBase::winBattle); + CCS->musich->playMusic("Music/Win Battle", false); CCS->videoh->open(VIDEO_WIN); std::string str = CGI->generaltexth->allTexts[text]; @@ -437,21 +437,21 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect { case 0: //normal victory { - CCS->musich->playMusic(musicBase::loseCombat); + CCS->musich->playMusic("Music/LoseCombat", false); CCS->videoh->open(VIDEO_LOSE_BATTLE_START); new CLabel(235, 235, FONT_SMALL, CENTER, Colors::Cornsilk, CGI->generaltexth->allTexts[311]); break; } case 1: //flee { - CCS->musich->playMusic(musicBase::retreatBattle); + CCS->musich->playMusic("Music/Retreat Battle", false); CCS->videoh->open(VIDEO_RETREAT_START); new CLabel(235, 235, FONT_SMALL, CENTER, Colors::Cornsilk, CGI->generaltexth->allTexts[310]); break; } case 2: //surrender { - CCS->musich->playMusic(musicBase::surrenderBattle); + CCS->musich->playMusic("Music/Surrender Battle", false); CCS->videoh->open(VIDEO_SURRENDER); new CLabel(235, 235, FONT_SMALL, CENTER, Colors::Cornsilk, CGI->generaltexth->allTexts[309]); break; diff --git a/client/CAdvmapInterface.cpp b/client/CAdvmapInterface.cpp index 76b553e88..22526bd89 100644 --- a/client/CAdvmapInterface.cpp +++ b/client/CAdvmapInterface.cpp @@ -987,7 +987,7 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView /*= true*/) auto pos = sel->visitablePos(); auto tile = LOCPLINT->cb->getTile(pos); if(tile) - CCS->musich->playMusic(CCS->musich->terrainMusics[tile->tertype], -1); + CCS->musich->playMusicFromSet("terrain", tile->tertype, true); } if(centerView) centerOn(sel); @@ -1487,7 +1487,7 @@ const IShipyard * CAdvMapInt::ourInaccessibleShipyard(const CGObjectInstance *ob void CAdvMapInt::aiTurnStarted() { adjustActiveness(true); - CCS->musich->playMusicFromSet(CCS->musich->aiMusics); + CCS->musich->playMusicFromSet("enemy-turn", true); adventureInt->minimap.setAIRadar(true); adventureInt->infoBar.startEnemyTurn(LOCPLINT->cb->getCurrentPlayer()); adventureInt->infoBar.showAll(screen);//force refresh on inactive object diff --git a/client/CAnimation.cpp b/client/CAnimation.cpp index 69241beca..81776f2c1 100644 --- a/client/CAnimation.cpp +++ b/client/CAnimation.cpp @@ -2,6 +2,7 @@ #include #include "../lib/Filesystem/CResourceLoader.h" +#include "../lib/Filesystem/ISimpleResourceLoader.h" #include "../lib/JsonNode.h" #include "../lib/vcmi_endian.h" @@ -934,9 +935,16 @@ void CAnimation::init(CDefFile * file) source[mapIt->first].resize(mapIt->second); } - if (CResourceHandler::get()->existsResource(ResourceID(std::string("SPRITES/") + name, EResType::TEXT))) + auto & configList = CResourceHandler::get()->getResourcesWithName( + ResourceID(std::string("SPRITES/") + name, EResType::TEXT)); + + BOOST_FOREACH(auto & entry, configList) { - const JsonNode config(ResourceID(std::string("SPRITES/") + name, EResType::TEXT)); + auto stream = entry.getLoader()->load(entry.getResourceName()); + std::unique_ptr textData(new ui8[stream->getSize()]); + stream->read(textData.get(), stream->getSize()); + + const JsonNode config((char*)textData.get(), stream->getSize()); std::string basepath; basepath = config["basepath"].String(); diff --git a/client/CBitmapHandler.cpp b/client/CBitmapHandler.cpp index 738cdeae6..42c29f604 100644 --- a/client/CBitmapHandler.cpp +++ b/client/CBitmapHandler.cpp @@ -76,7 +76,16 @@ SDL_Surface * BitmapHandler::loadH3PCX(ui8 * pcx, size_t size) } else { - ret = CSDL_Ext::createSurfaceWithBpp<3>(width, height); +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + int bmask = 0xff0000; + int gmask = 0x00ff00; + int rmask = 0x0000ff; +#else + int bmask = 0x0000ff; + int gmask = 0x00ff00; + int rmask = 0xff0000; +#endif + ret = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, rmask, gmask, bmask, 0); //it == 0xC; for (int i=0; ionSelect = boost::bind(&CCastleInterface::townChange, this); recreateIcons(); - CCS->musich->playMusic(CCS->musich->townMusics[town->subID], -1); + CCS->musich->playMusicFromSet("town-theme", town->subID, true); bicons = CDefHandler::giveDefEss(graphics->buildingPics[town->subID]); } diff --git a/client/CMusicBase.h b/client/CMusicBase.h deleted file mode 100644 index 9f5c92ad7..000000000 --- a/client/CMusicBase.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -/* - * CMusicBase.h, part of VCMI engine - * - * Authors: listed in file AUTHORS in main folder - * - * License: GNU General Public License v2.0 or later - * Full text of license available in license.txt file, in main folder - * - */ - -// Use some magic to keep the list of files and their code name in sync. -// FIXME: first half of this list should be read from campmusic.txt - -#define VCMI_MUSIC_LIST \ - VCMI_MUSIC_ID(campainMusic01) VCMI_MUSIC_FILE("CampainMusic01.mp3") \ - VCMI_MUSIC_ID(campainMusic02) VCMI_MUSIC_FILE("CampainMusic02.mp3") \ - VCMI_MUSIC_ID(campainMusic03) VCMI_MUSIC_FILE("CampainMusic03.mp3") \ - VCMI_MUSIC_ID(campainMusic04) VCMI_MUSIC_FILE("CampainMusic04.mp3") \ - VCMI_MUSIC_ID(campainMusic05) VCMI_MUSIC_FILE("CampainMusic05.mp3") \ - VCMI_MUSIC_ID(campainMusic06) VCMI_MUSIC_FILE("CampainMusic06.mp3") \ - VCMI_MUSIC_ID(campainMusic07) VCMI_MUSIC_FILE("CampainMusic07.mp3") \ - VCMI_MUSIC_ID(campainMusic08) VCMI_MUSIC_FILE("CampainMusic08.mp3") \ - VCMI_MUSIC_ID(campainMusic09) VCMI_MUSIC_FILE("CampainMusic09.mp3") \ - VCMI_MUSIC_ID(AITheme0) VCMI_MUSIC_FILE("AITheme0.mp3") \ - VCMI_MUSIC_ID(AITheme1) VCMI_MUSIC_FILE("AITHEME1.MP3") \ - VCMI_MUSIC_ID(AITheme2) VCMI_MUSIC_FILE("AITHEME2.MP3") \ - VCMI_MUSIC_ID(combat1) VCMI_MUSIC_FILE("COMBAT01.MP3") \ - VCMI_MUSIC_ID(combat2) VCMI_MUSIC_FILE("COMBAT02.MP3") \ - VCMI_MUSIC_ID(combat3) VCMI_MUSIC_FILE("COMBAT03.MP3") \ - VCMI_MUSIC_ID(combat4) VCMI_MUSIC_FILE("COMBAT04.MP3") \ - VCMI_MUSIC_ID(castleTown) VCMI_MUSIC_FILE("CstleTown.mp3") \ - VCMI_MUSIC_ID(towerTown) VCMI_MUSIC_FILE("TowerTown.mp3") \ - VCMI_MUSIC_ID(rampartTown) VCMI_MUSIC_FILE("RAMPART.MP3") \ - VCMI_MUSIC_ID(infernoTown) VCMI_MUSIC_FILE("InfernoTown.mp3") \ - VCMI_MUSIC_ID(necroTown) VCMI_MUSIC_FILE("necroTown.mp3") \ - VCMI_MUSIC_ID(dungeonTown) VCMI_MUSIC_FILE("DUNGEON.MP3") \ - VCMI_MUSIC_ID(strongHoldTown) VCMI_MUSIC_FILE("StrongHold.mp3") \ - VCMI_MUSIC_ID(fortressTown) VCMI_MUSIC_FILE("FortressTown.mp3") \ - VCMI_MUSIC_ID(elemTown) VCMI_MUSIC_FILE("ElemTown.mp3") \ - VCMI_MUSIC_ID(dirt) VCMI_MUSIC_FILE("DIRT.MP3") \ - VCMI_MUSIC_ID(sand) VCMI_MUSIC_FILE("SAND.MP3") \ - VCMI_MUSIC_ID(grass) VCMI_MUSIC_FILE("GRASS.MP3") \ - VCMI_MUSIC_ID(snow) VCMI_MUSIC_FILE("SNOW.MP3") \ - VCMI_MUSIC_ID(swamp) VCMI_MUSIC_FILE("SWAMP.MP3") \ - VCMI_MUSIC_ID(rough) VCMI_MUSIC_FILE("ROUGH.MP3") \ - VCMI_MUSIC_ID(underground) VCMI_MUSIC_FILE("Underground.mp3") \ - VCMI_MUSIC_ID(lava) VCMI_MUSIC_FILE("LAVA.MP3") \ - VCMI_MUSIC_ID(water) VCMI_MUSIC_FILE("WATER.MP3") \ - VCMI_MUSIC_ID(goodTheme) VCMI_MUSIC_FILE("GoodTheme.mp3") \ - VCMI_MUSIC_ID(neutralTheme) VCMI_MUSIC_FILE("NeutralTheme.mp3") \ - VCMI_MUSIC_ID(evilTheme) VCMI_MUSIC_FILE("EvilTheme.mp3") \ - VCMI_MUSIC_ID(secretTheme) VCMI_MUSIC_FILE("SecretTheme.mp3") \ - VCMI_MUSIC_ID(loopLepr) VCMI_MUSIC_FILE("LoopLepr.mp3") \ - VCMI_MUSIC_ID(mainMenu) VCMI_MUSIC_FILE("MAINMENU.MP3") \ - VCMI_MUSIC_ID(winScenario) VCMI_MUSIC_FILE("Win Scenario.mp3" ) \ - VCMI_MUSIC_ID(campainMusic10) VCMI_MUSIC_FILE("CampainMusic10.mp3") \ - VCMI_MUSIC_ID(bladeABcampaign) VCMI_MUSIC_FILE("BladeABCampaign.mp3") \ - VCMI_MUSIC_ID(bladeDBcampaign) VCMI_MUSIC_FILE("BladeDBCampaign.mp3") \ - VCMI_MUSIC_ID(bladeDScampaign) VCMI_MUSIC_FILE("BladeDSCampaign.mp3") \ - VCMI_MUSIC_ID(bladeFLcampaign) VCMI_MUSIC_FILE("BladeFLCampaign.mp3") \ - VCMI_MUSIC_ID(bladeFWcampaign) VCMI_MUSIC_FILE("BladeFWCampaign.mp3") \ - VCMI_MUSIC_ID(bladePWcampaign) VCMI_MUSIC_FILE("BladePFCampaign.mp3") \ - VCMI_MUSIC_ID(campainMusic11) VCMI_MUSIC_FILE("CampainMusic11.mp3") \ - VCMI_MUSIC_ID(loseCampain) VCMI_MUSIC_FILE("Lose Campain.mp3") \ - VCMI_MUSIC_ID(loseCastle) VCMI_MUSIC_FILE("LoseCastle.mp3") \ - VCMI_MUSIC_ID(loseCombat) VCMI_MUSIC_FILE("LoseCombat.mp3") \ - VCMI_MUSIC_ID(mainMenuWoG) VCMI_MUSIC_FILE("MainMenuWoG.mp3") \ - VCMI_MUSIC_ID(retreatBattle) VCMI_MUSIC_FILE("Retreat Battle.mp3") \ - VCMI_MUSIC_ID(surrenderBattle) VCMI_MUSIC_FILE("Surrender Battle.mp3") \ - VCMI_MUSIC_ID(ultimateLose) VCMI_MUSIC_FILE("UltimateLose.mp3") \ - VCMI_MUSIC_ID(winBattle) VCMI_MUSIC_FILE("Win Battle.mp3") \ - VCMI_MUSIC_ID(defendCastle) VCMI_MUSIC_FILE("Defend Castle.mp3") \ - -class musicBase -{ -public: - // Make a list of enums -#define VCMI_MUSIC_ID(x) x, -#define VCMI_MUSIC_FILE(y) - enum musicID { - music_todo=0, // temp entry until code is fixed - VCMI_MUSIC_LIST - }; -#undef VCMI_MUSIC_ID -#undef VCMI_MUSIC_FILE -}; - - diff --git a/client/CMusicHandler.cpp b/client/CMusicHandler.cpp index 3e50403c2..9dc57d8d2 100644 --- a/client/CMusicHandler.cpp +++ b/client/CMusicHandler.cpp @@ -53,7 +53,7 @@ void CAudioBase::init() void CAudioBase::release() { if (initialized) - { + { Mix_CloseAudio(); initialized = false; } @@ -332,40 +332,27 @@ CMusicHandler::CMusicHandler(): { listener(boost::bind(&CMusicHandler::onVolumeChange, this, _1)); // Map music IDs - -#ifdef CPP11_USE_INITIALIZERS_LIST - - #define VCMI_MUSIC_ID(x) { musicBase::x , - #define VCMI_MUSIC_FILE(y) y }, - musics = { VCMI_MUSIC_LIST}; - #undef VCMI_MUSIC_NAME - #undef VCMI_MUSIC_FILE - -#else - - #define VCMI_MUSIC_ID(x) ( musicBase::x , - #define VCMI_MUSIC_FILE(y) y ) - musics = map_list_of - VCMI_MUSIC_LIST; - #undef VCMI_MUSIC_NAME - #undef VCMI_MUSIC_FILE - -#endif // Vectors for helper - aiMusics += musicBase::AITheme0, musicBase::AITheme1, musicBase::AITheme2; + const std::string setEnemy[] = {"AITheme0", "AITheme1", "AITheme2"}; + const std::string setBattle[] = {"Combat01", "Combat02", "Combat03", "Combat04"}; + const std::string setTerrain[] = {"Dirt", "Sand", "Grass", "Snow", "Swamp", "Rough", "Underground", "Lava", "Water"}; + const std::string setTowns[] = {"CstleTown", "Rampart", "TowerTown", "InfernoTown", + "NecroTown", "Dungeon", "Stronghold", "FortressTown", "ElemTown"}; - battleMusics += musicBase::combat1, musicBase::combat2, - musicBase::combat3, musicBase::combat4; + auto fillSet = [=](std::string setName, const std::string list[], size_t amount) + { + for (size_t i=0; i < amount; i++) + addEntryToSet(setName, i, std::string("music/") + list[i]); + }; + fillSet("enemy-turn", setEnemy, ARRAY_COUNT(setEnemy)); + fillSet("battle", setBattle, ARRAY_COUNT(setBattle)); + fillSet("terrain", setTerrain, ARRAY_COUNT(setTerrain)); + fillSet("town-theme", setTowns, ARRAY_COUNT(setTowns)); +} - townMusics += musicBase::castleTown, musicBase::rampartTown, - musicBase::towerTown, musicBase::infernoTown, - musicBase::necroTown, musicBase::dungeonTown, - musicBase::strongHoldTown, musicBase::fortressTown, - musicBase::elemTown; - - terrainMusics += musicBase::dirt, musicBase::sand, musicBase::grass, - musicBase::snow, musicBase::swamp, musicBase::rough, - musicBase::underground, musicBase::lava,musicBase::water; +void CMusicHandler::addEntryToSet(std::string set, int musicID, std::string musicURI) +{ + musicsSet[set][musicID] = musicURI; } void CMusicHandler::init() @@ -391,23 +378,46 @@ void CMusicHandler::release() CAudioBase::release(); } -// Plays a music -// loop: -1 always repeats, 0=do not play, 1+=number of loops -void CMusicHandler::playMusic(musicBase::musicID musicID, int loop) +void CMusicHandler::playMusic(std::string musicURI, bool loop) { - if (current.get() != NULL && *current == musicID) + if (current && current->isTrack( musicURI)) return; - queueNext(new MusicEntry(this, musicID, loop)); + queueNext(new MusicEntry(this, "", musicURI, loop)); } -// Helper. Randomly plays tracks from music_vec -void CMusicHandler::playMusicFromSet(std::vector &music_vec, int loop) +void CMusicHandler::playMusicFromSet(std::string whichSet, bool loop) { - if (current.get() != NULL && *current == music_vec) + auto selectedSet = musicsSet.find(whichSet); + if (selectedSet == musicsSet.end()) + { + tlog0 << "Error: playing music from non-existing set: " << whichSet << "\n"; + return; + } + + if (current && current->isSet(whichSet)) return; - queueNext(new MusicEntry(this, music_vec, loop)); + queueNext(new MusicEntry(this, whichSet, "", loop)); +} + + +void CMusicHandler::playMusicFromSet(std::string whichSet, int entryID, bool loop) +{ + auto selectedSet = musicsSet.find(whichSet); + if (selectedSet == musicsSet.end()) + { + tlog0 << "Error: playing music from non-existing set: " << whichSet << "\n"; + return; + } + + auto selectedEntry = selectedSet->second.find(entryID); + if (selectedEntry == selectedSet->second.end()) + { + tlog0 << "Error: playing non-existing entry " << entryID << " from set: " << whichSet << "\n"; + return; + } + queueNext(new MusicEntry(this, "", selectedEntry->second, loop)); } void CMusicHandler::queueNext(MusicEntry *queued) @@ -430,7 +440,6 @@ void CMusicHandler::queueNext(MusicEntry *queued) } } -// Stop and free the current music void CMusicHandler::stopMusic(int fade_ms) { if (!initialized) @@ -441,10 +450,8 @@ void CMusicHandler::stopMusic(int fade_ms) if (current.get() != NULL) current->stop(fade_ms); next.reset(); - } -// Sets the music volume, from 0 (mute) to 100 void CMusicHandler::setVolume(ui32 percent) { CAudioBase::setVolume(percent); @@ -453,7 +460,6 @@ void CMusicHandler::setVolume(ui32 percent) Mix_VolumeMusic((MIX_MAX_VOLUME * volume)/100); } -// Called by SDL when a music finished. void CMusicHandler::musicFinishedCallback(void) { boost::mutex::scoped_lock guard(musicMutex); @@ -474,50 +480,39 @@ void CMusicHandler::musicFinishedCallback(void) } } -MusicEntry::MusicEntry(CMusicHandler *_owner, musicBase::musicID _musicID, int _loopCount): - owner(_owner), - music(NULL), - loopCount(_loopCount) +MusicEntry::MusicEntry(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped): + owner(owner), + music(nullptr), + looped(looped), + setName(setName) { - load(_musicID); + if (!musicURI.empty()) + load(musicURI); } - -MusicEntry::MusicEntry(CMusicHandler *_owner, std::vector &_musicVec, int _loopCount): - currentID(musicBase::music_todo), - owner(_owner), - music(NULL), - loopCount(_loopCount), - musicVec(_musicVec) -{ - //In this case music will be loaded only on playing - no need to call load() here -} - MusicEntry::~MusicEntry() { - tlog5<<"Del-ing music file "<musics[ID]; + currentName = musicURI; - tlog5<<"Loading music file "<getResourceName(ResourceID(musicURI, EResType::MUSIC)).c_str()); if(!music) { - tlog3 << "Warning: Cannot open " << filename << ": " << Mix_GetError() << std::endl; + tlog3 << "Warning: Cannot open " << currentName << ": " << Mix_GetError() << std::endl; return; } @@ -529,16 +524,19 @@ void MusicEntry::load(musicBase::musicID ID) bool MusicEntry::play() { - if (loopCount == 0) + if (!looped && music) //already played once - return return false; - if (loopCount > 0) - loopCount--; + if (!setName.empty()) + { + auto set = owner->musicsSet[setName]; + size_t entryID = rand() % set.size(); + auto iterator = set.begin(); + std::advance(iterator, entryID); + load(iterator->second); + } - if (!musicVec.empty()) - load(musicVec.at(rand() % musicVec.size())); - - tlog5<<"Playing music file "< &_musicVec) const +bool MusicEntry::isTrack(std::string track) { - return musicVec == _musicVec; + return setName.empty() && track == currentName; } diff --git a/client/CMusicHandler.h b/client/CMusicHandler.h index b32844562..11f3d7b22 100644 --- a/client/CMusicHandler.h +++ b/client/CMusicHandler.h @@ -2,7 +2,6 @@ #include "CConfigHandler.h" #include "CSoundBase.h" -#include "CMusicBase.h" #include "../lib/CCreatureHandler.h" #include "CSndHandler.h" @@ -113,22 +112,21 @@ class CMusicHandler; //Class for handling one music file class MusicEntry { - std::string filename; //used only for debugging and console messages - musicBase::musicID currentID; CMusicHandler *owner; Mix_Music *music; - int loopCount; - //if not empty - vector from which music will be randomly selected - std::vector musicVec; + bool looped; + //if not null - set from which music will be randomly selected + std::string setName; + std::string currentName; - void load(musicBase::musicID); + + void load(std::string musicURI); public: - bool operator == (musicBase::musicID musicID) const; - bool operator == (std::vector &_musicVec) const; + bool isSet(std::string setName); + bool isTrack(std::string trackName); - MusicEntry(CMusicHandler *owner, musicBase::musicID musicID, int _loopCount); - MusicEntry(CMusicHandler *owner, std::vector &_musicVec, int _loopCount); + MusicEntry(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped); ~MusicEntry(); bool play(); @@ -149,22 +147,26 @@ private: unique_ptr next; void queueNext(MusicEntry *queued); + + std::map > musicsSet; public: CMusicHandler(); + /// add entry with URI musicURI in set. Track will have ID musicID + void addEntryToSet(std::string set, int musicID, std::string musicURI); + void init(); void release(); void setVolume(ui32 percent); - // Musics - std::map musics; - std::vector aiMusics; - std::vector battleMusics; - std::vector townMusics; - std::vector terrainMusics; - - void playMusic(musicBase::musicID musicID, int loop=1); - void playMusicFromSet(std::vector &music_vec, int loop=1); + /// play track by URI, if loop = true music will be looped + void playMusic(std::string musicURI, bool loop); + /// play random track from this set + void playMusicFromSet(std::string musicSet, bool loop); + /// play specific track from set + void playMusicFromSet(std::string musicSet, int entryID, bool loop); void stopMusic(int fade_ms=1000); void musicFinishedCallback(void); + + friend class MusicEntry; }; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index ae68f491a..b30d06d4b 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -256,7 +256,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) if(makingTurn && ho->tempOwner == playerID) //we are moving our hero - we may need to update assigned path { //We may need to change music - select new track, music handler will change it if needed - CCS->musich->playMusic(CCS->musich->terrainMusics[LOCPLINT->cb->getTile(ho->visitablePos())->tertype], -1); + CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(ho->visitablePos())->tertype, true); if(details.result == TryMoveHero::TELEPORTATION) { diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index 0b0be3abd..e7b3e7da8 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -259,7 +259,7 @@ void CMenuScreen::show(SDL_Surface * to) void CMenuScreen::activate() { - CCS->musich->playMusic(musicBase::mainMenu, -1); + CCS->musich->playMusic("Music/MainMenu", true); if (!config["video"].isNull()) CCS->videoh->open(config["video"]["name"].String()); CIntObject::activate(); @@ -567,13 +567,15 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti else if(Type == CMenuScreen::campaignList) { bordered = false; - bg = new CPicture(BitmapHandler::loadBitmap("CamCust.bmp"), 0, 0, true); + bg = new CPicture("CamCust.bmp", 0, 0); pos = bg->center(); } else { bordered = true; - bg = new CPicture(BitmapHandler::loadBitmap(rand()%2 ? "ZPIC1000.bmp" : "ZPIC1001.bmp"), 0, 0, true); + //load random background + const JsonVector & bgNames = (*CGP->pregameConfig)["game-select"].Vector(); + bg = new CPicture(bgNames[rand() % bgNames.size()].String(), 0, 0); pos = bg->center(); } diff --git a/client/CPreGame.h b/client/CPreGame.h index 62f33f450..b086b3a83 100644 --- a/client/CPreGame.h +++ b/client/CPreGame.h @@ -489,14 +489,14 @@ public: /// Handles background screen, loads graphics for victory/loss condition and random town or hero selection class CGPreGame : public CIntObject, public IUpdateable { - const JsonNode * const pregameConfig; - void loadGraphics(); void disposeGraphics(); CGPreGame(); //Use createIfNotPresent public: + const JsonNode * const pregameConfig; + CMenuScreen* menu; SDL_Surface *nHero, *rHero, *nTown, *rTown; // none/random hero/town imgs diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index d567f4044..71066f0ac 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -431,15 +431,27 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState) CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg, const CStackInstance * Creature) { + OBJ_CONSTRUCTION_CAPTURING_ALL; addUsedEvents(LCLICK | RCLICK | HOVER); + pos.x += x; + pos.y += y; + owner = Owner; + //assert(Creature == CGI->creh->creatures[Creature->idNumber]); upg = Upg; ID = IID; myStack = Creature; creature = Creature ? Creature->type : NULL; + if (creature) + { + std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT"; + creatureImage = new CAnimImage(imgName, creature->idNumber + 2); + } + else + creatureImage = nullptr; + count = Creature ? Creature->count : 0; - pos.x += x; - pos.y += y; + if(Owner->smallIcons) { pos.w = 32; @@ -450,7 +462,6 @@ CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg pos.w = 58; pos.h = 64; } - owner = Owner; } void CGarrisonSlot::showAll(SDL_Surface * to) @@ -458,9 +469,9 @@ void CGarrisonSlot::showAll(SDL_Surface * to) std::map &imgs = (owner->smallIcons ? graphics->smallImgs : graphics->bigImgs); if(creature) { + creatureImage->showAll(to); char buf[15]; SDL_itoa(count,buf,10); - blitAt(imgs[creature->idNumber],pos,to); printTo(buf, pos.x+pos.w, pos.y+pos.h+1, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, Colors::Cornsilk, to); if((owner->highlighted==this) @@ -476,12 +487,6 @@ void CGarrisonSlot::showAll(SDL_Surface * to) } } -CGarrisonInt::~CGarrisonInt() -{/* - for(size_t i = 0; igetResourceName(ResourceID("config/NEUTRAL.PAL")), std::ios::binary); for(int i=0; i<32; ++i) { ncp.read((char*)&neutralColorPalette[i].r,1); diff --git a/client/UIFramework/CIntObjectClasses.cpp b/client/UIFramework/CIntObjectClasses.cpp index a25718e6e..e9455a4e7 100644 --- a/client/UIFramework/CIntObjectClasses.cpp +++ b/client/UIFramework/CIntObjectClasses.cpp @@ -1303,7 +1303,6 @@ void CBoundedLabel::recalculateLines(const std::string &Txt) const Font &f = *graphics->fonts[font]; int lineHeight = f.height; - int lineCapacity = pos.h / lineHeight; lines = CMessage::breakText(Txt, pos.w, font); diff --git a/client/UIFramework/SDL_Extensions.cpp b/client/UIFramework/SDL_Extensions.cpp index c0ec5b4e6..ee334db4a 100644 --- a/client/UIFramework/SDL_Extensions.cpp +++ b/client/UIFramework/SDL_Extensions.cpp @@ -45,7 +45,7 @@ SDL_Surface * CSDL_Ext::copySurface(SDL_Surface * mod) //returns copy of given s template SDL_Surface * CSDL_Ext::createSurfaceWithBpp(int width, int height) { - int rMask = 0, gMask = 0, bMask = 0, aMask = 0; + Uint32 rMask = 0, gMask = 0, bMask = 0, aMask = 0; Channels::px::r.set((Uint8*)&rMask, 255); Channels::px::g.set((Uint8*)&gMask, 255); diff --git a/config/filesystem.json b/config/filesystem.json index 86026c24e..afb388b0d 100644 --- a/config/filesystem.json +++ b/config/filesystem.json @@ -26,6 +26,7 @@ "SOUNDS/": [ {"type" : "file", "path" : "ALL/Data/H3ab_ahd.snd"}, + {"type" : "file", "path" : "ALL/Data/Heroes3-cd2.snd"}, {"type" : "file", "path" : "ALL/Data/Heroes3.snd"}, //WoG have overriden sounds with .82m extension in Data {"type" : "dir", "path" : "GLOBAL/Data"}, diff --git a/config/mainmenu.json b/config/mainmenu.json index 00fd63050..14420c6c9 100644 --- a/config/mainmenu.json +++ b/config/mainmenu.json @@ -1,4 +1,6 @@ { + //images used in game selection screen + "game-select" : ["ZPIC1000", "ZPIC1001"], //Main menu window, consists of several sub-menus aka items "window": { @@ -68,7 +70,7 @@ { "x":90, "y":72, "file":"DATA/GOOD1.H3C", "image":"CAMPGD1S", "video":"CGOOD1", "open": true }, { "x":539, "y":72, "file":"DATA/EVIL1.H3C", "image":"CAMPEV1S", "video":"CEVIL1", "open": true }, { "x":43, "y":245, "file":"DATA/GOOD2.H3C", "image":"CAMPGD2S", "video":"CGOOD2", "open": true }, - { "x":313, "y":244, "file":"DATA/NEUTRAL.H3C", "image":"CAMPNEUS", "video":"CNEUTRAL", "open": true }, + { "x":313, "y":244, "file":"DATA/NEUTRAL1.H3C", "image":"CAMPNEUS", "video":"CNEUTRAL", "open": true }, { "x":586, "y":246, "file":"DATA/EVIL2.H3C", "image":"CAMPEV2S", "video":"CEVIL2", "open": true }, { "x":34, "y":417, "file":"DATA/GOOD3.H3C", "image":"CAMPGD3S", "video":"CGOOD3", "open": true }, { "x":404, "y":414, "file":"DATA/SECRET.H3C", "image":"CAMPSCTS", "video":"CSECRET", "open": true } diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 29e26b5d2..69339238f 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -560,8 +560,7 @@ void CGeneralTextHandler::load() zcrexp.push_back(nameBuf); } - std::string threatLevelDir = GameConstants::DATA_DIR + "/config/threatlevel.txt"; - std::ifstream ifs(threatLevelDir.c_str(), std::ios::in | std::ios::binary); + std::ifstream ifs(CResourceHandler::get()->getResourceName(ResourceID("config/threatlevel.txt")), std::ios::binary); getline(ifs, buf); //skip 1st line for (int i = 0; i < 13; ++i) { diff --git a/lib/Filesystem/CResourceLoader.cpp b/lib/Filesystem/CResourceLoader.cpp index 10f43b455..fd4f0b7d7 100644 --- a/lib/Filesystem/CResourceLoader.cpp +++ b/lib/Filesystem/CResourceLoader.cpp @@ -118,6 +118,17 @@ ResourceLocator CResourceLoader::getResource(const ResourceID & resourceIdent) c return resource->second.back(); } +const std::list & CResourceLoader::getResourcesWithName(const ResourceID & resourceIdent) const +{ + static const std::list emptyList; + auto resource = resources.find(resourceIdent); + + if (resource == resources.end()) + return emptyList; + return resource->second; +} + + std::string CResourceLoader::getResourceName(const ResourceID & resourceIdent) const { auto locator = getResource(resourceIdent); @@ -147,6 +158,11 @@ void CResourceLoader::addLoader(std::string mountPoint, ISimpleResourceLoader * // Create identifier and locator and add them to the resources list ResourceID ident(mountPoint, file.getStem(), file.getType()); + + //check if entry can be directory. Will only work for filesystem loader but H3 archives don't have dirs anyway. + if (boost::filesystem::is_directory(loader->getOrigin() + '/' + entry)) + ident.setType(EResType::DIRECTORY); + ResourceLocator locator(loader, entry); resources[ident].push_back(locator); } @@ -247,6 +263,7 @@ std::string EResTypeHelper::getEResTypeAsString(EResType::Type type) MAP_ENUM(CLIENT_SAVEGAME) MAP_ENUM(LIB_SAVEGAME) MAP_ENUM(SERVER_SAVEGAME) + MAP_ENUM(DIRECTORY) MAP_ENUM(OTHER); #undef MAP_ENUM @@ -279,17 +296,10 @@ void CResourceHandler::initialize() //create "LOCAL" dir with current userDir (may be same as rootDir) initialLoader->addLoader("LOCAL/", userDir); - - //check for presence of "VCMI" mod. If found - add it to our initial FS - std::string filename = initialLoader->getResourceName(ResourceID("ALL/MODS/VCMI")); - if (!filename.empty()) - initialLoader->addLoader("ALL/", new CFilesystemLoader(filename, 2)); } void CResourceHandler::loadFileSystem(const std::string fsConfigURI) { - //TODO: better way to detect fs config. - // right now it can be: global_dir/config/, local_dir/config, global/mods/vcmi/config, local/mods/vcmi/config auto fsConfigData = initialLoader->loadData(ResourceID(fsConfigURI, EResType::TEXT)); const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second); @@ -298,26 +308,26 @@ void CResourceHandler::loadFileSystem(const std::string fsConfigURI) { BOOST_FOREACH(auto & entry, mountPoint.second.Vector()) { - tlog5 << "loading resource at " << entry["path"].String() << ": "; - std::string filename = initialLoader->getResourceName(ResourceID(entry["path"].String())); + tlog5 << "loading resource at " << entry["path"].String() << "\n"; - if (!filename.empty()) + if (entry["type"].String() == "dir") { - if (entry["type"].String() == "dir") + std::string filename = initialLoader->getResourceName(ResourceID(entry["path"].String(), EResType::DIRECTORY)); + if (!filename.empty()) { int depth = 16; if (!entry["depth"].isNull()) depth = entry["depth"].Float(); resourceLoader->addLoader(mountPoint.first, new CFilesystemLoader(filename, depth)); } - - if (entry["type"].String() == "file") - resourceLoader->addLoader(mountPoint.first, new CLodArchiveLoader(filename)); - - tlog5 << "OK\n"; } - else - tlog5 << "Not found\n"; + + if (entry["type"].String() == "file") + { + std::string filename = initialLoader->getResourceName(ResourceID(entry["path"].String(), EResType::ARCHIVE)); + if (!filename.empty()) + resourceLoader->addLoader(mountPoint.first, new CLodArchiveLoader(filename)); + } } } } diff --git a/lib/Filesystem/CResourceLoader.h b/lib/Filesystem/CResourceLoader.h index bd5d408ec..3d49d3084 100644 --- a/lib/Filesystem/CResourceLoader.h +++ b/lib/Filesystem/CResourceLoader.h @@ -53,6 +53,7 @@ namespace EResType CLIENT_SAVEGAME, LIB_SAVEGAME, SERVER_SAVEGAME, + DIRECTORY, OTHER }; } @@ -259,9 +260,12 @@ public: * @return resource locator for this resource or empty one if resource was not found */ ResourceLocator getResource(const ResourceID & resourceIdent) const; + + /// returns ALL overriden resources with same name, including last one acessible via getResource + const std::list & getResourcesWithName(const ResourceID & resourceIdent) const; + /// returns real name of file in filesystem. Not usable for archives std::string getResourceName(const ResourceID & resourceIdent) const; - /// return size of file or 0 if not found /** * Get iterator for looping all files matching filter