mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Merge commit '3c844002626f48007a888d4dd8b0b45ec867fc9a' into feature/VCMIMapFormat1
Conflicts: lib/mapObjects/CGHeroInstance.cpp
This commit is contained in:
		| @@ -1017,7 +1017,7 @@ void CBattleInterface::stackRemoved(int stackID) | ||||
| 			setActiveStack(nullptr); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	delete creAnims[stackID]; | ||||
| 	creAnims.erase(stackID); | ||||
| 	creDir.erase(stackID); | ||||
| @@ -1201,18 +1201,18 @@ void CBattleInterface::stackIsCatapulting(const CatapultAttack & ca) | ||||
| 		for(auto attackInfo : ca.attackedParts) | ||||
| 		{ | ||||
| 			addNewAnim(new CShootingAnimation(this, stack, attackInfo.destinationTile, nullptr, true, attackInfo.damageDealt)); | ||||
| 		}		 | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		//no attacker stack, assume spell-related (earthquake) - only hit animation  | ||||
| 		//no attacker stack, assume spell-related (earthquake) - only hit animation | ||||
| 		for(auto attackInfo : ca.attackedParts) | ||||
| 		{ | ||||
| 			Point destPos = CClickableHex::getXYUnitAnim(attackInfo.destinationTile, nullptr, this) + Point(99, 120); | ||||
| 	 | ||||
|  | ||||
| 			addNewAnim(new CSpellEffectAnimation(this, "SGEXPL.DEF", destPos.x, destPos.y)); | ||||
| 		} | ||||
| 	}	 | ||||
| 	} | ||||
|  | ||||
| 	waitForAnims(); | ||||
|  | ||||
| @@ -1323,9 +1323,9 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc ) | ||||
|  | ||||
| 	//displaying message in console | ||||
| 	std::vector<std::string> logLines; | ||||
| 	 | ||||
|  | ||||
| 	spell.prepareBattleLog(curInt->cb.get(), sc, logLines); | ||||
| 	 | ||||
|  | ||||
| 	for(auto line : logLines) | ||||
| 		console->addText(line); | ||||
|  | ||||
| @@ -1427,15 +1427,15 @@ void CBattleInterface::displayEffect(ui32 effect, int destTile, bool areaEffect) | ||||
| } | ||||
|  | ||||
| void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile, bool areaEffect) | ||||
| {	 | ||||
| { | ||||
| 	if(animation.pause > 0) | ||||
| 	{ | ||||
| 		addNewAnim(new CDummyAnimation(this, animation.pause));	 | ||||
| 		addNewAnim(new CDummyAnimation(this, animation.pause)); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));	 | ||||
| 	}	 | ||||
| 		addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTile, bool areaEffect) | ||||
| @@ -1444,11 +1444,11 @@ void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTi | ||||
|  | ||||
| 	if(spell == nullptr) | ||||
| 		return; | ||||
| 		 | ||||
|  | ||||
| 	for(const CSpell::TAnimation & animation : spell->animationInfo.cast) | ||||
| 	{ | ||||
| 		displaySpellAnimation(animation, destinationTile, areaEffect); | ||||
| 	}	 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect) | ||||
| @@ -1933,7 +1933,7 @@ void CBattleInterface::bTacticNextStack(const CStack *current /*= nullptr*/) | ||||
| 	waitForAnims(); | ||||
|  | ||||
| 	TStacks stacksOfMine = tacticianInterface->cb->battleGetStacks(CBattleCallback::ONLY_MINE); | ||||
| 	stacksOfMine.erase(std::remove_if(stacksOfMine.begin(), stacksOfMine.end(), &immobile), stacksOfMine.end()); | ||||
| 	vstd::erase_if(stacksOfMine, &immobile); | ||||
| 	auto it = vstd::find(stacksOfMine, current); | ||||
| 	if(it != stacksOfMine.end() && ++it != stacksOfMine.end()) | ||||
| 		stackActivated(*it); | ||||
| @@ -2072,7 +2072,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) | ||||
| 			case ANY_CREATURE: | ||||
| 				if (shere && shere->alive() && isCastingPossibleHere (sactive, shere, myNumber)) | ||||
| 					legalAction = true; | ||||
| 				break;				 | ||||
| 				break; | ||||
| 			case HOSTILE_CREATURE_SPELL: | ||||
| 				if (shere && shere->alive() && !ourStack && isCastingPossibleHere (sactive, shere, myNumber)) | ||||
| 					legalAction = true; | ||||
|   | ||||
| @@ -32,7 +32,7 @@ | ||||
|  */ | ||||
|  | ||||
| CArtPlace::CArtPlace(Point position, const CArtifactInstance * Art): | ||||
|     locked(false), picked(false), marked(false), ourArt(Art) | ||||
| 	locked(false), picked(false), marked(false), ourArt(Art) | ||||
| { | ||||
| 	pos += position; | ||||
| 	pos.w = pos.h = 44; | ||||
| @@ -180,7 +180,7 @@ void CArtPlace::clickLeft(tribool down, bool previousState) | ||||
| 						if(srcInBackpack && srcInSameHero) | ||||
| 						{ | ||||
| 							if(!ourArt								//cannot move from backpack to AFTER backpack -> combined with vstd::amin above it will guarantee that dest is at most the last artifact | ||||
| 							  || ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted | ||||
| 								|| ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted | ||||
| 								vstd::advance(ourOwner->commonInfo->dst.slotID, -1); | ||||
| 						} | ||||
| 						if(srcInSameHero && ourOwner->commonInfo->dst.slotID == ourOwner->commonInfo->src.slotID) //we came to src == dst | ||||
| @@ -386,70 +386,36 @@ void CArtPlace::setArtifact(const CArtifactInstance *art) | ||||
| 		image->disable(); | ||||
| 		text = std::string(); | ||||
| 		hoverText = CGI->generaltexth->allTexts[507]; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	image->enable(); | ||||
| 	image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex); | ||||
|  | ||||
| 	text = art->getEffectiveDescription(ourOwner->curHero); | ||||
|  | ||||
| 	if(art->artType->id == ArtifactID::SPELL_SCROLL) | ||||
| 	{ | ||||
| 		int spellID = art->getGivenSpellID(); | ||||
| 		if(spellID >= 0) | ||||
| 		{ | ||||
| 			//add spell component info (used to provide a pic in r-click popup) | ||||
| 			baseType = CComponent::spell; | ||||
| 			type = spellID; | ||||
| 			bonusValue = 0; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		image->enable(); | ||||
| 		image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex); | ||||
|  | ||||
| 		std::string artDesc = ourArt->artType->Description(); | ||||
| 		if (vstd::contains (artDesc, '{')) | ||||
| 			text = artDesc; | ||||
| 		else | ||||
| 			text = '{' + ourArt->artType->Name() + "}\n\n" + artDesc; //workaround for new artifacts with single name, turns it to H3-style | ||||
|  | ||||
| 		if(art->artType->id == ArtifactID::SPELL_SCROLL) | ||||
| 		{ | ||||
| 			// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll. | ||||
| 			// so we want to replace text in [...] with a spell name | ||||
| 			// however other language versions don't have name placeholder at all, so we have to be careful | ||||
| 			int spellID = art->getGivenSpellID(); | ||||
| 			size_t nameStart = text.find_first_of('['); | ||||
| 			size_t nameEnd = text.find_first_of(']', nameStart); | ||||
| 			if(spellID >= 0) | ||||
| 			{ | ||||
| 				if(nameStart != std::string::npos  &&  nameEnd != std::string::npos) | ||||
| 					text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->objects[spellID]->name); | ||||
|  | ||||
| 				//add spell component info (used to provide a pic in r-click popup) | ||||
| 				baseType = CComponent::spell; | ||||
| 				type = spellID; | ||||
| 				bonusValue = 0; | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			baseType = CComponent::artifact; | ||||
| 			type = art->artType->id; | ||||
| 			bonusValue = 0; | ||||
| 		} | ||||
| 		if (art->artType->constituents) //display info about components of combined artifact | ||||
| 		{ | ||||
| 			//TODO | ||||
| 		} | ||||
| 		else if (art->artType->constituentOf.size()) //display info about set | ||||
| 		{ | ||||
| 			std::string artList; | ||||
| 			auto combinedArt = art->artType->constituentOf[0]; | ||||
| 			text += "\n\n"; | ||||
| 			text += "{" + combinedArt->Name() + "}"; | ||||
| 			int wornArtifacts = 0; | ||||
| 			for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set? | ||||
| 			{ | ||||
| 				artList += "\n" + a->Name(); | ||||
| 				if (ourOwner->curHero->hasArt(a->id, true)) | ||||
| 					wornArtifacts++; | ||||
| 			} | ||||
| 			text += " (" + boost::str(boost::format("%d") % wornArtifacts) +  " / " + | ||||
| 				boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList; | ||||
| 			//TODO: fancy colors and fonts for this text | ||||
| 		} | ||||
|  | ||||
| 		if (locked) // Locks should appear as empty. | ||||
| 			hoverText = CGI->generaltexth->allTexts[507]; | ||||
| 		else | ||||
| 			hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name()); | ||||
| 		baseType = CComponent::artifact; | ||||
| 		type = art->artType->id; | ||||
| 		bonusValue = 0; | ||||
| 	} | ||||
|  | ||||
| 	if (locked) // Locks should appear as empty. | ||||
| 		hoverText = CGI->generaltexth->allTexts[507]; | ||||
| 	else | ||||
| 		hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name()); | ||||
| } | ||||
|  | ||||
| void CArtifactsOfHero::SCommonPart::reset() | ||||
| @@ -811,7 +777,7 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact | ||||
| 	} | ||||
| 	else if(src.slot >= GameConstants::BACKPACK_START && | ||||
| 	        src.slot <  commonInfo->src.slotID && | ||||
| 			src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one | ||||
| 			    src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one | ||||
| 	{ | ||||
| 		//int fixedSlot = src.hero->getArtPos(commonInfo->src.art); | ||||
| 		vstd::advance(commonInfo->src.slotID, -1); | ||||
| @@ -825,14 +791,14 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact | ||||
| 	} | ||||
|  | ||||
| 	updateParentWindow(); | ||||
|  	int shift = 0; | ||||
| 	int shift = 0; | ||||
| // 	if(dst.slot >= Arts::BACKPACK_START && dst.slot - Arts::BACKPACK_START < backpackPos) | ||||
| // 		shift++; | ||||
| // | ||||
|  	if(src.slot < GameConstants::BACKPACK_START  &&  dst.slot - GameConstants::BACKPACK_START < backpackPos) | ||||
| 	if(src.slot < GameConstants::BACKPACK_START  &&  dst.slot - GameConstants::BACKPACK_START < backpackPos) | ||||
| 		shift++; | ||||
| 	if(dst.slot < GameConstants::BACKPACK_START  &&  src.slot - GameConstants::BACKPACK_START < backpackPos) | ||||
|  		shift--; | ||||
| 		shift--; | ||||
|  | ||||
| 	if( (isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START) | ||||
| 	 || (isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) ) | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
| #include "../CMessage.h" | ||||
| #include "../CGameInfo.h" | ||||
| #include "../widgets/Images.h" | ||||
| #include "../widgets/CArtifactHolder.h" | ||||
| #include "../windows/CAdvmapInterface.h" | ||||
|  | ||||
| #include "../../lib/CArtHandler.h" | ||||
| @@ -144,14 +145,26 @@ size_t CComponent::getIndex() | ||||
|  | ||||
| std::string CComponent::getDescription() | ||||
| { | ||||
| 	switch (compType) | ||||
| 	switch(compType) | ||||
| 	{ | ||||
| 	case primskill:  return (subtype < 4)? CGI->generaltexth->arraytxt[2+subtype] //Primary skill | ||||
| 										 : CGI->generaltexth->allTexts[149]; //mana | ||||
| 	case secskill:   return CGI->generaltexth->skillInfoTexts[subtype][val-1]; | ||||
| 	case resource:   return CGI->generaltexth->allTexts[242]; | ||||
| 	case creature:   return ""; | ||||
| 	case artifact:   return CGI->arth->artifacts[subtype]->Description(); | ||||
| 	case artifact: | ||||
| 	{ | ||||
| 		std::unique_ptr<CArtifactInstance> art; | ||||
| 		if (subtype != ArtifactID::SPELL_SCROLL) | ||||
| 		{ | ||||
| 			art.reset(CArtifactInstance::createNewArtifactInstance(subtype)); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			art.reset(CArtifactInstance::createScroll(static_cast<SpellID>(val))); | ||||
| 		} | ||||
| 		return art->getEffectiveDescription(); | ||||
| 	} | ||||
| 	case experience: return CGI->generaltexth->allTexts[241]; | ||||
| 	case spell:      return CGI->spellh->objects[subtype]->getLevelInfo(val).description; | ||||
| 	case morale:     return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)]; | ||||
| @@ -166,7 +179,7 @@ std::string CComponent::getDescription() | ||||
|  | ||||
| std::string CComponent::getSubtitle() | ||||
| { | ||||
| 	if (!perDay) | ||||
| 	if(!perDay) | ||||
| 		return getSubtitleInternal(); | ||||
|  | ||||
| 	std::string ret = CGI->generaltexth->allTexts[3]; | ||||
|   | ||||
| @@ -28,6 +28,7 @@ | ||||
| #include "../lib/CHeroHandler.h" | ||||
| #include "../lib/mapObjects/CGHeroInstance.h" | ||||
| #include "../lib/NetPacksBase.h" | ||||
| #include "../mapHandler.h" | ||||
|  | ||||
| /* | ||||
|  * CHeroWindow.cpp, part of VCMI engine | ||||
| @@ -275,6 +276,9 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals | ||||
| 	if(!LOCPLINT->cb->howManyTowns() && LOCPLINT->cb->howManyHeroes() == 1) | ||||
| 		noDismiss = true; | ||||
|  | ||||
| 	if(curHero->isMissionCritical()) | ||||
| 		noDismiss = true; | ||||
|  | ||||
| 	dismissButton->block(!!curHero->visitedTown || noDismiss); | ||||
|  | ||||
| 	if(curHero->getSecSkillLevel(SecondarySkill::TACTICS) == 0) | ||||
| @@ -343,10 +347,10 @@ void CHeroWindow::commanderWindow() | ||||
| void CHeroWindow::showAll(SDL_Surface * to) | ||||
| { | ||||
| 	CIntObject::showAll(to); | ||||
| 	  | ||||
|  | ||||
| 	//printing hero's name | ||||
| 	printAtMiddleLoc(curHero->name, 190, 38, FONT_BIG, Colors::YELLOW, to); | ||||
| 	  | ||||
|  | ||||
| 	//printing hero's level | ||||
| 	std::string secondLine= CGI->generaltexth->allTexts[342]; | ||||
| 	boost::algorithm::replace_first(secondLine,"%d",boost::lexical_cast<std::string>(curHero->level)); | ||||
| @@ -360,14 +364,14 @@ void CHeroWindow::showAll(SDL_Surface * to) | ||||
| 	 	primarySkill << primSkillAreas[m]->bonusValue; | ||||
| 	 	printAtMiddleLoc(primarySkill.str(), 53 + 70 * m, 166, FONT_SMALL, Colors::WHITE, to); | ||||
| 	} | ||||
| 	  | ||||
|  | ||||
| 	//secondary skills | ||||
| 	for(size_t v=0; v<std::min(secSkillAreas.size(), curHero->secSkills.size()); ++v) | ||||
| 	{ | ||||
| 	 	printAtLoc(CGI->generaltexth->levels[curHero->secSkills[v].second-1], (v%2) ? 212 : 68, 280 + 48 * (v/2), FONT_SMALL, Colors::WHITE, to); | ||||
| 	 	printAtLoc(CGI->generaltexth->skillName[curHero->secSkills[v].first], (v%2) ? 212 : 68, 300 + 48 * (v/2), FONT_SMALL, Colors::WHITE, to); | ||||
| 	} | ||||
| 	  | ||||
|  | ||||
| 	//printing special ability | ||||
| 	printAtLoc(curHero->type->specName, 69, 205, FONT_SMALL, Colors::WHITE, to); | ||||
| 	std::ostringstream expstr; | ||||
|   | ||||
| @@ -269,7 +269,7 @@ void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState) | ||||
| 		case ARTIFACT_TYPE: | ||||
| 		case ARTIFACT_PLACEHOLDER: | ||||
| 			if(id >= 0) | ||||
| 				adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down); | ||||
| 				adventureInt->handleRightClick(hlp->getEffectiveDescription(), down); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| @@ -500,14 +500,14 @@ void CTradeWindow::getPositionsFor(std::vector<Rect> &poss, bool Left, EType typ | ||||
| 		int h, w, x, y, dx, dy; | ||||
| 		int leftToRightOffset; | ||||
| 		getBaseForPositions(type, dx, dy, x, y, h, w, !Left, leftToRightOffset); | ||||
| 		 | ||||
| 		const std::vector<Rect> tmp =  | ||||
|  | ||||
| 		const std::vector<Rect> tmp = | ||||
| 		{ | ||||
| 			genRect(h, w, x, y), genRect(h, w, x + dx, y), genRect(h, w, x + 2*dx, y), | ||||
| 			genRect(h, w, x, y + dy), genRect(h, w, x + dx, y + dy), genRect(h, w, x + 2*dx, y + dy), | ||||
| 			genRect(h, w, x + dx, y + 2*dy)			 | ||||
| 			genRect(h, w, x + dx, y + 2*dy) | ||||
| 		}; | ||||
| 		 | ||||
|  | ||||
| 		vstd::concatenate(poss, tmp); | ||||
|  | ||||
| 		if(!Left) | ||||
|   | ||||
| @@ -23,7 +23,8 @@ | ||||
| 		"ALL_CREATURES_GET_DOUBLE_MONTHS" : false, | ||||
| 		"NEGATIVE_LUCK" : false, | ||||
| 		"MAX_HEROES_AVAILABLE_PER_PLAYER" : 16, | ||||
| 		"MAX_HEROES_ON_MAP_PER_PLAYER" : 8	 | ||||
| 		"MAX_HEROES_ON_MAP_PER_PLAYER" : 8, | ||||
| 		"WINNING_HERO_WITH_NO_TROOPS_RETREATS": true | ||||
|  | ||||
| 	}, | ||||
| 	"modules": | ||||
|   | ||||
| @@ -764,9 +764,14 @@ std::string CArtifactInstance::nodeName() const | ||||
| } | ||||
|  | ||||
| CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s) | ||||
| { | ||||
| 	return createScroll(s->id); | ||||
| } | ||||
|  | ||||
| CArtifactInstance *CArtifactInstance::createScroll(SpellID sid) | ||||
| { | ||||
| 	auto ret = new CArtifactInstance(VLC->arth->artifacts[ArtifactID::SPELL_SCROLL]); | ||||
| 	auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, s->id); | ||||
| 	auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, sid); | ||||
| 	ret->addNewBonus(b); | ||||
| 	return ret; | ||||
| } | ||||
| @@ -778,6 +783,48 @@ void CArtifactInstance::init() | ||||
| 	setNodeType(ARTIFACT_INSTANCE); | ||||
| } | ||||
|  | ||||
| std::string CArtifactInstance::getEffectiveDescription( | ||||
| 	const CGHeroInstance *hero) const | ||||
| { | ||||
| 	std::string text = artType->Description(); | ||||
| 	if (!vstd::contains(text, '{')) | ||||
| 		text = '{' + artType->Name() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style | ||||
|  | ||||
| 	if(artType->id == ArtifactID::SPELL_SCROLL) | ||||
| 	{ | ||||
| 		// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll. | ||||
| 		// so we want to replace text in [...] with a spell name | ||||
| 		// however other language versions don't have name placeholder at all, so we have to be careful | ||||
| 		int spellID = getGivenSpellID(); | ||||
| 		size_t nameStart = text.find_first_of('['); | ||||
| 		size_t nameEnd = text.find_first_of(']', nameStart); | ||||
| 		if(spellID >= 0) | ||||
| 		{ | ||||
| 			if(nameStart != std::string::npos  &&  nameEnd != std::string::npos) | ||||
| 				text = text.replace(nameStart, nameEnd - nameStart + 1, VLC->spellh->objects[spellID]->name); | ||||
| 		} | ||||
| 	} | ||||
| 	else if (hero && artType->constituentOf.size()) //display info about set | ||||
| 	{ | ||||
| 		std::string artList; | ||||
| 		auto combinedArt = artType->constituentOf[0]; | ||||
| 		text += "\n\n"; | ||||
| 		text += "{" + combinedArt->Name() + "}"; | ||||
| 		int wornArtifacts = 0; | ||||
| 		for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set? | ||||
| 		{ | ||||
| 			artList += "\n" + a->Name(); | ||||
| 			if (hero->hasArt(a->id, true)) | ||||
| 				wornArtifacts++; | ||||
| 		} | ||||
| 		text += " (" + boost::str(boost::format("%d") % wornArtifacts) +  " / " + | ||||
| 			boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList; | ||||
| 		//TODO: fancy colors and fonts for this text | ||||
| 	} | ||||
|  | ||||
| 	return text; | ||||
| } | ||||
|  | ||||
| ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const | ||||
| { | ||||
| 	for(auto slot : artType->possibleSlots.at(h->bearerType())) | ||||
| @@ -926,7 +973,7 @@ SpellID CArtifactInstance::getGivenSpellID() const | ||||
| 	const Bonus * b = getBonusLocalFirst(Selector::type(Bonus::SPELL)); | ||||
| 	if(!b) | ||||
| 	{ | ||||
|         logGlobal->warnStream() << "Warning: " << nodeName() << " doesn't bear any spell!"; | ||||
| 		logGlobal->warnStream() << "Warning: " << nodeName() << " doesn't bear any spell!"; | ||||
| 		return SpellID::NONE; | ||||
| 	} | ||||
| 	return SpellID(b->subtype); | ||||
|   | ||||
| @@ -124,6 +124,7 @@ public: | ||||
| 	void deserializationFix(); | ||||
| 	void setType(CArtifact *Art); | ||||
|  | ||||
| 	std::string getEffectiveDescription(const CGHeroInstance *hero = nullptr) const; | ||||
| 	ArtifactPosition firstAvailableSlot(const CArtifactSet *h) const; | ||||
| 	ArtifactPosition firstBackpackSlot(const CArtifactSet *h) const; | ||||
| 	SpellID getGivenSpellID() const; //to be used with scrolls (and similar arts), -1 if none | ||||
| @@ -148,6 +149,7 @@ public: | ||||
| 	} | ||||
|  | ||||
| 	static CArtifactInstance *createScroll(const CSpell *s); | ||||
| 	static CArtifactInstance *createScroll(SpellID sid); | ||||
| 	static CArtifactInstance *createNewArtifactInstance(CArtifact *Art); | ||||
| 	static CArtifactInstance *createNewArtifactInstance(int aid); | ||||
| }; | ||||
|   | ||||
| @@ -66,6 +66,10 @@ const PlayerState * CGameInfoCallback::getPlayer(PlayerColor color, bool verbose | ||||
| { | ||||
| 	//funtion written from scratch since it's accessed A LOT by AI | ||||
|  | ||||
| 	if(!color.isValidPlayer()) | ||||
| 	{ | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	auto player = gs->players.find(color); | ||||
| 	if (player != gs->players.end()) | ||||
| 	{ | ||||
| @@ -229,13 +233,13 @@ bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown | ||||
| 	{ | ||||
| 		if(!detailed && nullptr != selectedObject) | ||||
| 		{ | ||||
| 			const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);		 | ||||
| 			const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject); | ||||
| 			if(nullptr != selectedHero) | ||||
| 				detailed = selectedHero->hasVisions(town, 1);			 | ||||
| 				detailed = selectedHero->hasVisions(town, 1); | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		dest.initFromTown(static_cast<const CGTownInstance *>(town), detailed); | ||||
| 	}		 | ||||
| 	} | ||||
| 	else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2) | ||||
| 		dest.initFromArmy(static_cast<const CArmedInstance *>(town), detailed); | ||||
| 	else | ||||
| @@ -268,28 +272,28 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero | ||||
| 	ERROR_RET_VAL_IF(!isVisible(h->getPosition(false)), "That hero is not visible!", false); | ||||
|  | ||||
| 	bool accessFlag = hasAccess(h->tempOwner); | ||||
| 	 | ||||
|  | ||||
| 	if(!accessFlag && nullptr != selectedObject) | ||||
| 	{ | ||||
| 		const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);		 | ||||
| 		const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject); | ||||
| 		if(nullptr != selectedHero) | ||||
| 			accessFlag = selectedHero->hasVisions(hero, 1);			 | ||||
| 			accessFlag = selectedHero->hasVisions(hero, 1); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	dest.initFromHero(h, accessFlag); | ||||
| 	 | ||||
|  | ||||
| 	//DISGUISED bonus implementation | ||||
| 	 | ||||
|  | ||||
| 	if(getPlayerRelations(getLocalPlayer(), hero->tempOwner) == PlayerRelations::ENEMIES) | ||||
| 	{ | ||||
| 		//todo: bonus cashing	 | ||||
| 		//todo: bonus cashing | ||||
| 		int disguiseLevel = h->valOfBonuses(Selector::typeSubtype(Bonus::DISGUISED, 0)); | ||||
| 		 | ||||
| 		auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info)		 | ||||
|  | ||||
| 		auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info) | ||||
| 		{ | ||||
| 			int maxAIValue = 0; | ||||
| 			const CCreature * mostStrong = nullptr; | ||||
| 			 | ||||
|  | ||||
| 			for(auto & elem : info.army) | ||||
| 			{ | ||||
| 				if(elem.second.type->AIValue > maxAIValue) | ||||
| @@ -298,7 +302,7 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero | ||||
| 					mostStrong = elem.second.type; | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			if(nullptr == mostStrong)//just in case | ||||
| 				logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Unable to select most strong stack" << disguiseLevel; | ||||
| 			else | ||||
| @@ -307,25 +311,25 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero | ||||
| 					elem.second.type = mostStrong; | ||||
| 				} | ||||
| 		}; | ||||
| 		 | ||||
| 		auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info)		 | ||||
|  | ||||
| 		auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info) | ||||
| 		{ | ||||
| 			doBasicDisguise(info); | ||||
| 			 | ||||
|  | ||||
| 			for(auto & elem : info.army) | ||||
| 				elem.second.count = 0; | ||||
| 		}; | ||||
| 		 | ||||
| 		auto doExpertDisguise = [this,h](InfoAboutHero & info)		 | ||||
|  | ||||
| 		auto doExpertDisguise = [this,h](InfoAboutHero & info) | ||||
| 		{ | ||||
| 			for(auto & elem : info.army) | ||||
| 				elem.second.count = 0; | ||||
| 			 | ||||
|  | ||||
| 			const auto factionIndex = getStartInfo(false)->playerInfos.at(h->tempOwner).castle; | ||||
| 			 | ||||
|  | ||||
| 			int maxAIValue = 0; | ||||
| 			const CCreature * mostStrong = nullptr; | ||||
| 			 | ||||
|  | ||||
| 			for(auto creature : VLC->creh->creatures) | ||||
| 			{ | ||||
| 				if(creature->faction == factionIndex && creature->AIValue > maxAIValue) | ||||
| @@ -334,35 +338,35 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero | ||||
| 					mostStrong = creature; | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
|  | ||||
| 			if(nullptr != mostStrong) //possible, faction may have no creatures at all | ||||
| 				for(auto & elem : info.army) | ||||
| 					elem.second.type = mostStrong; | ||||
| 		};				 | ||||
| 		 | ||||
| 		 | ||||
| 		}; | ||||
|  | ||||
|  | ||||
| 		switch (disguiseLevel) | ||||
| 		{ | ||||
| 		case 0: | ||||
| 			//no bonus at all - do nothing | ||||
| 			break;		 | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			doBasicDisguise(dest); | ||||
| 			break;		 | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			doAdvancedDisguise(dest); | ||||
| 			break;		 | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			doExpertDisguise(dest); | ||||
| 			break;		 | ||||
| 			break; | ||||
| 		default: | ||||
| 			//invalid value | ||||
| 			logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Invalid DISGUISED bonus value " << disguiseLevel; | ||||
| 			break; | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| @@ -486,7 +490,7 @@ std::shared_ptr<boost::multi_array<TerrainTile*, 3>> CGameInfoCallback::getAllVi | ||||
|  | ||||
|  | ||||
| 	boost::multi_array<TerrainTile*, 3> tileArray(boost::extents[width][height][levels]); | ||||
| 	 | ||||
|  | ||||
| 	for (size_t x = 0; x < width; x++) | ||||
| 		for (size_t y = 0; y < height; y++) | ||||
| 			for (size_t z = 0; z < levels; z++) | ||||
| @@ -964,4 +968,3 @@ void IGameEventRealizer::setObjProperty(ObjectInstanceID objid, int prop, si64 v | ||||
| 	sob.val = static_cast<ui32>(val); | ||||
| 	commitPackage(&sob); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1219,8 +1219,8 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar | ||||
| 				// remove heroes which didn't reached the end of the scenario, but were available at the start | ||||
| 				for(auto hero : lostCrossoverHeroes) | ||||
| 				{ | ||||
| 					crossoverHeroes.heroesFromAnyPreviousScenarios.erase(range::remove_if(crossoverHeroes.heroesFromAnyPreviousScenarios, | ||||
| 						CGObjectInstanceBySubIdFinder(hero)), crossoverHeroes.heroesFromAnyPreviousScenarios.end()); | ||||
| 					vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios, | ||||
| 					               CGObjectInstanceBySubIdFinder(hero)); | ||||
| 				} | ||||
|  | ||||
| 				// now add heroes which completed the scenario | ||||
| @@ -2268,7 +2268,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c | ||||
|  | ||||
| 	for (const TriggeredEvent & event : map->triggeredEvents) | ||||
| 	{ | ||||
| 		if ((event.trigger.test(evaluateEvent))) | ||||
| 		if (event.trigger.test(evaluateEvent)) | ||||
| 		{ | ||||
| 			if (event.effect.type == EventEffect::VICTORY) | ||||
| 				return EVictoryLossCheckResult::victory(event.onFulfill, event.effect.toOtherMessage); | ||||
| @@ -2285,7 +2285,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c | ||||
| 	return EVictoryLossCheckResult(); | ||||
| } | ||||
|  | ||||
| bool CGameState::checkForVictory( PlayerColor player, const EventCondition & condition ) const | ||||
| bool CGameState::checkForVictory(PlayerColor player, const EventCondition & condition) const | ||||
| { | ||||
| 	const PlayerState *p = CGameInfoCallback::getPlayer(player); | ||||
| 	switch (condition.condition) | ||||
|   | ||||
| @@ -551,21 +551,42 @@ CModHandler::CModHandler() | ||||
|  | ||||
| void CModHandler::loadConfigFromFile (std::string name) | ||||
| { | ||||
| 	std::string paths; | ||||
| 	for(auto& p : CResourceHandler::get()->getResourceNames(ResourceID("config/" + name))) | ||||
| 	{ | ||||
| 		paths += p + ", "; | ||||
| 	} | ||||
| 	paths = paths.substr(0, paths.size() - 2); | ||||
| 	logGlobal->debugStream() << "Loading hardcoded features settings from [" << paths << "], result:"; | ||||
| 	settings.data = JsonUtils::assembleFromFiles("config/" + name); | ||||
| 	const JsonNode & hardcodedFeatures = settings.data["hardcodedFeatures"]; | ||||
| 	settings.MAX_HEROES_AVAILABLE_PER_PLAYER = hardcodedFeatures["MAX_HEROES_AVAILABLE_PER_PLAYER"].Float(); | ||||
| 	logGlobal->debugStream() << "\tMAX_HEROES_AVAILABLE_PER_PLAYER\t" << settings.MAX_HEROES_AVAILABLE_PER_PLAYER; | ||||
| 	settings.MAX_HEROES_ON_MAP_PER_PLAYER = hardcodedFeatures["MAX_HEROES_ON_MAP_PER_PLAYER"].Float(); | ||||
| 	logGlobal->debugStream() << "\tMAX_HEROES_ON_MAP_PER_PLAYER\t" << settings.MAX_HEROES_ON_MAP_PER_PLAYER; | ||||
| 	settings.CREEP_SIZE = hardcodedFeatures["CREEP_SIZE"].Float(); | ||||
| 	logGlobal->debugStream() << "\tCREEP_SIZE\t" << settings.CREEP_SIZE; | ||||
| 	settings.WEEKLY_GROWTH = hardcodedFeatures["WEEKLY_GROWTH_PERCENT"].Float(); | ||||
| 	logGlobal->debugStream() << "\tWEEKLY_GROWTH\t" << settings.WEEKLY_GROWTH; | ||||
| 	settings.NEUTRAL_STACK_EXP = hardcodedFeatures["NEUTRAL_STACK_EXP_DAILY"].Float(); | ||||
| 	logGlobal->debugStream() << "\tNEUTRAL_STACK_EXP\t" << settings.NEUTRAL_STACK_EXP; | ||||
| 	settings.MAX_BUILDING_PER_TURN = hardcodedFeatures["MAX_BUILDING_PER_TURN"].Float(); | ||||
| 	logGlobal->debugStream() << "\tMAX_BUILDING_PER_TURN\t" << settings.MAX_BUILDING_PER_TURN; | ||||
| 	settings.DWELLINGS_ACCUMULATE_CREATURES = hardcodedFeatures["DWELLINGS_ACCUMULATE_CREATURES"].Bool(); | ||||
| 	logGlobal->debugStream() << "\tDWELLINGS_ACCUMULATE_CREATURES\t" << settings.DWELLINGS_ACCUMULATE_CREATURES; | ||||
| 	settings.ALL_CREATURES_GET_DOUBLE_MONTHS = hardcodedFeatures["ALL_CREATURES_GET_DOUBLE_MONTHS"].Bool(); | ||||
| 	logGlobal->debugStream() << "\tALL_CREATURES_GET_DOUBLE_MONTHS\t" << settings.ALL_CREATURES_GET_DOUBLE_MONTHS; | ||||
| 	settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS = hardcodedFeatures["WINNING_HERO_WITH_NO_TROOPS_RETREATS"].Bool(); | ||||
| 	logGlobal->debugStream() << "\tWINNING_HERO_WITH_NO_TROOPS_RETREATS\t" << settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS; | ||||
| 	const JsonNode & gameModules = settings.data["modules"]; | ||||
| 	modules.STACK_EXP = gameModules["STACK_EXPERIENCE"].Bool(); | ||||
|   logGlobal->debugStream() << "\tSTACK_EXP\t" << modules.STACK_EXP; | ||||
| 	modules.STACK_ARTIFACT = gameModules["STACK_ARTIFACTS"].Bool(); | ||||
| 	logGlobal->debugStream() << "\tSTACK_ARTIFACT\t" << modules.STACK_ARTIFACT; | ||||
| 	modules.COMMANDERS = gameModules["COMMANDERS"].Bool(); | ||||
| 	logGlobal->debugStream() << "\tCOMMANDERS\t" << modules.COMMANDERS; | ||||
| 	modules.MITHRIL = gameModules["MITHRIL"].Bool(); | ||||
| 	logGlobal->debugStream() << "\tMITHRIL\t" << modules.MITHRIL; | ||||
| } | ||||
|  | ||||
| // currentList is passed by value to get current list of depending mods | ||||
|   | ||||
| @@ -259,11 +259,21 @@ public: | ||||
| 		bool ALL_CREATURES_GET_DOUBLE_MONTHS; | ||||
| 		int MAX_HEROES_AVAILABLE_PER_PLAYER; | ||||
| 		int MAX_HEROES_ON_MAP_PER_PLAYER; | ||||
| 		bool WINNING_HERO_WITH_NO_TROOPS_RETREATS; | ||||
|  | ||||
| 		template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 		{ | ||||
| 			h & data & CREEP_SIZE & WEEKLY_GROWTH & NEUTRAL_STACK_EXP & MAX_BUILDING_PER_TURN; | ||||
| 			h & DWELLINGS_ACCUMULATE_CREATURES & ALL_CREATURES_GET_DOUBLE_MONTHS & MAX_HEROES_AVAILABLE_PER_PLAYER & MAX_HEROES_ON_MAP_PER_PLAYER; | ||||
| 			h & DWELLINGS_ACCUMULATE_CREATURES & ALL_CREATURES_GET_DOUBLE_MONTHS & | ||||
| 			MAX_HEROES_AVAILABLE_PER_PLAYER & MAX_HEROES_ON_MAP_PER_PLAYER; | ||||
| 			if(version >= 756) | ||||
| 			{ | ||||
| 				h & WINNING_HERO_WITH_NO_TROOPS_RETREATS; | ||||
| 			} | ||||
| 			else if(!h.saving) | ||||
| 			{ | ||||
| 				WINNING_HERO_WITH_NO_TROOPS_RETREATS = true; | ||||
| 			} | ||||
| 		} | ||||
| 	} settings; | ||||
|  | ||||
|   | ||||
| @@ -56,6 +56,15 @@ JsonNode::JsonNode(ResourceID && fileURI): | ||||
| 	*this = parser.parse(fileURI.getName()); | ||||
| } | ||||
|  | ||||
| JsonNode::JsonNode(const ResourceID & fileURI): | ||||
| 	type(DATA_NULL) | ||||
| { | ||||
| 	auto file = CResourceHandler::get()->load(fileURI)->readAll(); | ||||
|  | ||||
| 	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second); | ||||
| 	*this = parser.parse(fileURI.getName()); | ||||
| } | ||||
|  | ||||
| JsonNode::JsonNode(ResourceID && fileURI, bool &isValidSyntax): | ||||
| 	type(DATA_NULL) | ||||
| { | ||||
| @@ -328,7 +337,7 @@ void JsonUtils::parseTypedBonusShort(const JsonVector& source, Bonus *dest) | ||||
| 	resolveIdentifier(source[2],dest->subtype); | ||||
| 	dest->additionalInfo = source[3].Float(); | ||||
| 	dest->duration = Bonus::PERMANENT; //TODO: handle flags (as integer) | ||||
| 	dest->turnsRemain = 0;	 | ||||
| 	dest->turnsRemain = 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -343,7 +352,7 @@ Bonus * JsonUtils::parseBonus (const JsonVector &ability_vec) //TODO: merge with | ||||
| 		return b; | ||||
| 	} | ||||
| 	b->type = it->second; | ||||
| 	 | ||||
|  | ||||
| 	parseTypedBonusShort(ability_vec, b); | ||||
| 	return b; | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|   | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| class JsonNode; | ||||
| @@ -55,6 +55,7 @@ public: | ||||
| 	explicit JsonNode(const char * data, size_t datasize); | ||||
| 	//Create tree from JSON file | ||||
|  	explicit JsonNode(ResourceID && fileURI); | ||||
|  	explicit JsonNode(const ResourceID & fileURI); | ||||
| 	explicit JsonNode(ResourceID && fileURI, bool & isValidSyntax); | ||||
| 	//Copy c-tor | ||||
| 	JsonNode(const JsonNode ©); | ||||
| @@ -125,9 +126,9 @@ namespace JsonUtils | ||||
| 	/** | ||||
| 	 * @brief parse short bonus format, excluding type | ||||
| 	 * @note sets duration to Permament | ||||
| 	 */	 | ||||
| 	 */ | ||||
| 	DLL_LINKAGE void parseTypedBonusShort(const JsonVector &source, Bonus *dest); | ||||
| 	 | ||||
|  | ||||
| 	/// | ||||
| 	DLL_LINKAGE Bonus * parseBonus (const JsonVector &ability_vec); | ||||
| 	DLL_LINKAGE Bonus * parseBonus (const JsonNode &bonus); | ||||
| @@ -144,7 +145,7 @@ namespace JsonUtils | ||||
| 	 * @note this function will destroy data in source | ||||
| 	 */ | ||||
| 	DLL_LINKAGE void merge(JsonNode & dest, JsonNode & source); | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * @brief recursively merges source into dest, replacing identical fields | ||||
| 	 * struct : recursively calls this function | ||||
| @@ -152,12 +153,12 @@ namespace JsonUtils | ||||
| 	 * values : value in source will replace value in dest | ||||
| 	 * null   : if value in source is present but set to null it will delete entry in dest | ||||
| 	 * @note this function will preserve data stored in source by creating copy | ||||
| 	 */  | ||||
| 	 */ | ||||
| 	DLL_LINKAGE void mergeCopy(JsonNode & dest, JsonNode source); | ||||
| 	 | ||||
|  | ||||
|     /** @brief recursively merges descendant into copy of base node | ||||
|      * Result emulates inheritance semantic | ||||
|      *  | ||||
|      * | ||||
|      * | ||||
|      */ | ||||
| 	DLL_LINKAGE void inherit(JsonNode & descendant, const JsonNode & base); | ||||
| @@ -200,7 +201,7 @@ namespace JsonDetail | ||||
| { | ||||
| 	// conversion helpers for JsonNode::convertTo (partial template function instantiation is illegal in c++) | ||||
|  | ||||
| 	template <typename T, int arithm>  | ||||
| 	template <typename T, int arithm> | ||||
| 	struct JsonConvImpl; | ||||
|  | ||||
| 	template <typename T> | ||||
| @@ -229,7 +230,7 @@ namespace JsonDetail | ||||
| 			///this should be triggered only for numeric types and enums | ||||
| 			static_assert(boost::mpl::or_<std::is_arithmetic<Type>, std::is_enum<Type>, boost::is_class<Type> >::value, "Unsupported type for JsonNode::convertTo()!"); | ||||
| 			return JsonConvImpl<Type, boost::mpl::or_<std::is_enum<Type>, boost::is_class<Type> >::value >::convertImpl(node); | ||||
| 				 | ||||
|  | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
|   | ||||
| @@ -362,6 +362,10 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs ) | ||||
| 		p->heroes -= h; | ||||
| 		h->detachFrom(h->whereShouldBeAttached(gs)); | ||||
| 		h->tempOwner = PlayerColor::NEUTRAL; //no one owns beaten hero | ||||
| 		vstd::erase_if(h->artifactsInBackpack, [](const ArtSlotInfo& asi) | ||||
| 		{ | ||||
| 			return asi.artifact->artType->id == ArtifactID::GRAIL; | ||||
| 		}); | ||||
|  | ||||
| 		if(h->visitedTown) | ||||
| 		{ | ||||
| @@ -590,7 +594,11 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs ) | ||||
|  | ||||
| 	h->setOwner(player); | ||||
| 	h->pos = tile; | ||||
| 	h->movement =  h->maxMovePoints(true); | ||||
| 	bool fresh = !h->isInitialized(); | ||||
| 	if(fresh) | ||||
| 	{ // this is a fresh hero who hasn't appeared yet | ||||
| 		h->movement = h->maxMovePoints(true); | ||||
| 	} | ||||
|  | ||||
| 	gs->hpool.heroesPool.erase(hid); | ||||
| 	if(h->id == ObjectInstanceID()) | ||||
| @@ -604,7 +612,10 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs ) | ||||
| 	gs->map->heroesOnMap.push_back(h); | ||||
| 	p->heroes.push_back(h); | ||||
| 	h->attachTo(p); | ||||
| 	h->initObj(); | ||||
| 	if(fresh) | ||||
| 	{ | ||||
| 		h->initObj(); | ||||
| 	} | ||||
| 	gs->map->addBlockVisTiles(h); | ||||
|  | ||||
| 	if(t) | ||||
|   | ||||
| @@ -87,6 +87,20 @@ boost::optional<std::string> CFilesystemList::getResourceName(const ResourceID & | ||||
| 	return boost::optional<std::string>(); | ||||
| } | ||||
|  | ||||
| std::set<std::string> CFilesystemList::getResourceNames(const ResourceID & resourceName) const | ||||
| { | ||||
| 	std::set<std::string> paths; | ||||
| 	for(auto& loader : getResourcesWithName(resourceName)) | ||||
| 	{ | ||||
| 		auto rn = loader->getResourceName(resourceName); | ||||
| 		if(rn) | ||||
| 		{ | ||||
| 			paths.insert(*rn); | ||||
| 		} | ||||
| 	} | ||||
| 	return std::move(paths); | ||||
| } | ||||
|  | ||||
| std::unordered_set<ResourceID> CFilesystemList::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const | ||||
| { | ||||
| 	std::unordered_set<ResourceID> ret; | ||||
|   | ||||
| @@ -59,16 +59,9 @@ class DLL_LINKAGE CFilesystemList : public ISimpleResourceLoader | ||||
| 	std::set<ISimpleResourceLoader *> writeableLoaders; | ||||
|  | ||||
| 	//FIXME: this is only compile fix, should be removed in the end | ||||
| 	CFilesystemList(CFilesystemList &)  | ||||
|     {  | ||||
| 		//class is not copyable  | ||||
|     }  | ||||
|     CFilesystemList &operator=(CFilesystemList &)  | ||||
|     {  | ||||
|         //class is not copyable  | ||||
|         return *this;  | ||||
|     } | ||||
|  | ||||
| 	CFilesystemList(CFilesystemList &) = delete; | ||||
| 	CFilesystemList &operator=(CFilesystemList &) = delete; | ||||
|  | ||||
| public: | ||||
| 	CFilesystemList(); | ||||
| 	~CFilesystemList(); | ||||
| @@ -78,6 +71,7 @@ public: | ||||
| 	bool existsResource(const ResourceID & resourceName) const override; | ||||
| 	std::string getMountPoint() const override; | ||||
| 	boost::optional<std::string> getResourceName(const ResourceID & resourceName) const override; | ||||
| 	std::set<std::string> getResourceNames(const ResourceID & resourceName) const override; | ||||
| 	std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override; | ||||
| 	bool createResource(std::string filename, bool update = false) override; | ||||
| 	std::vector<const ISimpleResourceLoader *> getResourcesWithName(const ResourceID & resourceName) const override; | ||||
|   | ||||
| @@ -53,6 +53,22 @@ public: | ||||
| 		return boost::optional<std::string>(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets all full names of matching resources, e.g. names of files in filesystem. | ||||
| 	 * | ||||
| 	 * @return std::set with names. | ||||
| 	 */ | ||||
| 	virtual std::set<std::string> getResourceNames(const ResourceID & resourceName) const | ||||
| 	{ | ||||
| 		std::set<std::string> result; | ||||
| 		auto rn = getResourceName(resourceName); | ||||
| 		if(rn) | ||||
| 		{ | ||||
| 			result.insert(*rn); | ||||
| 		} | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Get list of files that matches filter function | ||||
| 	 * | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
| #include "../CCreatureHandler.h" | ||||
| #include "../BattleState.h" | ||||
| #include "../CTownHandler.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "CGTownInstance.h" | ||||
|  | ||||
| ///helpers | ||||
| @@ -220,7 +221,9 @@ CGHeroInstance::CGHeroInstance() | ||||
| 	setNodeType(HERO); | ||||
| 	ID = Obj::HERO; | ||||
| 	tacticFormationEnabled = inTownGarrison = false; | ||||
| 	mana = movement = portrait = -1; | ||||
| 	mana = UNINITIALIZED_MANA; | ||||
| 	movement = UNINITIALIZED_MOVEMENT; | ||||
| 	portrait = UNINITIALIZED_PORTRAIT; | ||||
| 	isStanding = true; | ||||
| 	moveDir = 4; | ||||
| 	level = 1; | ||||
| @@ -1466,3 +1469,26 @@ void CGHeroInstance::readJsonOptions(const JsonNode& json) | ||||
| 	CGObjectInstance::readOwner(json); | ||||
| 	CArtifactSet::readJson(json["artifacts"]); | ||||
| } | ||||
| bool CGHeroInstance::isMissionCritical() const | ||||
| { | ||||
| 	for(const TriggeredEvent & event : IObjectInterface::cb->getMapHeader()->triggeredEvents) | ||||
| 	{ | ||||
| 		if(event.trigger.test([&](const EventCondition & condition) | ||||
| 		{ | ||||
| 			if (condition.condition == EventCondition::CONTROL && condition.object) | ||||
| 			{ | ||||
| 				auto hero = dynamic_cast<const CGHeroInstance*>(condition.object); | ||||
| 				return (hero != this); | ||||
| 			} | ||||
| 			else if(condition.condition == EventCondition::IS_HUMAN) | ||||
| 			{ | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		})) | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
| class CHero; | ||||
| class CGBoat; | ||||
| class CGTownInstance; | ||||
| class CMap; | ||||
| struct TerrainTile; | ||||
| struct TurnInfo; | ||||
|  | ||||
| @@ -64,6 +65,9 @@ public: | ||||
| 	ConstTransitivePtr<CCommanderInstance> commander; | ||||
| 	const CGBoat *boat; //set to CGBoat when sailing | ||||
|  | ||||
| 	static const ui32 UNINITIALIZED_PORTRAIT = -1; | ||||
| 	static const ui32 UNINITIALIZED_MANA = -1; | ||||
| 	static const ui32 UNINITIALIZED_MOVEMENT = -1; | ||||
|  | ||||
| 	//std::vector<const CArtifact*> artifacts; //hero's artifacts from bag | ||||
| 	//std::map<ui16, const CArtifact*> artifWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 | ||||
| @@ -124,6 +128,11 @@ public: | ||||
| 		} | ||||
| 	} skillsInfo; | ||||
|  | ||||
| 	inline bool isInitialized() const | ||||
| 	{ // has this hero been on the map at least once? | ||||
| 		return movement != UNINITIALIZED_MOVEMENT && mana != UNINITIALIZED_MANA; | ||||
| 	} | ||||
|  | ||||
| 	//int3 getSightCenter() const; //"center" tile from which the sight distance is calculated | ||||
| 	int getSightRadious() const override; //sight distance (should be used if player-owned structure) | ||||
| 	////////////////////////////////////////////////////////////////////////// | ||||
| @@ -203,6 +212,8 @@ public: | ||||
| 	void updateSkill(SecondarySkill which, int val); | ||||
|  | ||||
| 	bool hasVisions(const CGObjectInstance * target, const int subtype) const; | ||||
| 	/// If this hero perishes, the scenario is failed | ||||
| 	bool isMissionCritical() const; | ||||
|  | ||||
| 	CGHeroInstance(); | ||||
| 	virtual ~CGHeroInstance(); | ||||
|   | ||||
| @@ -295,10 +295,10 @@ void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int val, int nega | ||||
|  | ||||
| void CGPandoraBox::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const | ||||
| { | ||||
| 	if(result.winner) | ||||
| 		return; | ||||
|  | ||||
| 	giveContentsUpToExp(hero); | ||||
| 	if(result.winner == 0) | ||||
| 	{ | ||||
| 		giveContentsUpToExp(hero); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const | ||||
|   | ||||
| @@ -133,10 +133,10 @@ namespace JsonRandom | ||||
| 		if (value["type"].getType() == JsonNode::DATA_STRING) | ||||
| 			return SpellID(VLC->modh->identifiers.getIdentifier("spell", value["type"]).get()); | ||||
|  | ||||
| 		spells.erase(std::remove_if(spells.begin(), spells.end(), [=](SpellID spell) | ||||
| 		vstd::erase_if(spells, [=](SpellID spell) | ||||
| 		{ | ||||
| 			return VLC->spellh->objects[spell]->level != si32(value["level"].Float()); | ||||
| 		}), spells.end()); | ||||
| 		}); | ||||
|  | ||||
| 		return SpellID(*RandomGeneratorUtil::nextItem(spells, rng)); | ||||
| 	} | ||||
|   | ||||
| @@ -454,22 +454,26 @@ void CGCreature::flee( const CGHeroInstance * h ) const | ||||
|  | ||||
| void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const | ||||
| { | ||||
|  | ||||
| 	if(result.winner==0) | ||||
| 	if(result.winner == 0) | ||||
| 	{ | ||||
| 		giveReward(hero); | ||||
| 		cb->removeObject(this); | ||||
| 	} | ||||
| 	else if(result.winner > 1) // draw | ||||
| 	{ | ||||
| 		// guarded reward is lost forever on draw | ||||
| 		cb->removeObject(this); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		//merge stacks into one | ||||
| 		TSlots::const_iterator i; | ||||
| 		CCreature * cre = VLC->creh->creatures[formation.basicType]; | ||||
| 		for (i = stacks.begin(); i != stacks.end(); i++) | ||||
| 		for(i = stacks.begin(); i != stacks.end(); i++) | ||||
| 		{ | ||||
| 			if (cre->isMyUpgrade(i->second->type)) | ||||
| 			if(cre->isMyUpgrade(i->second->type)) | ||||
| 			{ | ||||
| 				cb->changeStackType (StackLocation(this, i->first), cre); //un-upgrade creatures | ||||
| 				cb->changeStackType(StackLocation(this, i->first), cre); //un-upgrade creatures | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -477,16 +481,16 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult & | ||||
| 		if(!hasStackAtSlot(SlotID(0))) | ||||
| 			cb->moveStack(StackLocation(this, stacks.begin()->first), StackLocation(this, SlotID(0)), stacks.begin()->second->count); | ||||
|  | ||||
| 		while (stacks.size() > 1) //hopefully that's enough | ||||
| 		while(stacks.size() > 1) //hopefully that's enough | ||||
| 		{ | ||||
| 			// TODO it's either overcomplicated (if we assume there'll be only one stack) or buggy (if we allow multiple stacks... but that'll also cause troubles elsewhere) | ||||
| 			i = stacks.end(); | ||||
| 			i--; | ||||
| 			SlotID slot = getSlotFor(i->second->type); | ||||
| 			if (slot == i->first) //no reason to move stack to its own slot | ||||
| 			if(slot == i->first) //no reason to move stack to its own slot | ||||
| 				break; | ||||
| 			else | ||||
| 				cb->moveStack (StackLocation(this, i->first), StackLocation(this, slot), i->second->count); | ||||
| 				cb->moveStack(StackLocation(this, i->first), StackLocation(this, slot), i->second->count); | ||||
| 		} | ||||
|  | ||||
| 		cb->setObjProperty(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties | ||||
|   | ||||
| @@ -124,6 +124,11 @@ static inline double distance(int3 a, int3 b) | ||||
| } | ||||
| static void giveExp(BattleResult &r) | ||||
| { | ||||
| 	if(r.winner > 1) | ||||
| 	{ | ||||
| 		// draw | ||||
| 		return; | ||||
| 	} | ||||
| 	r.exp[0] = 0; | ||||
| 	r.exp[1] = 0; | ||||
| 	for(auto i = r.casualties[!r.winner].begin(); i!=r.casualties[!r.winner].end(); i++) | ||||
| @@ -471,7 +476,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer | ||||
| 	const CArmedInstance *bEndArmy2 = gs->curB->sides.at(1).armyObject; | ||||
| 	const BattleResult::EResult result = battleResult.get()->result; | ||||
|  | ||||
| 	auto findBattleQuery = [this] () -> std::shared_ptr<CBattleQuery> | ||||
| 	auto findBattleQuery = [this]() -> std::shared_ptr<CBattleQuery> | ||||
| 	{ | ||||
| 		for(auto &q : queries.allQueries()) | ||||
| 		{ | ||||
| @@ -526,66 +531,70 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	std::vector<ui32> arts; //display them in window | ||||
| 	std::vector<const CArtifactInstance *> arts; //display them in window | ||||
|  | ||||
| 	if (result == BattleResult::NORMAL && finishingBattle->winnerHero) | ||||
| 	if(result == BattleResult::NORMAL && finishingBattle->winnerHero) | ||||
| 	{ | ||||
| 		if (finishingBattle->loserHero) | ||||
| 		auto sendMoveArtifact = [&](const CArtifactInstance *art, MoveArtifact *ma) | ||||
| 		{ | ||||
| 			auto artifactsWorn = finishingBattle->loserHero->artifactsWorn; //TODO: wrap it into a function, somehow (boost::variant -_-) | ||||
| 			arts.push_back(art); | ||||
| 			ma->dst = ArtifactLocation(finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); | ||||
| 			sendAndApply(ma); | ||||
| 		}; | ||||
| 		if(finishingBattle->loserHero) | ||||
| 		{ | ||||
| 			//TODO: wrap it into a function, somehow (boost::variant -_-) | ||||
| 			auto artifactsWorn = finishingBattle->loserHero->artifactsWorn; | ||||
| 			for (auto artSlot : artifactsWorn) | ||||
| 			{ | ||||
| 				MoveArtifact ma; | ||||
| 				ma.src = ArtifactLocation (finishingBattle->loserHero, artSlot.first); | ||||
| 				ma.src = ArtifactLocation(finishingBattle->loserHero, artSlot.first); | ||||
| 				const CArtifactInstance * art =  ma.src.getArt(); | ||||
| 				if (art && !art->artType->isBig() && art->artType->id != ArtifactID::SPELLBOOK) // don't move war machines or locked arts (spellbook) | ||||
| 				if(art && !art->artType->isBig() && | ||||
| 				    art->artType->id != ArtifactID::SPELLBOOK) | ||||
| 						// don't move war machines or locked arts (spellbook) | ||||
| 				{ | ||||
| 					arts.push_back (art->artType->id); | ||||
| 					ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); | ||||
| 					sendAndApply(&ma); | ||||
| 					sendMoveArtifact(art, &ma); | ||||
| 				} | ||||
| 			} | ||||
| 			while (!finishingBattle->loserHero->artifactsInBackpack.empty()) | ||||
| 			while(!finishingBattle->loserHero->artifactsInBackpack.empty()) | ||||
| 			{ | ||||
| 				//we assume that no big artifacts can be found | ||||
| 				MoveArtifact ma; | ||||
| 				ma.src = ArtifactLocation (finishingBattle->loserHero, | ||||
| 				ma.src = ArtifactLocation(finishingBattle->loserHero, | ||||
| 					ArtifactPosition(GameConstants::BACKPACK_START)); //backpack automatically shifts arts to beginning | ||||
| 				const CArtifactInstance * art =  ma.src.getArt(); | ||||
| 				arts.push_back (art->artType->id); | ||||
| 				ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); | ||||
| 				sendAndApply(&ma); | ||||
| 				if(art->artType->id != ArtifactID::GRAIL) //grail may not be won | ||||
| 				{ | ||||
| 					sendMoveArtifact(art, &ma); | ||||
| 				} | ||||
| 			} | ||||
| 			if (finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero? | ||||
| 			if(finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero? | ||||
| 			{ | ||||
| 				artifactsWorn = finishingBattle->loserHero->commander->artifactsWorn; | ||||
| 				for (auto artSlot : artifactsWorn) | ||||
| 				for(auto artSlot : artifactsWorn) | ||||
| 				{ | ||||
| 					MoveArtifact ma; | ||||
| 					ma.src = ArtifactLocation (finishingBattle->loserHero->commander.get(), artSlot.first); | ||||
| 					ma.src = ArtifactLocation(finishingBattle->loserHero->commander.get(), artSlot.first); | ||||
| 					const CArtifactInstance * art =  ma.src.getArt(); | ||||
| 					if (art && !art->artType->isBig()) | ||||
| 					{ | ||||
| 						arts.push_back (art->artType->id); | ||||
| 						ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); | ||||
| 						sendAndApply(&ma); | ||||
| 						sendMoveArtifact(art, &ma); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		for (auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks) | ||||
| 		for(auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks) | ||||
| 		{ | ||||
| 			auto artifactsWorn = armySlot.second->artifactsWorn; | ||||
| 			for (auto artSlot : artifactsWorn) | ||||
| 			{ | ||||
| 				MoveArtifact ma; | ||||
| 				ma.src = ArtifactLocation (armySlot.second, artSlot.first); | ||||
| 				ma.src = ArtifactLocation(armySlot.second, artSlot.first); | ||||
| 				const CArtifactInstance * art =  ma.src.getArt(); | ||||
| 				if (art && !art->artType->isBig()) | ||||
| 				{ | ||||
| 					arts.push_back (art->artType->id); | ||||
| 					ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); | ||||
| 					sendAndApply(&ma); | ||||
| 					sendMoveArtifact(art, &ma); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @@ -593,23 +602,25 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer | ||||
|  | ||||
| 	sendAndApply(battleResult.data); //after this point casualties objects are destroyed | ||||
|  | ||||
| 	if (arts.size()) //display loot | ||||
| 	if(arts.size()) //display loot | ||||
| 	{ | ||||
| 		InfoWindow iw; | ||||
| 		iw.player = finishingBattle->winnerHero->tempOwner; | ||||
|  | ||||
| 		iw.text.addTxt (MetaString::GENERAL_TXT, 30); //You have captured enemy artifact | ||||
|  | ||||
| 		for (auto id : arts) //TODO; separate function to display loot for various ojects? | ||||
| 		for(auto art : arts) //TODO; separate function to display loot for various ojects? | ||||
| 		{ | ||||
| 			iw.components.push_back (Component (Component::ARTIFACT, id, 0, 0)); | ||||
| 			iw.components.push_back(Component( | ||||
| 				Component::ARTIFACT, art->artType->id, | ||||
| 				art->artType->id == ArtifactID::SPELL_SCROLL? art->getGivenSpellID() : 0, 0)); | ||||
| 			if(iw.components.size() >= 14) | ||||
| 			{ | ||||
| 				sendAndApply(&iw); | ||||
| 				iw.components.clear(); | ||||
| 			} | ||||
| 		} | ||||
| 		if (iw.components.size()) | ||||
| 		if(iw.components.size()) | ||||
| 		{ | ||||
| 			sendAndApply(&iw); | ||||
| 		} | ||||
| @@ -712,7 +723,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) | ||||
|  | ||||
| 	setBattle(nullptr); | ||||
|  | ||||
| 	if(visitObjectAfterVictory && result.winner==0) | ||||
| 	if(visitObjectAfterVictory && result.winner==0 && !finishingBattle->winnerHero->stacks.empty()) | ||||
| 	{ | ||||
| 		logGlobal->traceStream() << "post-victory visit"; | ||||
| 		visitObjectOnTile(*getTile(finishingBattle->winnerHero->getPosition()), finishingBattle->winnerHero); | ||||
| @@ -734,13 +745,34 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) | ||||
| 			sah.army[0].setCreature(SlotID(0), finishingBattle->loserHero->type->initialArmy.at(0).creature, 1); | ||||
| 		} | ||||
|  | ||||
| 		if(const CGHeroInstance *another =  getPlayer(finishingBattle->loser)->availableHeroes.at(1)) | ||||
| 		if(const CGHeroInstance *another =  getPlayer(finishingBattle->loser)->availableHeroes.at(0)) | ||||
| 			sah.hid[1] = another->subID; | ||||
| 		else | ||||
| 			sah.hid[1] = -1; | ||||
|  | ||||
| 		sendAndApply(&sah); | ||||
| 	} | ||||
| 	if(result.winner != 2 && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty()) | ||||
| 	{ | ||||
| 		RemoveObject ro(finishingBattle->winnerHero->id); | ||||
| 		sendAndApply(&ro); | ||||
|  | ||||
| 		if (VLC->modh->settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS) | ||||
| 		{ | ||||
| 			SetAvailableHeroes sah; | ||||
| 			sah.player = finishingBattle->victor; | ||||
| 			sah.hid[0] = finishingBattle->winnerHero->subID; | ||||
| 			sah.army[0].clear(); | ||||
| 			sah.army[0].setCreature(SlotID(0), finishingBattle->winnerHero->type->initialArmy.at(0).creature, 1); | ||||
|  | ||||
| 			if(const CGHeroInstance *another =  getPlayer(finishingBattle->victor)->availableHeroes.at(0)) | ||||
| 				sah.hid[1] = another->subID; | ||||
| 			else | ||||
| 				sah.hid[1] = -1; | ||||
|  | ||||
| 			sendAndApply(&sah); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance, int targetHex) | ||||
| @@ -3322,23 +3354,27 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl | ||||
| //		|| (getHeroCount(player, false) >= GameConstants::MAX_HEROES_PER_PLAYER && complain("Cannot hire hero, only 8 wandering heroes are allowed!"))) | ||||
| 	if((p->resources.at(Res::GOLD) < GameConstants::HERO_GOLD_COST && complain("Not enough gold for buying hero!")) | ||||
| 		|| ((!t) && (getHeroCount(player, false) >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER && complain("Cannot hire hero, too many wandering heroes already!"))) | ||||
| 			|| ((t) && (getHeroCount(player, true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER && complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))) ) | ||||
|  | ||||
| 	return false; | ||||
| 		|| ((t) && (getHeroCount(player, true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER && complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))) ) | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if(t) //tavern in town | ||||
| 	{ | ||||
| 		if(    (!t->hasBuilt(BuildingID::TAVERN)  && complain("No tavern!")) | ||||
| 			|| (t->visitingHero  && complain("There is visiting hero - no place!"))) | ||||
| 		if((!t->hasBuilt(BuildingID::TAVERN) && complain("No tavern!")) | ||||
| 			 || (t->visitingHero  && complain("There is visiting hero - no place!"))) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	else if(obj->ID == Obj::TAVERN) | ||||
| 	{ | ||||
| 		if(getTile(obj->visitablePos())->visitableObjects.back() != obj  &&  complain("Tavern entry must be unoccupied!")) | ||||
| 		if(getTile(obj->visitablePos())->visitableObjects.back() != obj && complain("Tavern entry must be unoccupied!")) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	const CGHeroInstance *nh = p->availableHeroes.at(hid); | ||||
| 	if (!nh) | ||||
| 	{ | ||||
| @@ -3353,13 +3389,14 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl | ||||
| 	hr.tile = obj->visitablePos() + nh->getVisitableOffset(); | ||||
| 	sendAndApply(&hr); | ||||
|  | ||||
|  | ||||
| 	std::map<ui32, ConstTransitivePtr<CGHeroInstance> > pool = gs->unusedHeroesFromPool(); | ||||
|  | ||||
| 	const CGHeroInstance *theOtherHero = p->availableHeroes.at(!hid); | ||||
| 	const CGHeroInstance *newHero = nullptr; | ||||
| 	if (theOtherHero) //on XXL maps all heroes can be imprisoned :( | ||||
| 	{ | ||||
| 		newHero = gs->hpool.pickHeroFor(false, player, getNativeTown(player), pool, gs->getRandomGenerator(), theOtherHero->type->heroClass); | ||||
| 	} | ||||
|  | ||||
| 	SetAvailableHeroes sah; | ||||
| 	sah.player = player; | ||||
| @@ -3371,7 +3408,9 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl | ||||
| 		sah.army[hid].setCreature(SlotID(0), newHero->type->initialArmy[0].creature, 1); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		sah.hid[hid] = -1; | ||||
| 	} | ||||
|  | ||||
| 	sah.hid[!hid] = theOtherHero ? theOtherHero->subID : -1; | ||||
| 	sendAndApply(&sah); | ||||
| @@ -5663,16 +5702,18 @@ void CGameHandler::giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact | ||||
|  | ||||
| void CGameHandler::setBattleResult(BattleResult::EResult resultType, int victoriusSide) | ||||
| { | ||||
| 	if(battleResult.get()) | ||||
| 	boost::unique_lock<boost::mutex> guard(battleResult.mx); | ||||
| 	if(battleResult.data) | ||||
| 	{ | ||||
| 		complain("There is already set result?"); | ||||
| 		complain((boost::format("The battle result has been already set (to %d, asked to %d)") | ||||
| 		          % battleResult.data->result % resultType).str()); | ||||
| 		return; | ||||
| 	} | ||||
| 	auto br = new BattleResult; | ||||
| 	br->result = resultType; | ||||
| 	br->winner = victoriusSide; //surrendering side loses | ||||
| 	gs->curB->calculateCasualties(br->casualties); | ||||
| 	battleResult.set(br); | ||||
| 	battleResult.data = br; | ||||
| } | ||||
|  | ||||
| void CGameHandler::commitPackage( CPackForClient *pack ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user