mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	| @@ -619,7 +619,7 @@ void AIGateway::commanderGotLevel(const CCommanderInstance * commander, std::vec | ||||
|  | ||||
| void AIGateway::showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel, bool safeToAutoaccept) | ||||
| { | ||||
| 	LOG_TRACE_PARAMS(logAi, "text '%s', askID '%i', soundID '%i', selection '%i', cancel '%i'", text % askID % soundID % selection % cancel % safeToAutoaccept); | ||||
| 	LOG_TRACE_PARAMS(logAi, "text '%s', askID '%i', soundID '%i', selection '%i', cancel '%i', autoaccept '%i'", text % askID % soundID % selection % cancel % safeToAutoaccept); | ||||
| 	NET_EVENT_HANDLER; | ||||
| 	status.addQuery(askID, boost::str(boost::format("Blocking dialog query with %d components - %s") | ||||
| 									  % components.size() % text)); | ||||
|   | ||||
| @@ -94,7 +94,7 @@ std::string CompleteQuest::questToString() const | ||||
| 		return "find " + VLC->generaltexth->tentColors[q.obj->subID] + " keymaster tent"; | ||||
| 	} | ||||
|  | ||||
| 	if(q.quest->questName == CQuest::missionName(0)) | ||||
| 	if(q.quest->questName == CQuest::missionName(EQuestMission::NONE)) | ||||
| 		return "inactive quest"; | ||||
|  | ||||
| 	MetaString ms; | ||||
|   | ||||
| @@ -146,7 +146,10 @@ TSubgoal CollectRes::whatToDoToTrade() | ||||
| 	markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool | ||||
| 	{ | ||||
| 		auto * o = dynamic_cast<const CGObjectInstance *>(market); | ||||
| 		if(o && !(o->ID == Obj::TOWN && o->tempOwner == ai->playerID)) | ||||
| 		// FIXME: disabled broken visitation of external markets | ||||
| 		//if(o && !(o->ID == Obj::TOWN && o->tempOwner == ai->playerID)) | ||||
|  | ||||
| 		if(o && o->ID == Obj::TOWN) | ||||
| 		{ | ||||
| 			if(!ai->isAccessible(o->visitablePos())) | ||||
| 				return true; | ||||
|   | ||||
| @@ -99,7 +99,7 @@ std::string CompleteQuest::completeMessage() const | ||||
|  | ||||
| std::string CompleteQuest::questToString() const | ||||
| { | ||||
| 	if(q.quest->questName == CQuest::missionName(0)) | ||||
| 	if(q.quest->questName == CQuest::missionName(EQuestMission::NONE)) | ||||
| 		return "inactive quest"; | ||||
|  | ||||
| 	MetaString ms; | ||||
|   | ||||
| @@ -657,7 +657,7 @@ void VCAI::commanderGotLevel(const CCommanderInstance * commander, std::vector<u | ||||
|  | ||||
| void VCAI::showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel, bool safeToAutoaccept) | ||||
| { | ||||
| 	LOG_TRACE_PARAMS(logAi, "text '%s', askID '%i', soundID '%i', selection '%i', cancel '%i'", text % askID % soundID % selection % cancel % safeToAutoaccept); | ||||
| 	LOG_TRACE_PARAMS(logAi, "text '%s', askID '%i', soundID '%i', selection '%i', cancel '%i', autoaccept '%i'", text % askID % soundID % selection % cancel % safeToAutoaccept); | ||||
| 	NET_EVENT_HANDLER; | ||||
| 	int sel = 0; | ||||
| 	status.addQuery(askID, boost::str(boost::format("Blocking dialog query with %d components - %s") | ||||
|   | ||||
| @@ -386,6 +386,69 @@ | ||||
| 	"vcmi.stackExperience.rank.8" : "Elite", | ||||
| 	"vcmi.stackExperience.rank.9" : "Master", | ||||
| 	"vcmi.stackExperience.rank.10" : "Ace", | ||||
| 	 | ||||
| 	// Strings for HotA Seer Hut / Quest Guards | ||||
| 	"core.seerhut.quest.heroClass.complete.0" : "Ah, you are %s.  Here's a gift for you.  Do you accept?", | ||||
| 	"core.seerhut.quest.heroClass.complete.1" : "Ah, you are %s.  Here's a gift for you.  Do you accept?", | ||||
| 	"core.seerhut.quest.heroClass.complete.2" : "Ah, you are %s.  Here's a gift for you.  Do you accept?", | ||||
| 	"core.seerhut.quest.heroClass.complete.3" : "The guards note that you are %s and offer to let you pass.  Do you accept?", | ||||
| 	"core.seerhut.quest.heroClass.complete.4" : "The guards note that you are %s and offer to let you pass.  Do you accept?", | ||||
| 	"core.seerhut.quest.heroClass.complete.5" : "The guards note that you are %s and offer to let you pass.  Do you accept?", | ||||
| 	"core.seerhut.quest.heroClass.description.0" : "Send %s to %s", | ||||
| 	"core.seerhut.quest.heroClass.description.1" : "Send %s to %s", | ||||
| 	"core.seerhut.quest.heroClass.description.2" : "Send %s to %s", | ||||
| 	"core.seerhut.quest.heroClass.description.3" : "Send %s to open gate", | ||||
| 	"core.seerhut.quest.heroClass.description.4" : "Send %s to open gate", | ||||
| 	"core.seerhut.quest.heroClass.description.5" : "Send %s to open gate", | ||||
| 	"core.seerhut.quest.heroClass.hover.0" : "(seeks hero of %s class)", | ||||
| 	"core.seerhut.quest.heroClass.hover.1" : "(seeks hero of %s class)", | ||||
| 	"core.seerhut.quest.heroClass.hover.2" : "(seeks hero of %s class)", | ||||
| 	"core.seerhut.quest.heroClass.hover.3" : "(seeks hero of %s class)", | ||||
| 	"core.seerhut.quest.heroClass.hover.4" : "(seeks hero of %s class)", | ||||
| 	"core.seerhut.quest.heroClass.hover.5" : "(seeks hero of %s class)", | ||||
| 	"core.seerhut.quest.heroClass.receive.0" : "I've got a gift for %s.", | ||||
| 	"core.seerhut.quest.heroClass.receive.1" : "I've got a gift for %s.", | ||||
| 	"core.seerhut.quest.heroClass.receive.2" : "I've got a gift for %s.", | ||||
| 	"core.seerhut.quest.heroClass.receive.3" : "The guards here say they will only let %s pass.", | ||||
| 	"core.seerhut.quest.heroClass.receive.4" : "The guards here say they will only let %s pass.", | ||||
| 	"core.seerhut.quest.heroClass.receive.5" : "The guards here say they will only let %s pass.", | ||||
| 	"core.seerhut.quest.heroClass.visit.0" : "You are not %s.  I've got nothing for you. Begone!", | ||||
| 	"core.seerhut.quest.heroClass.visit.1" : "You are not %s.  I've got nothing for you. Begone!", | ||||
| 	"core.seerhut.quest.heroClass.visit.2" : "You are not %s.  I've got nothing for you. Begone!", | ||||
| 	"core.seerhut.quest.heroClass.visit.3" : "The guards here will only let %s pass.", | ||||
| 	"core.seerhut.quest.heroClass.visit.4" : "The guards here will only let %s pass.", | ||||
| 	"core.seerhut.quest.heroClass.visit.5" : "The guards here will only let %s pass.", | ||||
| 	 | ||||
| 	"core.seerhut.quest.reachDate.complete.0" : "I'm free now.  Here's what I've got for you.  Do you accept?", | ||||
| 	"core.seerhut.quest.reachDate.complete.1" : "I'm free now.  Here's what I've got for you.  Do you accept?", | ||||
| 	"core.seerhut.quest.reachDate.complete.2" : "I'm free now.  Here's what I've got for you.  Do you accept?", | ||||
| 	"core.seerhut.quest.reachDate.complete.3" : "You are free to go through now.  Do you wish to pass?", | ||||
| 	"core.seerhut.quest.reachDate.complete.4" : "You are free to go through now.  Do you wish to pass?", | ||||
| 	"core.seerhut.quest.reachDate.complete.5" : "You are free to go through now.  Do you wish to pass?", | ||||
| 	"core.seerhut.quest.reachDate.description.0" : "Wait till %s for %s", | ||||
| 	"core.seerhut.quest.reachDate.description.1" : "Wait till %s for %s", | ||||
| 	"core.seerhut.quest.reachDate.description.2" : "Wait till %s for %s", | ||||
| 	"core.seerhut.quest.reachDate.description.3" : "Wait till %s to open gate", | ||||
| 	"core.seerhut.quest.reachDate.description.4" : "Wait till %s to open gate", | ||||
| 	"core.seerhut.quest.reachDate.description.5" : "Wait till %s to open gate", | ||||
| 	"core.seerhut.quest.reachDate.hover.0" : "(Return not before %s)", | ||||
| 	"core.seerhut.quest.reachDate.hover.1" : "(Return not before %s)", | ||||
| 	"core.seerhut.quest.reachDate.hover.2" : "(Return not before %s)", | ||||
| 	"core.seerhut.quest.reachDate.hover.3" : "(Return not before %s)", | ||||
| 	"core.seerhut.quest.reachDate.hover.4" : "(Return not before %s)", | ||||
| 	"core.seerhut.quest.reachDate.hover.5" : "(Return not before %s)", | ||||
| 	"core.seerhut.quest.reachDate.receive.0" : "I'm busy.  Come back not before %s", | ||||
| 	"core.seerhut.quest.reachDate.receive.1" : "I'm busy.  Come back not before %s", | ||||
| 	"core.seerhut.quest.reachDate.receive.2" : "I'm busy.  Come back not before %s", | ||||
| 	"core.seerhut.quest.reachDate.receive.3" : "Closed till %s.", | ||||
| 	"core.seerhut.quest.reachDate.receive.4" : "Closed till %s.", | ||||
| 	"core.seerhut.quest.reachDate.receive.5" : "Closed till %s.", | ||||
| 	"core.seerhut.quest.reachDate.visit.0" : "I'm busy.  Come back not before %s.", | ||||
| 	"core.seerhut.quest.reachDate.visit.1" : "I'm busy.  Come back not before %s.", | ||||
| 	"core.seerhut.quest.reachDate.visit.2" : "I'm busy.  Come back not before %s.", | ||||
| 	"core.seerhut.quest.reachDate.visit.3" : "Closed till %s.", | ||||
| 	"core.seerhut.quest.reachDate.visit.4" : "Closed till %s.", | ||||
| 	"core.seerhut.quest.reachDate.visit.5" : "Closed till %s.", | ||||
|  | ||||
| 	"core.bonus.ADDITIONAL_ATTACK.name": "Double Strike", | ||||
| 	"core.bonus.ADDITIONAL_ATTACK.description": "Attacks twice", | ||||
|   | ||||
| @@ -240,7 +240,7 @@ void CBonusSelection::createBonusesIcons() | ||||
| 		} | ||||
| 		case CampaignBonusType::SECONDARY_SKILL: | ||||
| 			desc.appendLocalString(EMetaText::GENERAL_TXT, 718); | ||||
| 			desc.replaceTextID(TextIdentifier("core", "genrltxt", "levels", bonDescs[i].info3 - 1).get()); | ||||
| 			desc.replaceTextID(TextIdentifier("core", "skilllev", bonDescs[i].info3 - 1).get()); | ||||
| 			desc.replaceName(SecondarySkill(bonDescs[i].info2)); | ||||
| 			picNumber = bonDescs[i].info2 * 3 + bonDescs[i].info3 - 1; | ||||
|  | ||||
|   | ||||
| @@ -65,6 +65,7 @@ | ||||
| 			}, | ||||
| 			"airConflux": { | ||||
| 				"index": 7, | ||||
| 				"bannedForRandomDwelling" : true, | ||||
| 				"creatures": [["airElemental"]], | ||||
| 				"sounds": { | ||||
| 					"ambient": ["LOOPAIR"] | ||||
| @@ -107,6 +108,7 @@ | ||||
| 			}, | ||||
| 			"earthConflux": { | ||||
| 				"index": 13, | ||||
| 				"bannedForRandomDwelling" : true, | ||||
| 				"creatures": [["earthElemental"]], | ||||
| 				"sounds": { | ||||
| 					"ambient": ["LOOPEART"] | ||||
| @@ -128,6 +130,7 @@ | ||||
| 			}, | ||||
| 			"fireConflux": { | ||||
| 				"index": 16, | ||||
| 				"bannedForRandomDwelling" : true, | ||||
| 				"creatures": [["fireElemental"]], | ||||
| 				"sounds": { | ||||
| 					"ambient": ["LOOPFIRE"] | ||||
| @@ -345,6 +348,7 @@ | ||||
| 			}, | ||||
| 			"waterConflux": { | ||||
| 				"index": 47, | ||||
| 				"bannedForRandomDwelling" : true, | ||||
| 				"creatures": [["waterElemental"]], | ||||
| 				"sounds": { | ||||
| 					"ambient": ["LOOPFOUN"] | ||||
| @@ -538,6 +542,7 @@ | ||||
| 		"types" : { | ||||
| 			"elementalConflux" : { | ||||
| 				"index" : 0, | ||||
| 				"bannedForRandomDwelling" : true, | ||||
| 				"creatures" : [ // 4 separate "levels" to give them separate growth | ||||
| 					[ "airElemental" ], | ||||
| 					[ "waterElemental" ], | ||||
|   | ||||
| @@ -810,7 +810,8 @@ void CModListView::installFiles(QStringList files) | ||||
| 			images.push_back(filename); | ||||
| 	} | ||||
|  | ||||
| 	manager->loadRepositories(repositories); | ||||
| 	if (!repositories.empty()) | ||||
| 		manager->loadRepositories(repositories); | ||||
|  | ||||
| 	if(!mods.empty()) | ||||
| 		installMods(mods); | ||||
|   | ||||
| @@ -569,7 +569,9 @@ CGeneralTextHandler::CGeneralTextHandler(): | ||||
|  | ||||
| 		for (size_t i = 0; i < 9; ++i) //9 types of quests | ||||
| 		{ | ||||
| 			std::string questName = CQuest::missionName(1+i); | ||||
| 			EQuestMission missionID = static_cast<EQuestMission>(i+1); | ||||
|  | ||||
| 			std::string questName = CQuest::missionName(missionID); | ||||
|  | ||||
| 			for (size_t j = 0; j < 5; ++j) | ||||
| 			{ | ||||
|   | ||||
| @@ -351,12 +351,12 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(MapObjectID type, MapObj | ||||
| 	} | ||||
| 	catch (std::out_of_range & e) | ||||
| 	{ | ||||
| 		// Leave catch block silently | ||||
| 		// Leave catch block silently and handle error in block outside of try ... catch - all valid values should use 'return' in try block | ||||
| 	} | ||||
|  | ||||
| 	std::string errorString = "Failed to find object of type " + std::to_string(type.getNum()) + "::" + std::to_string(subtype.getNum()); | ||||
| 	logGlobal->error(errorString); | ||||
| 	throw std::runtime_error(errorString); | ||||
| 	throw std::out_of_range(errorString); | ||||
| } | ||||
|  | ||||
| TObjectTypeHandler CObjectClassesHandler::getHandlerFor(const std::string & scope, const std::string & type, const std::string & subtype) const | ||||
|   | ||||
| @@ -51,6 +51,12 @@ void DwellingInstanceConstructor::initTypeData(const JsonNode & input) | ||||
| 		assert(!availableCreatures[currentLevel].empty()); | ||||
| 	} | ||||
| 	guards = input["guards"]; | ||||
| 	bannedForRandomDwelling = input["bannedForRandomDwelling"].Bool(); | ||||
| } | ||||
|  | ||||
| bool DwellingInstanceConstructor::isBannedForRandomDwelling() const | ||||
| { | ||||
| 	return bannedForRandomDwelling; | ||||
| } | ||||
|  | ||||
| bool DwellingInstanceConstructor::objectFilter(const CGObjectInstance * obj, std::shared_ptr<const ObjectTemplate> tmpl) const | ||||
|   | ||||
| @@ -23,6 +23,7 @@ class DwellingInstanceConstructor : public CDefaultObjectTypeHandler<CGDwelling> | ||||
| 	std::vector<std::vector<const CCreature *>> availableCreatures; | ||||
|  | ||||
| 	JsonNode guards; | ||||
| 	bool bannedForRandomDwelling = false; | ||||
|  | ||||
| protected: | ||||
| 	bool objectFilter(const CGObjectInstance * obj, std::shared_ptr<const ObjectTemplate> tmpl) const override; | ||||
| @@ -34,6 +35,7 @@ public: | ||||
| 	void initializeObject(CGDwelling * object) const override; | ||||
| 	void randomizeObject(CGDwelling * object, CRandomGenerator & rng) const override; | ||||
|  | ||||
| 	bool isBannedForRandomDwelling() const; | ||||
| 	bool producesCreature(const CCreature * crea) const; | ||||
| 	std::vector<const CCreature *> getProducedCreatures() const; | ||||
| }; | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
| #include "../CConfigHandler.h" | ||||
| #include "../GameSettings.h" | ||||
| #include "../IGameCallback.h" | ||||
| #include "../mapObjectConstructors/CObjectClassesHandler.h" | ||||
| #include "../networkPacks/PacksForClient.h" | ||||
| #include "../networkPacks/PacksForClientBattle.h" | ||||
| #include "../networkPacks/StackLocation.h" | ||||
| @@ -217,6 +218,18 @@ void CGCreature::pickRandomObject(CRandomGenerator & rand) | ||||
| 			subID = VLC->creh->pickRandomMonster(rand, 7); | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	try { | ||||
| 		// sanity check | ||||
| 		VLC->objtypeh->getHandlerFor(ID, subID); | ||||
| 	} | ||||
| 	catch (const std::out_of_range & ) | ||||
| 	{ | ||||
| 		// Try to generate some debug information if sanity check failed | ||||
| 		CreatureID creatureID(subID.getNum()); | ||||
| 		throw std::out_of_range("Failed to find handler for creature " + std::to_string(creatureID.getNum()) + ", identifer:" + creatureID.toEntity(VLC)->getJsonKey()); | ||||
| 	} | ||||
|  | ||||
| 	ID = MapObjectID::MONSTER; | ||||
| 	setType(ID, subID); | ||||
| } | ||||
|   | ||||
| @@ -146,7 +146,7 @@ void CGDwelling::pickRandomObject(CRandomGenerator & rand) | ||||
| 			{ | ||||
| 				const auto * handler = dynamic_cast<const DwellingInstanceConstructor *>(VLC->objtypeh->getHandlerFor(primaryID, entry).get()); | ||||
|  | ||||
| 				if (handler->producesCreature(cid.toCreature())) | ||||
| 				if (!handler->isBannedForRandomDwelling() && handler->producesCreature(cid.toCreature())) | ||||
| 					return MapObjectSubID(entry); | ||||
| 			} | ||||
| 			return MapObjectSubID(); | ||||
|   | ||||
| @@ -133,11 +133,7 @@ void CGObjectInstance::setType(MapObjectID newID, MapObjectSubID newSubID) | ||||
| 	//recalculate blockvis tiles - new appearance might have different blockmap than before | ||||
| 	cb->gameState()->map->removeBlockVisTiles(this, true); | ||||
| 	auto handler = VLC->objtypeh->getHandlerFor(newID, newSubID); | ||||
| 	if(!handler) | ||||
| 	{ | ||||
| 		logGlobal->error("Unknown object type %d:%d at %s", newID, newSubID, visitablePos().toString()); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if(!handler->getTemplates(tile.terType->getId()).empty()) | ||||
| 	{ | ||||
| 		appearance = handler->getTemplates(tile.terType->getId())[0]; | ||||
|   | ||||
| @@ -49,7 +49,7 @@ CQuest::CQuest(): | ||||
| 	isCustomNext(false), | ||||
| 	isCustomComplete(false), | ||||
| 	repeatedQuest(false), | ||||
| 	questName(CQuest::missionName(0)) | ||||
| 	questName(CQuest::missionName(EQuestMission::NONE)) | ||||
| { | ||||
| } | ||||
|  | ||||
| @@ -59,9 +59,9 @@ static std::string visitedTxt(const bool visited) | ||||
| 	return VLC->generaltexth->allTexts[id]; | ||||
| } | ||||
|  | ||||
| const std::string & CQuest::missionName(int mission) | ||||
| const std::string & CQuest::missionName(EQuestMission mission) | ||||
| { | ||||
| 	static const std::array<std::string, 13> names = { | ||||
| 	static const std::array<std::string, 14> names = { | ||||
| 		"empty", | ||||
| 		"heroLevel", | ||||
| 		"primarySkill", | ||||
| @@ -72,9 +72,10 @@ const std::string & CQuest::missionName(int mission) | ||||
| 		"bringResources", | ||||
| 		"bringHero", | ||||
| 		"bringPlayer", | ||||
| 		"hotaINVALID", // only used for h3m parsing | ||||
| 		"keymaster", | ||||
| 		"hota", | ||||
| 		"other" | ||||
| 		"heroClass", | ||||
| 		"reachDate" | ||||
| 	}; | ||||
|  | ||||
| 	if(static_cast<size_t>(mission) < names.size()) | ||||
| @@ -307,20 +308,18 @@ void CQuest::getCompletionText(IGameCallback * cb, MetaString &iwText) const | ||||
| void CQuest::defineQuestName() | ||||
| { | ||||
| 	//standard quests | ||||
| 	questName = CQuest::missionName(0); | ||||
| 	if(mission != Rewardable::Limiter{}) questName = CQuest::missionName(12); | ||||
| 	if(mission.heroLevel > 0) questName = CQuest::missionName(1); | ||||
| 	for(auto & s : mission.primary) if(s) questName = CQuest::missionName(2); | ||||
| 	if(!mission.spells.empty()) questName = CQuest::missionName(2); | ||||
| 	if(!mission.secondary.empty()) questName = CQuest::missionName(2); | ||||
| 	if(killTarget != ObjectInstanceID::NONE && !heroName.empty()) questName = CQuest::missionName(3); | ||||
| 	if(killTarget != ObjectInstanceID::NONE && stackToKill != CreatureID::NONE) questName = CQuest::missionName(4); | ||||
| 	if(!mission.artifacts.empty()) questName = CQuest::missionName(5); | ||||
| 	if(!mission.creatures.empty()) questName = CQuest::missionName(6); | ||||
| 	if(mission.resources.nonZero()) questName = CQuest::missionName(7); | ||||
| 	if(!mission.heroes.empty()) questName = CQuest::missionName(8); | ||||
| 	if(!mission.players.empty()) questName = CQuest::missionName(9); | ||||
| 	if(mission.daysPassed > 0 || !mission.heroClasses.empty()) questName = CQuest::missionName(11); | ||||
| 	questName = CQuest::missionName(EQuestMission::NONE); | ||||
| 	if(mission.heroLevel > 0) questName = CQuest::missionName(EQuestMission::LEVEL); | ||||
| 	for(auto & s : mission.primary) if(s) questName = CQuest::missionName(EQuestMission::PRIMARY_SKILL); | ||||
| 	if(killTarget != ObjectInstanceID::NONE && !heroName.empty()) questName = CQuest::missionName(EQuestMission::KILL_HERO); | ||||
| 	if(killTarget != ObjectInstanceID::NONE && stackToKill != CreatureID::NONE) questName = CQuest::missionName(EQuestMission::KILL_CREATURE); | ||||
| 	if(!mission.artifacts.empty()) questName = CQuest::missionName(EQuestMission::ARTIFACT); | ||||
| 	if(!mission.creatures.empty()) questName = CQuest::missionName(EQuestMission::ARMY); | ||||
| 	if(mission.resources.nonZero()) questName = CQuest::missionName(EQuestMission::RESOURCES); | ||||
| 	if(!mission.heroes.empty()) questName = CQuest::missionName(EQuestMission::HERO); | ||||
| 	if(!mission.players.empty()) questName = CQuest::missionName(EQuestMission::PLAYER); | ||||
| 	if(mission.daysPassed > 0) questName = CQuest::missionName(EQuestMission::HOTA_REACH_DATE); | ||||
| 	if(!mission.heroClasses.empty()) questName = CQuest::missionName(EQuestMission::HOTA_HERO_CLASS); | ||||
| } | ||||
|  | ||||
| void CQuest::addKillTargetReplacements(MetaString &out) const | ||||
| @@ -466,7 +465,7 @@ void CGSeerHut::initObj(CRandomGenerator & rand) | ||||
| 	if(quest->mission == Rewardable::Limiter{} && quest->killTarget == ObjectInstanceID::NONE) | ||||
| 		quest->isCompleted = true; | ||||
| 	 | ||||
| 	if(quest->questName == quest->missionName(0)) | ||||
| 	if(quest->questName == quest->missionName(EQuestMission::NONE)) | ||||
| 	{ | ||||
| 		quest->firstVisitText.appendTextID(TextIdentifier("core", "seehut", "empty", quest->completedOption).get()); | ||||
| 	} | ||||
|   | ||||
| @@ -17,11 +17,30 @@ VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class CGCreature; | ||||
|  | ||||
| enum class EQuestMission { | ||||
| 	NONE = 0, | ||||
| 	LEVEL = 1, | ||||
| 	PRIMARY_SKILL = 2, | ||||
| 	KILL_HERO = 3, | ||||
| 	KILL_CREATURE = 4, | ||||
| 	ARTIFACT = 5, | ||||
| 	ARMY = 6, | ||||
| 	RESOURCES = 7, | ||||
| 	HERO = 8, | ||||
| 	PLAYER = 9, | ||||
| 	HOTA_MULTI = 10, | ||||
| 	// end of H3 missions | ||||
|  | ||||
| 	KEYMASTER = 11, | ||||
| 	HOTA_HERO_CLASS = 12, | ||||
| 	HOTA_REACH_DATE = 13, | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CQuest final | ||||
| { | ||||
| public: | ||||
|  | ||||
| 	static const std::string & missionName(int index); | ||||
| 	static const std::string & missionName(EQuestMission index); | ||||
| 	static const std::string & missionState(int index); | ||||
| 	 | ||||
| 	std::string questName; | ||||
|   | ||||
| @@ -1257,7 +1257,7 @@ void CGObelisk::setPropertyDer(ObjProperty what, ObjPropertyID identifier) | ||||
| 				if(progress > cb->gameState()->map->obeliskCount) | ||||
| 				{ | ||||
| 					logGlobal->error("Visited %d of %d", static_cast<int>(progress), cb->gameState()->map->obeliskCount); | ||||
| 					throw std::runtime_error("Player visited more obelisks than present on map!"); | ||||
| 					throw std::runtime_error("Player visited " + std::to_string(progress) + " obelisks out of " + std::to_string(cb->gameState()->map->obeliskCount) + " present on map!"); | ||||
| 				} | ||||
|  | ||||
| 				break; | ||||
|   | ||||
| @@ -35,23 +35,7 @@ class SpellID; | ||||
| class PlayerColor; | ||||
| class int3; | ||||
|  | ||||
| enum class EQuestMission { | ||||
| 	NONE = 0, | ||||
| 	LEVEL = 1, | ||||
| 	PRIMARY_SKILL = 2, | ||||
| 	KILL_HERO = 3, | ||||
| 	KILL_CREATURE = 4, | ||||
| 	ARTIFACT = 5, | ||||
| 	ARMY = 6, | ||||
| 	RESOURCES = 7, | ||||
| 	HERO = 8, | ||||
| 	PLAYER = 9, | ||||
| 	HOTA_MULTI = 10, | ||||
| 	// end of H3 missions | ||||
| 	KEYMASTER = 100, | ||||
| 	HOTA_HERO_CLASS = 101, | ||||
| 	HOTA_REACH_DATE = 102 | ||||
| }; | ||||
| enum class EQuestMission; | ||||
|  | ||||
| enum class EVictoryConditionType : int8_t | ||||
| { | ||||
|   | ||||
| @@ -760,7 +760,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta | ||||
|  | ||||
| 					//TODO we need find batter way to handle double-wide stacks | ||||
| 					//currently if only second occupied stack part is standing on gate / bridge hex then stack will start to wait for bridge to lower before it's needed. Though this is just a visual bug. | ||||
| 					if (curStack->doubleWide()) | ||||
| 					if (curStack->doubleWide() && i + 2 < path.first.size()) | ||||
| 					{ | ||||
| 						BattleHex otherHex = curStack->occupiedHex(hex); | ||||
| 						if (otherHex.isValid() && needOpenGates(otherHex)) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user