1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

- 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
This commit is contained in:
Ivan Savenko 2012-08-06 07:34:37 +00:00
parent 385be09248
commit 708ad6ac7f
24 changed files with 212 additions and 256 deletions

View File

@ -2576,7 +2576,7 @@ int3 whereToExplore(HeroPtr h)
}
catch(cannotFulfillGoalException &e)
{
std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow], metryka taksówkowa
std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow]
try
{
return ai->explorationNewPoint(radius, h, tiles);

View File

@ -268,16 +268,16 @@ namespace vstd
//checks if a is between b and c
template <typename t1, typename t2, typename t3>
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 <typename t1, typename t2, typename t3>
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 <typename t1, typename t2>

View File

@ -371,7 +371,13 @@ 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));
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;
@ -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);
}
}

View File

@ -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;

View File

@ -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

View File

@ -2,6 +2,7 @@
#include <SDL_image.h>
#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<ui8[]> 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();

View File

@ -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; i<height; i++)
@ -127,7 +136,7 @@ SDL_Surface * BitmapHandler::loadBitmapFromDir(std::string path, std::string fna
ret = IMG_LoadTyped_RW(
//create SDL_RW with our data (will be deleted by SDL)
SDL_RWFromConstMem((void*)readFile.first.release(), readFile.second),
SDL_RWFromConstMem((void*)readFile.first.get(), readFile.second),
1, // mark it for auto-deleting
&info.getExtension()[0] + 1); //pass extension without dot (+1 character)

View File

@ -951,7 +951,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
townlist->onSelect = 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]);
}

View File

@ -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
};

View File

@ -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<musicBase::musicID> &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<musicBase::musicID> &_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 "<<filename<<"\n";
tlog5<<"Del-ing music file "<<currentName<<"\n";
if (music)
Mix_FreeMusic(music);
}
void MusicEntry::load(musicBase::musicID ID)
void MusicEntry::load(std::string musicURI)
{
if (music)
{
tlog5<<"Del-ing music file "<<filename<<"\n";
tlog5<<"Del-ing music file "<<currentName<<"\n";
Mix_FreeMusic(music);
}
currentID = ID;
filename = GameConstants::DATA_DIR + "/Mp3/";
filename += owner->musics[ID];
currentName = musicURI;
tlog5<<"Loading music file "<<filename<<"\n";
tlog5<<"Loading music file "<<musicURI<<"\n";
music = Mix_LoadMUS(filename.c_str());
music = Mix_LoadMUS(CResourceHandler::get()->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 "<<filename<<"\n";
tlog5<<"Playing music file "<<currentName<<"\n";
if(Mix_PlayMusic(music, 1) == -1)
{
tlog1 << "Unable to play music (" << Mix_GetError() << ")" << std::endl;
@ -549,17 +547,17 @@ bool MusicEntry::play()
void MusicEntry::stop(int fade_ms)
{
tlog5<<"Stoping music file "<<filename<<"\n";
loopCount = 0;
tlog5<<"Stoping music file "<<currentName<<"\n";
looped = false;
Mix_FadeOutMusic(fade_ms);
}
bool MusicEntry::operator == (musicBase::musicID _musicID) const
bool MusicEntry::isSet(std::string set)
{
return musicVec.empty() && currentID == _musicID;
return !setName.empty() && set == setName;
}
bool MusicEntry::operator == (std::vector<musicBase::musicID> &_musicVec) const
bool MusicEntry::isTrack(std::string track)
{
return musicVec == _musicVec;
return setName.empty() && track == currentName;
}

View File

@ -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<musicBase::musicID> 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<musicBase::musicID> &_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<musicBase::musicID> &_musicVec, int _loopCount);
MusicEntry(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped);
~MusicEntry();
bool play();
@ -149,22 +147,26 @@ private:
unique_ptr<MusicEntry> next;
void queueNext(MusicEntry *queued);
std::map<std::string, std::map<int, std::string> > 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<musicBase::musicID, std::string> musics;
std::vector<musicBase::musicID> aiMusics;
std::vector<musicBase::musicID> battleMusics;
std::vector<musicBase::musicID> townMusics;
std::vector<musicBase::musicID> terrainMusics;
void playMusic(musicBase::musicID musicID, int loop=1);
void playMusicFromSet(std::vector<musicBase::musicID> &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;
};

View File

@ -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)
{

View File

@ -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();
}

View File

@ -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

View File

@ -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<int,SDL_Surface*> &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; i<splitButtons.size(); i++)
delete splitButtons[i];*/
}
void CGarrisonInt::addSplitBtn(CAdventureMapButton * button)
{
addChild(button);

View File

@ -306,7 +306,6 @@ class CGarrisonInt;
/// A single garrison slot which holds one creature of a specific amount
class CGarrisonSlot : public CIntObject
{
public:
int ID; //for identification
CGarrisonInt *owner;
const CStackInstance *myStack; //NULL if slot is empty
@ -315,6 +314,8 @@ public:
int upg; //0 - up garrison, 1 - down garrison
bool highlight;
CAnimImage * creatureImage;
public:
virtual void hover (bool on); //call-in
const CArmedInstance * getObj();
bool our();
@ -322,6 +323,8 @@ public:
void clickLeft(tribool down, bool previousState);
void showAll(SDL_Surface * to);
CGarrisonSlot(CGarrisonInt *Owner, int x, int y, int IID, int Upg=0, const CStackInstance * Creature=NULL);
friend class CGarrisonInt;
};
/// Class which manages slots of upper and lower garrison, splitting of units
@ -367,7 +370,6 @@ public:
//smallImgs - units images size 64x58 or 32x32;
//twoRows - display slots in 2 row (1st row = 4 slots, 2nd = 3 slots)
CGarrisonInt(int x, int y, int inx, const Point &garsOffset, SDL_Surface *pomsur, const Point &SurOffset, const CArmedInstance *s1, const CArmedInstance *s2=NULL, bool _removableUnits = true, bool smallImgs = false, bool _twoRows=false); //c-tor
~CGarrisonInt(); //d-tor
};
/// draws picture with creature on background, use Animated=true to get animation

