mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Implement configurable object sounds: ambient, visit and removal
* If there more than one sound for visit or removal random is played * At moment only the first ambient sound will be used
This commit is contained in:
		
				
					committed by
					
						 DJWarmonger
						DJWarmonger
					
				
			
			
				
	
			
			
			
						parent
						
							e9bfbb70c1
						
					
				
				
					commit
					f15cadc87b
				
			| @@ -26,6 +26,11 @@ SPELLS: | ||||
| MODS: | ||||
| * Improve support for WoG commander artifacts and skill descriptions | ||||
| * Added basic support for secondary skill modding | ||||
| * Map object sounds can now be configured via json | ||||
|  | ||||
| SOUND: | ||||
| * Fixed many mising or wrong pickup and visit sounds for map objects | ||||
| * All map objects now have ambient sounds identical to OH3 | ||||
|  | ||||
| 0.98 -> 0.99 | ||||
|  | ||||
|   | ||||
| @@ -1280,6 +1280,10 @@ static void handleEvent(SDL_Event & ev) | ||||
| 		case FULLSCREEN_TOGGLED: | ||||
| 			fullScreenChanged(); | ||||
| 			break; | ||||
| 		case INTERFACE_CHANGED: | ||||
| 			if(LOCPLINT) | ||||
| 				LOCPLINT->updateAmbientSounds(); | ||||
| 			break; | ||||
| 		default: | ||||
| 			logGlobal->error("Unknown user event. Code %d", ev.user.code); | ||||
| 			break; | ||||
|   | ||||
| @@ -82,8 +82,10 @@ void CSoundHandler::onVolumeChange(const JsonNode &volumeNode) | ||||
| } | ||||
|  | ||||
| CSoundHandler::CSoundHandler(): | ||||
| 	listener(settings.listen["general"]["sound"]) | ||||
| 	listener(settings.listen["general"]["sound"]), | ||||
| 	ambientConfig(JsonNode(ResourceID("config/ambientSounds.json"))) | ||||
| { | ||||
| 	allTilesSource = ambientConfig["allTilesSource"].Bool(); | ||||
| 	listener(std::bind(&CSoundHandler::onVolumeChange, this, _1)); | ||||
|  | ||||
| 	// Vectors for helper(s) | ||||
| @@ -112,6 +114,8 @@ CSoundHandler::CSoundHandler(): | ||||
| void CSoundHandler::init() | ||||
| { | ||||
| 	CAudioBase::init(); | ||||
| 	if(ambientConfig["allocateChannels"].isNumber()) | ||||
| 		Mix_AllocateChannels(ambientConfig["allocateChannels"].Integer()); | ||||
|  | ||||
| 	if (initialized) | ||||
| 	{ | ||||
| @@ -160,6 +164,21 @@ Mix_Chunk *CSoundHandler::GetSoundChunk(std::string &sound, bool cache) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int CSoundHandler::ambientDistToVolume(int distance) const | ||||
| { | ||||
| 	if(distance >= ambientConfig["distances"].Vector().size()) | ||||
| 		return 0; | ||||
|  | ||||
| 	int volume = ambientConfig["distances"].Vector()[distance].Integer(); | ||||
| 	return volume * ambientConfig["volume"].Integer() * getVolume() / 10000; | ||||
| } | ||||
|  | ||||
| void CSoundHandler::ambientStopSound(std::string soundId) | ||||
| { | ||||
| 	stopSound(ambientChannels[soundId]); | ||||
| 	setChannelVolume(ambientChannels[soundId], 100); | ||||
| } | ||||
|  | ||||
| // Plays a sound, and return its channel so we can fade it out later | ||||
| int CSoundHandler::playSound(soundBase::soundID soundID, int repeats) | ||||
| { | ||||
| @@ -216,7 +235,13 @@ void CSoundHandler::setVolume(ui32 percent) | ||||
| 	CAudioBase::setVolume(percent); | ||||
|  | ||||
| 	if (initialized) | ||||
| 		Mix_Volume(-1, (MIX_MAX_VOLUME * volume)/100); | ||||
| 		setChannelVolume(-1, volume); | ||||
| } | ||||
|  | ||||
| // Sets the sound volume, from 0 (mute) to 100 | ||||
| void CSoundHandler::setChannelVolume(int channel, ui32 percent) | ||||
| { | ||||
| 	Mix_Volume(channel, (MIX_MAX_VOLUME * percent)/100); | ||||
| } | ||||
|  | ||||
| void CSoundHandler::setCallback(int channel, std::function<void()> function) | ||||
| @@ -244,6 +269,54 @@ void CSoundHandler::soundFinishedCallback(int channel) | ||||
| 	callbacks.erase(iter); | ||||
| } | ||||
|  | ||||
| int CSoundHandler::ambientGetRange() const | ||||
| { | ||||
| 	return ambientConfig["range"].Integer(); | ||||
| } | ||||
|  | ||||
| bool CSoundHandler::ambientCheckVisitable() const | ||||
| { | ||||
| 	return !allTilesSource; | ||||
| } | ||||
|  | ||||
| void CSoundHandler::ambientUpdateChannels(std::map<std::string, int> sounds) | ||||
| { | ||||
| 	std::vector<std::string> stoppedSounds; | ||||
| 	for(auto & pair : ambientChannels) | ||||
| 	{ | ||||
| 		if(!vstd::contains(sounds, pair.first)) | ||||
| 		{ | ||||
| 			ambientStopSound(pair.first); | ||||
| 			stoppedSounds.push_back(pair.first); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			CCS->soundh->setChannelVolume(pair.second, ambientDistToVolume(sounds[pair.first])); | ||||
| 		} | ||||
| 	} | ||||
| 	for(auto soundId : stoppedSounds) | ||||
| 		ambientChannels.erase(soundId); | ||||
|  | ||||
| 	for(auto & pair : sounds) | ||||
| 	{ | ||||
| 		if(!vstd::contains(ambientChannels, pair.first)) | ||||
| 		{ | ||||
| 			int channel = CCS->soundh->playSound(pair.first, -1); | ||||
| 			CCS->soundh->setChannelVolume(channel, ambientDistToVolume(pair.second)); | ||||
| 			CCS->soundh->ambientChannels.insert(std::make_pair(pair.first, channel)); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CSoundHandler::ambientStopAllChannels() | ||||
| { | ||||
| 	for(auto ch : ambientChannels) | ||||
| 	{ | ||||
| 		ambientStopSound(ch.first); | ||||
| 	} | ||||
| 	ambientChannels.clear(); | ||||
| } | ||||
|  | ||||
| void CMusicHandler::onVolumeChange(const JsonNode &volumeNode) | ||||
| { | ||||
| 	setVolume(volumeNode.Float()); | ||||
|   | ||||
| @@ -29,7 +29,7 @@ public: | ||||
| 	virtual void release() = 0; | ||||
|  | ||||
| 	virtual void setVolume(ui32 percent); | ||||
| 	ui32 getVolume() { return volume; }; | ||||
| 	ui32 getVolume() const { return volume; }; | ||||
| }; | ||||
|  | ||||
| class CSoundHandler: public CAudioBase | ||||
| @@ -49,6 +49,13 @@ private: | ||||
| 	//std::function will be nullptr if callback was not set | ||||
| 	std::map<int, std::function<void()> > callbacks; | ||||
|  | ||||
| 	int ambientDistToVolume(int distance) const; | ||||
| 	void ambientStopSound(std::string soundId); | ||||
|  | ||||
| 	const JsonNode ambientConfig; | ||||
| 	bool allTilesSource; | ||||
| 	std::map<std::string, int> ambientChannels; | ||||
|  | ||||
| public: | ||||
| 	CSoundHandler(); | ||||
|  | ||||
| @@ -56,6 +63,7 @@ public: | ||||
| 	void release() override; | ||||
|  | ||||
| 	void setVolume(ui32 percent) override; | ||||
| 	void setChannelVolume(int channel, ui32 percent); | ||||
|  | ||||
| 	// Sounds | ||||
| 	int playSound(soundBase::soundID soundID, int repeats=0); | ||||
| @@ -66,6 +74,11 @@ public: | ||||
| 	void setCallback(int channel, std::function<void()> function); | ||||
| 	void soundFinishedCallback(int channel); | ||||
|  | ||||
| 	int ambientGetRange() const; | ||||
| 	bool ambientCheckVisitable() const; | ||||
| 	void ambientUpdateChannels(std::map<std::string, int> currentSounds); | ||||
| 	void ambientStopAllChannels(); | ||||
|  | ||||
| 	// Sets | ||||
| 	std::vector<soundBase::soundID> pickupSounds; | ||||
| 	std::vector<soundBase::soundID> horseSounds; | ||||
|   | ||||
| @@ -136,6 +136,7 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player) | ||||
|  | ||||
| CPlayerInterface::~CPlayerInterface() | ||||
| { | ||||
| 	CCS->soundh->ambientStopAllChannels(); | ||||
| 	logGlobal->trace("\tHuman player interface for player %s being destructed", playerID.getStr()); | ||||
| 	//howManyPeople--; | ||||
| 	delete showingDialog; | ||||
| @@ -266,6 +267,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) | ||||
|  | ||||
| 	if (makingTurn  &&  hero->tempOwner == playerID) //we are moving our hero - we may need to update assigned path | ||||
| 	{ | ||||
| 		updateAmbientSounds(); | ||||
| 		//We may need to change music - select new track, music handler will change it if needed | ||||
| 		CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType, true); | ||||
|  | ||||
| @@ -446,6 +448,15 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero) | ||||
| 		adventureInt->selection = nullptr; | ||||
| } | ||||
|  | ||||
| void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) | ||||
| { | ||||
| 	EVENT_HANDLER_CALLED_BY_CLIENT; | ||||
| 	if(start && visitedObj->getVisitSound()) | ||||
| 	{ | ||||
| 		CCS->soundh->playSound(visitedObj->getVisitSound().get()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CPlayerInterface::heroCreated(const CGHeroInstance * hero) | ||||
| { | ||||
| 	EVENT_HANDLER_CALLED_BY_CLIENT; | ||||
| @@ -456,6 +467,7 @@ void CPlayerInterface::openTownWindow(const CGTownInstance * town) | ||||
| { | ||||
| 	if (castleInt) | ||||
| 		castleInt->close(); | ||||
|  | ||||
| 	castleInt = new CCastleInterface(town); | ||||
| 	GH.pushInt(castleInt); | ||||
| } | ||||
| @@ -1644,22 +1656,17 @@ void CPlayerInterface::centerView (int3 pos, int focusTime) | ||||
| 	CCS->curh->show(); | ||||
| } | ||||
|  | ||||
| void CPlayerInterface::objectRemoved( const CGObjectInstance *obj ) | ||||
| void CPlayerInterface::objectRemoved(const CGObjectInstance * obj) | ||||
| { | ||||
| 	EVENT_HANDLER_CALLED_BY_CLIENT; | ||||
| 	if (LOCPLINT->cb->getCurrentPlayer() == playerID) { | ||||
| 		std::string handlerName = VLC->objtypeh->getObjectHandlerName(obj->ID); | ||||
| 		if ((handlerName == "pickable") || (handlerName == "scholar") || (handlerName== "artifact") || (handlerName == "pandora")) { | ||||
| 			waitWhileDialog(); | ||||
| 			CCS->soundh->playSoundFromSet(CCS->soundh->pickupSounds); | ||||
| 		} else if ((handlerName == "monster") || (handlerName == "hero")) { | ||||
| 			waitWhileDialog(); | ||||
| 			CCS->soundh->playSound(soundBase::KillFade); | ||||
| 		} | ||||
| 	} | ||||
| 	if (obj->ID == Obj::HERO  &&  obj->tempOwner == playerID) | ||||
| 	if(LOCPLINT->cb->getCurrentPlayer() == playerID && obj->getRemovalSound()) | ||||
| 	{ | ||||
| 		const CGHeroInstance *h = static_cast<const CGHeroInstance*>(obj); | ||||
| 		waitWhileDialog(); | ||||
| 		CCS->soundh->playSound(obj->getRemovalSound().get()); | ||||
| 	} | ||||
| 	if(obj->ID == Obj::HERO && obj->tempOwner == playerID) | ||||
| 	{ | ||||
| 		const CGHeroInstance * h = static_cast<const CGHeroInstance *>(obj); | ||||
| 		heroKilled(h); | ||||
| 	} | ||||
| } | ||||
| @@ -1677,6 +1684,7 @@ const CArmedInstance * CPlayerInterface::getSelection() | ||||
| void CPlayerInterface::setSelection(const CArmedInstance * obj) | ||||
| { | ||||
| 	currentSelection = obj; | ||||
| 	updateAmbientSounds(true); | ||||
| } | ||||
|  | ||||
| void CPlayerInterface::update() | ||||
| @@ -2373,9 +2381,9 @@ void CPlayerInterface::acceptTurn() | ||||
| 	adventureInt->updateNextHero(nullptr); | ||||
| 	adventureInt->showAll(screen); | ||||
|  | ||||
| 	if (settings["session"]["autoSkip"].Bool() && !LOCPLINT->shiftPressed()) | ||||
| 	if(settings["session"]["autoSkip"].Bool() && !LOCPLINT->shiftPressed()) | ||||
| 	{ | ||||
| 		if (CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt())) | ||||
| 		if(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt())) | ||||
| 			iw->close(); | ||||
|  | ||||
| 		adventureInt->fendTurn(); | ||||
| @@ -2537,6 +2545,7 @@ void CPlayerInterface::showShipyardDialogOrProblemPopup(const IShipyard *obj) | ||||
| void CPlayerInterface::requestReturningToMainMenu() | ||||
| { | ||||
| 	sendCustomEvent(RETURN_TO_MAIN_MENU); | ||||
| 	CCS->soundh->ambientStopAllChannels(); | ||||
| 	cb->unregisterAllInterfaces(); | ||||
| } | ||||
|  | ||||
| @@ -2903,3 +2912,52 @@ void CPlayerInterface::showWorldViewEx(const std::vector<ObjectPosInfo>& objectP | ||||
|  | ||||
| 	viewWorldMap(); | ||||
| } | ||||
|  | ||||
| void CPlayerInterface::updateAmbientSounds(bool resetAll) | ||||
| { | ||||
| 	if(castleInt || battleInt || !makingTurn || !currentSelection) | ||||
| 	{ | ||||
| 		CCS->soundh->ambientStopAllChannels(); | ||||
| 		return; | ||||
| 	} | ||||
| 	else if(!dynamic_cast<CAdvMapInt *>(GH.topInt())) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	if(resetAll) | ||||
| 		CCS->soundh->ambientStopAllChannels(); | ||||
|  | ||||
| 	std::map<std::string, int> currentSounds; | ||||
| 	auto updateSounds = [&](std::string soundId, int distance) -> void | ||||
| 	{ | ||||
| 		if(vstd::contains(currentSounds, soundId)) | ||||
| 			currentSounds[soundId] = std::max(currentSounds[soundId], distance); | ||||
| 		else | ||||
| 			currentSounds.insert(std::make_pair(soundId, distance)); | ||||
| 	}; | ||||
|  | ||||
| 	int3 pos = currentSelection->getSightCenter(); | ||||
| 	std::unordered_set<int3, ShashInt3> tiles; | ||||
| 	cb->getVisibleTilesInRange(tiles, pos, CCS->soundh->ambientGetRange(), int3::DIST_CHEBYSHEV); | ||||
| 	for(int3 tile : tiles) | ||||
| 	{ | ||||
| 		int dist = pos.dist(tile, int3::DIST_CHEBYSHEV); | ||||
| 		// We want sound for every special terrain on tile and not just one on top | ||||
| 		for(auto & ttObj : CGI->mh->ttiles[tile.x][tile.y][tile.z].objects) | ||||
| 		{ | ||||
| 			auto obj = ttObj.obj; | ||||
| 			if(!obj || !obj->getAmbientSound()) | ||||
| 				continue; | ||||
|  | ||||
| 			// All tiles of static objects are sound sources. E.g Volcanos and special terrains | ||||
| 			// For visitable object only their visitable tile is sound source | ||||
| 			if(CCS->soundh->ambientCheckVisitable() && obj->isVisitable() && !obj->visitableAt(tile.x, tile.y)) | ||||
| 				continue; | ||||
|  | ||||
| 			updateSounds(obj->getAmbientSound().get(), dist); | ||||
| 		} | ||||
| 		if(CGI->mh->map->isCoastalTile(tile)) | ||||
| 			updateSounds("LOOPOCEA", dist); | ||||
| 	} | ||||
| 	CCS->soundh->ambientUpdateChannels(currentSounds); | ||||
| } | ||||
|   | ||||
| @@ -75,7 +75,8 @@ enum | ||||
| 	RETURN_TO_MENU_LOAD, | ||||
| 	FULLSCREEN_TOGGLED, | ||||
| 	PREPARE_RESTART_CAMPAIGN, | ||||
| 	FORCE_QUIT //quit client without question | ||||
| 	FORCE_QUIT, //quit client without question | ||||
| 	INTERFACE_CHANGED | ||||
| }; | ||||
|  | ||||
| /// Central class for managing user interface logic | ||||
| @@ -151,6 +152,7 @@ public: | ||||
| 	void artifactAssembled(const ArtifactLocation &al) override; | ||||
| 	void artifactDisassembled(const ArtifactLocation &al) override; | ||||
|  | ||||
| 	void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override; | ||||
| 	void heroCreated(const CGHeroInstance* hero) override; | ||||
| 	void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override; | ||||
| 	void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; | ||||
| @@ -263,6 +265,9 @@ public: | ||||
| 	void sendCustomEvent(int code); | ||||
| 	void proposeLoadingGame(); | ||||
|  | ||||
| 	// Ambient sounds | ||||
| 	void updateAmbientSounds(bool resetAll = false); | ||||
|  | ||||
| 	///returns true if all events are processed internally | ||||
| 	bool capturedAllEvents(); | ||||
|  | ||||
|   | ||||
| @@ -105,6 +105,8 @@ void CGuiHandler::popInt(IShowActivatable *top) | ||||
| 	if(!listInt.empty()) | ||||
| 		listInt.front()->activate(); | ||||
| 	totalRedraw(); | ||||
|  | ||||
| 	pushSDLEvent(SDL_USEREVENT, INTERFACE_CHANGED); | ||||
| } | ||||
|  | ||||
| void CGuiHandler::popIntTotally(IShowActivatable *top) | ||||
| @@ -129,6 +131,8 @@ void CGuiHandler::pushInt(IShowActivatable *newInt) | ||||
| 	newInt->activate(); | ||||
| 	objsToBlit.push_back(newInt); | ||||
| 	totalRedraw(); | ||||
|  | ||||
| 	pushSDLEvent(SDL_USEREVENT, INTERFACE_CHANGED); | ||||
| } | ||||
|  | ||||
| void CGuiHandler::popInts(int howMany) | ||||
| @@ -150,6 +154,8 @@ void CGuiHandler::popInts(int howMany) | ||||
| 		totalRedraw(); | ||||
| 	} | ||||
| 	fakeMouseMove(); | ||||
|  | ||||
| 	pushSDLEvent(SDL_USEREVENT, INTERFACE_CHANGED); | ||||
| } | ||||
|  | ||||
| IShowActivatable * CGuiHandler::topInt() | ||||
|   | ||||
| @@ -1512,6 +1512,7 @@ void CAdvMapInt::endingTurn() | ||||
| 		LOCPLINT->cingconsole->deactivate(); | ||||
| 	LOCPLINT->makingTurn = false; | ||||
| 	LOCPLINT->cb->endTurn(); | ||||
| 	CCS->soundh->ambientStopAllChannels(); | ||||
| } | ||||
|  | ||||
| const CGObjectInstance* CAdvMapInt::getActiveObject(const int3 &mapPos) | ||||
|   | ||||
							
								
								
									
										14
									
								
								config/ambientSounds.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								config/ambientSounds.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| { | ||||
|   // By default visitable objects only have ambient sound source on visitable tile | ||||
|   // Static objects and special terrains that have ambient sound have source on all tiles | ||||
|   // If set to true then all tiles will become source for visitable objects | ||||
|   "allTilesSource": false, | ||||
|   // By default SDL2_Mixer allocate 8 channels, but more sounds require more channels | ||||
|   "allocateChannels" : 16, | ||||
|   // Maximal ambient sounds volume must be about 20% of global effects volume | ||||
|   "volume" : 20, | ||||
|   // Maximal distance to objects using chebyshev distance | ||||
|   "range" : 3, | ||||
|   // Volume depend on distance, e.g 100% on same tile, 90% one tile away, etc | ||||
|   "distances": [100, 90, 60, 30] | ||||
| } | ||||
| @@ -908,6 +908,11 @@ bool CGameInfoCallback::isInTheMap(const int3 &pos) const | ||||
| 	return gs->map->isInTheMap(pos); | ||||
| } | ||||
|  | ||||
| void CGameInfoCallback::getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula) const | ||||
| { | ||||
| 	gs->getTilesInRange(tiles, pos, radious, getLocalPlayer(), -1, distanceFormula); | ||||
| } | ||||
|  | ||||
| const CArtifactInstance * CGameInfoCallback::getArtInstance( ArtifactInstanceID aid ) const | ||||
| { | ||||
| 	return gs->map->artInstances[aid.num]; | ||||
|   | ||||
| @@ -29,6 +29,7 @@ class CMapHeader; | ||||
| struct TeamState; | ||||
| struct QuestInfo; | ||||
| class int3; | ||||
| struct ShashInt3; | ||||
|  | ||||
|  | ||||
| class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase | ||||
| @@ -92,6 +93,7 @@ public: | ||||
| 	const TerrainTile * getTile(int3 tile, bool verbose = true) const; | ||||
| 	std::shared_ptr<boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const; | ||||
| 	bool isInTheMap(const int3 &pos) const; | ||||
| 	void getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const; | ||||
|  | ||||
| 	//town | ||||
| 	const CGTownInstance* getTown(ObjectInstanceID objid) const; | ||||
|   | ||||
| @@ -1043,5 +1043,20 @@ public: | ||||
| 		sound_after_last | ||||
| 	}; | ||||
| #undef VCMI_SOUND_NAME | ||||
| #define VCMI_SOUND_NAME(sequence) sounds.push_back("" #sequence ""); | ||||
|  | ||||
| 	static std::vector<std::string> & stringsList() | ||||
| 	{ | ||||
| 		static std::vector<std::string> sounds; | ||||
| 		if(!sounds.size()) | ||||
| 		{ | ||||
| 			sounds.push_back("invalid"); | ||||
| 			sounds.push_back("sound_todo"); | ||||
| 			VCMI_SOUND_LIST | ||||
| 			sounds.push_back("sound_after_last"); | ||||
| 		} | ||||
| 		return sounds; | ||||
| 	} | ||||
| #undef VCMI_SOUND_FILE | ||||
| #undef VCMI_SOUND_NAME | ||||
| }; | ||||
|   | ||||
| @@ -30,7 +30,7 @@ | ||||
| #include "../StringConstants.h" | ||||
|  | ||||
| ///helpers | ||||
| static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID) | ||||
| static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID = 0) | ||||
| { | ||||
| 	InfoWindow iw; | ||||
| 	iw.soundID = soundID; | ||||
| @@ -39,7 +39,7 @@ static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const u | ||||
| 	IObjectInterface::cb->sendAndApply(&iw); | ||||
| } | ||||
|  | ||||
| static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID) | ||||
| static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID = 0) | ||||
| { | ||||
| 	const PlayerColor playerID = h->getOwner(); | ||||
| 	showInfoDialog(playerID,txtID,soundID); | ||||
| @@ -443,7 +443,7 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const | ||||
| 			txt_id = 103; | ||||
| 		} | ||||
|  | ||||
| 		showInfoDialog(h,txt_id,soundBase::ROGUE); | ||||
| 		showInfoDialog(h,txt_id); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -53,7 +53,6 @@ void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const | ||||
| { | ||||
| 		BlockingDialog bd (true, false); | ||||
| 		bd.player = h->getOwner(); | ||||
| 		bd.soundID = soundBase::QUEST; | ||||
| 		bd.text.addTxt (MetaString::ADVOB_TXT, 14); | ||||
| 		cb->showBlockingDialog (&bd); | ||||
| } | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
| #include "../CGeneralTextHandler.h" | ||||
| #include "../CModHandler.h" | ||||
| #include "../JsonNode.h" | ||||
| #include "../CSoundBase.h" | ||||
|  | ||||
| #include "CRewardableConstructor.h" | ||||
| #include "CommonConstructors.h" | ||||
| @@ -201,6 +202,7 @@ CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(co | ||||
| 	{ | ||||
| 		loadObjectEntry(entry.first, entry.second, obj); | ||||
| 	} | ||||
|  | ||||
| 	return obj; | ||||
| } | ||||
|  | ||||
| @@ -348,6 +350,22 @@ std::string CObjectClassesHandler::getObjectName(si32 type, si32 subtype) const | ||||
| 	return getObjectName(type); | ||||
| } | ||||
|  | ||||
| SObjectSounds CObjectClassesHandler::getObjectSounds(si32 type) const | ||||
| { | ||||
| 	if(objects.count(type)) | ||||
| 		return objects.at(type)->sounds; | ||||
| 	logGlobal->error("Access to non existing object of type %d", type); | ||||
| 	return SObjectSounds(); | ||||
| } | ||||
|  | ||||
| SObjectSounds CObjectClassesHandler::getObjectSounds(si32 type, si32 subtype) const | ||||
| { | ||||
| 	if(knownSubObjects(type).count(subtype)) | ||||
| 		return getHandlerFor(type, subtype)->getSounds(); | ||||
| 	else | ||||
| 		return getObjectSounds(type); | ||||
| } | ||||
|  | ||||
| std::string CObjectClassesHandler::getObjectHandlerName(si32 type) const | ||||
| { | ||||
| 	return objects.at(type)->handlerName; | ||||
| @@ -414,6 +432,15 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional<std::strin | ||||
| 	else | ||||
| 		objectName.reset(input["name"].String()); | ||||
|  | ||||
| 	for(const JsonNode & node : input["sounds"]["ambient"].Vector()) | ||||
| 		sounds.ambient.push_back(node.String()); | ||||
|  | ||||
| 	for(const JsonNode & node : input["sounds"]["visit"].Vector()) | ||||
| 		sounds.visit.push_back(node.String()); | ||||
|  | ||||
| 	for(const JsonNode & node : input["sounds"]["removal"].Vector()) | ||||
| 		sounds.removal.push_back(node.String()); | ||||
|  | ||||
| 	initTypeData(input); | ||||
| } | ||||
|  | ||||
| @@ -440,6 +467,11 @@ boost::optional<std::string> AObjectTypeHandler::getCustomName() const | ||||
| 	return objectName; | ||||
| } | ||||
|  | ||||
| SObjectSounds AObjectTypeHandler::getSounds() const | ||||
| { | ||||
| 	return sounds; | ||||
| } | ||||
|  | ||||
| void AObjectTypeHandler::addTemplate(const ObjectTemplate & templ) | ||||
| { | ||||
| 	templates.push_back(templ); | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| /* | ||||
| /* | ||||
|  * CObjectClassesHandler.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
| @@ -19,6 +19,21 @@ | ||||
| class JsonNode; | ||||
| class CRandomGenerator; | ||||
|  | ||||
|  | ||||
| struct SObjectSounds | ||||
| { | ||||
| 	std::vector<std::string> ambient; | ||||
| 	std::vector<std::string> visit; | ||||
| 	std::vector<std::string> removal; | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & ambient; | ||||
| 		h & visit; | ||||
| 		h & removal; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| /// Structure that describes placement rules for this object in random map | ||||
| struct DLL_LINKAGE RandomMapInfo | ||||
| { | ||||
| @@ -108,6 +123,8 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable | ||||
| 	JsonNode base; /// describes base template | ||||
|  | ||||
| 	std::vector<ObjectTemplate> templates; | ||||
|  | ||||
| 	SObjectSounds sounds; | ||||
| protected: | ||||
| 	void preInitObject(CGObjectInstance * obj) const; | ||||
| 	virtual bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const; | ||||
| @@ -131,6 +148,7 @@ public: | ||||
|  | ||||
| 	/// Returns object-specific name, if set | ||||
| 	boost::optional<std::string> getCustomName() const; | ||||
| 	SObjectSounds getSounds() const; | ||||
|  | ||||
| 	void addTemplate(const ObjectTemplate & templ); | ||||
| 	void addTemplate(JsonNode config); | ||||
| @@ -172,6 +190,10 @@ public: | ||||
| 			h & typeName; | ||||
| 			h & subTypeName; | ||||
| 		} | ||||
| 		if(version >= 778) | ||||
| 		{ | ||||
| 			h & sounds; | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| @@ -192,6 +214,8 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase | ||||
| 		std::map<si32, TObjectTypeHandler> subObjects; | ||||
| 		std::map<std::string, si32> subIds;//full id from core scope -> subtype | ||||
|  | ||||
| 		SObjectSounds sounds; | ||||
|  | ||||
| 		template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 		{ | ||||
| 			h & name; | ||||
| @@ -203,6 +227,10 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase | ||||
| 				h & identifier; | ||||
| 				h & subIds; | ||||
| 			} | ||||
| 			if(version >= 778) | ||||
| 			{ | ||||
| 				h & sounds; | ||||
| 			} | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| @@ -250,6 +278,9 @@ public: | ||||
| 	std::string getObjectName(si32 type) const; | ||||
| 	std::string getObjectName(si32 type, si32 subtype) const; | ||||
|  | ||||
| 	SObjectSounds getObjectSounds(si32 type) const; | ||||
| 	SObjectSounds getObjectSounds(si32 type, si32 subtype) const; | ||||
|  | ||||
| 	/// Returns handler string describing the handler (for use in client) | ||||
| 	std::string getObjectHandlerName(si32 type) const; | ||||
|  | ||||
|   | ||||
| @@ -53,7 +53,7 @@ static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const u | ||||
| 	showInfoDialog(playerID,txtID,soundID); | ||||
| }*/ | ||||
|  | ||||
| static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID) | ||||
| static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID = 0) | ||||
| { | ||||
| 	const PlayerColor playerID = h->getOwner(); | ||||
| 	showInfoDialog(playerID,txtID,soundID); | ||||
| @@ -271,6 +271,33 @@ std::string CGObjectInstance::getObjectName() const | ||||
| 	return VLC->objtypeh->getObjectName(ID, subID); | ||||
| } | ||||
|  | ||||
| boost::optional<std::string> CGObjectInstance::getAmbientSound() const | ||||
| { | ||||
| 	const auto & sounds = VLC->objtypeh->getObjectSounds(ID, subID).ambient; | ||||
| 	if(sounds.size()) | ||||
| 		return sounds.front(); // TODO: Support randomization of ambient sounds | ||||
|  | ||||
| 	return boost::none; | ||||
| } | ||||
|  | ||||
| boost::optional<std::string> CGObjectInstance::getVisitSound() const | ||||
| { | ||||
| 	const auto & sounds = VLC->objtypeh->getObjectSounds(ID, subID).visit; | ||||
| 	if(sounds.size()) | ||||
| 		return *RandomGeneratorUtil::nextItem(sounds, CRandomGenerator::getDefault()); | ||||
|  | ||||
| 	return boost::none; | ||||
| } | ||||
|  | ||||
| boost::optional<std::string> CGObjectInstance::getRemovalSound() const | ||||
| { | ||||
| 	const auto & sounds = VLC->objtypeh->getObjectSounds(ID, subID).removal; | ||||
| 	if(sounds.size()) | ||||
| 		return *RandomGeneratorUtil::nextItem(sounds, CRandomGenerator::getDefault()); | ||||
|  | ||||
| 	return boost::none; | ||||
| } | ||||
|  | ||||
| std::string CGObjectInstance::getHoverText(PlayerColor player) const | ||||
| { | ||||
| 	return getObjectName(); | ||||
| @@ -293,7 +320,7 @@ void CGObjectInstance::onHeroVisit( const CGHeroInstance * h ) const | ||||
| 	case Obj::SANCTUARY: | ||||
| 		{ | ||||
| 			//You enter the sanctuary and immediately feel as if a great weight has been lifted off your shoulders.  You feel safe here. | ||||
| 			showInfoDialog(h,114,soundBase::GETPROTECTION); | ||||
| 			showInfoDialog(h, 114); | ||||
| 		} | ||||
| 		break; | ||||
| 	case Obj::TAVERN: | ||||
|   | ||||
| @@ -144,6 +144,11 @@ public: | ||||
| 	std::set<int3> getBlockedOffsets() const; //returns set of relative positions blocked by this object | ||||
| 	bool isVisitable() const; //returns true if object is visitable | ||||
|  | ||||
| 	boost::optional<std::string> getAmbientSound() const; | ||||
| 	boost::optional<std::string> getVisitSound() const; | ||||
| 	boost::optional<std::string> getRemovalSound() const; | ||||
|  | ||||
|  | ||||
| 	/** VIRTUAL METHODS **/ | ||||
|  | ||||
| 	/// Returns true if player can pass through visitable tiles of this object | ||||
|   | ||||
| @@ -37,7 +37,7 @@ CQuest::CQuest() | ||||
| } | ||||
|  | ||||
| ///helpers | ||||
| static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID) | ||||
| static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID = 0) | ||||
| { | ||||
| 	InfoWindow iw; | ||||
| 	iw.soundID = soundID; | ||||
| @@ -46,7 +46,7 @@ static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const u | ||||
| 	IObjectInterface::cb->sendAndApply(&iw); | ||||
| } | ||||
|  | ||||
| static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID) | ||||
| static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID = 0) | ||||
| { | ||||
| 	const PlayerColor playerID = h->getOwner(); | ||||
| 	showInfoDialog(playerID,txtID,soundID); | ||||
| @@ -685,7 +685,6 @@ void CGSeerHut::onHeroVisit(const CGHeroInstance * h) const | ||||
| 		{ | ||||
| 			BlockingDialog bd (true, false); | ||||
| 			bd.player = h->getOwner(); | ||||
| 			bd.soundID = soundBase::QUEST; | ||||
|  | ||||
| 			getCompletionText (bd.text, bd.components, isCustom, h); | ||||
|  | ||||
| @@ -1085,7 +1084,7 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const | ||||
| 	} | ||||
| 	else | ||||
| 		txt_id=20; | ||||
| 	showInfoDialog(h,txt_id,soundBase::CAVEHEAD); | ||||
| 	showInfoDialog(h, txt_id); | ||||
| } | ||||
|  | ||||
| void CGBorderGuard::initObj(CRandomGenerator & rand) | ||||
| @@ -1116,13 +1115,12 @@ void CGBorderGuard::onHeroVisit(const CGHeroInstance * h) const | ||||
| 	{ | ||||
| 		BlockingDialog bd (true, false); | ||||
| 		bd.player = h->getOwner(); | ||||
| 		bd.soundID = soundBase::QUEST; | ||||
| 		bd.text.addTxt (MetaString::ADVOB_TXT, 17); | ||||
| 		cb->showBlockingDialog (&bd); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		showInfoDialog(h,18,soundBase::CAVEHEAD); | ||||
| 		showInfoDialog(h, 18); | ||||
|  | ||||
| 		AddQuest aq; | ||||
| 		aq.quest = QuestInfo (quest, this, visitablePos()); | ||||
|   | ||||
| @@ -500,7 +500,6 @@ void CGPickable::initObj(CRandomGenerator & rand) | ||||
| 	{ | ||||
| 	case Obj::CAMPFIRE: | ||||
| 		{ | ||||
| 			soundID = soundBase::experience; | ||||
| 			int givenRes = rand.nextInt(5); | ||||
| 			int givenAmm = rand.nextInt(4, 6); | ||||
|  | ||||
| @@ -514,7 +513,6 @@ void CGPickable::initObj(CRandomGenerator & rand) | ||||
| 	case Obj::FLOTSAM: | ||||
| 		{ | ||||
| 			int type = rand.nextInt(3); | ||||
| 			soundID = soundBase::GENIE; | ||||
| 			switch(type) | ||||
| 			{ | ||||
| 			case 0: | ||||
| @@ -553,7 +551,6 @@ void CGPickable::initObj(CRandomGenerator & rand) | ||||
| 		} | ||||
| 	case Obj::SEA_CHEST: | ||||
| 		{ | ||||
| 			soundID = soundBase::chest; | ||||
| 			int hlp = rand.nextInt(99); | ||||
| 			if(hlp < 20) | ||||
| 			{ | ||||
| @@ -581,7 +578,6 @@ void CGPickable::initObj(CRandomGenerator & rand) | ||||
| 		break; | ||||
| 	case Obj::SHIPWRECK_SURVIVOR: | ||||
| 		{ | ||||
| 			soundID = soundBase::experience; | ||||
| 			info.resize(1); | ||||
| 			loadRandomArtifact(rand, info[0], 55, 20, 20, 5); | ||||
| 			info[0].message.addTxt(MetaString::ADVOB_TXT, 125); | ||||
| @@ -594,7 +590,6 @@ void CGPickable::initObj(CRandomGenerator & rand) | ||||
| 			int hlp = rand.nextInt(99); | ||||
| 			if(hlp >= 95) | ||||
| 			{ | ||||
| 				soundID = soundBase::treasure; | ||||
| 				info.resize(1); | ||||
| 				loadRandomArtifact(rand, info[0], 100, 0, 0, 0); | ||||
| 				info[0].message.addTxt(MetaString::ADVOB_TXT,145); | ||||
| @@ -604,7 +599,6 @@ void CGPickable::initObj(CRandomGenerator & rand) | ||||
| 			} | ||||
| 			else if (hlp >= 65) | ||||
| 			{ | ||||
| 				soundID = soundBase::chest; | ||||
| 				onSelect.addTxt(MetaString::ADVOB_TXT,146); | ||||
| 				info.resize(2); | ||||
| 				info[0].reward.resources[Res::GOLD] = 2000; | ||||
| @@ -614,7 +608,6 @@ void CGPickable::initObj(CRandomGenerator & rand) | ||||
| 			} | ||||
| 			else if(hlp >= 33) | ||||
| 			{ | ||||
| 				soundID = soundBase::chest; | ||||
| 				onSelect.addTxt(MetaString::ADVOB_TXT,146); | ||||
| 				info.resize(2); | ||||
| 				info[0].reward.resources[Res::GOLD] = 1500; | ||||
| @@ -624,7 +617,6 @@ void CGPickable::initObj(CRandomGenerator & rand) | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				soundID = soundBase::chest; | ||||
| 				onSelect.addTxt(MetaString::ADVOB_TXT,146); | ||||
| 				info.resize(2); | ||||
| 				info[0].reward.resources[Res::GOLD] = 1000; | ||||
| @@ -662,11 +654,10 @@ void CGBonusingObject::initObj(CRandomGenerator & rand) | ||||
| 		configureBonusDuration(visit, Bonus::ONE_BATTLE, type, value, descrID); | ||||
| 	}; | ||||
|  | ||||
| 	auto configureMessage = [&](CVisitInfo & visit, int onGrantID, int onVisitedID, soundBase::soundID sound) | ||||
| 	auto configureMessage = [&](CVisitInfo & visit, int onGrantID, int onVisitedID) | ||||
| 	{ | ||||
| 		visit.message.addTxt(MetaString::ADVOB_TXT, onGrantID); | ||||
| 		onVisited.addTxt(MetaString::ADVOB_TXT, onVisitedID); | ||||
| 		soundID = sound; | ||||
| 	}; | ||||
|  | ||||
| 	info.resize(1); | ||||
| @@ -675,16 +666,16 @@ void CGBonusingObject::initObj(CRandomGenerator & rand) | ||||
| 	{ | ||||
| 	case Obj::BUOY: | ||||
| 			blockVisit = true; | ||||
| 		configureMessage(info[0], 21, 22, soundBase::MORALE); | ||||
| 		configureMessage(info[0], 21, 22); | ||||
| 		configureBonus(info[0], Bonus::MORALE, +1, 94); | ||||
| 		break; | ||||
| 	case Obj::SWAN_POND: | ||||
| 		configureMessage(info[0], 29, 30, soundBase::LUCK); | ||||
| 		configureMessage(info[0], 29, 30); | ||||
| 		configureBonus(info[0], Bonus::LUCK, 2, 67); | ||||
| 		info[0].reward.movePercentage = 0; | ||||
| 		break; | ||||
| 	case Obj::FAERIE_RING: | ||||
| 		configureMessage(info[0], 49, 50, soundBase::LUCK); | ||||
| 		configureMessage(info[0], 49, 50); | ||||
| 		configureBonus(info[0], Bonus::LUCK, 1, 71); | ||||
| 		break; | ||||
| 	case Obj::FOUNTAIN_OF_FORTUNE: | ||||
| @@ -694,7 +685,6 @@ void CGBonusingObject::initObj(CRandomGenerator & rand) | ||||
| 		{ | ||||
| 			configureBonus(info[i], Bonus::LUCK, i-1, 69); //NOTE: description have %d that should be replaced with value | ||||
| 			info[i].message.addTxt(MetaString::ADVOB_TXT, 55); | ||||
| 			soundID = soundBase::LUCK; | ||||
| 		} | ||||
| 		onVisited.addTxt(MetaString::ADVOB_TXT, 56); | ||||
| 		break; | ||||
| @@ -706,27 +696,26 @@ void CGBonusingObject::initObj(CRandomGenerator & rand) | ||||
| 			info[i].limiter.dayOfWeek = i+1; | ||||
| 			configureBonus(info[i], (i%2) ? Bonus::MORALE : Bonus::LUCK, 1, 68); | ||||
| 			info[i].message.addTxt(MetaString::ADVOB_TXT, 62); | ||||
| 			soundID = soundBase::experience; | ||||
| 		} | ||||
| 		info.back().limiter.dayOfWeek = 7; | ||||
| 		configureBonus(info.back(), Bonus::MORALE, 1, 68); // on last day of week | ||||
| 		configureBonus(info.back(), Bonus::LUCK,   1, 68); | ||||
| 		configureMessage(info.back(), 62, 63, soundBase::experience); | ||||
| 		configureMessage(info.back(), 62, 63); | ||||
|  | ||||
| 		break; | ||||
| 	case Obj::MERMAID: | ||||
| 		blockVisit = true; | ||||
| 		configureMessage(info[0], 83, 82, soundBase::LUCK); | ||||
| 		configureMessage(info[0], 83, 82); | ||||
| 		configureBonus(info[0], Bonus::LUCK, 1, 72); | ||||
| 		break; | ||||
| 	case Obj::RALLY_FLAG: | ||||
| 		configureMessage(info[0], 111, 110, soundBase::MORALE); | ||||
| 		configureMessage(info[0], 111, 110); | ||||
| 		configureBonus(info[0], Bonus::MORALE, 1, 102); | ||||
| 		configureBonus(info[0], Bonus::LUCK,   1, 102); | ||||
| 		info[0].reward.movePoints = 400; | ||||
| 		break; | ||||
| 	case Obj::OASIS: | ||||
| 		configureMessage(info[0], 95, 94, soundBase::MORALE); | ||||
| 		configureMessage(info[0], 95, 94); | ||||
| 		configureBonus(info[0], Bonus::MORALE, 1, 95); | ||||
| 		info[0].reward.movePoints = 800; | ||||
| 		break; | ||||
| @@ -739,20 +728,19 @@ void CGBonusingObject::initObj(CRandomGenerator & rand) | ||||
| 		info[0].message.addTxt(MetaString::ADVOB_TXT, 140); | ||||
| 		info[1].message.addTxt(MetaString::ADVOB_TXT, 140); | ||||
| 		onVisited.addTxt(MetaString::ADVOB_TXT, 141); | ||||
| 		soundID = soundBase::temple; | ||||
| 		break; | ||||
| 	case Obj::WATERING_HOLE: | ||||
| 		configureMessage(info[0], 166, 167, soundBase::MORALE); | ||||
| 		configureMessage(info[0], 166, 167); | ||||
| 		configureBonus(info[0], Bonus::MORALE, 1, 100); | ||||
| 		info[0].reward.movePoints = 400; | ||||
| 		break; | ||||
| 	case Obj::FOUNTAIN_OF_YOUTH: | ||||
| 		configureMessage(info[0], 57, 58, soundBase::MORALE); | ||||
| 		configureMessage(info[0], 57, 58); | ||||
| 		configureBonus(info[0], Bonus::MORALE, 1, 103); | ||||
| 		info[0].reward.movePoints = 400; | ||||
| 		break; | ||||
| 	case Obj::STABLES: | ||||
| 		configureMessage(info[0], 137, 136, soundBase::STORE); | ||||
| 		configureMessage(info[0], 137, 136); | ||||
| 		configureBonusDuration(info[0], Bonus::ONE_WEEK, Bonus::LAND_MOVEMENT, 400, 0); | ||||
| 		info[0].reward.movePoints = 400; | ||||
| 		break; | ||||
| @@ -838,7 +826,6 @@ void CGOnceVisitable::initObj(CRandomGenerator & rand) | ||||
| 	case Obj::CORPSE: | ||||
| 		{ | ||||
| 			onEmpty.addTxt(MetaString::ADVOB_TXT, 38); | ||||
| 			soundID = soundBase::MYSTERY; | ||||
| 			blockVisit = true; | ||||
| 			if(rand.nextInt(99) < 20) | ||||
| 			{ | ||||
| @@ -851,7 +838,6 @@ void CGOnceVisitable::initObj(CRandomGenerator & rand) | ||||
| 		break; | ||||
| 	case Obj::LEAN_TO: | ||||
| 		{ | ||||
| 			soundID = soundBase::GENIE; | ||||
| 			onEmpty.addTxt(MetaString::ADVOB_TXT, 65); | ||||
| 			info.resize(1); | ||||
| 			int type =  rand.nextInt(5); //any basic resource without gold | ||||
| @@ -863,7 +849,6 @@ void CGOnceVisitable::initObj(CRandomGenerator & rand) | ||||
| 		break; | ||||
| 	case Obj::WARRIORS_TOMB: | ||||
| 		{ | ||||
| 			soundID = soundBase::GRAVEYARD; | ||||
| 			onSelect.addTxt(MetaString::ADVOB_TXT, 161); | ||||
|  | ||||
| 			info.resize(2); | ||||
| @@ -880,7 +865,6 @@ void CGOnceVisitable::initObj(CRandomGenerator & rand) | ||||
| 		break; | ||||
| 	case Obj::WAGON: | ||||
| 		{ | ||||
| 			soundID = soundBase::GENIE; | ||||
| 			onVisited.addTxt(MetaString::ADVOB_TXT, 156); | ||||
|  | ||||
| 			int hlp = rand.nextInt(99); | ||||
| @@ -921,7 +905,6 @@ void CGVisitableOPH::initObj(CRandomGenerator & rand) | ||||
| 	switch(ID) | ||||
| 	{ | ||||
| 		case Obj::ARENA: | ||||
| 			soundID = soundBase::NOMAD; | ||||
| 			info.resize(2); | ||||
| 			info[0].reward.primary[PrimarySkill::ATTACK] = 2; | ||||
| 			info[1].reward.primary[PrimarySkill::DEFENSE] = 2; | ||||
| @@ -932,40 +915,34 @@ void CGVisitableOPH::initObj(CRandomGenerator & rand) | ||||
| 		case Obj::MERCENARY_CAMP: | ||||
| 			info.resize(1); | ||||
| 			info[0].reward.primary[PrimarySkill::ATTACK] = 1; | ||||
| 			soundID = soundBase::NOMAD; | ||||
| 			info[0].message.addTxt(MetaString::ADVOB_TXT, 80); | ||||
| 			onVisited.addTxt(MetaString::ADVOB_TXT, 81); | ||||
| 			break; | ||||
| 		case Obj::MARLETTO_TOWER: | ||||
| 			info.resize(1); | ||||
| 			info[0].reward.primary[PrimarySkill::DEFENSE] = 1; | ||||
| 			soundID = soundBase::NOMAD; | ||||
| 			info[0].message.addTxt(MetaString::ADVOB_TXT, 39); | ||||
| 			onVisited.addTxt(MetaString::ADVOB_TXT, 40); | ||||
| 			break; | ||||
| 		case Obj::STAR_AXIS: | ||||
| 			info.resize(1); | ||||
| 			info[0].reward.primary[PrimarySkill::SPELL_POWER] = 1; | ||||
| 			soundID = soundBase::gazebo; | ||||
| 			info[0].message.addTxt(MetaString::ADVOB_TXT, 100); | ||||
| 			onVisited.addTxt(MetaString::ADVOB_TXT, 101); | ||||
| 			break; | ||||
| 		case Obj::GARDEN_OF_REVELATION: | ||||
| 			info.resize(1); | ||||
| 			info[0].reward.primary[PrimarySkill::KNOWLEDGE] = 1; | ||||
| 			soundID = soundBase::GETPROTECTION; | ||||
| 			info[0].message.addTxt(MetaString::ADVOB_TXT, 59); | ||||
| 			onVisited.addTxt(MetaString::ADVOB_TXT, 60); | ||||
| 			break; | ||||
| 		case Obj::LEARNING_STONE: | ||||
| 			info.resize(1); | ||||
| 			info[0].reward.gainedExp = 1000; | ||||
| 			soundID = soundBase::gazebo; | ||||
| 			info[0].message.addTxt(MetaString::ADVOB_TXT, 143); | ||||
| 			onVisited.addTxt(MetaString::ADVOB_TXT, 144); | ||||
| 			break; | ||||
| 		case Obj::TREE_OF_KNOWLEDGE: | ||||
| 			soundID = soundBase::gazebo; | ||||
| 			info.resize(1); | ||||
| 			canRefuse = true; | ||||
| 			info[0].reward.gainedLevels = 1; | ||||
| @@ -1010,7 +987,6 @@ void CGVisitableOPH::initObj(CRandomGenerator & rand) | ||||
| 				visit.message.addTxt(MetaString::ADVOB_TXT, 66); | ||||
| 				info.push_back(visit); | ||||
| 			} | ||||
| 			soundID = soundBase::gazebo; | ||||
| 			break; | ||||
| 		} | ||||
| 		case Obj::SCHOOL_OF_MAGIC: | ||||
| @@ -1022,7 +998,6 @@ void CGVisitableOPH::initObj(CRandomGenerator & rand) | ||||
| 			onSelect.addTxt(MetaString::ADVOB_TXT, 71); | ||||
| 			onVisited.addTxt(MetaString::ADVOB_TXT, 72); | ||||
| 			onEmpty.addTxt(MetaString::ADVOB_TXT, 73); | ||||
| 			soundID = soundBase::faerie; | ||||
| 			canRefuse = true; | ||||
| 			break; | ||||
| 		case Obj::SCHOOL_OF_WAR: | ||||
| @@ -1034,7 +1009,6 @@ void CGVisitableOPH::initObj(CRandomGenerator & rand) | ||||
| 			onSelect.addTxt(MetaString::ADVOB_TXT, 158); | ||||
| 			onVisited.addTxt(MetaString::ADVOB_TXT, 159); | ||||
| 			onEmpty.addTxt(MetaString::ADVOB_TXT, 160); | ||||
| 			soundID = soundBase::MILITARY; | ||||
| 			canRefuse = true; | ||||
| 			break; | ||||
| 	} | ||||
| @@ -1063,17 +1037,14 @@ void CGVisitableOPW::initObj(CRandomGenerator & rand) | ||||
| 	switch (ID) | ||||
| 	{ | ||||
| 	case Obj::MYSTICAL_GARDEN: | ||||
| 		soundID = soundBase::experience; | ||||
| 		onEmpty.addTxt(MetaString::ADVOB_TXT, 93); | ||||
| 		info[0].message.addTxt(MetaString::ADVOB_TXT, 92); | ||||
| 		break; | ||||
| 	case Obj::WINDMILL: | ||||
| 		soundID = soundBase::GENIE; | ||||
| 		onEmpty.addTxt(MetaString::ADVOB_TXT, 169); | ||||
| 		info[0].message.addTxt(MetaString::ADVOB_TXT, 170); | ||||
| 		break; | ||||
| 	case Obj::WATER_WHEEL: | ||||
| 		soundID = soundBase::GENIE; | ||||
| 		onEmpty.addTxt(MetaString::ADVOB_TXT, 165); | ||||
| 		info[0].message.addTxt(MetaString::ADVOB_TXT, 164); | ||||
| 		break; | ||||
| @@ -1146,7 +1117,6 @@ void CGMagicSpring::initObj(CRandomGenerator & rand) | ||||
| 	info.push_back(visit); // two rewards, one for each entrance | ||||
| 	info.push_back(visit); | ||||
| 	onEmpty.addTxt(MetaString::ADVOB_TXT, 75); | ||||
| 	soundID = soundBase::GENIE; | ||||
| } | ||||
|  | ||||
| std::vector<int3> CGMagicSpring::getVisitableOffsets() const | ||||
|   | ||||
| @@ -40,16 +40,17 @@ static void openWindow(const OpenWindow::EWindow type, const int id1, const int | ||||
| 	IObjectInterface::cb->sendAndApply(&ow); | ||||
| } | ||||
|  | ||||
| static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID) | ||||
| static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID = 0) | ||||
| { | ||||
| 	InfoWindow iw; | ||||
| 	iw.soundID = soundID; | ||||
| 	if(soundID) | ||||
| 		iw.soundID = soundID; | ||||
| 	iw.player = playerID; | ||||
| 	iw.text.addTxt(MetaString::ADVOB_TXT,txtID); | ||||
| 	IObjectInterface::cb->sendAndApply(&iw); | ||||
| } | ||||
|  | ||||
| static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID) | ||||
| static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID = 0) | ||||
| { | ||||
| 	const PlayerColor playerID = h->getOwner(); | ||||
| 	showInfoDialog(playerID,txtID,soundID); | ||||
| @@ -1324,7 +1325,6 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const | ||||
| 		{ | ||||
| 		case Obj::ARTIFACT: | ||||
| 			{ | ||||
| 				iw.soundID = soundBase::treasure; //play sound only for non-scroll arts | ||||
| 				iw.components.push_back(Component(Component::ARTIFACT, subID, 0, 0)); | ||||
| 				if(message.length()) | ||||
| 					iw.text << message; | ||||
| @@ -1446,7 +1446,6 @@ void CGWitchHut::initObj(CRandomGenerator & rand) | ||||
| void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const | ||||
| { | ||||
| 	InfoWindow iw; | ||||
| 	iw.soundID = soundBase::gazebo; | ||||
| 	iw.player = h->getOwner(); | ||||
| 	if(!wasVisited(h->tempOwner)) | ||||
| 		cb->setObjProperty(id, CGWitchHut::OBJPROP_VISITED, h->tempOwner.getNum()); | ||||
| @@ -1535,7 +1534,7 @@ void CGMagicWell::onHeroVisit( const CGHeroInstance * h ) const | ||||
| 	{ | ||||
| 		message = 79; | ||||
| 	} | ||||
| 	showInfoDialog(h,message,soundBase::faerie); | ||||
| 	showInfoDialog(h, message); | ||||
| } | ||||
|  | ||||
| std::string CGMagicWell::getHoverText(const CGHeroInstance * hero) const | ||||
| @@ -1552,7 +1551,6 @@ void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const | ||||
| 	case Obj::REDWOOD_OBSERVATORY: | ||||
| 	case Obj::PILLAR_OF_FIRE: | ||||
| 	{ | ||||
| 		iw.soundID = soundBase::LIGHTHOUSE; | ||||
| 		iw.text.addTxt(MetaString::ADVOB_TXT,98 + (ID==Obj::PILLAR_OF_FIRE)); | ||||
|  | ||||
| 		FoWChange fw; | ||||
| @@ -1589,7 +1587,6 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const | ||||
| 		cb->setObjProperty(id, CGShrine::OBJPROP_VISITED, h->tempOwner.getNum()); | ||||
|  | ||||
| 	InfoWindow iw; | ||||
| 	iw.soundID = soundBase::temple; | ||||
| 	iw.player = h->getOwner(); | ||||
| 	iw.text.addTxt(MetaString::ADVOB_TXT,127 + ID - 88); | ||||
| 	iw.text.addTxt(MetaString::SPELL_NAME,spell); | ||||
| @@ -1678,7 +1675,6 @@ void CGSignBottle::initObj(CRandomGenerator & rand) | ||||
| void CGSignBottle::onHeroVisit( const CGHeroInstance * h ) const | ||||
| { | ||||
| 	InfoWindow iw; | ||||
| 	iw.soundID = soundBase::STORE; | ||||
| 	iw.player = h->getOwner(); | ||||
| 	iw.text << message; | ||||
| 	cb->showInfoDialog(&iw); | ||||
| @@ -1706,7 +1702,6 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const | ||||
| 	} | ||||
|  | ||||
| 	InfoWindow iw; | ||||
| 	iw.soundID = soundBase::gazebo; | ||||
| 	iw.player = h->getOwner(); | ||||
| 	iw.text.addTxt(MetaString::ADVOB_TXT,115); | ||||
|  | ||||
| @@ -1874,7 +1869,7 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const | ||||
| { | ||||
| 	if (ID == Obj::HUT_OF_MAGI) | ||||
| 	{ | ||||
| 		showInfoDialog(h, 61, soundBase::LIGHTHOUSE); | ||||
| 		showInfoDialog(h, 61); | ||||
|  | ||||
| 		if (!eyelist[subID].empty()) | ||||
| 		{ | ||||
| @@ -1904,7 +1899,7 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const | ||||
| 	} | ||||
| 	else if (ID == Obj::EYE_OF_MAGI) | ||||
| 	{ | ||||
| 		showInfoDialog(h,48,soundBase::invalid); | ||||
| 		showInfoDialog(h, 48); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -1926,7 +1921,6 @@ std::string CGSirens::getHoverText(const CGHeroInstance * hero) const | ||||
| void CGSirens::onHeroVisit( const CGHeroInstance * h ) const | ||||
| { | ||||
| 	InfoWindow iw; | ||||
| 	iw.soundID = soundBase::DANGER; | ||||
| 	iw.player = h->tempOwner; | ||||
| 	if(h->hasBonusFrom(Bonus::OBJECT,ID)) //has already visited Sirens | ||||
| 	{ | ||||
| @@ -2030,18 +2024,17 @@ void CCartographer::onHeroVisit( const CGHeroInstance * h ) const | ||||
| 			assert(text); | ||||
| 			BlockingDialog bd (true, false); | ||||
| 			bd.player = h->getOwner(); | ||||
| 			bd.soundID = soundBase::LIGHTHOUSE; | ||||
| 			bd.text.addTxt (MetaString::ADVOB_TXT, text); | ||||
| 			cb->showBlockingDialog (&bd); | ||||
| 		} | ||||
| 		else //if he cannot afford | ||||
| 		{ | ||||
| 			showInfoDialog(h,28,soundBase::CAVEHEAD); | ||||
| 			showInfoDialog(h, 28); | ||||
| 		} | ||||
| 	} | ||||
| 	else //if he already visited carographer | ||||
| 	{ | ||||
| 		showInfoDialog(h,24,soundBase::CAVEHEAD); | ||||
| 		showInfoDialog(h, 24); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -2144,7 +2137,7 @@ void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const | ||||
| 	{ | ||||
| 		PlayerColor oldOwner = tempOwner; | ||||
| 		cb->setOwner(this,h->tempOwner); //not ours? flag it! | ||||
| 		showInfoDialog(h,69,soundBase::LIGHTHOUSE); | ||||
| 		showInfoDialog(h, 69); | ||||
| 		giveBonusTo(h->tempOwner); | ||||
|  | ||||
| 		if(oldOwner < PlayerColor::PLAYER_LIMIT) //remove bonus from old owner | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| #include "../ConstTransitivePtr.h" | ||||
| #include "../GameConstants.h" | ||||
|  | ||||
| const ui32 SERIALIZATION_VERSION = 777; | ||||
| const ui32 SERIALIZATION_VERSION = 778; | ||||
| const ui32 MINIMAL_SERIALIZATION_VERSION = 753; | ||||
| const std::string SAVEGAME_MAGIC = "VCMISVG"; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user