View File

@ -64,8 +64,7 @@ void Graphics::loadPaletteAndColors()
neutralColorPalette = new SDL_Color[32];
std::ifstream ncp;
std::string neutralFile = GameConstants::DATA_DIR + "/config/NEUTRAL.PAL";
ncp.open(neutralFile.c_str(), std::ios::binary);
ncp.open(CResourceHandler::get()->getResourceName(ResourceID("config/NEUTRAL.PAL")), std::ios::binary);
for(int i=0; i<32; ++i)
{
ncp.read((char*)&neutralColorPalette[i].r,1);

View File

@ -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);

View File

@ -45,7 +45,7 @@ SDL_Surface * CSDL_Ext::copySurface(SDL_Surface * mod) //returns copy of given s
template<int bpp>
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<bpp>::r.set((Uint8*)&rMask, 255);
Channels::px<bpp>::g.set((Uint8*)&gMask, 255);

View File

@ -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"},

View File

@ -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 }

View File

@ -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)
{

View File

@ -118,6 +118,17 @@ ResourceLocator CResourceLoader::getResource(const ResourceID & resourceIdent) c
return resource->second.back();
}
const std::list<ResourceLocator> & CResourceLoader::getResourcesWithName(const ResourceID & resourceIdent) const
{
static const std::list<ResourceLocator> 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")
{
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")
{
std::string filename = initialLoader->getResourceName(ResourceID(entry["path"].String(), EResType::ARCHIVE));
if (!filename.empty())
resourceLoader->addLoader(mountPoint.first, new CLodArchiveLoader(filename));
tlog5 << "OK\n";
}
else
tlog5 << "Not found\n";
}
}
}

View File

@ -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<ResourceLocator> & 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