mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Next part of town configuration:
- town screen is mostly implemented, has some minor issues - factions are now separate from towns, neutrals have faction with id=9 - more constants to GameConstants: town-specific buildings, strings for terrains and resources - replaced most access to builtBuildings with isBuilt() method - replaced id's with enums for town subtype and buildings id's
This commit is contained in:
		| @@ -1241,7 +1241,7 @@ bool VCAI::tryBuildStructure(const CGTownInstance * t, int building, unsigned in | ||||
| 	//erase all already built buildings | ||||
| 	for (auto buildIter = toBuild.begin(); buildIter != toBuild.end();) | ||||
| 	{ | ||||
| 		if (vstd::contains(t->builtBuildings, *buildIter)) | ||||
| 		if (t->hasBuilt(*buildIter)) | ||||
| 			toBuild.erase(buildIter++); | ||||
| 		else | ||||
| 			buildIter++; | ||||
| @@ -1991,7 +1991,7 @@ void VCAI::tryRealize(CGoal g) | ||||
| const CGTownInstance * VCAI::findTownWithTavern() const | ||||
| { | ||||
| 	BOOST_FOREACH(const CGTownInstance *t, cb->getTownsInfo()) | ||||
| 		if(vstd::contains(t->builtBuildings, EBuilding::TAVERN) && !t->visitingHero) | ||||
| 		if(t->hasBuilt(EBuilding::TAVERN) && !t->visitingHero) | ||||
| 			return t; | ||||
|  | ||||
| 	return NULL; | ||||
|   | ||||
| @@ -165,17 +165,11 @@ bool CCallback::assembleArtifacts (const CGHeroInstance * hero, ui16 artifactSlo | ||||
|  | ||||
| bool CCallback::buildBuilding(const CGTownInstance *town, si32 buildingID) | ||||
| { | ||||
| 	//CGTownInstance * t = const_cast<CGTownInstance *>(town); | ||||
|  | ||||
| 	if(town->tempOwner!=player) | ||||
| 		return false; | ||||
|  | ||||
| 	if(!canBuildStructure(town, buildingID)) | ||||
| 		return false; | ||||
| // 	const CBuilding *b = CGI->buildh->buildings[t->subID][buildingID]; | ||||
| // 	for(int i=0;i<b->resources.size();i++) | ||||
| // 		if(b->resources[i] > gs->players[player].resources[i]) | ||||
| // 			return false; //lack of resources | ||||
|  | ||||
| 	BuildStructure pack(town->id,buildingID); | ||||
| 	sendRequest(&pack); | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #include "../lib/JsonNode.h" | ||||
| #include "../lib/Filesystem/CResourceLoader.h" | ||||
| #include "../lib/map.h" | ||||
| #include "../lib/CModHandler.h" | ||||
| #include "../lib/CObjectHandler.h" | ||||
| #include "../lib/CGameState.h" | ||||
| #include "../lib/CGeneralTextHandler.h" | ||||
| @@ -294,7 +295,7 @@ void CTownList::CTownItem::update() | ||||
| 	if (!town->hasFort()) | ||||
| 		iconIndex += GameConstants::F_NUMBER*2; | ||||
|  | ||||
| 	if(town->builded >= GameConstants::MAX_BUILDING_PER_TURN) | ||||
| 	if(town->builded >= CGI->modh->settings.MAX_BUILDING_PER_TURN) | ||||
| 		iconIndex++; | ||||
|  | ||||
| 	picture->setFrame(iconIndex + 2); | ||||
|   | ||||
| @@ -760,7 +760,8 @@ void CAdvMapInt::show(SDL_Surface * to) | ||||
| void CAdvMapInt::selectionChanged() | ||||
| { | ||||
| 	const CGTownInstance *to = LOCPLINT->towns[townList.getSelectedIndex()]; | ||||
| 	select(to); | ||||
| 	if (selection != to) | ||||
| 		select(to); | ||||
| } | ||||
| void CAdvMapInt::centerOn(int3 on) | ||||
| { | ||||
| @@ -877,7 +878,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key) | ||||
| 				const CGTownInstance *townWithMarket = NULL; | ||||
| 				BOOST_FOREACH(const CGTownInstance *t, LOCPLINT->cb->getTownsInfo()) | ||||
| 				{ | ||||
| 					if(vstd::contains(t->builtBuildings, 14)) | ||||
| 					if(t->hasBuilt(EBuilding::MARKETPLACE)) | ||||
| 					{ | ||||
| 						townWithMarket = t; | ||||
| 						break; | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
| #include "../lib/CBuildingHandler.h" | ||||
| #include "../lib/CCreatureHandler.h" | ||||
| #include "../lib/CGeneralTextHandler.h" | ||||
| #include "../lib/CModHandler.h" | ||||
| #include "../lib/CObjectHandler.h" | ||||
| #include "../lib/CSpellHandler.h" | ||||
| #include "../lib/CTownHandler.h" | ||||
| @@ -36,19 +37,6 @@ using namespace boost::assign; | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| int hordeToDwellingID(int bid)//helper, converts horde buiding ID into corresponding dwelling ID | ||||
| { | ||||
| 	const CGTownInstance * t = LOCPLINT->castleInt->town; | ||||
| 	switch (bid) | ||||
| 	{ | ||||
| 		case 18: return t->town->hordeLvl[0] + 30; | ||||
| 		case 19: return t->town->hordeLvl[0] + 37; | ||||
| 		case 24: return t->town->hordeLvl[1] + 30; | ||||
| 		case 25: return t->town->hordeLvl[1] + 37; | ||||
| 		default: return bid; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance *Town, const CStructure *Str) | ||||
| 	:CShowableAnim(0, 0, Str->defName, CShowableAnim::BASE | CShowableAnim::USE_RLE), | ||||
| 	parent(Par), | ||||
| @@ -80,10 +68,8 @@ CBuildingRect::~CBuildingRect() | ||||
|  | ||||
| bool CBuildingRect::operator<(const CBuildingRect & p2) const | ||||
| { | ||||
| 	if(str->pos.z != p2.str->pos.z) | ||||
| 		return (str->pos.z) < (p2.str->pos.z); | ||||
| 	else | ||||
| 		return (str->ID) < (p2.str->ID); | ||||
| 	return (str->pos.z) < (p2.str->pos.z); | ||||
|  | ||||
| } | ||||
|  | ||||
| void CBuildingRect::hover(bool on) | ||||
| @@ -108,18 +94,18 @@ void CBuildingRect::hover(bool on) | ||||
|  | ||||
| void CBuildingRect::clickLeft(tribool down, bool previousState) | ||||
| { | ||||
| 	if( previousState && !down && area && (parent->selectedBuilding==this) ) | ||||
| 	if( previousState && !down && area && (parent->selectedBuilding==this) && str->building ) | ||||
| 		if (!CSDL_Ext::isTransparent(area, GH.current->motion.x-pos.x, GH.current->motion.y-pos.y) ) //inside building image | ||||
| 			parent->buildingClicked(str->ID); | ||||
| 			parent->buildingClicked(str->building->bid); | ||||
| } | ||||
|  | ||||
| void CBuildingRect::clickRight(tribool down, bool previousState) | ||||
| { | ||||
| 	if((!area) || (!((bool)down)) || (this!=parent->selectedBuilding)) | ||||
| 	if((!area) || (!((bool)down)) || (this!=parent->selectedBuilding) || str->building == nullptr) | ||||
| 		return; | ||||
| 	if( !CSDL_Ext::isTransparent(area, GH.current->motion.x-pos.x, GH.current->motion.y-pos.y) ) //inside building image | ||||
| 	{ | ||||
| 		int bid = hordeToDwellingID(str->ID); | ||||
| 		int bid = str->building->bid; | ||||
| 		const CBuilding *bld = town->town->buildings[bid]; | ||||
| 		if (bid < EBuilding::DWELL_FIRST) | ||||
| 		{ | ||||
| @@ -208,13 +194,16 @@ void CBuildingRect::showAll(SDL_Surface * to) | ||||
| 		blitAtLoc(border,0,0,to); | ||||
| } | ||||
|  | ||||
| std::string getBuildingSubtitle(int tid, int bid)//hover text for building | ||||
| std::string getBuildingSubtitle(const CStructure * structure)//hover text for building | ||||
| { | ||||
| 	const CGTownInstance * t = LOCPLINT->castleInt->town; | ||||
| 	bid = hordeToDwellingID(bid); | ||||
| 	if (!structure->building) | ||||
| 		return ""; | ||||
|  | ||||
| 	int bid = structure->building->bid; | ||||
|  | ||||
| 	if (bid<30)//non-dwellings - only buiding name | ||||
| 		return CGI->townh->towns[tid].buildings[bid]->Name(); | ||||
| 		return t->town->buildings[structure->building->bid]->Name(); | ||||
| 	else//dwellings - recruit %creature% | ||||
| 	{ | ||||
| 		int creaID = t->creatures[(bid-30)%GameConstants::CREATURES_PER_TOWN].second.back();//taking last of available creatures | ||||
| @@ -240,7 +229,7 @@ void CBuildingRect::mouseMoved (const SDL_MouseMotionEvent & sEvent) | ||||
| 			  || (*parent->selectedBuilding)<(*this)) //or we are on top | ||||
| 			{ | ||||
| 				parent->selectedBuilding = this; | ||||
| 				GH.statusbar->print(getBuildingSubtitle(str->townID, str->ID)); | ||||
| 				GH.statusbar->print(getBuildingSubtitle(str)); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -453,177 +442,113 @@ CCastleBuildings::CCastleBuildings(const CGTownInstance* Town): | ||||
| { | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL; | ||||
|  | ||||
| 	background = new CPicture(graphics->townBgs[town->subID]); | ||||
| 	background = new CPicture(town->town->clientInfo.townBackground); | ||||
| 	pos.w = background->pos.w; | ||||
| 	pos.h = background->pos.h; | ||||
|  | ||||
| 	recreate(); | ||||
| } | ||||
|  | ||||
| void CCastleBuildings::recreate() | ||||
| { | ||||
| 	selectedBuilding = nullptr; | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL; | ||||
| 	//clear existing buildings | ||||
| 	BOOST_FOREACH(auto build, buildings) | ||||
| 		delete build; | ||||
| 	buildings.clear(); | ||||
| 	groups.clear(); | ||||
|  | ||||
| 	//Generate buildings list | ||||
| 	for (std::set<si32>::const_iterator building=town->builtBuildings.begin(); building!=town->builtBuildings.end(); building++) | ||||
| 	{ | ||||
| 		auto structure = town->town->clientInfo.structures.find(*building); | ||||
|  | ||||
| 		if(structure != town->town->clientInfo.structures.end() && structure->second) | ||||
| 		{ | ||||
| 			if(structure->second->group<0) // no group - just add it | ||||
| 				buildings.push_back(new CBuildingRect(this, town, structure->second)); | ||||
| 			else // generate list for each group | ||||
| 				groups[structure->second->group].push_back(structure->second); | ||||
| 		} | ||||
| 	} | ||||
| 	auto buildingsCopy = town->builtBuildings;// a bit modified copy of built buildings | ||||
|  | ||||
| 	CStructure * shipyard = town->town->clientInfo.structures[6]; | ||||
| 	//ship in shipyard | ||||
| 	if(shipyard && vstd::contains(groups, shipyard->group)) | ||||
| 	if(vstd::contains(town->builtBuildings, EBuilding::SHIPYARD)) | ||||
| 	{ | ||||
| 		std::vector <const CGObjectInstance *> vobjs = LOCPLINT->cb->getVisitableObjs(town->bestLocation()); | ||||
| 		if(!vobjs.empty() && (vobjs.front()->ID == 8 || vobjs.front()->ID == GameConstants::HEROI_TYPE)) //there is visitable obj at shipyard output tile and it's a boat or hero (on boat) | ||||
| 		//there is visitable obj at shipyard output tile and it's a boat or hero (on boat) | ||||
| 		if(!vobjs.empty() && (vobjs.front()->ID == 8 || vobjs.front()->ID == GameConstants::HEROI_TYPE)) | ||||
| 		{ | ||||
| 			groups[shipyard->group].push_back(town->town->clientInfo.structures[20]); | ||||
| 			buildingsCopy.insert(EBuilding::SHIP); | ||||
| 		} | ||||
| 	} | ||||
| 	//Create building for each group | ||||
| 	for (std::map< int, std::vector<const CStructure*> >::iterator group = groups.begin(); group != groups.end(); group++) | ||||
|  | ||||
| 	BOOST_FOREACH(const CStructure * structure, town->town->clientInfo.structures) | ||||
| 	{ | ||||
| 		std::sort(group->second.begin(), group->second.end(), structSorter); | ||||
| 		buildings.push_back(new CBuildingRect(this, town, group->second.back())); | ||||
| 		if (!structure->building) | ||||
| 		{ | ||||
| 			buildings.push_back(new CBuildingRect(this, town, structure)); | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (vstd::contains(buildingsCopy, structure->building->bid)) | ||||
| 		{ | ||||
| 			groups[structure->building->getBase()].push_back(structure); | ||||
| 		} | ||||
| 	} | ||||
| 	std::sort(buildings.begin(),buildings.end(),buildSorter); | ||||
| 	checkRules(); | ||||
|  | ||||
| 	BOOST_FOREACH(auto & entry, groups) | ||||
| 	{ | ||||
| 		const CBuilding * build = town->town->buildings[entry.first]; | ||||
|  | ||||
| 		const CStructure * toAdd = *boost::max_element(entry.second, [=](const CStructure * a, const CStructure * b) | ||||
| 		{ | ||||
| 			return build->getDistance(a->building->bid) | ||||
| 			     < build->getDistance(b->building->bid); | ||||
| 		}); | ||||
|  | ||||
| 		buildings.push_back(new CBuildingRect(this, town, toAdd)); | ||||
| 	} | ||||
| 	boost::sort(buildings, [] (const CBuildingRect * a, const CBuildingRect * b) | ||||
| 	{ | ||||
| 		return *a < *b; | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| CCastleBuildings::~CCastleBuildings() | ||||
| { | ||||
| } | ||||
|  | ||||
| void CCastleBuildings::checkRules() | ||||
| { | ||||
| 	//if town tonwID have building toCheck | ||||
| 	//then set animation of building buildID to firstA..lastA | ||||
| 	//else set to firstB..lastB | ||||
| 	struct AnimRule | ||||
| 	{ | ||||
| 		int townID, buildID; | ||||
| 		int toCheck; | ||||
| 		size_t firstA, lastA; | ||||
| 		size_t firstB, lastB; | ||||
| 	}; | ||||
| 	 | ||||
| 	static const AnimRule animRule[2] =  | ||||
| 	{ | ||||
| 		{5, 21, 4, 10, size_t(-1), 0,  10}, //Mana Vortex, Dungeon | ||||
| 		{0,  6, 8,  1, size_t(-1), 0,  1}   //Shipyard, Castle | ||||
| 	}; | ||||
| 	 | ||||
| 	for (size_t i=0; i<2; i++) | ||||
| 	{ | ||||
| 		if ( town->subID != animRule[i].townID ) //wrong town | ||||
| 			continue; | ||||
| 		 | ||||
| 		int buildingID = animRule[i].buildID; | ||||
| 		//check if this building have been upgraded (Ship is upgrade of Shipyard) | ||||
| 		int groupID = town->town->clientInfo.structures[animRule[i].buildID]->group; | ||||
| 		if (groupID != -1) | ||||
| 		{ | ||||
| 			std::map< int, std::vector<const CStructure*> >::const_iterator git= groups.find(groupID); | ||||
| 			if ( git == groups.end() || git->second.empty() ) | ||||
| 				continue; | ||||
| 			buildingID  = git->second.back()->ID; | ||||
| 		} | ||||
|  | ||||
| 		BOOST_FOREACH(CBuildingRect* rect, buildings) | ||||
| 		{ | ||||
| 			if ( rect->str->ID == buildingID ) | ||||
| 			{ | ||||
| 				if (vstd::contains(town->builtBuildings, animRule[i].toCheck)) | ||||
| 					rect->set(0,animRule[i].firstA, animRule[i].lastA); | ||||
| 				else | ||||
| 					rect->set(0,animRule[i].firstB, animRule[i].lastB); | ||||
|  | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CCastleBuildings::addBuilding(int building) | ||||
| { | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL; | ||||
| 	auto structure = town->town->clientInfo.structures.find(building); | ||||
| 	//FIXME: implement faster method without complete recreation of town | ||||
| 	int base = town->town->buildings[building]->getBase(); | ||||
|  | ||||
| 	if(structure != town->town->clientInfo.structures.end()) //we have info about that structure | ||||
| 	recreate(); | ||||
|  | ||||
| 	auto & structures = groups[base]; | ||||
|  | ||||
| 	BOOST_FOREACH(CBuildingRect * rect, buildings) | ||||
| 	{ | ||||
| 		if(structure->second->group<0) //no group - just add it | ||||
| 		if (vstd::contains(structures, rect->str)) | ||||
| 		{ | ||||
| 			buildings.push_back(new CBuildingRect(this, town, structure->second)); | ||||
| 			buildings.back()->stateCounter = 0; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			//find last building in this group and replace it with new building if needed | ||||
| 			groups[structure->second->group].push_back(structure->second); | ||||
| 			int newBuilding = groups[structure->second->group].back()->ID; | ||||
| 			if (newBuilding == building) | ||||
| 			{ | ||||
| 				for (std::vector< CBuildingRect* >::iterator it=buildings.begin() ; it !=buildings.end(); it++ ) | ||||
| 				{ | ||||
| 					if ((*it)->str->ID == newBuilding) | ||||
| 					{ | ||||
| 						delete *it; | ||||
| 						buildings.erase(it); | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				buildings.push_back(new CBuildingRect(this, town, structure->second)); | ||||
| 				buildings.back()->stateCounter = 0; | ||||
| 			} | ||||
| 			//reset animation | ||||
| 			if (structures.size() == 1) | ||||
| 				rect->stateCounter = 0; // transparency -> fully visible stage | ||||
| 			else | ||||
| 				rect->stateCounter = 16; // already in fully visible stage | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	std::sort(buildings.begin(),buildings.end(),buildSorter); | ||||
| 	checkRules(); | ||||
| } | ||||
|  | ||||
| void CCastleBuildings::removeBuilding(int building) | ||||
| { | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL; | ||||
| 	auto structure = town->town->clientInfo.structures.find(building); | ||||
|  | ||||
| 	if(structure != town->town->clientInfo.structures.end()) //we have info about that structure | ||||
| 	{ | ||||
| 		if(structure->second->group<0) //no group - just add it | ||||
| 		{ | ||||
| 			for (std::vector< CBuildingRect* >::iterator it=buildings.begin() ; it !=buildings.end(); it++ ) | ||||
| 			{ | ||||
| 				if ((*it)->str->ID == building) | ||||
| 				{ | ||||
| 					delete *it; | ||||
| 					buildings.erase(it); | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			groups[structure->second->group].pop_back(); | ||||
| 			delete buildings[building]; | ||||
| 			if (!groups[structure->second->group].empty()) | ||||
| 				buildings.push_back(new CBuildingRect(this, town, structure->second)); | ||||
| 		} | ||||
| 	} | ||||
| 	std::sort(buildings.begin(),buildings.end(),buildSorter); | ||||
| 	checkRules(); | ||||
| 	//FIXME: implement faster method without complete recreation of town | ||||
| 	recreate(); | ||||
| } | ||||
|  | ||||
| void CCastleBuildings::show(SDL_Surface * to) | ||||
| { | ||||
| 	CIntObject::show(to); | ||||
| 	for (std::vector< CBuildingRect* >::const_iterator it=buildings.begin() ; it !=buildings.end(); it++ ) | ||||
| 		(*it)->show(to); | ||||
| 	BOOST_FOREACH(CBuildingRect * str, buildings) | ||||
| 		str->show(to); | ||||
| } | ||||
|  | ||||
| void CCastleBuildings::showAll(SDL_Surface * to) | ||||
| { | ||||
| 	CIntObject::showAll(to); | ||||
| 	for (std::vector< CBuildingRect* >::const_iterator it=buildings.begin() ; it !=buildings.end(); it++ ) | ||||
| 		(*it)->showAll(to); | ||||
| 	BOOST_FOREACH(CBuildingRect * str, buildings) | ||||
| 		str->showAll(to); | ||||
| } | ||||
|  | ||||
| const CGHeroInstance* CCastleBuildings::getHero() | ||||
| @@ -638,7 +563,6 @@ const CGHeroInstance* CCastleBuildings::getHero() | ||||
| void CCastleBuildings::buildingClicked(int building) | ||||
| { | ||||
| 	tlog5<<"You've clicked on "<<building<<std::endl; | ||||
| 	building = hordeToDwellingID(building); | ||||
| 	const CBuilding *b = town->town->buildings.find(building)->second; | ||||
|  | ||||
| 	if(building >= EBuilding::DWELL_FIRST) | ||||
| @@ -689,11 +613,13 @@ void CCastleBuildings::buildingClicked(int building) | ||||
| 		case EBuilding::SPECIAL_1: | ||||
| 				switch(town->subID) | ||||
| 				{ | ||||
| 				case 1://Mystic Pond | ||||
| 				case ETownType::RAMPART://Mystic Pond | ||||
| 						enterFountain(building); | ||||
| 						break; | ||||
|  | ||||
| 				case 2: case 5: case 8://Artifact Merchant | ||||
| 				case ETownType::TOWER: | ||||
| 				case ETownType::DUNGEON://Artifact Merchant | ||||
| 				case ETownType::CONFLUX: | ||||
| 						if(town->visitingHero) | ||||
| 							GH.pushInt(new CMarketplaceWindow(town, town->visitingHero, EMarketMode::RESOURCE_ARTIFACT)); | ||||
| 						else | ||||
| @@ -713,18 +639,18 @@ void CCastleBuildings::buildingClicked(int building) | ||||
| 		case EBuilding::SPECIAL_2: | ||||
| 				switch(town->subID) | ||||
| 				{ | ||||
| 				case 1: //Fountain of Fortune | ||||
| 				case ETownType::RAMPART: //Fountain of Fortune | ||||
| 						enterFountain(building); | ||||
| 						break; | ||||
|  | ||||
| 				case 6: //Freelancer's Guild | ||||
| 				case ETownType::STRONGHOLD: //Freelancer's Guild | ||||
| 						if(getHero()) | ||||
| 							GH.pushInt(new CMarketplaceWindow(town, getHero(), EMarketMode::CREATURE_RESOURCE)); | ||||
| 						else | ||||
| 							LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s. | ||||
| 						break; | ||||
|  | ||||
| 				case 8: //Magic University | ||||
| 				case ETownType::CONFLUX: //Magic University | ||||
| 						if (getHero()) | ||||
| 							GH.pushInt(new CUniversityWindow(getHero(), town)); | ||||
| 						else | ||||
| @@ -740,26 +666,26 @@ void CCastleBuildings::buildingClicked(int building) | ||||
| 		case EBuilding::SPECIAL_3: | ||||
| 				switch(town->subID) | ||||
| 				{ | ||||
| 				case 0: //Brotherhood of sword | ||||
| 				case ETownType::CASTLE: //Brotherhood of sword | ||||
| 						LOCPLINT->showTavernWindow(town); | ||||
| 						break; | ||||
|  | ||||
| 				case 3: //Castle Gate | ||||
| 				case ETownType::INFERNO: //Castle Gate | ||||
| 						enterCastleGate(); | ||||
| 						break; | ||||
|  | ||||
| 				case 4: //Skeleton Transformer | ||||
| 				case ETownType::NECROPOLIS: //Skeleton Transformer | ||||
| 						GH.pushInt( new CTransformerWindow(getHero(), town) ); | ||||
| 						break; | ||||
|  | ||||
| 				case 5: //Portal of Summoning | ||||
| 				case ETownType::DUNGEON: //Portal of Summoning | ||||
| 						if (town->creatures[GameConstants::CREATURES_PER_TOWN].second.empty())//No creatures | ||||
| 							LOCPLINT->showInfoDialog(CGI->generaltexth->tcommands[30]); | ||||
| 						else | ||||
| 							enterDwelling(GameConstants::CREATURES_PER_TOWN); | ||||
| 						break; | ||||
|  | ||||
| 				case 6: //Ballista Yard | ||||
| 				case ETownType::STRONGHOLD: //Ballista Yard | ||||
| 						enterBlacksmith(4); | ||||
| 						break; | ||||
|  | ||||
| @@ -810,12 +736,12 @@ void CCastleBuildings::enterCastleGate() | ||||
| 	{ | ||||
| 		const CGTownInstance *t = Towns[i]; | ||||
| 		if (t->id != this->town->id && t->visitingHero == NULL && //another town, empty and this is | ||||
| 			t->subID == 3 && vstd::contains(t->builtBuildings, 22))//inferno with castle gate | ||||
| 			t->hasBuilt(EBuilding::CASTLE_GATE, ETownType::INFERNO)) | ||||
| 		{ | ||||
| 			availableTowns.push_back(t->id);//add to the list | ||||
| 		} | ||||
| 	} | ||||
| 	CPicture *titlePic = new CPicture (LOCPLINT->castleInt->bicons->ourImages[22].bitmap, 0,0, false);//will be deleted by selection window | ||||
| 	CPicture *titlePic = new CPicture (LOCPLINT->castleInt->bicons->ourImages[EBuilding::CASTLE_GATE].bitmap, 0,0, false);//will be deleted by selection window | ||||
| 	GH.pushInt (new CObjectListWindow(availableTowns, titlePic, CGI->generaltexth->jktexts[40], | ||||
| 	    CGI->generaltexth->jktexts[41], boost::bind (&CCastleInterface::castleTeleport, LOCPLINT->castleInt, _1))); | ||||
| } | ||||
| @@ -831,8 +757,10 @@ void CCastleBuildings::enterFountain(int building) | ||||
| 	std::vector<CComponent*> comps(1, new CComponent(CComponent::building,town->subID,building)); | ||||
|  | ||||
| 	std::string descr = town->town->buildings.find(building)->second->Description(); | ||||
| 	if ( building == 21)//we need description for mystic pond as well | ||||
| 		descr += "\n\n"+town->town->buildings.find(17)->second->Description(); | ||||
|  | ||||
| 	if ( building == EBuilding::FOUNTAIN_OF_FORTUNE) | ||||
| 		descr += "\n\n"+town->town->buildings.find(EBuilding::MYSTIC_POND)->second->Description(); | ||||
|  | ||||
| 	if (town->bonusValue.first == 0)//fountain was builded this week | ||||
| 		descr += "\n\n"+ CGI->generaltexth->allTexts[677]; | ||||
| 	else//fountain produced something; | ||||
| @@ -948,9 +876,9 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst | ||||
| 	townlist->onSelect = boost::bind(&CCastleInterface::townChange, this); | ||||
|  | ||||
| 	recreateIcons(); | ||||
| 	CCS->musich->playMusicFromSet("town-theme", town->subID, true); | ||||
| 	CCS->musich->playMusic(town->town->clientInfo.musicTheme, true); | ||||
| 	 | ||||
| 	bicons = CDefHandler::giveDefEss(graphics->buildingPics[town->subID]); | ||||
| 	bicons = CDefHandler::giveDefEss(town->town->clientInfo.buildingsIcons); | ||||
| } | ||||
|  | ||||
| CCastleInterface::~CCastleInterface() | ||||
| @@ -1013,7 +941,7 @@ void CCastleInterface::recreateIcons() | ||||
| 	if (!town->hasFort()) | ||||
| 		iconIndex += GameConstants::F_NUMBER*2; | ||||
|  | ||||
| 	if(town->builded >= GameConstants::MAX_BUILDING_PER_TURN) | ||||
| 	if(town->builded >= CGI->modh->settings.MAX_BUILDING_PER_TURN) | ||||
| 		iconIndex++; | ||||
|  | ||||
| 	icon->setFrame(iconIndex); | ||||
| @@ -1319,7 +1247,7 @@ CHallInterface::CBuildingBox::CBuildingBox(int x, int y, const CGTownInstance * | ||||
| 	static int panelIndex[9] = { 3,  3,  3, 0, 0, 2, 2,  1, 2}; | ||||
| 	static int  iconIndex[9] = {-1, -1, -1, 0, 0, 1, 2, -1, 1}; | ||||
|  | ||||
| 	picture = new CAnimImage(graphics->buildingPics[town->subID], building->bid, 0, 2, 2); | ||||
| 	picture = new CAnimImage(town->town->clientInfo.buildingsIcons, building->bid, 0, 2, 2); | ||||
| 	panel = new CAnimImage("TPTHBAR", panelIndex[state], 0,   1, 73); | ||||
| 	if ( iconIndex[state] >=0 ) | ||||
| 		icon  = new CAnimImage("TPTHCHK",  iconIndex[state], 0, 136, 56); | ||||
| @@ -1355,18 +1283,8 @@ CHallInterface::CHallInterface(const CGTownInstance *Town): | ||||
| 				int buildingID = boxList[row][col][item]; | ||||
| 				building = town->town->buildings[buildingID]; | ||||
|  | ||||
| 				if (buildingID == 18 || buildingID == 24) | ||||
| 				{ | ||||
| 					if ( (buildingID == 18 && !vstd::contains(town->builtBuildings, town->town->hordeLvl[0]+37)) | ||||
| 					  || (buildingID == 24 && !vstd::contains(town->builtBuildings, town->town->hordeLvl[1]+37)) ) | ||||
| 						break; // horde present, no upgraded dwelling -> select 18 or 24 | ||||
| 					else | ||||
| 						continue; //upgraded dwelling, no horde -> select 19 or 25 | ||||
| 				} | ||||
|  | ||||
| 				if(vstd::contains(town->builtBuildings,buildingID)) | ||||
| 					continue; | ||||
| 				break; | ||||
| 				if(!vstd::contains(town->builtBuildings,buildingID)) | ||||
| 					break; | ||||
| 			} | ||||
| 			int posX = pos.w/2 - boxList[row].size()*154/2 - (boxList[row].size()-1)*20 + 194*col, | ||||
| 			    posY = 35 + 104*row; | ||||
| @@ -1419,7 +1337,7 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin | ||||
| { | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL; | ||||
|  | ||||
| 	new CAnimImage(graphics->buildingPics[town->subID], building->bid, 0, 125, 50); | ||||
| 	new CAnimImage(town->town->clientInfo.buildingsIcons, building->bid, 0, 125, 50); | ||||
| 	new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); | ||||
|  | ||||
| 	new CLabel(197, 30, FONT_MEDIUM, CENTER, Colors::Cornsilk, | ||||
| @@ -1581,7 +1499,7 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance * | ||||
| 		addUsedEvents(LCLICK | RCLICK | HOVER);//Activate only if dwelling is present | ||||
| 	 | ||||
| 	icons = new CPicture("ZPCAINFO", 261, 3); | ||||
| 	buildingPic = new CAnimImage(graphics->buildingPics[town->subID], buildingID, 0, 4, 21); | ||||
| 	buildingPic = new CAnimImage(town->town->clientInfo.buildingsIcons, buildingID, 0, 4, 21); | ||||
|  | ||||
| 	const CCreature* creature = NULL; | ||||
|  | ||||
| @@ -1651,7 +1569,7 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner): | ||||
| { | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL; | ||||
| 	 | ||||
| 	window = new CPicture(graphics->guildBgs[owner->town->subID], 332, 76); | ||||
| 	window = new CPicture(owner->town->town->clientInfo.guildWindow , 332, 76); | ||||
| 	 | ||||
| 	resdatabar = new CMinorResDataBar; | ||||
| 	resdatabar->pos.x += pos.x; | ||||
|   | ||||
| @@ -114,15 +114,14 @@ public: | ||||
| class CCastleBuildings : public CIntObject | ||||
| { | ||||
| 	CPicture *background; | ||||
| 	//List of buildings for each group | ||||
| 	std::map< int, std::vector<const CStructure*> > groups; | ||||
| 	//Vector with all blittable buildings | ||||
| 	std::vector<CBuildingRect*> buildings; | ||||
| 	//List of buildings and structures that can represent them | ||||
| 	std::map< si32, std::vector<const CStructure*> > groups; | ||||
| 	// actual IntObject's visible on screen | ||||
| 	std::vector< CBuildingRect * > buildings; | ||||
|  | ||||
| 	const CGTownInstance * town; | ||||
|  | ||||
| 	const CGHeroInstance* getHero();//Select hero for buildings usage | ||||
| 	void checkRules();//Check animation rules (special anims for Shipyard and Mana Vortex) | ||||
|  | ||||
| 	void enterBlacksmith(int ArtifactID);//support for blacksmith + ballista yard | ||||
| 	void enterBuilding(int building);//for buildings with simple description + pic left-click messages | ||||
| @@ -134,6 +133,7 @@ class CCastleBuildings : public CIntObject | ||||
| 	void openMagesGuild(); | ||||
| 	void openTownHall(); | ||||
|  | ||||
| 	void recreate(); | ||||
| public: | ||||
| 	CBuildingRect * selectedBuilding; | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include "../CCallback.h" | ||||
| #include "../lib/CCreatureHandler.h" //creatures name for objects list | ||||
| #include "../lib/CGeneralTextHandler.h" | ||||
| #include "../lib/CModHandler.h" //for buildings per turn | ||||
| #include "../lib/CObjectHandler.h" //Hero/Town objects | ||||
| #include "../lib/CHeroHandler.h" // only for calculating required xp? worth it? | ||||
| #include "CAnimation.h" //CAnimImage | ||||
| @@ -797,7 +798,7 @@ CTownItem::CTownItem(const CGTownInstance* Town): | ||||
| 	if (!town->hasFort()) | ||||
| 		iconIndex += GameConstants::F_NUMBER*2; | ||||
|  | ||||
| 	if(town->builded >= GameConstants::MAX_BUILDING_PER_TURN) | ||||
| 	if(town->builded >= CGI->modh->settings.MAX_BUILDING_PER_TURN) | ||||
| 		iconIndex++; | ||||
|  | ||||
| 	picture = new CAnimImage("ITPT", iconIndex, 0, 5, 6); | ||||
|   | ||||
| @@ -329,8 +329,6 @@ CMusicHandler::CMusicHandler(): | ||||
| 	const std::string setEnemy[] = {"AITheme0", "AITheme1", "AITheme2"}; | ||||
| 	const std::string setBattle[] = {"Combat01", "Combat02", "Combat03", "Combat04"}; | ||||
| 	const std::string setTerrain[] = {"Dirt",	"Sand",	"Grass", "Snow", "Swamp", "Rough", "Underground", "Lava", "Water"}; | ||||
| 	const std::string setTowns[] =  {"CstleTown", "Rampart", "TowerTown", "InfernoTown", | ||||
| 	        "NecroTown", "Dungeon", "Stronghold", "FortressTown", "ElemTown"}; | ||||
|  | ||||
| 	auto fillSet = [=](std::string setName, const std::string list[], size_t amount) | ||||
| 	{ | ||||
| @@ -340,7 +338,6 @@ CMusicHandler::CMusicHandler(): | ||||
| 	fillSet("enemy-turn", setEnemy, ARRAY_COUNT(setEnemy)); | ||||
| 	fillSet("battle", setBattle, ARRAY_COUNT(setBattle)); | ||||
| 	fillSet("terrain", setTerrain, ARRAY_COUNT(setTerrain)); | ||||
| 	fillSet("town-theme", setTowns, ARRAY_COUNT(setTowns)); | ||||
| } | ||||
|  | ||||
| void CMusicHandler::addEntryToSet(std::string set, int musicID, std::string musicURI) | ||||
|   | ||||
| @@ -22,6 +22,7 @@ | ||||
| #include "../lib/CBuildingHandler.h" | ||||
| #include "../lib/CGeneralTextHandler.h" | ||||
| #include "../lib/CHeroHandler.h" | ||||
| #include "../lib/CModHandler.h" | ||||
| #include "../lib/CObjectHandler.h" | ||||
| #include "../lib/CSpellHandler.h" | ||||
| #include "../lib/CTownHandler.h" | ||||
| @@ -162,7 +163,7 @@ void CTownTooltip::init(const InfoAboutTown &town) | ||||
| 	size_t imageIndex = town.tType->typeID * 2; | ||||
| 	if (town.fortLevel == 0) | ||||
| 		imageIndex += GameConstants::F_NUMBER * 2; | ||||
| 	if (town.built >= GameConstants::MAX_BUILDING_PER_TURN) | ||||
| 	if (town.built >= CGI->modh->settings.MAX_BUILDING_PER_TURN) | ||||
| 		imageIndex++; | ||||
|  | ||||
| 	new CAnimImage("itpt", imageIndex, 0, 3, 2); | ||||
| @@ -861,7 +862,7 @@ const std::vector<std::string> CComponent::getFileName() | ||||
| 	case spell:      return gen(spellsArr); | ||||
| 	case morale:     return gen(moraleArr); | ||||
| 	case luck:       return gen(luckArr); | ||||
| 	case building:   return std::vector<std::string>(4, graphics->buildingPics[subtype]); | ||||
| 	case building:   return std::vector<std::string>(4, CGI->townh->towns[subtype].clientInfo.buildingsIcons); | ||||
| 	case hero:       return gen(heroArr); | ||||
| 	case flag:       return gen(flagArr); | ||||
| 	} | ||||
| @@ -1244,18 +1245,17 @@ void CSelWindow::madeChoice() | ||||
| 	LOCPLINT->cb->selectionMade(ret+1,ID); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| CCreaturePic::CCreaturePic(int x, int y, const CCreature *cre, bool Big, bool Animated) | ||||
| { | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL; | ||||
| 	pos.x+=x; | ||||
| 	pos.y+=y; | ||||
|  | ||||
| 	assert(vstd::contains(CGI->townh->factions, cre->faction)); | ||||
| 	if(Big) | ||||
| 		bg = new CPicture(graphics->backgrounds[cre->faction],0,0,false); | ||||
| 		bg = new CPicture(CGI->townh->factions[cre->faction].creatureBg130); | ||||
| 	else | ||||
| 		bg = new CPicture(graphics->backgroundsm[cre->faction],0,0,false); | ||||
| 		bg = new CPicture(CGI->townh->factions[cre->faction].creatureBg120); | ||||
| 	bg->needRefresh = true; | ||||
| 	anim = new CCreatureAnim(0, 0, cre->animDefName, Rect()); | ||||
| 	anim->clipRect(cre->isDoubleWide()?170:150, 155, bg->pos.w, bg->pos.h); | ||||
| @@ -2437,10 +2437,17 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstan | ||||
| 	{ | ||||
| 		switch (mode) | ||||
| 		{ | ||||
| 		break; case EMarketMode::CREATURE_RESOURCE: title = CGI->townh->towns[6].buildings[21]->Name(); | ||||
| 		break; case EMarketMode::RESOURCE_ARTIFACT: title = CGI->townh->towns[market->o->subID].buildings[17]->Name(); | ||||
| 		break; case EMarketMode::ARTIFACT_RESOURCE: title = CGI->townh->towns[market->o->subID].buildings[17]->Name(); | ||||
| 		break; default: title = CGI->generaltexth->allTexts[158]; | ||||
| 		break; case EMarketMode::CREATURE_RESOURCE: | ||||
| 			title = CGI->townh->towns[ETownType::STRONGHOLD].buildings[EBuilding::FREELANCERS_GUILD]->Name(); | ||||
|  | ||||
| 		break; case EMarketMode::RESOURCE_ARTIFACT: | ||||
| 			title = CGI->townh->towns[market->o->subID].buildings[EBuilding::ARTIFACT_MERCHANT]->Name(); | ||||
|  | ||||
| 		break; case EMarketMode::ARTIFACT_RESOURCE: | ||||
| 			title = CGI->townh->towns[market->o->subID].buildings[EBuilding::ARTIFACT_MERCHANT]->Name(); | ||||
|  | ||||
| 		break; default: | ||||
| 			title = CGI->generaltexth->allTexts[158]; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| @@ -5253,7 +5260,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket | ||||
| 		titlePic = new CPicture("UNIVBLDG"); | ||||
| 	else | ||||
| 	if (market->o->ID == GameConstants::TOWNI_TYPE) | ||||
| 		titlePic = new CAnimImage(graphics->buildingPics[market->o->subID], 21); | ||||
| 		titlePic = new CAnimImage(CGI->townh->towns[ETownType::CONFLUX].clientInfo.buildingsIcons, EBuilding::MAGIC_UNIVERSITY); | ||||
| 	else | ||||
| 		tlog0<<"Error: Image for university was not found!\n";//This should not happen | ||||
|  | ||||
|   | ||||
| @@ -87,13 +87,6 @@ void Graphics::loadPaletteAndColors() | ||||
| 		playerColors[i].unused = 255; | ||||
| 	} | ||||
| 	neutralColor->r = 0x84; neutralColor->g = 0x84; neutralColor->b = 0x84; neutralColor->unused = 255;//gray | ||||
| 	const JsonNode config(ResourceID("config/town_pictures.json")); | ||||
| 	BOOST_FOREACH(const JsonNode &p, config["town_pictures"].Vector()) { | ||||
|  | ||||
| 		townBgs.push_back(p["town_background"].String()); | ||||
| 		guildBgs.push_back(p["guild_background"].String()); | ||||
| 		buildingPics.push_back(p["building_picture"].String()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Graphics::initializeBattleGraphics() | ||||
| @@ -158,13 +151,6 @@ Graphics::Graphics() | ||||
| 	tasks += GET_DEF_ESS(spellscr,"SPELLSCR.DEF"); | ||||
| 	tasks += GET_DEF_ESS(heroMoveArrows,"ADAG.DEF"); | ||||
|  | ||||
| 	const JsonNode config(ResourceID("config/creature_backgrounds.json")); | ||||
| 	BOOST_FOREACH(const JsonNode &b, config["backgrounds"].Vector()) { | ||||
| 		const int id = b["id"].Float(); | ||||
| 		tasks += GET_SURFACE(backgrounds[id], b["bg130"].String()); | ||||
| 		tasks += GET_SURFACE(backgroundsm[id], b["bg120"].String()); | ||||
| 	} | ||||
|  | ||||
| 	CThreadHelper th(&tasks,std::max((ui32)1,boost::thread::hardware_concurrency())); | ||||
| 	th.run(); | ||||
|  | ||||
|   | ||||
| @@ -64,12 +64,7 @@ public: | ||||
| 	//creatures | ||||
| 	std::map<int,SDL_Surface*> smallImgs; //creature ID -> small 32x32 img of creature; //ID=-2 is for blank (black) img; -1 for the border | ||||
| 	std::map<int,SDL_Surface*> bigImgs; //creature ID -> big 58x64 img of creature; //ID=-2 is for blank (black) img; -1 for the border | ||||
| 	std::map<int,SDL_Surface*> backgrounds; //castle ID -> 100x130 background creature image // -1 is for neutral | ||||
| 	std::map<int,SDL_Surface*> backgroundsm; //castle ID -> 100x120 background creature image // -1 is for neutral | ||||
| 	//towns | ||||
| 	std::vector< std::string > buildingPics;//filenames of def with building images (used rarely, too big to keep them loaded) | ||||
| 	std::vector< std::string > townBgs;//backgrounds of town | ||||
| 	std::vector< std::string > guildBgs;// name of bitmaps with imgs for mage guild screen | ||||
| 	std::map<int, std::string> ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type | ||||
| 	//for battles | ||||
| 	std::vector< std::vector< std::string > > battleBacks; //battleBacks[terType] - vector of possible names for certain terrain type | ||||
|   | ||||
| @@ -401,11 +401,11 @@ void NewStructures::applyCl( CClient *cl ) | ||||
| 	CGTownInstance *town = GS(cl)->getTown(tid); | ||||
| 	BOOST_FOREACH(si32 id, bid) | ||||
| 	{ | ||||
| 		if(id==13) //fort or capitol | ||||
| 		if(id== EBuilding::CAPITOL) //fort or capitol | ||||
| 		{ | ||||
| 			town->defInfo = GS(cl)->capitols[town->subID]; | ||||
| 		} | ||||
| 		if(id ==7) | ||||
| 		if(id == EBuilding::FORT) | ||||
| 		{ | ||||
| 			town->defInfo = GS(cl)->forts[town->subID]; | ||||
| 		} | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,19 +0,0 @@ | ||||
| { | ||||
| 	// Backgrounds for creatures. | ||||
| 	//   id: town ID (0 to 8, -1 is neutral) | ||||
| 	//   bg120: 100x120 background creature image | ||||
| 	//   bg130: 100x130 background creature image | ||||
| 	 | ||||
| 	"backgrounds": [ | ||||
| 		{ "id": -1, "bg120": "TPCASNEU.bmp", "bg130": "CRBKGNEU.bmp" }, | ||||
| 		{ "id": 0,  "bg120": "TPCASCAS.bmp", "bg130": "CRBKGCAS.bmp" }, | ||||
| 		{ "id": 1,  "bg120": "TPCASRAM.bmp", "bg130": "CRBKGRAM.bmp" }, | ||||
| 		{ "id": 2,  "bg120": "TPCASTOW.bmp", "bg130": "CRBKGTOW.bmp" }, | ||||
| 		{ "id": 3,  "bg120": "TPCASINF.bmp", "bg130": "CRBKGINF.bmp" }, | ||||
| 		{ "id": 4,  "bg120": "TPCASNEC.bmp", "bg130": "CRBKGNEC.bmp" }, | ||||
| 		{ "id": 5,  "bg120": "TPCASDUN.bmp", "bg130": "CRBKGDUN.bmp" }, | ||||
| 		{ "id": 6,  "bg120": "TPCASSTR.bmp", "bg130": "CRBKGSTR.bmp" }, | ||||
| 		{ "id": 7,  "bg120": "TPCASFOR.bmp", "bg130": "CRBKGFOR.bmp" }, | ||||
| 		{ "id": 8,  "bg120": "TPCASELE.bmp", "bg130": "CRBKGELE.bmp" } | ||||
| 	] | ||||
| } | ||||
| @@ -1225,7 +1225,7 @@ | ||||
| 				"id": 116, | ||||
| 				"level": 4, | ||||
| 				"name": [ "GoldGolem" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "SPELL_DAMAGE_REDUCTION", 85, -1, 0 ],		//gold golems reduce dmg from spells | ||||
| 							   	 [ "NON_LIVING", 0, 0, 0 ] ],		 	   	//diamond golems are non-living | ||||
| 				"defname": "CGGOLE.DEF" | ||||
| @@ -1235,7 +1235,7 @@ | ||||
| 				"id": 117, | ||||
| 				"level": 5, | ||||
| 				"name": [ "DiamondGolem" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "SPELL_DAMAGE_REDUCTION", 95, -1, 0 ],		//diamond golems reduce dmg from spells | ||||
| 							   	 [ "NON_LIVING", 0, 0, 0 ] ],				//psychic elementals shouldn't get morale | ||||
| 				"defname": "CDGOLE.DEF" | ||||
| @@ -1357,7 +1357,7 @@ | ||||
| 				"id": 132, | ||||
| 				"level": 10, | ||||
| 				"name": [ "AzureDragon" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "DRAGON_NATURE", 0, 0, 0 ], | ||||
| 							   	 [ "TWO_HEX_ATTACK_BREATH", 0, 0, 0 ],  //azure dragon's breath | ||||
| 							   	 [ "FEARLESS", 0, 0, 0 ],  //azure dragon is immune to fear | ||||
| @@ -1370,7 +1370,7 @@ | ||||
| 				"id": 133, | ||||
| 				"level": 10, | ||||
| 				"name": [ "CrystalDragon" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "DRAGON_NATURE", 0, 0, 0 ] ],			//crystal dragon is a dragon | ||||
| 				"ability_remove": [ "FLYING" ], | ||||
| 				"defname": "CCDRGN.DEF" | ||||
| @@ -1380,7 +1380,7 @@ | ||||
| 				"id": 134, | ||||
| 				"level": 8, | ||||
| 				"name": [ "FairieDragon" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "DRAGON_NATURE", 0, 0, 0 ],			//faerie dragon is a dragon | ||||
| 								[ "CASTS", 5, 0, 0 ], | ||||
| 								[ "CREATURE_SPELL_POWER", 500, 0, 0], 	//5 spell power per dragon | ||||
| @@ -1399,7 +1399,7 @@ | ||||
| 				"id": 135, | ||||
| 				"level": 10, | ||||
| 				"name": [ "RustDragon" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "SPELL_AFTER_ATTACK", 100, 80, 0 ],			//always reduce defense | ||||
| 							   	 [ "ACID_BREATH", 25, 0, 20 ],			//20% chance to do 25 damage | ||||
| 								 [ "DRAGON_NATURE", 0, 0, 0 ] ],			//rust dragon is a dragon | ||||
| @@ -1410,7 +1410,7 @@ | ||||
| 				"id": 136, | ||||
| 				"level": 6, | ||||
| 				"name": [ "Enchanter" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "NO_WALL_PENALTY", 0, 0, 0 ], | ||||
| 								 [ "ENCHANTER", 3, 28, 3],		//air shield | ||||
| 								 [ "ENCHANTER", 3, 41, 3],		//bless | ||||
| @@ -1428,7 +1428,7 @@ | ||||
| 				"id": 137, | ||||
| 				"level": 4, | ||||
| 				"name": [ "Sharpshooter" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "NO_WALL_PENALTY", 0, 0, 0 ], | ||||
| 							   	 [ "NO_DISTANCE_PENALTY", 0, 0, 0 ] ],			//Sharpshooter | ||||
| 				"defname": "CSHARP.DEF", | ||||
| @@ -1440,7 +1440,7 @@ | ||||
| 				"id": 138, | ||||
| 				"level": 1, | ||||
| 				"name": [ "Halfling" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "CHALF.DEF", | ||||
| 				"projectile_defname": "PHALF.DEF", | ||||
| 				"projectile_spin": true | ||||
| @@ -1450,7 +1450,7 @@ | ||||
| 				"id": 139, | ||||
| 				"level": 1, | ||||
| 				"name": [ "Peasant" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "CPEAS.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1458,7 +1458,7 @@ | ||||
| 				"id": 140, | ||||
| 				"level": 2, | ||||
| 				"name": [ "Boar" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "DOUBLE_WIDE", 0, 0, 0 ] ], 			//boar should be treated as double-wide | ||||
| 				"defname": "CBOAR.DEF" | ||||
| 			}, | ||||
| @@ -1467,7 +1467,7 @@ | ||||
| 				"id": 141, | ||||
| 				"level": 3, | ||||
| 				"name": [ "Mummy" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "UNDEAD", 0, 0, 0 ] ],  | ||||
| 				"defname": "CMUMMY.DEF" | ||||
| 			}, | ||||
| @@ -1476,7 +1476,7 @@ | ||||
| 				"id": 142, | ||||
| 				"level": 3, | ||||
| 				"name": [ "warrior" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "DOUBLE_WIDE", 0, 0, 0 ] ], 			//nomads should be treated as double-wide | ||||
| 				"defname": "CNOMAD.DEF" | ||||
| 			}, | ||||
| @@ -1485,7 +1485,7 @@ | ||||
| 				"id": 143, | ||||
| 				"level": 2, | ||||
| 				"name": [ "Rogue" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "CROGUE.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1493,7 +1493,7 @@ | ||||
| 				"id": 144, | ||||
| 				"level": 5, | ||||
| 				"name": [ "OgreShaman" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "FULL_HP_REGENERATION", 0, 0, 0 ] ], 			//troll | ||||
| 				"defname": "CTROLL.DEF" | ||||
| 			}, | ||||
| @@ -1502,7 +1502,7 @@ | ||||
| 				"id": 145, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Catapult" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "SMCATA.DEF", | ||||
| 				"projectile_defname": "SMCATX.DEF", | ||||
| 				"projectile_spin": true | ||||
| @@ -1512,7 +1512,7 @@ | ||||
| 				"id": 146, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Ballista" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "SMBAL.DEF", | ||||
| 				"projectile_defname": "SMBALX.DEF", | ||||
| 				"projectile_spin": false | ||||
| @@ -1522,7 +1522,7 @@ | ||||
| 				"id": 147, | ||||
| 				"level": 0, | ||||
| 				"name": [ "FirstAidTent" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "HEALER", 0, 0, 0 ] ],					//Gorynyches fly | ||||
| 				"defname": "SMTENT.DEF" | ||||
| 			}, | ||||
| @@ -1531,7 +1531,7 @@ | ||||
| 				"id": 148, | ||||
| 				"level": 0, | ||||
| 				"name": [ "AmmoCart" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "NOT_ACTIVE", 0, 0, 0 ] ],						//hell hound doesn't fly | ||||
| 				"defname": "SMCART.DEF" | ||||
| 			}, | ||||
| @@ -1540,7 +1540,7 @@ | ||||
| 				"id": 149, | ||||
| 				"level": 0, | ||||
| 				"name": [ "ArrowTower" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "SHOOTER", 0, 0, 0 ] ],						//cerberus doesn't fly | ||||
| 				"defname": "x" | ||||
| 			}, | ||||
| @@ -1627,7 +1627,7 @@ | ||||
| 				"id": 159, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Ghost" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM159G.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1635,7 +1635,7 @@ | ||||
| 				"id": 160, | ||||
| 				"level": 0, | ||||
| 				"name": [ "God1War" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM160G.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1643,7 +1643,7 @@ | ||||
| 				"id": 161, | ||||
| 				"level": 0, | ||||
| 				"name": [ "God2Peace" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM161G.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1651,7 +1651,7 @@ | ||||
| 				"id": 162, | ||||
| 				"level": 0, | ||||
| 				"name": [ "God3Mana" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM162G.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1659,7 +1659,7 @@ | ||||
| 				"id": 163, | ||||
| 				"level": 0, | ||||
| 				"name": [ "God4Lore" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM163G.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1667,7 +1667,7 @@ | ||||
| 				"id": 164, | ||||
| 				"level": 0, | ||||
| 				"name": [ "MinotaurKing" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM164GD.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1675,7 +1675,7 @@ | ||||
| 				"id": 165, | ||||
| 				"level": 0, | ||||
| 				"name": [ "MineralElemental" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM165GD.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1683,7 +1683,7 @@ | ||||
| 				"id": 166, | ||||
| 				"level": 0, | ||||
| 				"name": [ "ElectricityElemental" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM166GD.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1691,7 +1691,7 @@ | ||||
| 				"id": 167, | ||||
| 				"level": 0, | ||||
| 				"name": [ "AncientBasilisk" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM167GD.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1699,7 +1699,7 @@ | ||||
| 				"id": 168, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Gorynych" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "FLYING", 0, 0, 0 ] ], | ||||
| 				"defname": "ZM168DG.DEF" | ||||
| 			}, | ||||
| @@ -1708,7 +1708,7 @@ | ||||
| 				"id": 169, | ||||
| 				"level": 0, | ||||
| 				"name": [ "WarZealot" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM169ZL.DEF", | ||||
| 				"projectile_defname": "CPRZEAX.DEF", | ||||
| 				"projectile_spin": false | ||||
| @@ -1718,7 +1718,7 @@ | ||||
| 				"id": 170, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Myriad" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM170SW.DEF", | ||||
| 				"projectile_defname": "PLCBOWX.DEF", | ||||
| 				"projectile_spin": false | ||||
| @@ -1728,7 +1728,7 @@ | ||||
| 				"id": 171, | ||||
| 				"level": 0, | ||||
| 				"name": [ "MedusaMatriarch" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM171SR.DEF", | ||||
| 				"projectile_defname": "PLCBOWX.DEF", | ||||
| 				"projectile_spin": false | ||||
| @@ -1738,7 +1738,7 @@ | ||||
| 				"id": 172, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Nightmare" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM172N.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1746,7 +1746,7 @@ | ||||
| 				"id": 173, | ||||
| 				"level": 0, | ||||
| 				"name": [ "SantaGremlin" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM173M.DEF", | ||||
| 				"projectile_defname": "CPRGRE.DEF", | ||||
| 				"projectile_spin": true | ||||
| @@ -1756,7 +1756,7 @@ | ||||
| 				"id": 174, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Paladin1" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], | ||||
| 								[ "CASTS", 1, 0, 0 ] , | ||||
| 								[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , | ||||
| @@ -1769,7 +1769,7 @@ | ||||
| 				"id": 175, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Hierophant1" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], | ||||
| 								[ "CASTS", 1, 0, 0 ] , | ||||
| 								[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , | ||||
| @@ -1782,7 +1782,7 @@ | ||||
| 				"id": 176, | ||||
| 				"level": 0, | ||||
| 				"name": [ "TempleGuardian1" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], | ||||
| 								[ "CASTS", 1, 0, 0 ] , | ||||
| 								[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , | ||||
| @@ -1795,7 +1795,7 @@ | ||||
| 				"id": 177, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Succubus1" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], | ||||
| 								[ "CASTS", 1, 0, 0 ] , | ||||
| 								[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , | ||||
| @@ -1808,7 +1808,7 @@ | ||||
| 				"id": 178, | ||||
| 				"level": 0, | ||||
| 				"name": [ "SoulEater1" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], | ||||
| 								[ "CASTS", 1, 0, 0 ] , | ||||
| 								[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , | ||||
| @@ -1821,7 +1821,7 @@ | ||||
| 				"id": 179, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Brute1" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], | ||||
| 								[ "CASTS", 1, 0, 0 ] , | ||||
| 								[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , | ||||
| @@ -1834,7 +1834,7 @@ | ||||
| 				"id": 180, | ||||
| 				"level": 0, | ||||
| 				"name": [ "OgreLeader1" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], | ||||
| 								[ "CASTS", 1, 0, 0 ] , | ||||
| 								[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , | ||||
| @@ -1847,7 +1847,7 @@ | ||||
| 				"id": 181, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Shaman1" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], | ||||
| 								[ "CASTS", 1, 0, 0 ] , | ||||
| 								[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , | ||||
| @@ -1860,7 +1860,7 @@ | ||||
| 				"id": 182, | ||||
| 				"level": 0, | ||||
| 				"name": [ "AstralSpirit1" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], | ||||
| 								[ "CASTS", 1, 0, 0 ] , | ||||
| 								[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , | ||||
| @@ -1873,7 +1873,7 @@ | ||||
| 				"id": 183, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Paladin2" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM174NPC.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1881,7 +1881,7 @@ | ||||
| 				"id": 184, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Hierophant2" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM175NPC.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1889,7 +1889,7 @@ | ||||
| 				"id": 185, | ||||
| 				"level": 0, | ||||
| 				"name": [ "TempleGuardian2" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM176NPC.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1897,7 +1897,7 @@ | ||||
| 				"id": 186, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Succubus2" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM177NPC.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1905,7 +1905,7 @@ | ||||
| 				"id": 187, | ||||
| 				"level": 0, | ||||
| 				"name": [ "SoulEater2" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM178NPC.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1913,7 +1913,7 @@ | ||||
| 				"id": 188, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Brute2" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM179NPC.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1921,7 +1921,7 @@ | ||||
| 				"id": 189, | ||||
| 				"level": 0, | ||||
| 				"name": [ "OgreLeader2" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM180NPC.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1929,7 +1929,7 @@ | ||||
| 				"id": 190, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Shaman2" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM181NPC.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1937,7 +1937,7 @@ | ||||
| 				"id": 191, | ||||
| 				"level": 0, | ||||
| 				"name": [ "AstralSpirit2" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM182NPC.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1945,7 +1945,7 @@ | ||||
| 				"id": 192, | ||||
| 				"level": 0, | ||||
| 				"name": [ "SylvanCentaur" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM192Z.DEF", | ||||
| 				"projectile_defname": "PELFX.DEF", | ||||
| 				"projectile_spin": false | ||||
| @@ -1955,7 +1955,7 @@ | ||||
| 				"id": 193, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Sorceress" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM193Z.DEF", | ||||
| 				"projectile_defname": "CPRZEAX.DEF", | ||||
| 				"projectile_spin": false | ||||
| @@ -1965,7 +1965,7 @@ | ||||
| 				"id": 194, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Werewolf" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM194Z.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1973,7 +1973,7 @@ | ||||
| 				"id": 195, | ||||
| 				"level": 0, | ||||
| 				"name": [ "HellSteed" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM195Z.DEF" | ||||
| 			}, | ||||
|  | ||||
| @@ -1981,7 +1981,7 @@ | ||||
| 				"id": 196, | ||||
| 				"level": 0, | ||||
| 				"name": [ "Dracolich" ], | ||||
| 				"faction": -1, | ||||
| 				"faction": 9, | ||||
| 				"defname": "ZM196Z.DEF", | ||||
| 				"projectile_defname": "SMBALX.DEF", | ||||
| 				"projectile_spin": false | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
| 		"CREEP_SIZE": 4000, | ||||
| 		"WEEKLY_GROWTH_PERCENT" : 10, | ||||
| 		"NEUTRAL_STACK_EXP_DAILY" : 500, | ||||
| 		"MAX_BUILDING_PER_TURN" : 1, | ||||
| 		"DWELLINGS_ACCUMULATE_CREATURES" : true, | ||||
| 		"ALL_CREATURES_GET_DOUBLE_MONTHS" : false | ||||
| 	}, | ||||
| @@ -16,4 +17,4 @@ | ||||
| 		"COMMANDERS": true, | ||||
| 		"MITHRIL": false //so far unused | ||||
| 	} | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,52 +1,13 @@ | ||||
| { | ||||
| 	// Terrains properties related to heroes - ordered by faction (0 to 8) | ||||
| 	//   cost: ?? | ||||
| 	//   native: native terrain for the faction | ||||
|  | ||||
| 	"terrains": [ | ||||
| 		{ | ||||
| 			"costs": [ 100, 150, 100, 150, 175, 125, 100, 100, 100, -1 ], | ||||
| 			"native": 2 | ||||
| 		}, | ||||
|  | ||||
| 		{ | ||||
| 			"costs": [ 100, 150, 100, 150, 175, 125, 100, 100, 100, -1 ], | ||||
| 			"native": 2 | ||||
| 		}, | ||||
|  | ||||
| 		{ | ||||
| 			"costs": 	[ 100, 150, 100, 100, 175, 125, 100, 100, 100, -1 ], | ||||
| 			"native": 3 | ||||
| 		}, | ||||
|  | ||||
| 		{ | ||||
| 			"costs": 	[ 100, 150, 100, 150, 175, 125, 100, 100, 100, -1 ], | ||||
| 			"native": 7 | ||||
| 		}, | ||||
|  | ||||
| 		{ | ||||
| 			"costs": 	[ 100, 150, 100, 150, 175, 125, 100, 100, 100, -1 ], | ||||
| 			"native": 0 | ||||
| 		}, | ||||
|  | ||||
| 		{ | ||||
| 			"costs": 	[ 100, 150, 100, 150, 175, 125, 100, 100, 100, -1 ], | ||||
| 			"native": 6 | ||||
| 		}, | ||||
|  | ||||
| 		{ | ||||
| 			"costs": 	[ 100, 150, 100, 150, 175, 100, 100, 100, 100, -1 ], | ||||
| 			"native": 5 | ||||
| 		}, | ||||
|  | ||||
| 		{ | ||||
| 			"costs": 	[ 100, 150, 100, 150, 100, 125, 100, 100, 100, -1 ], | ||||
| 			"native": 4 | ||||
| 		}, | ||||
|  | ||||
| 		{ | ||||
| 			"costs": 	[ 100, 150, 100, 150, 175, 125, 100, 100, 100, -1 ], | ||||
| 			"native": 2 | ||||
| 		} | ||||
| 	] | ||||
| 	// Movement costs on different terrains | ||||
| 	"dirt"     : 100, | ||||
| 	"sand"     : 150, | ||||
| 	"grass"    : 100, | ||||
| 	"snow"     : 150, | ||||
| 	"swamp"    : 175, | ||||
| 	"rough"    : 125, | ||||
| 	"subterra" : 100, | ||||
| 	"lava"     : 100, | ||||
| 	"water"    : 100, | ||||
| 	"rock"     : -1, | ||||
| } | ||||
|   | ||||
| @@ -1,17 +0,0 @@ | ||||
| { | ||||
| 	// Some town pictures, ordered by the town number (0 to 8) | ||||
| 	//   town_background: background inside the town | ||||
| 	//   guild_background: background inside the mage guild | ||||
| 	//   building_picture: picture inside the city hall | ||||
| 	"town_pictures": [ | ||||
| 		{ "town_background": "TBCSBACK.bmp", "guild_background": "TPMAGECS.bmp", "building_picture": "HALLCSTL.DEF" }, | ||||
| 		{ "town_background": "TBRMBACK.bmp", "guild_background": "TPMAGERM.bmp", "building_picture": "HALLRAMP.DEF" }, | ||||
| 		{ "town_background": "TBTWBACK.bmp", "guild_background": "TPMAGETW.bmp", "building_picture": "HALLTOWR.DEF" }, | ||||
| 		{ "town_background": "TBINBACK.bmp", "guild_background": "TPMAGEIN.bmp", "building_picture": "HALLINFR.DEF" }, | ||||
| 		{ "town_background": "TBNCBACK.bmp", "guild_background": "TPMAGENC.bmp", "building_picture": "HALLNECR.DEF" }, | ||||
| 		{ "town_background": "TBDNBACK.bmp", "guild_background": "TPMAGEDN.bmp", "building_picture": "HALLDUNG.DEF" }, | ||||
| 		{ "town_background": "TBSTBACK.bmp", "guild_background": "TPMAGEST.bmp", "building_picture": "HALLSTRN.DEF" }, | ||||
| 		{ "town_background": "TBFRBACK.bmp", "guild_background": "TPMAGEFR.bmp", "building_picture": "HALLFORT.DEF" }, | ||||
| 		{ "town_background": "TBELBACK.bmp", "guild_background": "TPMAGEEL.bmp", "building_picture": "HALLELEM.DEF" } | ||||
| 	] | ||||
| } | ||||
| @@ -596,7 +596,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int battlefieldTyp | ||||
| 	{ | ||||
| 		curB->town = town; | ||||
| 		curB->siege = town->fortLevel(); | ||||
| 		curB->terrainType = VLC->heroh->nativeTerrains[town->town->typeID]; | ||||
| 		curB->terrainType = VLC->townh->factions[town->town->typeID].nativeTerrain; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
|   | ||||
| @@ -321,7 +321,7 @@ bool CBattleInfoEssentials::battleCanFlee(int player) const | ||||
| 	if(mySide == BattleSide::DEFENDER  &&  battleGetSiegeLevel()) | ||||
| 	{ | ||||
| 		auto town = battleGetDefendedTown(); | ||||
| 		if(!(town->subID == 6  &&  town->hasBuilt(EBuilding::SPECIAL_1))) //not a stronghold with escape tunnel | ||||
| 		if(!town->hasBuilt(EBuilding::ESCAPE_TUNNEL, ETownType::STRONGHOLD)) | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "Filesystem/CResourceLoader.h" | ||||
| #include "VCMI_Lib.h" | ||||
| #include "CGameState.h" | ||||
| #include "CHeroHandler.h" | ||||
| #include "CTownHandler.h" | ||||
| #include "CModHandler.h" | ||||
|  | ||||
| using namespace boost::assign; | ||||
| @@ -148,11 +148,9 @@ std::string CCreature::nodeName() const | ||||
|  | ||||
| bool CCreature::isItNativeTerrain(int terrain) const | ||||
| { | ||||
| 	if(!vstd::iswithin(faction, 0, 9)) | ||||
| 		return false; | ||||
|  | ||||
| 	assert(vstd::contains(VLC->townh->factions, faction)); | ||||
| 	//not good handler dependency | ||||
| 	return VLC->heroh->nativeTerrains[faction] == terrain; | ||||
| 	return VLC->townh->factions[faction].nativeTerrain == terrain; | ||||
| } | ||||
|  | ||||
| int readNumber(int & befi, int & i, int andame, std::string & buf) //helper function for void CCreatureHandler::loadCreatures() and loadUnitAnimInfo() | ||||
|   | ||||
| @@ -528,7 +528,12 @@ std::pair<int,int> CGameState::pickObject (CGObjectInstance *obj) | ||||
| 			{ | ||||
| 				f = scenarioOps->getIthPlayersSettings(align).castle; | ||||
| 			} | ||||
| 			if(f<0) f = ran()%VLC->townh->towns.size(); | ||||
| 			if(f<0) | ||||
| 			{ | ||||
| 				auto iter = VLC->townh->towns.begin(); | ||||
| 				std::advance(iter, ran()%VLC->townh->towns.size()); | ||||
| 				f = iter->first; | ||||
| 			} | ||||
| 			return std::pair<int,int>(GameConstants::TOWNI_TYPE,f); | ||||
| 		} | ||||
| 	case 162: //random monster lvl5 | ||||
| @@ -1290,15 +1295,15 @@ void CGameState::init(StartInfo * si) | ||||
| 		if(vti->builtBuildings.find(-50)!=vti->builtBuildings.end()) //give standard set of buildings | ||||
| 		{ | ||||
| 			vti->builtBuildings.erase(-50); | ||||
| 			vti->builtBuildings.insert(10); | ||||
| 			vti->builtBuildings.insert(5); | ||||
| 			vti->builtBuildings.insert(30); | ||||
| 			vti->builtBuildings.insert(EBuilding::VILLAGE_HALL); | ||||
| 			vti->builtBuildings.insert(EBuilding::TAVERN); | ||||
| 			vti->builtBuildings.insert(EBuilding::DWELL_FIRST); | ||||
| 			if(ran()%2) | ||||
| 				vti->builtBuildings.insert(31); | ||||
| 				vti->builtBuildings.insert(EBuilding::DWELL_FIRST+1); | ||||
| 		} | ||||
|  | ||||
| 		if (vstd::contains(vti->builtBuildings,(6)) && vti->state()==2) | ||||
| 			vti->builtBuildings.erase(6);//if we have harbor without water - erase it (this is H3 behaviour) | ||||
| 		if (vstd::contains(vti->builtBuildings, EBuilding::SHIPYARD) && vti->state()==2) | ||||
| 			vti->builtBuildings.erase(EBuilding::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour) | ||||
|  | ||||
| 		//init hordes | ||||
| 		for (int i = 0; i<GameConstants::CREATURES_PER_TOWN; i++) | ||||
| @@ -1307,15 +1312,15 @@ void CGameState::init(StartInfo * si) | ||||
| 				vti->builtBuildings.erase(-31-i);//remove old ID | ||||
| 				if (vti->town->hordeLvl[0] == i)//if town first horde is this one | ||||
| 				{ | ||||
| 					vti->builtBuildings.insert(18);//add it | ||||
| 					if (vstd::contains(vti->builtBuildings,(37+i)))//if we have upgraded dwelling as well | ||||
| 						vti->builtBuildings.insert(19);//add it as well | ||||
| 					vti->builtBuildings.insert(EBuilding::HORDE_1);//add it | ||||
| 					if (vstd::contains(vti->builtBuildings,(EBuilding::DWELL_UP_FIRST+i)))//if we have upgraded dwelling as well | ||||
| 						vti->builtBuildings.insert(EBuilding::HORDE_1_UPGR);//add it as well | ||||
| 				} | ||||
| 				if (vti->town->hordeLvl[1] == i)//if town second horde is this one | ||||
| 				{ | ||||
| 					vti->builtBuildings.insert(24); | ||||
| 					if (vstd::contains(vti->builtBuildings,(37+i))) | ||||
| 						vti->builtBuildings.insert(25); | ||||
| 					vti->builtBuildings.insert(EBuilding::HORDE_2); | ||||
| 					if (vstd::contains(vti->builtBuildings,(EBuilding::DWELL_UP_FIRST+i))) | ||||
| 						vti->builtBuildings.insert(EBuilding::HORDE_2_UPGR); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| @@ -1327,9 +1332,9 @@ void CGameState::init(StartInfo * si) | ||||
| 				{ | ||||
| 					ev->buildings.erase(-31-i); | ||||
| 					if (vti->town->hordeLvl[0] == i) | ||||
| 						ev->buildings.insert(18); | ||||
| 						ev->buildings.insert(EBuilding::HORDE_1); | ||||
| 					if (vti->town->hordeLvl[1] == i) | ||||
| 						ev->buildings.insert(24); | ||||
| 						ev->buildings.insert(EBuilding::HORDE_2); | ||||
| 				} | ||||
| 		} | ||||
| 		//init spells | ||||
| @@ -2058,7 +2063,7 @@ int CGameState::victoryCheck( ui8 player ) const | ||||
| 			BOOST_FOREACH(const CGTownInstance *t, map->towns) | ||||
| 				if((t == map->victoryCondition.obj || !map->victoryCondition.obj) | ||||
| 					&& t->tempOwner == player | ||||
| 					&& vstd::contains(t->builtBuildings, EBuilding::GRAIL)) | ||||
| 					&& t->hasBuilt(EBuilding::GRAIL)) | ||||
| 					return 1; | ||||
| 			break; | ||||
|  | ||||
| @@ -2687,7 +2692,7 @@ void InfoAboutTown::initFromTown(const CGTownInstance *t, bool detailed) | ||||
| 		//include details about hero | ||||
| 		details = new Details; | ||||
| 		details->goldIncome = t->dailyIncome(); | ||||
| 		details->customRes = vstd::contains(t->builtBuildings, 15); | ||||
| 		details->customRes = t->hasBuilt(EBuilding::RESOURCE_SILO); | ||||
| 		details->hallLevel = t->hallLevel(); | ||||
| 		details->garrisonedHero = t->garrisonHero; | ||||
| 	} | ||||
|   | ||||
| @@ -356,26 +356,11 @@ ui64 CHeroHandler::reqExp (ui32 level) const | ||||
|  | ||||
| void CHeroHandler::loadTerrains() | ||||
| { | ||||
| 	int faction = 0; | ||||
| 	const JsonNode config(ResourceID("config/terrains.json")); | ||||
|  | ||||
| 	nativeTerrains.resize(GameConstants::F_NUMBER); | ||||
|  | ||||
| 	BOOST_FOREACH(const JsonNode &terrain, config["terrains"].Vector()) { | ||||
|  | ||||
| 		BOOST_FOREACH(const JsonNode &cost, terrain["costs"].Vector()) { | ||||
| 			int curCost = cost.Float(); | ||||
|  | ||||
| 			heroClasses[2*faction]->terrCosts.push_back(curCost); | ||||
| 			heroClasses[2*faction+1]->terrCosts.push_back(curCost); | ||||
| 		} | ||||
|  | ||||
| 		nativeTerrains[faction] = terrain["native"].Float(); | ||||
|  | ||||
| 		faction ++; | ||||
| 	} | ||||
|  | ||||
| 	assert(faction == GameConstants::F_NUMBER); | ||||
| 	terrCosts.reserve(GameConstants::TERRAIN_TYPES); | ||||
| 	BOOST_FOREACH(const std::string & name, GameConstants::TERRAIN_NAMES) | ||||
| 		terrCosts.push_back(config[name].Float()); | ||||
| } | ||||
|  | ||||
| CHero::CHero() | ||||
|   | ||||
| @@ -69,7 +69,6 @@ public: | ||||
| 	std::vector<std::pair<int,int> > primChance;//primChance[PRIMARY_SKILL_ID] - first is for levels 2 - 9, second for 10+;;; probability (%) of getting point of primary skill when getting new level | ||||
| 	std::vector<int> proSec; //probabilities of gaining secondary skills (out of 112), in id order | ||||
| 	int selectionProbability[9]; //probability of selection in towns | ||||
| 	std::vector<int> terrCosts; //default costs of going through terrains: dirt, sand, grass, snow, swamp, rough, subterranean, lava, water, rock; -1 means terrain is imapassable | ||||
|  | ||||
| 	int chooseSecSkill(const std::set<int> & possibles) const; //picks secondary skill out from given possibilities | ||||
| 	CHeroClass(); //c-tor | ||||
| @@ -78,7 +77,7 @@ public: | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & skillLimit & name & aggression & initialAttack & initialDefence & initialPower & initialKnowledge & primChance | ||||
| 			& proSec & selectionProbability & terrCosts & alignment; | ||||
| 			& proSec & selectionProbability & alignment; | ||||
| 	} | ||||
| 	EAlignment::EAlignment getAlignment(); | ||||
| }; | ||||
| @@ -125,6 +124,9 @@ public: | ||||
| 	std::vector< ConstTransitivePtr<CHero> > heroes; //changed from nodrze | ||||
| 	std::vector<CHeroClass *> heroClasses; | ||||
| 	std::vector<ui64> expPerLevel; //expPerLEvel[i] is amount of exp needed to reach level i; if it is not in this vector, multiplicate last value by 1,2 to get next value | ||||
|  | ||||
| 	 //default costs of going through terrains: dirt, sand, grass, snow, swamp, rough, subterranean, lava, water, rock; -1 means terrain is imapassable | ||||
| 	std::vector<int> terrCosts; | ||||
| 	 | ||||
| 	struct SBallisticsLevelInfo | ||||
| 	{ | ||||
| @@ -142,8 +144,6 @@ public: | ||||
| 	std::map<int, CObstacleInfo> obstacles; //info about obstacles that may be placed on battlefield | ||||
| 	std::map<int, CObstacleInfo> absoluteObstacles; //info about obstacles that may be placed on battlefield | ||||
|  | ||||
| 	std::vector<int> nativeTerrains; //info about native terrains of different factions | ||||
|  | ||||
| 	void loadObstacles(); //loads info about obstacles | ||||
|  | ||||
| 	std::vector<SPuzzleInfo> puzzleInfo[GameConstants::F_NUMBER]; //descriptions of puzzles | ||||
| @@ -161,7 +161,7 @@ public: | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & heroClasses & heroes & expPerLevel & ballistics & nativeTerrains & puzzleInfo; | ||||
| 		h & heroClasses & heroes & expPerLevel & ballistics & terrCosts & puzzleInfo; | ||||
| 		h & obstacles & absoluteObstacles; | ||||
| 		if(!h.saving) | ||||
| 		{ | ||||
|   | ||||
| @@ -53,6 +53,7 @@ void CModHandler::loadConfigFromFile (std::string name) | ||||
| 	settings.CREEP_SIZE = hardcodedFeatures["CREEP_SIZE"].Float(); | ||||
| 	settings.WEEKLY_GROWTH = hardcodedFeatures["WEEKLY_GROWTH_PERCENT"].Float(); | ||||
| 	settings.NEUTRAL_STACK_EXP = hardcodedFeatures["NEUTRAL_STACK_EXP_DAILY"].Float(); | ||||
| 	settings.MAX_BUILDING_PER_TURN = hardcodedFeatures["MAX_BUILDING_PER_TURN"].Float(); | ||||
| 	settings.DWELLINGS_ACCUMULATE_CREATURES = hardcodedFeatures["DWELLINGS_ACCUMULATE_CREATURES"].Bool(); | ||||
| 	settings.ALL_CREATURES_GET_DOUBLE_MONTHS = hardcodedFeatures["ALL_CREATURES_GET_DOUBLE_MONTHS"].Bool(); | ||||
|  | ||||
| @@ -125,11 +126,7 @@ CCreature * CModHandler::loadCreature (const JsonNode &node) | ||||
| 	if (!value->isNull()) | ||||
| 		cre->addBonus(value->Float(), Bonus::CASTS); | ||||
|  | ||||
| 	value = &node["doubleWide"]; | ||||
| 	if (!value->isNull()) | ||||
| 		cre->doubleWide = value->Bool(); | ||||
| 	else | ||||
| 		cre->doubleWide = false; | ||||
| 	cre->doubleWide = value->Bool(); | ||||
|  | ||||
| 	value = &node["abilities"]; | ||||
| 	if (!value->isNull()) | ||||
| @@ -191,7 +188,7 @@ void CModHandler::recreateHandlers() | ||||
| { | ||||
| 	//TODO: consider some template magic to unify all handlers? | ||||
|  | ||||
| 	VLC->arth->artifacts.clear();  | ||||
| 	VLC->arth->artifacts.clear(); | ||||
| 	VLC->creh->creatures.clear(); //TODO: what about items from original game? | ||||
|  | ||||
| 	BOOST_FOREACH (auto mod, activeMods) | ||||
| @@ -213,4 +210,4 @@ void CModHandler::recreateHandlers() | ||||
|  | ||||
| CModHandler::~CModHandler() | ||||
| { | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -83,6 +83,7 @@ public: | ||||
| 		int CREEP_SIZE; // neutral stacks won't grow beyond this number | ||||
| 		int WEEKLY_GROWTH; //percent | ||||
| 		int NEUTRAL_STACK_EXP;  | ||||
| 		int MAX_BUILDING_PER_TURN; | ||||
| 		bool DWELLINGS_ACCUMULATE_CREATURES; | ||||
| 		bool ALL_CREATURES_GET_DOUBLE_MONTHS; | ||||
|  | ||||
|   | ||||
| @@ -553,7 +553,10 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		ret = type->heroClass->terrCosts[from.tertype]; | ||||
| 		assert(vstd::contains(VLC->townh->factions, type->heroType / 2)); | ||||
| 		if (VLC->townh->factions[type->heroType / 2].nativeTerrain != from.tertype) //non-native terrain | ||||
| 			ret = VLC->heroh->terrCosts[from.tertype]; | ||||
|  | ||||
| 		ret = std::max(ret - 25*unsigned(getSecSkillLevel(CGHeroInstance::PATHFINDING)), 100u); //reduce 25% of terrain penalty for each pathfinding level | ||||
| 	} | ||||
| 	return ret; | ||||
| @@ -1820,11 +1823,11 @@ void CGDwelling::fightOver(const CGHeroInstance *h, BattleResult *result) const | ||||
|  | ||||
| int CGTownInstance::getSightRadious() const //returns sight distance | ||||
| { | ||||
| 	if (subID == 2) //tower | ||||
| 	if (subID == ETownType::TOWER) | ||||
| 	{ | ||||
| 		if ((builtBuildings.find(26)) != builtBuildings.end()) //skyship | ||||
| 		if (hasBuilt(EBuilding::GRAIL)) //skyship | ||||
| 			return -1; //entire map | ||||
| 		else if ((builtBuildings.find(21)) != builtBuildings.end()) //lookout tower | ||||
| 		else if (hasBuilt(EBuilding::LOOKOUT_TOWER)) //lookout tower | ||||
| 			return 20; | ||||
| 	} | ||||
| 	return 5; | ||||
| @@ -1854,38 +1857,38 @@ void CGTownInstance::setPropertyDer(ui8 what, ui32 val) | ||||
| } | ||||
| CGTownInstance::EFortLevel CGTownInstance::fortLevel() const //0 - none, 1 - fort, 2 - citadel, 3 - castle | ||||
| { | ||||
| 	if((builtBuildings.find(9))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::CASTLE)) | ||||
| 		return CASTLE; | ||||
| 	if((builtBuildings.find(8))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::CITADEL)) | ||||
| 		return CITADEL; | ||||
| 	if((builtBuildings.find(7))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::FORT)) | ||||
| 		return FORT; | ||||
| 	return NONE; | ||||
| } | ||||
|  | ||||
| int CGTownInstance::hallLevel() const // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol | ||||
| { | ||||
| 	if ((builtBuildings.find(13))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::CAPITOL)) | ||||
| 		return 3; | ||||
| 	if ((builtBuildings.find(12))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::CITY_HALL)) | ||||
| 		return 2; | ||||
| 	if ((builtBuildings.find(11))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::TOWN_HALL)) | ||||
| 		return 1; | ||||
| 	if ((builtBuildings.find(10))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::VILLAGE_HALL)) | ||||
| 		return 0; | ||||
| 	return -1; | ||||
| } | ||||
| int CGTownInstance::mageGuildLevel() const | ||||
| { | ||||
| 	if ((builtBuildings.find(4))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::MAGES_GUILD_5)) | ||||
| 		return 5; | ||||
| 	if ((builtBuildings.find(3))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::MAGES_GUILD_4)) | ||||
| 		return 4; | ||||
| 	if ((builtBuildings.find(2))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::MAGES_GUILD_3)) | ||||
| 		return 3; | ||||
| 	if ((builtBuildings.find(1))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::MAGES_GUILD_2)) | ||||
| 		return 2; | ||||
| 	if ((builtBuildings.find(0))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::MAGES_GUILD_1)) | ||||
| 		return 1; | ||||
| 	return 0; | ||||
| } | ||||
| @@ -1896,7 +1899,7 @@ int CGTownInstance::creatureDwellingLevel(int dwelling) const | ||||
| 		return -1; | ||||
| 	for (int i=0; ; i++) | ||||
| 	{ | ||||
| 		if (!vstd::contains(builtBuildings, 30+dwelling+i*GameConstants::CREATURES_PER_TOWN)) | ||||
| 		if (!hasBuilt(EBuilding::DWELL_FIRST+dwelling+i*GameConstants::CREATURES_PER_TOWN)) | ||||
| 			return i-1; | ||||
| 	} | ||||
| } | ||||
| @@ -1915,7 +1918,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const | ||||
|  | ||||
| 	if (level<0 || level >=GameConstants::CREATURES_PER_TOWN) | ||||
| 		return ret; | ||||
| 	if (!vstd::contains(builtBuildings, EBuilding::DWELL_FIRST+level)) | ||||
| 	if (!hasBuilt(EBuilding::DWELL_FIRST+level)) | ||||
| 		return ret; //no dwelling | ||||
|  | ||||
| 	const CCreature *creature = VLC->creh->creatures[creatures[level].second.back()]; | ||||
| @@ -1924,17 +1927,17 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const | ||||
|  | ||||
| 	ret.entries.push_back(GrowthInfo::Entry(VLC->generaltexth->allTexts[590], base));// \n\nBasic growth %d" | ||||
|  | ||||
| 	if ( vstd::contains(builtBuildings, EBuilding::CASTLE)) | ||||
| 	if (hasBuilt(EBuilding::CASTLE)) | ||||
| 		ret.entries.push_back(GrowthInfo::Entry(subID, EBuilding::CASTLE, castleBonus = base)); | ||||
| 	else if ( vstd::contains(builtBuildings, EBuilding::CITADEL)) | ||||
| 	else if (hasBuilt(EBuilding::CITADEL)) | ||||
| 		ret.entries.push_back(GrowthInfo::Entry(subID, EBuilding::CITADEL, castleBonus = base / 2)); | ||||
|  | ||||
| 	if(town->hordeLvl[0] == level)//horde 1 | ||||
| 		if( vstd::contains(builtBuildings, EBuilding::HORDE_1) || vstd::contains(builtBuildings, EBuilding::HORDE_1_UPGR)) | ||||
| 		if(hasBuilt(EBuilding::HORDE_1)) | ||||
| 			ret.entries.push_back(GrowthInfo::Entry(subID, EBuilding::HORDE_1, creature->hordeGrowth)); | ||||
|  | ||||
| 	if(town->hordeLvl[1] == level)//horde 2 | ||||
| 		if(vstd::contains(builtBuildings, EBuilding::HORDE_2) || vstd::contains(builtBuildings, EBuilding::HORDE_2_UPGR)) | ||||
| 		if(hasBuilt(EBuilding::HORDE_2)) | ||||
| 			ret.entries.push_back(GrowthInfo::Entry(subID, EBuilding::HORDE_2, creature->hordeGrowth)); | ||||
|  | ||||
| 	int dwellingBonus = 0; | ||||
| @@ -1958,7 +1961,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const | ||||
| 	BOOST_FOREACH(const Bonus *b, *bonuses2) | ||||
| 		ret.entries.push_back(GrowthInfo::Entry(b->Description() + " %+d", b->val * (base + castleBonus) / 100)); | ||||
|  | ||||
| 	if(vstd::contains(builtBuildings, EBuilding::GRAIL)) //grail - +50% to ALL (so far added) growth | ||||
| 	if(hasBuilt(EBuilding::GRAIL)) //grail - +50% to ALL (so far added) growth | ||||
| 		ret.entries.push_back(GrowthInfo::Entry(subID, EBuilding::GRAIL, ret.totalGrowth() / 2)); | ||||
|  | ||||
| 	return ret; | ||||
| @@ -1967,25 +1970,26 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const | ||||
| int CGTownInstance::dailyIncome() const | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	if ((builtBuildings.find(26))!=builtBuildings.end()) | ||||
| 	if (hasBuilt(EBuilding::GRAIL)) | ||||
| 		ret+=5000; | ||||
| 	if ((builtBuildings.find(13))!=builtBuildings.end()) | ||||
|  | ||||
| 	if (hasBuilt(EBuilding::CAPITOL)) | ||||
| 		ret+=4000; | ||||
| 	else if ((builtBuildings.find(12))!=builtBuildings.end()) | ||||
| 	else if (hasBuilt(EBuilding::CITY_HALL)) | ||||
| 		ret+=2000; | ||||
| 	else if ((builtBuildings.find(11))!=builtBuildings.end()) | ||||
| 	else if (hasBuilt(EBuilding::TOWN_HALL)) | ||||
| 		ret+=1000; | ||||
| 	else if ((builtBuildings.find(10))!=builtBuildings.end()) | ||||
| 	else if (hasBuilt(EBuilding::VILLAGE_HALL)) | ||||
| 		ret+=500; | ||||
| 	return ret; | ||||
| } | ||||
| bool CGTownInstance::hasFort() const | ||||
| { | ||||
| 	return (builtBuildings.find(7))!=builtBuildings.end(); | ||||
| 	return hasBuilt(EBuilding::FORT); | ||||
| } | ||||
| bool CGTownInstance::hasCapitol() const | ||||
| { | ||||
| 	return (builtBuildings.find(13))!=builtBuildings.end(); | ||||
| 	return hasBuilt(EBuilding::CAPITOL); | ||||
| } | ||||
| CGTownInstance::CGTownInstance() | ||||
| 	:IShipyard(this), IMarket(this) | ||||
| @@ -2006,8 +2010,10 @@ int CGTownInstance::spellsAtLevel(int level, bool checkGuild) const | ||||
| 	if(checkGuild && mageGuildLevel() < level) | ||||
| 		return 0; | ||||
| 	int ret = 6 - level; //how many spells are available at this level | ||||
| 	if(subID == 2   &&   vstd::contains(builtBuildings,22)) //magic library in Tower | ||||
|  | ||||
| 	if (hasBuilt(EBuilding::LIBRARY, ETownType::TOWER)) | ||||
| 		ret++; | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| @@ -2074,7 +2080,7 @@ void CGTownInstance::initObj() | ||||
| 	blockVisit = true; | ||||
| 	hoverName = name + ", " + town->Name(); | ||||
|  | ||||
| 	if (subID == 5) | ||||
| 	if (subID == ETownType::DUNGEON) | ||||
| 		creatures.resize(GameConstants::CREATURES_PER_TOWN+1);//extra dwelling for Dungeon | ||||
| 	else | ||||
| 		creatures.resize(GameConstants::CREATURES_PER_TOWN); | ||||
| @@ -2109,7 +2115,9 @@ void CGTownInstance::newTurn() const | ||||
| { | ||||
| 	if (cb->getDate(1) == 1) //reset on new week | ||||
| 	{ | ||||
| 		if (vstd::contains(builtBuildings,17) && subID == 1 && cb->getDate(0) != 1 && (tempOwner < GameConstants::PLAYER_LIMIT) )//give resources for Rampart, Mystic Pond | ||||
| 		//give resources for Rampart, Mystic Pond | ||||
| 		if (hasBuilt(EBuilding::MYSTIC_POND, ETownType::RAMPART) | ||||
| 			&& cb->getDate(0) != 1 && (tempOwner < GameConstants::PLAYER_LIMIT)) | ||||
| 		{ | ||||
| 			int resID = rand()%4+2;//bonus to random rare resource | ||||
| 			resID = (resID==2)?1:resID; | ||||
| @@ -2119,10 +2127,10 @@ void CGTownInstance::newTurn() const | ||||
| 			cb->setObjProperty (id, 15, resVal); | ||||
| 		} | ||||
|  | ||||
| 		if ( subID == 5 ) | ||||
| 		if ( subID == ETownType::DUNGEON ) | ||||
| 			for (std::vector<CGTownBuilding*>::const_iterator i = bonusingBuildings.begin(); i!=bonusingBuildings.end(); i++) | ||||
| 		{ | ||||
| 			if ((*i)->ID == 21) | ||||
| 			if ((*i)->ID == EBuilding::MANA_VORTEX) | ||||
| 				cb->setObjProperty (id, 12, (*i)->id); //reset visitors for Mana Vortex | ||||
| 		} | ||||
|  | ||||
| @@ -2249,7 +2257,7 @@ int CGTownInstance::getBoatType() const | ||||
|  | ||||
| int CGTownInstance::getMarketEfficiency() const | ||||
| { | ||||
| 	if(!vstd::contains(builtBuildings, 14)) | ||||
| 	if (!hasBuilt(EBuilding::MARKETPLACE)) | ||||
| 		return 0; | ||||
|  | ||||
| 	const PlayerState *p = cb->getPlayer(tempOwner); | ||||
| @@ -2257,7 +2265,7 @@ int CGTownInstance::getMarketEfficiency() const | ||||
|  | ||||
| 	int marketCount = 0; | ||||
| 	BOOST_FOREACH(const CGTownInstance *t, p->towns) | ||||
| 		if(vstd::contains(t->builtBuildings, 14)) | ||||
| 		if(t->hasBuilt(EBuilding::MARKETPLACE)) | ||||
| 			marketCount++; | ||||
|  | ||||
| 	return marketCount; | ||||
| @@ -2269,16 +2277,22 @@ bool CGTownInstance::allowsTrade(EMarketMode::EMarketMode mode) const | ||||
| 	{ | ||||
| 	case EMarketMode::RESOURCE_RESOURCE: | ||||
| 	case EMarketMode::RESOURCE_PLAYER: | ||||
| 		return vstd::contains(builtBuildings, 14); // 	marketplace | ||||
| 		return hasBuilt(EBuilding::MARKETPLACE); | ||||
|  | ||||
| 	case EMarketMode::ARTIFACT_RESOURCE: | ||||
| 	case EMarketMode::RESOURCE_ARTIFACT: | ||||
| 		return (subID == 2 || subID == 5 || subID == 8) && vstd::contains(builtBuildings, 17);//artifact merchants | ||||
| 		return hasBuilt(EBuilding::ARTIFACT_MERCHANT, ETownType::TOWER) | ||||
| 		    || hasBuilt(EBuilding::ARTIFACT_MERCHANT, ETownType::DUNGEON) | ||||
| 		    || hasBuilt(EBuilding::ARTIFACT_MERCHANT, ETownType::CONFLUX); | ||||
|  | ||||
| 	case EMarketMode::CREATURE_RESOURCE: | ||||
| 		return subID == 6 && vstd::contains(builtBuildings, 21); //Freelancer's guild | ||||
| 		return hasBuilt(EBuilding::FREELANCERS_GUILD, ETownType::STRONGHOLD); | ||||
|  | ||||
| 	case EMarketMode::CREATURE_UNDEAD: | ||||
| 		return subID == 4 && vstd::contains(builtBuildings, 22);//Skeleton transformer | ||||
| 		return hasBuilt(EBuilding::SKELETON_TRANSFORMER, ETownType::NECROPOLIS); | ||||
|  | ||||
| 	case EMarketMode::RESOURCE_SKILL: | ||||
| 		return subID == 8 && vstd::contains(builtBuildings, 21);//Magic University | ||||
| 		return hasBuilt(EBuilding::MAGIC_UNIVERSITY, ETownType::CONFLUX); | ||||
| 	default: | ||||
| 		assert(0); | ||||
| 		return false; | ||||
| @@ -2326,50 +2340,50 @@ void CGTownInstance::recreateBuildingsBonuses() | ||||
| 	BOOST_FOREACH(Bonus *b, bl) | ||||
| 		removeBonus(b); | ||||
|  | ||||
| 	//tricky! -> checks tavern only if no bratherhood of sword or not a castle | ||||
| 	if(subID != ETownType::CASTLE || !addBonusIfBuilt(EBuilding::BROTHERHOOD, Bonus::MORALE, +2)) | ||||
| 		addBonusIfBuilt(EBuilding::TAVERN, Bonus::MORALE, +1); | ||||
|  | ||||
| 	if(subID != 0 || !addBonusIfBuilt(22, Bonus::MORALE, +2)) //tricky! -> checks tavern only if no bratherhood of sword or not a castle | ||||
| 		addBonusIfBuilt(5, Bonus::MORALE, +1); | ||||
|  | ||||
| 	if(subID == 0) //castle | ||||
| 	if(subID == ETownType::CASTLE) //castle | ||||
| 	{ | ||||
| 		addBonusIfBuilt(17, Bonus::SEA_MOVEMENT, +500, make_shared<CPropagatorNodeType>(PLAYER)); //lighthouses | ||||
| 		addBonusIfBuilt(26, Bonus::MORALE, +2, make_shared<CPropagatorNodeType>(PLAYER)); //colossus | ||||
| 		addBonusIfBuilt(EBuilding::LIGHTHOUSE, Bonus::SEA_MOVEMENT, +500, make_shared<CPropagatorNodeType>(PLAYER)); | ||||
| 		addBonusIfBuilt(EBuilding::GRAIL,      Bonus::MORALE, +2, make_shared<CPropagatorNodeType>(PLAYER)); //colossus | ||||
| 	} | ||||
| 	else if(subID == 1) //rampart | ||||
| 	else if(subID == ETownType::RAMPART) //rampart | ||||
| 	{ | ||||
| 		addBonusIfBuilt(21, Bonus::LUCK, +2); //fountain of fortune | ||||
| 		addBonusIfBuilt(21, Bonus::LUCK, +2, make_shared<CPropagatorNodeType>(PLAYER)); //guardian spirit | ||||
| 		addBonusIfBuilt(EBuilding::FOUNTAIN_OF_FORTUNE, Bonus::LUCK, +2); //fountain of fortune | ||||
| 		addBonusIfBuilt(EBuilding::GRAIL, Bonus::LUCK, +2, make_shared<CPropagatorNodeType>(PLAYER)); //guardian spirit | ||||
| 	} | ||||
| 	else if(subID == 2) //tower | ||||
| 	else if(subID == ETownType::RAMPART) //tower | ||||
| 	{ | ||||
| 		addBonusIfBuilt(26, Bonus::PRIMARY_SKILL, +15, PrimarySkill::KNOWLEDGE); //grail | ||||
| 		addBonusIfBuilt(EBuilding::GRAIL, Bonus::PRIMARY_SKILL, +15, PrimarySkill::KNOWLEDGE); //grail | ||||
| 	} | ||||
| 	else if(subID == 3) //Inferno | ||||
| 	else if(subID == ETownType::TOWER) //Inferno | ||||
| 	{ | ||||
| 		addBonusIfBuilt(21, Bonus::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER); //Brimstone Clouds | ||||
| 		addBonusIfBuilt(EBuilding::STORMCLOUDS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER); //Brimstone Clouds | ||||
| 	} | ||||
| 	else if(subID == 4) //necropolis | ||||
| 	else if(subID == ETownType::NECROPOLIS) //necropolis | ||||
| 	{ | ||||
| 		addBonusIfBuilt(17, Bonus::DARKNESS, +20); | ||||
| 		addBonusIfBuilt(21, Bonus::SECONDARY_SKILL_PREMY, +10, make_shared<CPropagatorNodeType>(PLAYER), CGHeroInstance::NECROMANCY); //necromancy amplifier | ||||
| 		addBonusIfBuilt(26, Bonus::SECONDARY_SKILL_PREMY, +20, make_shared<CPropagatorNodeType>(PLAYER), CGHeroInstance::NECROMANCY); //Soul prison | ||||
| 		addBonusIfBuilt(EBuilding::COVER_OF_DARKNESS,    Bonus::DARKNESS, +20); | ||||
| 		addBonusIfBuilt(EBuilding::NECROMANCY_AMPLIFIER, Bonus::SECONDARY_SKILL_PREMY, +10, make_shared<CPropagatorNodeType>(PLAYER), CGHeroInstance::NECROMANCY); //necromancy amplifier | ||||
| 		addBonusIfBuilt(EBuilding::GRAIL, Bonus::SECONDARY_SKILL_PREMY, +20, make_shared<CPropagatorNodeType>(PLAYER), CGHeroInstance::NECROMANCY); //Soul prison | ||||
| 	} | ||||
| 	else if(subID == 5) //Dungeon | ||||
| 	else if(subID == ETownType::DUNGEON) //Dungeon | ||||
| 	{ | ||||
| 		addBonusIfBuilt(26, Bonus::PRIMARY_SKILL, +12, PrimarySkill::SPELL_POWER); //grail | ||||
| 		addBonusIfBuilt(EBuilding::GRAIL, Bonus::PRIMARY_SKILL, +12, PrimarySkill::SPELL_POWER); //grail | ||||
| 	} | ||||
| 	else if(subID == 6) //Stronghold | ||||
| 	else if(subID == ETownType::STRONGHOLD) //Stronghold | ||||
| 	{ | ||||
| 		addBonusIfBuilt(26, Bonus::PRIMARY_SKILL, +20, PrimarySkill::ATTACK); //grail | ||||
| 		addBonusIfBuilt(EBuilding::GRAIL, Bonus::PRIMARY_SKILL, +20, PrimarySkill::ATTACK); //grail | ||||
| 	} | ||||
| 	else if(subID == 7) //Fortress | ||||
| 	else if(subID == ETownType::FORTRESS) //Fortress | ||||
| 	{ | ||||
| 		addBonusIfBuilt(21, Bonus::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE); //Glyphs of Fear | ||||
| 		addBonusIfBuilt(22, Bonus::PRIMARY_SKILL, +2, PrimarySkill::ATTACK); //Blood Obelisk | ||||
| 		addBonusIfBuilt(26, Bonus::PRIMARY_SKILL, +10, PrimarySkill::ATTACK); //grail | ||||
| 		addBonusIfBuilt(26, Bonus::PRIMARY_SKILL, +10, PrimarySkill::DEFENSE); //grail | ||||
| 		addBonusIfBuilt(EBuilding::GLYPHS_OF_FEAR, Bonus::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE); //Glyphs of Fear | ||||
| 		addBonusIfBuilt(EBuilding::BLOOD_OBELISK,  Bonus::PRIMARY_SKILL, +2, PrimarySkill::ATTACK); //Blood Obelisk | ||||
| 		addBonusIfBuilt(EBuilding::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::ATTACK); //grail | ||||
| 		addBonusIfBuilt(EBuilding::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::DEFENSE); //grail | ||||
| 	} | ||||
| 	else if(subID == 8) | ||||
| 	else if(subID == ETownType::CONFLUX) | ||||
| 	{ | ||||
|  | ||||
| 	} | ||||
| @@ -2382,7 +2396,7 @@ bool CGTownInstance::addBonusIfBuilt(int building, int type, int val, int subtyp | ||||
|  | ||||
| bool CGTownInstance::addBonusIfBuilt(int building, int type, int val, TPropagatorPtr prop, int subtype /*= -1*/) | ||||
| { | ||||
| 	if(vstd::contains(builtBuildings, building)) | ||||
| 	if(hasBuilt(building)) | ||||
| 	{ | ||||
| 		std::ostringstream descr; | ||||
| 		descr << VLC->generaltexth->buildings[subID][building].first << " "; | ||||
| @@ -2466,11 +2480,19 @@ const CArmedInstance * CGTownInstance::getUpperArmy() const | ||||
| 	return this; | ||||
| } | ||||
|  | ||||
| bool CGTownInstance::hasBuilt(int buildingID, int townID) const | ||||
| { | ||||
| 	if (townID == town->typeID || townID == ETownType::ANY) | ||||
| 		return hasBuilt(buildingID); | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool CGTownInstance::hasBuilt(int buildingID) const | ||||
| { | ||||
| 	return vstd::contains(builtBuildings, buildingID); | ||||
| } | ||||
|  | ||||
|  | ||||
| bool CGVisitableOPH::wasVisited (const CGHeroInstance * h) const | ||||
| { | ||||
| 	return vstd::contains(visitors, h->id); | ||||
| @@ -2828,13 +2850,13 @@ void COPWBonus::setProperty(ui8 what, ui32 val) | ||||
| void COPWBonus::onHeroVisit (const CGHeroInstance * h) const | ||||
| { | ||||
| 	int heroID = h->id; | ||||
| 	if (town->builtBuildings.find(ID) != town->builtBuildings.end()) | ||||
| 	if (town->hasBuilt(ID)) | ||||
| 	{ | ||||
| 		InfoWindow iw; | ||||
| 		iw.player = h->tempOwner; | ||||
| 		switch (town->subID) | ||||
| 		{ | ||||
| 			case 0: //Stables | ||||
| 			case ETownType::CASTLE: //Stables | ||||
| 				if (!h->hasBonusFrom(Bonus::OBJECT, 94)) //does not stack with advMap Stables | ||||
| 				{ | ||||
| 					GiveBonus gb; | ||||
| @@ -2845,7 +2867,7 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const | ||||
| 					cb->showInfoDialog(&iw); | ||||
| 				} | ||||
| 				break; | ||||
| 			case 5: //Mana Vortex | ||||
| 			case ETownType::DUNGEON: //Mana Vortex | ||||
| 				if (visitors.empty() && h->mana <= h->manaLimit() * 2) | ||||
| 				{ | ||||
| 					cb->setManaPoints (heroID, 2 * h->manaLimit()); | ||||
| @@ -2872,34 +2894,34 @@ void CTownBonus::setProperty (ui8 what, ui32 val) | ||||
| void CTownBonus::onHeroVisit (const CGHeroInstance * h) const | ||||
| { | ||||
| 	int heroID = h->id; | ||||
| 	if ((town->builtBuildings.find(ID) != town->builtBuildings.end()) && (visitors.find(heroID) == visitors.end())) | ||||
| 	if (town->hasBuilt(ID) && visitors.find(heroID) == visitors.end()) | ||||
| 	{ | ||||
| 		InfoWindow iw; | ||||
| 		int what=0, val=0, mid=0; | ||||
| 		switch (ID) | ||||
| 		{ | ||||
| 			case 23: | ||||
| 			case EBuilding::SPECIAL_4: | ||||
| 				switch(town->subID) | ||||
| 				{ | ||||
| 					case 2: //wall | ||||
| 					case ETownType::TOWER: //wall | ||||
| 						what = 3; | ||||
| 						val = 1; | ||||
| 						mid = 581; | ||||
| 						iw.components.push_back (Component(Component::PRIM_SKILL, 3, 1, 0)); | ||||
| 						break; | ||||
| 					case 3: //order of fire | ||||
| 					case ETownType::INFERNO: //order of fire | ||||
| 						what = 2; | ||||
| 						val = 1; | ||||
| 						mid = 582; | ||||
| 						iw.components.push_back (Component(Component::PRIM_SKILL, 2, 1, 0)); | ||||
| 						break; | ||||
| 					case 6://hall of valhalla | ||||
| 					case ETownType::STRONGHOLD://hall of valhalla | ||||
| 						what = 0; | ||||
| 						val = 1; | ||||
| 						mid = 584; | ||||
| 						iw.components.push_back (Component(Component::PRIM_SKILL, 0, 1, 0)); | ||||
| 						break; | ||||
| 					case 5://academy of battle scholars | ||||
| 					case ETownType::DUNGEON://academy of battle scholars | ||||
| 						what = 4; | ||||
| 						val = 1000*(100+h->getSecSkillLevel(CGHeroInstance::LEARNING)*5)/100.0; | ||||
| 						mid = 583; | ||||
| @@ -2907,10 +2929,10 @@ void CTownBonus::onHeroVisit (const CGHeroInstance * h) const | ||||
| 						break; | ||||
| 				} | ||||
| 				break; | ||||
| 			case 17: | ||||
| 			case EBuilding::SPECIAL_1: | ||||
| 				switch(town->subID) | ||||
| 				{ | ||||
| 					case 7: //cage of warlords | ||||
| 					case ETownType::FORTRESS: //cage of warlords | ||||
| 						what = 1; | ||||
| 						val = 1; | ||||
| 						mid = 585; | ||||
| @@ -6919,7 +6941,7 @@ void CArmedInstance::updateMoraleBonusFromArmy() | ||||
| 	//-1 modifier for any Necropolis unit in army | ||||
| 	const ui8 UNDEAD_MODIFIER_ID = -2; | ||||
| 	Bonus *undeadModifier = getBonusList().getFirst(Selector::source(Bonus::ARMY, UNDEAD_MODIFIER_ID)); | ||||
|  	if(vstd::contains(factions,4)) | ||||
|  	if(vstd::contains(factions, ETownType::NECROPOLIS)) | ||||
| 	{ | ||||
| 		if(!undeadModifier) | ||||
| 			addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, UNDEAD_MODIFIER_ID, VLC->generaltexth->arraytxt[116])); | ||||
|   | ||||
| @@ -1,11 +1,5 @@ | ||||
| #pragma once | ||||
|  | ||||
|  | ||||
| #ifndef _MSC_VER | ||||
| #include "CHeroHandler.h" | ||||
| #include "CTownHandler.h" | ||||
| #include "../lib/VCMI_Lib.h" | ||||
| #endif | ||||
| #include "../lib/CCreatureSet.h" | ||||
| #include "CArtHandler.h" | ||||
| #include "../lib/ConstTransitivePtr.h" | ||||
| @@ -636,7 +630,9 @@ public: | ||||
| 	GrowthInfo getGrowthInfo(int level) const; | ||||
| 	bool hasFort() const; | ||||
| 	bool hasCapitol() const; | ||||
| 	//checks if building is constructed and town has same subID | ||||
| 	bool hasBuilt(int buildingID) const; | ||||
| 	bool hasBuilt(int buildingID, int townID) const; | ||||
| 	int dailyIncome() const; //calculates daily income of this town | ||||
| 	int spellsAtLevel(int level, bool checkGuild) const; //levels are counted from 1 (1 - 5) | ||||
| 	void removeCapitols (ui8 owner) const; | ||||
|   | ||||
| @@ -17,8 +17,6 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| static std::string emptyStr = ""; | ||||
|  | ||||
| const std::string & CBuilding::Name() const | ||||
| { | ||||
| 	if(name.length()) | ||||
| @@ -26,7 +24,7 @@ const std::string & CBuilding::Name() const | ||||
| 	else if(vstd::contains(VLC->generaltexth->buildings,tid) && vstd::contains(VLC->generaltexth->buildings[tid],bid)) | ||||
| 		return VLC->generaltexth->buildings[tid][bid].first; | ||||
| 	tlog2 << "Warning: Cannot find name text for building " << bid << "for " << tid << "town.\n"; | ||||
| 	return emptyStr; | ||||
| 	return name; | ||||
| } | ||||
|  | ||||
| const std::string & CBuilding::Description() const | ||||
| @@ -36,7 +34,30 @@ const std::string & CBuilding::Description() const | ||||
| 	else if(vstd::contains(VLC->generaltexth->buildings,tid) && vstd::contains(VLC->generaltexth->buildings[tid],bid)) | ||||
| 		return VLC->generaltexth->buildings[tid][bid].second; | ||||
| 	tlog2 << "Warning: Cannot find description text for building " << bid << "for " << tid << "town.\n"; | ||||
| 	return emptyStr; | ||||
| 	return description; | ||||
| } | ||||
|  | ||||
| CBuilding::BuildingType CBuilding::getBase() const | ||||
| { | ||||
| 	const CBuilding * build = this; | ||||
| 	while (build->upgrade >= 0) | ||||
| 		build = VLC->townh->towns[build->tid].buildings[build->upgrade]; | ||||
|  | ||||
| 	return build->bid; | ||||
| } | ||||
|  | ||||
| si32 CBuilding::getDistance(CBuilding::BuildingType buildID) const | ||||
| { | ||||
| 	const CBuilding * build = VLC->townh->towns[tid].buildings[buildID]; | ||||
| 	int distance = 0; | ||||
| 	while (build->upgrade >= 0 && build != this) | ||||
| 	{ | ||||
| 		build = VLC->townh->towns[build->tid].buildings[build->upgrade]; | ||||
| 		distance++; | ||||
| 	} | ||||
| 	if (build == this) | ||||
| 		return distance; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| const std::string & CTown::Name() const | ||||
| @@ -65,9 +86,8 @@ JsonNode readBuilding(CLegacyConfigParser & parser) | ||||
| 	JsonNode ret; | ||||
| 	JsonNode & cost = ret["cost"]; | ||||
|  | ||||
| 	const std::string resources [] = {"wood", "mercury", "ore", "sulfur", "crystal", "gems", "gold"}; | ||||
|  | ||||
| 	BOOST_FOREACH(const std::string & resID, resources) | ||||
| 	//note: this code will try to parse mithril as well but wil always return 0 for it | ||||
| 	BOOST_FOREACH(const std::string & resID, GameConstants::RESOURCE_NAMES) | ||||
| 		cost[resID].Float() = parser.readNumber(); | ||||
|  | ||||
| 	parser.endLine(); | ||||
| @@ -139,17 +159,27 @@ void CTownHandler::loadBuilding(CTown &town, const JsonNode & source) | ||||
| { | ||||
| 	CBuilding * ret = new CBuilding; | ||||
|  | ||||
| 	ret->name = source["name"].String(); | ||||
| 	ret->description = source["description"].String(); | ||||
| 	static const std::string modes [] = {"normal", "auto", "special", "grail"}; | ||||
|  | ||||
| 	ret->mode = boost::find(modes, source["mode"].String()) - modes; | ||||
|  | ||||
| 	ret->tid = town.typeID; | ||||
| 	ret->bid = source["id"].Float(); | ||||
|  | ||||
| 	ret->name = source["name"].String(); | ||||
| 	ret->description = source["description"].String(); | ||||
| 	ret->resources = TResources(source["cost"]); | ||||
|  | ||||
| 	BOOST_FOREACH(const JsonNode &building, source["requires"].Vector()) | ||||
| 		ret->requirements.insert(building.Float()); | ||||
|  | ||||
| 	if (!source["upgrades"].isNull()) | ||||
| 	{ | ||||
| 		ret->requirements.insert(source["upgrades"].Float()); | ||||
| 		ret->upgrade = source["upgrades"].Float(); | ||||
| 	} | ||||
| 	else | ||||
| 		ret->upgrade = -1; | ||||
|  | ||||
| 	town.buildings[ret->bid] = ret; | ||||
| } | ||||
|  | ||||
| @@ -165,57 +195,39 @@ void CTownHandler::loadStructure(CTown &town, const JsonNode & source) | ||||
| { | ||||
| 	CStructure * ret = new CStructure; | ||||
|  | ||||
| 	ret->ID = source["id"].Float(); | ||||
| 	if (source["id"].isNull()) | ||||
| 	{ | ||||
| 		ret->building = nullptr; | ||||
| 		ret->buildable = nullptr; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		ret->building = town.buildings[source["id"].Float()]; | ||||
|  | ||||
| 		if (source["builds"].isNull()) | ||||
| 			ret->buildable = ret->building; | ||||
| 		else | ||||
| 			ret->buildable = town.buildings[source["builds"].Float()]; | ||||
| 	} | ||||
|  | ||||
| 	ret->pos.x = source["x"].Float(); | ||||
| 	ret->pos.y = source["y"].Float(); | ||||
| 	ret->pos.z = 0; | ||||
| 	ret->pos.z = source["z"].Float(); | ||||
|  | ||||
| 	ret->defName = source["defname"].String(); | ||||
| 	ret->hiddenUpgrade = source["hidden"].Bool(); | ||||
| 	ret->defName = source["animation"].String(); | ||||
| 	ret->borderName = source["border"].String(); | ||||
| 	ret->areaName = source["area"].String(); | ||||
|  | ||||
| 	ret->group = -1; | ||||
| 	ret->townID = town.typeID; | ||||
|  | ||||
| 	town.clientInfo.structures[ret->ID] = ret; | ||||
| 	town.clientInfo.structures.push_back(ret); | ||||
| } | ||||
|  | ||||
| void CTownHandler::loadStructures(CTown &town, const JsonNode & source) | ||||
| { | ||||
| 	BOOST_FOREACH(const JsonNode &node, source["structures"].Vector()) | ||||
| 	BOOST_FOREACH(const JsonNode &node, source.Vector()) | ||||
| 	{ | ||||
| 		loadStructure(town, node); | ||||
| 	} | ||||
|  | ||||
| 	// Read buildings blit order for that city | ||||
| 	int itr = 1; | ||||
| 	BOOST_FOREACH(const JsonNode &node, source["blit_order"].Vector()) | ||||
| 	{ | ||||
| 		int buildingID = node.Float(); | ||||
|  | ||||
| 		/* Find the building and set its order. */ | ||||
| 		auto i2 = town.clientInfo.structures.find(buildingID); | ||||
| 		if (i2 != (town.clientInfo.structures.end())) | ||||
| 			i2->second->pos.z = itr++; | ||||
| 	} | ||||
|  | ||||
| 	// Iterate for each group for that city | ||||
| 	int groupID = 0; | ||||
| 	BOOST_FOREACH(const JsonNode &group, source["groups"].Vector()) | ||||
| 	{ | ||||
| 		groupID++; | ||||
|  | ||||
| 		// Iterate for each bulding value in the group | ||||
| 		BOOST_FOREACH(const JsonNode &value, group.Vector()) | ||||
| 		{ | ||||
| 			auto buildingIter = town.clientInfo.structures.find(value.Float()); | ||||
|  | ||||
| 			if (buildingIter != town.clientInfo.structures.end()) | ||||
| 			{ | ||||
| 				buildingIter->second->group = groupID; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CTownHandler::loadTownHall(CTown &town, const JsonNode & source) | ||||
| @@ -238,23 +250,27 @@ void CTownHandler::loadTownHall(CTown &town, const JsonNode & source) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CTownHandler::loadTown(std::vector<CTown> &towns, const JsonNode & source) | ||||
| void CTownHandler::loadClientData(CTown &town, const JsonNode & source) | ||||
| { | ||||
| 	towns.push_back(CTown()); | ||||
| 	CTown & town = towns.back(); | ||||
| 	town.clientInfo.hallBackground = source["hallBackground"].String(); | ||||
| 	town.clientInfo.musicTheme = source["musicTheme"].String(); | ||||
| 	town.clientInfo.townBackground = source["townBackground"].String(); | ||||
| 	town.clientInfo.guildWindow = source["guildWindow"].String(); | ||||
| 	town.clientInfo.buildingsIcons = source["buildingsIcons"].String(); | ||||
|  | ||||
| 	//TODO: allow loading name and names vector from json and from h3 txt's | ||||
|  | ||||
| 	town.typeID = towns.size() - 1; | ||||
| 	loadTownHall(town,   source["hallSlots"]); | ||||
| 	loadStructures(town, source["structures"]); | ||||
| } | ||||
|  | ||||
| void CTownHandler::loadTown(CTown &town, const JsonNode & source) | ||||
| { | ||||
| 	town.bonus = town.typeID; | ||||
| 	if (town.bonus==8) | ||||
| 		town.bonus=3; | ||||
|  | ||||
| 	town.clientInfo.hallBackground = source["hallBackground"].String(); | ||||
| 	town.mageLevel = source["mage_guild"].Float(); | ||||
| 	town.primaryRes  = source["primary_resource"].Float(); | ||||
| 	town.warMachine = source["war_machine"].Float(); | ||||
| 	town.mageLevel = source["mageGuild"].Float(); | ||||
| 	town.primaryRes  = source["primaryResource"].Float(); | ||||
| 	town.warMachine = source["warMachine"].Float(); | ||||
|  | ||||
| 	//  Horde building creature level | ||||
| 	BOOST_FOREACH(const JsonNode &node, source["horde"].Vector()) | ||||
| @@ -272,20 +288,37 @@ void CTownHandler::loadTown(std::vector<CTown> &towns, const JsonNode & source) | ||||
| 		town.creatures.push_back(level); | ||||
| 	} | ||||
|  | ||||
| 	loadTownHall(town,   source["hallSlots"]); | ||||
| 	loadBuildings(town,  source["buildings"]); | ||||
| 	loadStructures(town, source); | ||||
| 	loadBuildings(town, source["buildings"]); | ||||
| 	loadClientData(town,source); | ||||
| } | ||||
|  | ||||
| void CTownHandler::loadTowns(std::vector<CTown> &towns, const JsonNode & source) | ||||
| void CTownHandler::loadFactions(const JsonNode &source) | ||||
| { | ||||
| 	BOOST_FOREACH(const JsonNode & node, source.Vector()) | ||||
| 	BOOST_FOREACH(auto & node, source.Struct()) | ||||
| 	{ | ||||
| 		loadTown(towns, node); | ||||
| 	} | ||||
| 		int id = node.second["index"].Float(); | ||||
| 		CFaction & faction = factions[id]; | ||||
|  | ||||
| 	// ensure that correct number of town have been loaded. Safe to remove | ||||
| 	assert(towns.size() == GameConstants::F_NUMBER); | ||||
| 		faction.factionID = id; | ||||
| 		faction.name = node.first; | ||||
|  | ||||
| 		faction.creatureBg120 = node.second["creatureBackground"]["120px"].String(); | ||||
| 		faction.creatureBg130 = node.second["creatureBackground"]["130px"].String(); | ||||
|  | ||||
| 		if (!node.second["nativeTerrain"].isNull()) | ||||
| 		{ | ||||
| 			//get terrain as string and converto to numeric ID | ||||
| 			faction.nativeTerrain = boost::find(GameConstants::TERRAIN_NAMES, node.second["nativeTerrain"].String()) - GameConstants::TERRAIN_NAMES; | ||||
| 		} | ||||
| 		else | ||||
| 			faction.nativeTerrain = -1; | ||||
|  | ||||
| 		if (!node.second["town"].isNull()) | ||||
| 		{ | ||||
| 			towns[id].typeID = id; | ||||
| 			loadTown(towns[id], node.second["town"]); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CTownHandler::load() | ||||
| @@ -295,23 +328,33 @@ void CTownHandler::load() | ||||
| 	JsonNode legacyConfig; | ||||
| 	loadLegacyData(legacyConfig); | ||||
|  | ||||
| 	//hardocoded list of H3 factions. Should be only used to convert H3 configs | ||||
| 	static const std::string factionName [GameConstants::F_NUMBER] = | ||||
| 	{ | ||||
| 		"castle", "rampart", "tower", | ||||
| 		"inferno", "necropolis", "dungeon", | ||||
| 		"stronghold", "fortress", "conflux" | ||||
| 	}; | ||||
|  | ||||
| 	// semi-manually merge legacy config with towns json | ||||
| 	// legacy config have only one item: town buildings stored in 2d vector | ||||
| 	size_t townsToMerge = std::min(buildingsConf["towns"].Vector().size(), legacyConfig.Vector().size()); | ||||
| 	for (size_t i=0; i< townsToMerge; i++) | ||||
|  | ||||
| 	for (size_t i=0; i< legacyConfig.Vector().size(); i++) | ||||
| 	{ | ||||
| 		JsonNode & buildings = buildingsConf["towns"].Vector()[i]["buildings"]; | ||||
| 		JsonNode & buildings = buildingsConf[factionName[i]]["town"]["buildings"]; | ||||
| 		BOOST_FOREACH(JsonNode & building, buildings.Vector()) | ||||
| 		{ | ||||
| 			JsonNode & legacyFaction = legacyConfig.Vector()[i]; | ||||
| 			if (vstd::contains(building.Struct(), "id")) | ||||
| 			{ | ||||
| 				JsonNode & legacyBuilding = legacyConfig.Vector()[i].Vector()[building["id"].Float()]; | ||||
| 				//find same buildings in legacy and json configs | ||||
| 				JsonNode & legacyBuilding = legacyFaction.Vector()[building["id"].Float()]; | ||||
|  | ||||
| 				if (!legacyBuilding.isNull()) | ||||
| 				if (!legacyBuilding.isNull()) //merge if h3 config was found for this building | ||||
| 					JsonNode::merge(building, legacyBuilding); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	loadTowns(towns, buildingsConf["towns"]); | ||||
| 	loadFactions(buildingsConf); | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| #include "ConstTransitivePtr.h" | ||||
| #include "ResourceSet.h" | ||||
| #include "int3.h" | ||||
| //#include "GameConstants.h" | ||||
|  | ||||
| /* | ||||
|  * CTownHandler.h, part of VCMI engine | ||||
| @@ -22,20 +23,39 @@ class JsonNode; | ||||
| /// contains all mechanics-related data about town structures | ||||
| class DLL_LINKAGE CBuilding | ||||
| { | ||||
| 	typedef si32 BuildingType;//TODO: replace int with pointer? | ||||
|  | ||||
| 	std::string name; | ||||
| 	std::string description; | ||||
|  | ||||
| public: | ||||
| 	si32 tid, bid; //town ID and structure ID | ||||
| 	TResources resources; | ||||
| 	std::set<int> requirements; //set of required buildings | ||||
|  | ||||
| 	std::set<BuildingType> requirements; /// set of required buildings, includes upgradeOf; | ||||
| 	BuildingType upgrade; /// indicates that building "upgrade" can be improved by this, -1 = empty | ||||
|  | ||||
| 	enum EBuildMode | ||||
| 	{ | ||||
| 		BUILD_NORMAL,  // 0 - normal, default | ||||
| 		BUILD_AUTO,    // 1 - auto - building appears when all requirements are built | ||||
| 		BUILD_SPECIAL, // 2 - special - building can not be built normally | ||||
| 		BUILD_GRAIL    // 3 - grail - building reqires grail to be built | ||||
| 	}; | ||||
| 	ui32 mode; | ||||
|  | ||||
| 	const std::string &Name() const; | ||||
| 	const std::string &Description() const; | ||||
|  | ||||
| 	//return base of upgrade(s) or this | ||||
| 	BuildingType getBase() const; | ||||
|  | ||||
| 	// returns how many times build has to be upgraded to become build | ||||
| 	si32 getDistance(BuildingType build) const; | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & tid & bid & resources & name & description & requirements; | ||||
| 		h & tid & bid & resources & name & description & requirements & upgrade & mode; | ||||
| 	} | ||||
|  | ||||
| 	friend class CTownHandler; | ||||
| @@ -43,38 +63,35 @@ public: | ||||
|  | ||||
| /// This is structure used only by client | ||||
| /// Consists of all gui-related data about town structures | ||||
| /// Should be mode from lib to client | ||||
| /// Should be moved from lib to client | ||||
| struct DLL_LINKAGE CStructure | ||||
| { | ||||
| 	int ID; | ||||
| 	CBuilding * building;  // base building. If null - this structure will be always present on screen | ||||
| 	CBuilding * buildable; // building that will be used to determine built building and visible cost. Usually same as "building" | ||||
|  | ||||
| 	bool hiddenUpgrade; // used only if "building" is upgrade, if true - structure on town screen will behave exactly like parent (mouse clicks, hover texts, etc) | ||||
|  | ||||
| 	int3 pos; | ||||
| 	std::string defName, borderName, areaName; | ||||
| 	int townID, group; | ||||
|  | ||||
| 	bool operator<(const CStructure & p2) const | ||||
| 	{ | ||||
| 		if(pos.z != p2.pos.z) | ||||
| 			return (pos.z) < (p2.pos.z); | ||||
| 		else | ||||
| 			return (ID) < (p2.ID); | ||||
| 	} | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & ID & pos & defName & borderName & areaName & townID & group; | ||||
| 		h & pos & defName & borderName & areaName & building & buildable; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CTown | ||||
| { | ||||
| 	std::string name; //name of type | ||||
| 	std::string name; | ||||
| 	std::string description; | ||||
|  | ||||
| 	std::vector<std::string> names; //names of the town instances | ||||
|  | ||||
| public: | ||||
| 	ui32 typeID; | ||||
| 	ui32 typeID;//also works as factionID | ||||
|  | ||||
| 	/// level -> list of creatures on this tier | ||||
| 	/// TODO: replace with pointers to CCreature? | ||||
| 	// TODO: replace with pointers to CCreature | ||||
| 	std::vector<std::vector<si32> > creatures; | ||||
|  | ||||
| 	bmap<int, ConstTransitivePtr<CBuilding> > buildings; | ||||
| @@ -88,13 +105,21 @@ public: | ||||
| 	// Client-only data. Should be moved away from lib | ||||
| 	struct ClientInfo | ||||
| 	{ | ||||
| 		std::string musicTheme; | ||||
| 		std::string townBackground; | ||||
| 		std::string guildWindow; | ||||
| 		std::string buildingsIcons; | ||||
| 		std::string hallBackground; | ||||
| 		std::vector< std::vector< std::vector<int> > > hallSlots; /// vector[row][column] = list of buildings in this slot | ||||
| 		bmap<int, ConstTransitivePtr<CStructure> > structures; | ||||
| 		/// vector[row][column] = list of buildings in this slot | ||||
| 		std::vector< std::vector< std::vector<int> > > hallSlots; | ||||
|  | ||||
| 		/// list of town screen structures. | ||||
| 		/// NOTE: index in vector is meaningless. Vector used instead of list for a bit faster access | ||||
| 		std::vector<ConstTransitivePtr<CStructure> > structures; | ||||
|  | ||||
| 		template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 		{ | ||||
| 			h & hallBackground & hallSlots & structures; | ||||
| 			h & musicTheme & townBackground & guildWindow & buildingsIcons & hallBackground & hallSlots & structures; | ||||
| 		} | ||||
| 	} clientInfo; | ||||
|  | ||||
| @@ -110,6 +135,23 @@ public: | ||||
| 	friend class CTownHandler; | ||||
| }; | ||||
|  | ||||
| class CFaction | ||||
| { | ||||
| public: | ||||
| 	std::string name; //reference name, usually lower case | ||||
| 	ui32 factionID; | ||||
|  | ||||
| 	ui32 nativeTerrain; | ||||
|  | ||||
| 	std::string creatureBg120; | ||||
| 	std::string creatureBg130; | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & name & factionID & nativeTerrain & creatureBg120 & creatureBg130; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CTownHandler | ||||
| { | ||||
| 	/// loads CBuilding's into town | ||||
| @@ -123,28 +165,32 @@ class DLL_LINKAGE CTownHandler | ||||
| 	/// loads town hall vector (hallSlots) | ||||
| 	void loadTownHall(CTown &town, const JsonNode & source); | ||||
|  | ||||
| 	/// load town and insert it into towns vector | ||||
| 	void loadTown(std::vector<CTown> &towns, const JsonNode & source); | ||||
| 	void loadClientData(CTown &town, const JsonNode & source); | ||||
|  | ||||
| 	void loadTown(CTown &town, const JsonNode & source); | ||||
|  | ||||
| 	/// main loading function, accepts merged JSON source and add all entries from it into game | ||||
| 	/// all entries in JSON should be checked for validness before using this function | ||||
| 	void loadTowns(std::vector<CTown> &towns, const JsonNode & source); | ||||
| 	void loadFactions(const JsonNode & source); | ||||
|  | ||||
| 	/// load all available data from h3 txt(s) into json structure using format similar to vcmi configs | ||||
| 	/// returns 2d array [townID] [buildID] of buildings | ||||
| 	void loadLegacyData(JsonNode & dest); | ||||
|  | ||||
| public: | ||||
| 	std::vector<CTown> towns; | ||||
| 	std::map<ui32, CTown> towns; | ||||
| 	std::map<ui32, CFaction> factions; | ||||
|  | ||||
| 	CTownHandler(); //c-tor, set pointer in VLC to this | ||||
|  | ||||
| 	/// "entry point" for towns loading. | ||||
| 	/// reads legacy txt's from H3 + vcmi json, merges them | ||||
| 	/// and loads resulting structure to game using loadTowns method | ||||
| 	/// in future may require loaded Creature Handler | ||||
| 	void load(); | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & towns; | ||||
| 		h & towns & factions; | ||||
| 	} | ||||
| }; | ||||
|   | ||||
| @@ -72,19 +72,26 @@ namespace GameConstants | ||||
| 	const int ARTIFACTS_QUANTITY=171; | ||||
| 	const int HEROES_QUANTITY=156; | ||||
| 	const int SPELLS_QUANTITY=70; | ||||
| 	const int RESOURCE_QUANTITY=8; | ||||
| 	const int TERRAIN_TYPES=10; | ||||
| 	const int PRIMARY_SKILLS=4; | ||||
| 	const int NEUTRAL_PLAYER=255; | ||||
| 	const int NAMES_PER_TOWN=16; | ||||
| 	const int CREATURES_PER_TOWN = 7; //without upgrades | ||||
| 	const int MAX_BUILDING_PER_TURN = 1; | ||||
| 	const int SPELL_LEVELS = 5; | ||||
| 	const int AVAILABLE_HEROES_PER_PLAYER = 2; | ||||
| 	const int SPELLBOOK_GOLD_COST = 500; | ||||
|  | ||||
| 	const ui16 BACKPACK_START = 19; | ||||
| 	const int ID_CATAPULT = 3, ID_LOCK = 145; | ||||
|  | ||||
| 	const int TERRAIN_TYPES=10; | ||||
| 	const std::string TERRAIN_NAMES [TERRAIN_TYPES] = { | ||||
| 	    "dirt", "sand", "grass", "snow", "swamp", "rough", "subterra", "lava", "water", "rock" | ||||
| 	}; | ||||
|  | ||||
| 	const int RESOURCE_QUANTITY=8; | ||||
| 	const std::string RESOURCE_NAMES [RESOURCE_QUANTITY] = { | ||||
| 	    "wood", "mercury", "ore", "sulfur", "crystal", "gems", "gold", "mithril" | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| // Enum declarations | ||||
| @@ -109,18 +116,67 @@ namespace EAlignment | ||||
| 	enum EAlignment { GOOD, EVIL, NEUTRAL }; | ||||
| } | ||||
|  | ||||
| namespace ETownType | ||||
| { | ||||
| 	enum ETownType | ||||
| 	{ | ||||
| 		ANY = -1, | ||||
| 		CASTLE, RAMPART, TOWER, INFERNO, NECROPOLIS, DUNGEON, STRONGHOLD, FORTRESS, CONFLUX | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| namespace EBuilding | ||||
| { | ||||
| 	//Quite useful as long as most of building mechanics hardcoded | ||||
| 	// NOTE: all building with completely configurable mechanics will be removed from list | ||||
| 	enum EBuilding | ||||
| 	{ | ||||
| 		MAGES_GUILD_1,  MAGES_GUILD_2, MAGES_GUILD_3,     MAGES_GUILD_4,   MAGES_GUILD_5,  | ||||
| 		MAGES_GUILD_1,  MAGES_GUILD_2, MAGES_GUILD_3,     MAGES_GUILD_4,   MAGES_GUILD_5, | ||||
| 		TAVERN,         SHIPYARD,      FORT,              CITADEL,         CASTLE, | ||||
| 		VILLAGE_HALL,   TOWN_HALL,     CITY_HALL,         CAPITOL,         MARKETPLACE, | ||||
| 		RESOURCE_SILO,  BLACKSMITH,    SPECIAL_1,         HORDE_1,         HORDE_1_UPGR, | ||||
| 		SHIP,           SPECIAL_2,     SPECIAL_3,         SPECIAL_4,       HORDE_2, | ||||
| 		HORDE_2_UPGR,   GRAIL,         EXTRA_CITY_HALL,   EXTRA_TOWN_HALL, EXTRA_CAPITOL, | ||||
| 		DWELL_FIRST=30, DWELL_LAST=36, DWELL_UP_FIRST=37, DWELL_UP_LAST=43 | ||||
| 		HORDE_2_UPGR,   GRAIL,         EXTRA_TOWN_HALL,   EXTRA_CITY_HALL, EXTRA_CAPITOL, | ||||
| 		DWELL_FIRST=30, DWELL_LAST=36, DWELL_UP_FIRST=37, DWELL_UP_LAST=43, | ||||
|  | ||||
| 		//Special buildings for towns. | ||||
| 		LIGHTHOUSE  = SPECIAL_1, | ||||
| 		STABLES     = SPECIAL_2, //Castle | ||||
| 		BROTHERHOOD = SPECIAL_3, | ||||
|  | ||||
| 		MYSTIC_POND         = SPECIAL_1, | ||||
| 		FOUNTAIN_OF_FORTUNE = SPECIAL_3, //Rampart | ||||
| 		TREASURY            = SPECIAL_4, | ||||
|  | ||||
| 		ARTIFACT_MERCHANT = SPECIAL_1, | ||||
| 		LOOKOUT_TOWER     = SPECIAL_2, //Tower | ||||
| 		LIBRARY           = SPECIAL_3, | ||||
| 		WALL_OF_KNOWLEDGE = SPECIAL_4, | ||||
|  | ||||
| 		STORMCLOUDS   = SPECIAL_2, | ||||
| 		CASTLE_GATE   = SPECIAL_3, //Inferno | ||||
| 		ORDER_OF_FIRE = SPECIAL_4, | ||||
|  | ||||
| 		COVER_OF_DARKNESS    = SPECIAL_1, | ||||
| 		NECROMANCY_AMPLIFIER = SPECIAL_2, //Necropolis | ||||
| 		SKELETON_TRANSFORMER = SPECIAL_3, | ||||
|  | ||||
| 		//ARTIFACT_MERCHANT - same ID as in tower | ||||
| 		MANA_VORTEX      = SPECIAL_2, | ||||
| 		PORTAL_OF_SUMMON = SPECIAL_3, //Dungeon | ||||
| 		BATTLE_ACADEMY   = SPECIAL_4, | ||||
|  | ||||
| 		ESCAPE_TUNNEL     = SPECIAL_1, | ||||
| 		FREELANCERS_GUILD = SPECIAL_2, //Stronghold | ||||
| 		BALLISTA_YARD     = SPECIAL_3, | ||||
| 		HALL_OF_VALHALLA  = SPECIAL_4, | ||||
|  | ||||
| 		CAGE_OF_WARLORDS = SPECIAL_1, | ||||
| 		GLYPHS_OF_FEAR   = SPECIAL_2, // Fortress | ||||
| 		BLOOD_OBELISK    = SPECIAL_3, | ||||
|  | ||||
| 		//ARTIFACT_MERCHANT - same ID as in tower | ||||
| 		MAGIC_UNIVERSITY = SPECIAL_2, // Conflux | ||||
| 	}; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,20 +1,22 @@ | ||||
| #include "StdInc.h" | ||||
| #include "IGameCallback.h" | ||||
|  | ||||
| #include "../lib/CGameState.h" | ||||
| #include "../lib/map.h" | ||||
| #include <boost/random/linear_congruential.hpp> | ||||
|  | ||||
| #include "CGameState.h" | ||||
| #include "map.h" | ||||
| #include "CObjectHandler.h" | ||||
| #include "CHeroHandler.h" | ||||
| #include "StartInfo.h" | ||||
| #include "CArtHandler.h" | ||||
| #include "CSpellHandler.h" | ||||
| #include "../lib/VCMI_Lib.h" | ||||
| #include <boost/random/linear_congruential.hpp> | ||||
| #include "VCMI_Lib.h" | ||||
| #include "CTownHandler.h" | ||||
| #include "BattleState.h" | ||||
| #include "NetPacks.h" | ||||
| #include "CBuildingHandler.h" | ||||
| #include "GameConstants.h" | ||||
| #include "CModHandler.h" | ||||
|  | ||||
| /* | ||||
|  * IGameCallback.cpp, part of VCMI engine | ||||
| @@ -544,7 +546,7 @@ int CGameInfoCallback::canBuildStructure( const CGTownInstance *t, int ID ) | ||||
| 	ERROR_RET_VAL_IF(!canGetFullInfo(t), "Town is not owned!", -1); | ||||
|  | ||||
| 	int ret = EBuildingState::ALLOWED; | ||||
| 	if(t->builded >= GameConstants::MAX_BUILDING_PER_TURN) | ||||
| 	if(t->builded >= VLC->modh->settings.MAX_BUILDING_PER_TURN) | ||||
| 		ret = EBuildingState::CANT_BUILD_TODAY; //building limit | ||||
|  | ||||
| 	CBuilding * pom = t->town->buildings[ID]; | ||||
| @@ -561,7 +563,7 @@ int CGameInfoCallback::canBuildStructure( const CGTownInstance *t, int ID ) | ||||
|  | ||||
| 	for( std::set<int>::iterator ri  =  reqs.begin(); ri != reqs.end(); ri++ ) | ||||
| 	{ | ||||
| 		if(t->builtBuildings.find(*ri)==t->builtBuildings.end()) | ||||
| 		if(!t->hasBuilt(*ri)) | ||||
| 			ret = EBuildingState::PREREQUIRES; //lack of requirements - cannot build | ||||
| 	} | ||||
|  | ||||
| @@ -576,7 +578,7 @@ int CGameInfoCallback::canBuildStructure( const CGTownInstance *t, int ID ) | ||||
| 		{ | ||||
| 			BOOST_FOREACH(const CGTownInstance *t, ps->towns) | ||||
| 			{ | ||||
| 				if(vstd::contains(t->builtBuildings, 13)) | ||||
| 				if(t->hasBuilt(EBuilding::CAPITOL)) | ||||
| 				{ | ||||
| 					ret = EBuildingState::HAVE_CAPITAL; //no more than one capitol | ||||
| 					break; | ||||
| @@ -592,7 +594,7 @@ int CGameInfoCallback::canBuildStructure( const CGTownInstance *t, int ID ) | ||||
| 			ret = EBuildingState::NO_WATER; //lack of water | ||||
| 	} | ||||
|  | ||||
| 	if(t->builtBuildings.find(ID)!=t->builtBuildings.end())	//already built | ||||
| 	if(t->hasBuilt(ID))	//already built | ||||
| 		ret = EBuildingState::ALREADY_PRESENT; | ||||
| 	return ret; | ||||
| } | ||||
| @@ -603,32 +605,27 @@ std::set<int> CGameInfoCallback::getBuildingRequiments( const CGTownInstance *t, | ||||
|  | ||||
| 	std::set<int> used; | ||||
| 	used.insert(ID); | ||||
| 	std::set<int> reqs = t->town->buildings[ID]->requirements; | ||||
| 	auto reqs = t->town->buildings[ID]->requirements; | ||||
|  | ||||
| 	while(true) | ||||
| 	bool found; | ||||
| 	do | ||||
| 	{ | ||||
| 		size_t noloop=0; | ||||
| 		for(std::set<int>::iterator i=reqs.begin();i!=reqs.end();i++) | ||||
| 		found = false; | ||||
| 		for(auto i=reqs.begin();i!=reqs.end();i++) | ||||
| 		{ | ||||
| 			if(used.find(*i)==used.end()) //we haven't added requirements for this building | ||||
| 			{ | ||||
| 				found = true; | ||||
| 				auto & requires = t->town->buildings[*i]->requirements; | ||||
|  | ||||
| 				used.insert(*i); | ||||
| 				for( | ||||
| 					std::set<int>::iterator j= t->town->buildings[*i]->requirements.begin(); | ||||
| 					j!= t->town->buildings[*i]->requirements.end(); | ||||
| 				j++) | ||||
| 				{ | ||||
| 				for(auto j = requires.begin(); j!= requires.end(); j++) | ||||
| 					reqs.insert(*j);//creating full list of requirements | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				noloop++; | ||||
| 			} | ||||
| 		} | ||||
| 		if(noloop==reqs.size()) | ||||
| 			break; | ||||
| 	} | ||||
| 	while (found); | ||||
|  | ||||
| 	return reqs; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -501,7 +501,7 @@ bool JsonParser::extractString(std::string &str) | ||||
| 			str.append( &input[first], pos-first); | ||||
| 			return error("Closing quote not found!", true); | ||||
| 		} | ||||
| 		if (input[pos] < ' ') // control character | ||||
| 		if ((unsigned char)(input[pos]) < ' ') // control character | ||||
| 		{ | ||||
| 			str.append( &input[first], pos-first); | ||||
| 			first = pos+1; | ||||
|   | ||||
| @@ -2,23 +2,17 @@ | ||||
| #include "ResourceSet.h" | ||||
| #include "GameConstants.h" | ||||
| #include "JsonNode.h" | ||||
|  | ||||
|  | ||||
| Res::ResourceSet::ResourceSet() | ||||
| { | ||||
| 	resize(GameConstants::RESOURCE_QUANTITY, 0); | ||||
| } | ||||
| } | ||||
|  | ||||
| Res::ResourceSet::ResourceSet(const JsonNode & node) | ||||
| { | ||||
| 	resize(GameConstants::RESOURCE_QUANTITY, 0); | ||||
| 	at(0) = node["wood"].Float(); | ||||
| 	at(1) = node["mercury"].Float(); | ||||
| 	at(2) = node["ore"].Float(); | ||||
| 	at(3) = node["sulfur"].Float(); | ||||
| 	at(4) = node["crystal"].Float(); | ||||
| 	at(5) = node["gems"].Float(); | ||||
| 	at(6) = node["gold"].Float(); | ||||
| 	at(7) = node["mithril"].Float(); | ||||
| { | ||||
| 	reserve(GameConstants::RESOURCE_QUANTITY); | ||||
| 	BOOST_FOREACH(std::string name, GameConstants::RESOURCE_NAMES) | ||||
| 		push_back(node[name].Float()); | ||||
| } | ||||
|  | ||||
| bool Res::ResourceSet::nonZero() const | ||||
|   | ||||
							
								
								
									
										20
									
								
								lib/map.cpp
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								lib/map.cpp
									
									
									
									
									
								
							| @@ -57,18 +57,14 @@ static std::set<si32> convertBuildings(const std::set<si32> h3m, int castleID, b | ||||
| 	} | ||||
|  | ||||
| 	if(addAuxiliary) | ||||
| 	{ | ||||
| 		ret.insert(10); //village hall is always present | ||||
| 		ret.insert(-1); //houses near v.hall / eyecandies | ||||
| 		ret.insert(-2); //terrain eyecandy, if -1 is used | ||||
| 	} | ||||
| 		ret.insert(EBuilding::VILLAGE_HALL); //village hall is always present | ||||
|  | ||||
| 	if(ret.find(11)!=ret.end()) | ||||
| 		ret.insert(27); | ||||
| 	if(ret.find(12)!=ret.end()) | ||||
| 		ret.insert(28); | ||||
| 	if(ret.find(13)!=ret.end()) | ||||
| 		ret.insert(29); | ||||
| 	if(ret.find(EBuilding::CITY_HALL)!=ret.end()) | ||||
| 		ret.insert(EBuilding::EXTRA_CITY_HALL); | ||||
| 	if(ret.find(EBuilding::TOWN_HALL)!=ret.end()) | ||||
| 		ret.insert(EBuilding::EXTRA_TOWN_HALL); | ||||
| 	if(ret.find(EBuilding::CAPITOL)!=ret.end()) | ||||
| 		ret.insert(EBuilding::EXTRA_CAPITOL); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
| @@ -711,7 +707,7 @@ void Mapa::loadTown( CGObjectInstance * &nobj, const ui8 * bufor, int &i, int su | ||||
| 	else //standard buildings | ||||
| 	{ | ||||
| 		if(readChar(bufor,i)) //has fort | ||||
| 			nt->builtBuildings.insert(7); | ||||
| 			nt->builtBuildings.insert(EBuilding::FORT); | ||||
| 		nt->builtBuildings.insert(-50); //means that set of standard building should be included | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -1138,7 +1138,7 @@ void CGameHandler::newTurn() | ||||
| 		bool deityOfFireBuilt = false; | ||||
| 		BOOST_FOREACH(const CGTownInstance *t, gs->map->towns) | ||||
| 		{ | ||||
| 			if(t->subID == 3 && vstd::contains(t->builtBuildings, EBuilding::GRAIL)) | ||||
| 			if(t->hasBuilt(EBuilding::GRAIL, ETownType::INFERNO)) | ||||
| 			{ | ||||
| 				deityOfFireBuilt = true; | ||||
| 				break; | ||||
| @@ -1231,7 +1231,7 @@ void CGameHandler::newTurn() | ||||
| 			hth.id = h->id; | ||||
| 			hth.move = h->maxMovePoints(gs->map->getTile(h->getPosition(false)).tertype != TerrainTile::water); | ||||
|  | ||||
| 			if(h->visitedTown && vstd::contains(h->visitedTown->builtBuildings,0)) //if hero starts turn in town with mage guild | ||||
| 			if(h->visitedTown && h->visitedTown->hasBuilt(EBuilding::MAGES_GUILD_1)) //if hero starts turn in town with mage guild | ||||
| 				hth.mana = std::max(h->mana, h->manaLimit()); //restore all mana | ||||
| 			else | ||||
| 				hth.mana = std::max((si32)(0), std::max(h->mana, std::min((si32)(h->mana + h->manaRegain()), h->manaLimit()))); | ||||
| @@ -1258,11 +1258,11 @@ void CGameHandler::newTurn() | ||||
| 		handleTownEvents(t, n, newCreas); | ||||
| 		if(newWeek) //first day of week | ||||
| 		{ | ||||
| 			if(t->subID == 5 && vstd::contains(t->builtBuildings, EBuilding::SPECIAL_3)) | ||||
| 			if(t->hasBuilt(EBuilding::PORTAL_OF_SUMMON, ETownType::DUNGEON)) | ||||
| 				setPortalDwelling(t, true, (n.specialWeek == NewTurn::PLAGUE ? true : false)); //set creatures for Portal of Summoning | ||||
|  | ||||
| 			if(!firstTurn) | ||||
| 				if (t->subID == 1  && player < GameConstants::PLAYER_LIMIT && vstd::contains(t->builtBuildings, EBuilding::SPECIAL_3))//dwarven treasury | ||||
| 				if (t->hasBuilt(EBuilding::TREASURY, ETownType::RAMPART) && player < GameConstants::PLAYER_LIMIT) | ||||
| 						n.res[player][Res::GOLD] += hadGold[player]/10; //give 10% of starting gold | ||||
|  | ||||
| 			SetAvailableCreatures sac; | ||||
| @@ -1307,7 +1307,7 @@ void CGameHandler::newTurn() | ||||
| 		} | ||||
| 		if(!firstTurn  &&  player < GameConstants::PLAYER_LIMIT)//not the first day and town not neutral | ||||
| 		{ | ||||
| 			if(vstd::contains(t->builtBuildings, EBuilding::RESOURCE_SILO)) //there is resource silo | ||||
| 			if(t->hasBuilt(EBuilding::RESOURCE_SILO)) //there is resource silo | ||||
| 			{ | ||||
| 				if(t->town->primaryRes == 127) //we'll give wood and ore | ||||
| 				{ | ||||
| @@ -1322,7 +1322,7 @@ void CGameHandler::newTurn() | ||||
|  | ||||
| 			n.res[player][Res::GOLD] += t->dailyIncome(); | ||||
| 		} | ||||
| 		if(vstd::contains(t->builtBuildings, EBuilding::GRAIL) && t->subID == 2) | ||||
| 		if(t->hasBuilt(EBuilding::GRAIL, ETownType::TOWER)) | ||||
| 		{ | ||||
| 			// Skyship, probably easier to handle same as Veil of darkness | ||||
| 			//do it every new day after veils apply | ||||
| @@ -1579,7 +1579,7 @@ void CGameHandler::giveSpells( const CGTownInstance *t, const CGHeroInstance *h | ||||
| 	cs.learn = true; | ||||
| 	for(int i=0; i<std::min(t->mageGuildLevel(),h->getSecSkillLevel(CGHeroInstance::WISDOM)+2);i++) | ||||
| 	{ | ||||
| 		if (t->subID == 8 && vstd::contains(t->builtBuildings, EBuilding::GRAIL)) //Aurora Borealis | ||||
| 		if (t->hasBuilt(EBuilding::GRAIL, ETownType::CONFLUX)) //Aurora Borealis | ||||
| 		{ | ||||
| 			std::vector<ui16> spells; | ||||
| 			getAllowedSpells(spells, i); | ||||
| @@ -1795,9 +1795,9 @@ bool CGameHandler::teleportHero(si32 hid, si32 dstid, ui8 source, ui8 asker/* = | ||||
| 	const CGTownInstance *from = h->visitedTown; | ||||
| 	if(((h->getOwner() != t->getOwner()) | ||||
| 		&& complain("Cannot teleport hero to another player")) | ||||
| 	|| ((!from || from->subID!=3 || !vstd::contains(from->builtBuildings, EBuilding::SPECIAL_3)) | ||||
| 	|| ((!from || from->hasBuilt(EBuilding::CASTLE_GATE, ETownType::INFERNO)) | ||||
| 		&& complain("Hero must be in town with Castle gate for teleporting")) | ||||
| 	|| ((t->subID!=3 || !vstd::contains(t->builtBuildings, EBuilding::SPECIAL_3)) | ||||
| 	|| (t->hasBuilt(EBuilding::CASTLE_GATE, ETownType::INFERNO) | ||||
| 		&& complain("Cannot teleport hero to town without Castle gate in it"))) | ||||
| 			return false; | ||||
| 	int3 pos = t->visitablePos(); | ||||
| @@ -1818,7 +1818,7 @@ void CGameHandler::setOwner(int objid, ui8 owner) | ||||
| 	if(owner < GameConstants::PLAYER_LIMIT && getTown(objid)) //town captured | ||||
| 	{ | ||||
| 		const CGTownInstance * town = getTown(objid); | ||||
| 		if (town->subID == 5 && vstd::contains(town->builtBuildings, 22)) | ||||
| 		if (town->hasBuilt(EBuilding::PORTAL_OF_SUMMON, ETownType::DUNGEON)) | ||||
| 			setPortalDwelling(town, true, false); | ||||
|  | ||||
| 		if (!gs->getPlayer(owner)->towns.size())//player lost last town | ||||
| @@ -1837,7 +1837,7 @@ void CGameHandler::setOwner(int objid, ui8 owner) | ||||
| 	{ | ||||
| 		BOOST_FOREACH(const CGTownInstance *t, gs->getPlayer(owner)->towns) | ||||
| 		{ | ||||
| 			if (t->subID == 5 && vstd::contains(t->builtBuildings, 22)) | ||||
| 			if (t->hasBuilt(EBuilding::PORTAL_OF_SUMMON, ETownType::DUNGEON)) | ||||
| 				setPortalDwelling(t);//set initial creatures for all portals of summoning | ||||
| 		} | ||||
| 	} | ||||
| @@ -2421,27 +2421,34 @@ bool CGameHandler::buildStructure( si32 tid, si32 bid, bool force /*=false*/ ) | ||||
|  | ||||
| 	if(!force) | ||||
| 	{ | ||||
| 		if (gs->canBuildStructure(t,bid) != 7) | ||||
| 			COMPLAIN_RET("Cannot build that building!"); | ||||
|  | ||||
| 		if(bid == 26) //grail | ||||
| 		switch (b->mode) | ||||
| 		{ | ||||
| 			if(!t->visitingHero || !t->visitingHero->hasArt(2)) | ||||
| 				COMPLAIN_RET("Cannot build grail - hero doesn't have it") | ||||
| 			else | ||||
| 				removeArtifact(ArtifactLocation(t->visitingHero, t->visitingHero->getArtPos(2, false))); | ||||
| 		case CBuilding::BUILD_NORMAL : | ||||
| 		case CBuilding::BUILD_AUTO   : | ||||
| 			if (gs->canBuildStructure(t,bid) != EBuildingState::ALLOWED) | ||||
| 				COMPLAIN_RET("Cannot build that building!"); | ||||
| 			break; | ||||
|  | ||||
| 		case CBuilding::BUILD_SPECIAL: | ||||
| 			COMPLAIN_RET("This building can not be constructed!"); | ||||
| 			break; | ||||
|  | ||||
| 		case CBuilding::BUILD_GRAIL  : | ||||
| 			if(b->mode == CBuilding::BUILD_GRAIL) //needs grail | ||||
| 			{ | ||||
| 				if(!t->visitingHero || !t->visitingHero->hasArt(2)) | ||||
| 					COMPLAIN_RET("Cannot build this without grail!") | ||||
| 				else | ||||
| 					removeArtifact(ArtifactLocation(t->visitingHero, t->visitingHero->getArtPos(2, false))); | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	NewStructures ns; | ||||
| 	ns.tid = tid; | ||||
| 	//we have upgr. dwelling, upgr. horde will be builded as well | ||||
| 	if ( (bid == 18) && (vstd::contains(t->builtBuildings,(t->town->hordeLvl[0]+37))) ) | ||||
| 		ns.bid.insert(19); | ||||
| 	else if ( (bid == 24) && (vstd::contains(t->builtBuildings,(t->town->hordeLvl[1]+37))) ) | ||||
| 		ns.bid.insert(25); | ||||
|  | ||||
| 	else if(bid >= EBuilding::DWELL_FIRST) //dwelling | ||||
| 	if(bid >= EBuilding::DWELL_FIRST) //dwelling | ||||
| 	{ | ||||
| 		int level = (bid - EBuilding::DWELL_FIRST) % GameConstants::CREATURES_PER_TOWN; | ||||
| 		int upgradeNumber = (bid - EBuilding::DWELL_FIRST) / GameConstants::CREATURES_PER_TOWN; | ||||
| @@ -2451,14 +2458,6 @@ bool CGameHandler::buildStructure( si32 tid, si32 bid, bool force /*=false*/ ) | ||||
|  | ||||
| 		CCreature * crea = VLC->creh->creatures[t->town->creatures[level][upgradeNumber]]; | ||||
|  | ||||
| 		if (vstd::iswithin(bid, EBuilding::DWELL_UP_FIRST, EBuilding::DWELL_UP_LAST)) | ||||
| 		{ | ||||
| 			if ( (bid-37 == t->town->hordeLvl[0]) && (vstd::contains(t->builtBuildings,18)) ) | ||||
| 				ns.bid.insert(19);//we have horde, will be upgraded as well as dwelling | ||||
| 			if ( (bid-37 == t->town->hordeLvl[1]) && (vstd::contains(t->builtBuildings,24)) ) | ||||
| 				ns.bid.insert(25); | ||||
| 		} | ||||
|  | ||||
| 		SetAvailableCreatures ssi; | ||||
| 		ssi.tid = tid; | ||||
| 		ssi.creatures = t->creatures; | ||||
| @@ -2467,19 +2466,34 @@ bool CGameHandler::buildStructure( si32 tid, si32 bid, bool force /*=false*/ ) | ||||
| 		ssi.creatures[level].second.push_back(crea->idNumber); | ||||
| 		sendAndApply(&ssi); | ||||
| 	} | ||||
| 	else if(bid == 11) | ||||
| 		ns.bid.insert(27); | ||||
| 	else if(bid == 12) | ||||
| 		ns.bid.insert(28); | ||||
| 	else if(bid == 13) | ||||
| 		ns.bid.insert(29); | ||||
| 	else if ( t->subID == 5 && bid == 22 ) | ||||
| 	else if ( t->subID == ETownType::DUNGEON && bid == EBuilding::MANA_VORTEX ) | ||||
| 	{ | ||||
| 		setPortalDwelling(t); | ||||
| 	} | ||||
|  | ||||
| 	ns.bid.insert(bid); | ||||
| 	ns.builded = force?t->builded:(t->builded+1); | ||||
|  | ||||
| 	BOOST_FOREACH(auto & build, t->town->buildings) | ||||
| 	{ | ||||
| 		if (build.second->mode == CBuilding::BUILD_AUTO | ||||
| 		    && !vstd::contains(t->builtBuildings, build.second->bid)) | ||||
| 		{ | ||||
| 			bool canBuild = true; | ||||
| 			BOOST_FOREACH(int requires, build.second->requirements) | ||||
| 			{ | ||||
| 				if (!vstd::contains(t->builtBuildings, requires) | ||||
| 				 && !vstd::contains(ns.bid, requires)) | ||||
| 				{ | ||||
| 					canBuild = false; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			if (canBuild) | ||||
| 				ns.bid.insert(build.second->bid); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	sendAndApply(&ns); | ||||
|  | ||||
| 	//reveal ground for lookout tower | ||||
| @@ -2866,7 +2880,7 @@ bool CGameHandler::buyArtifact( ui32 hid, si32 aid ) | ||||
| 	CGTownInstance *town = hero->visitedTown; | ||||
| 	if(aid==0) //spellbook | ||||
| 	{ | ||||
| 		if((!vstd::contains(town->builtBuildings,si32(EBuilding::MAGES_GUILD_1)) && complain("Cannot buy a spellbook, no mage guild in the town!")) | ||||
| 		if((!town->hasBuilt(EBuilding::MAGES_GUILD_1) && complain("Cannot buy a spellbook, no mage guild in the town!")) | ||||
| 		    || (getResource(hero->getOwner(), Res::GOLD) < GameConstants::SPELLBOOK_GOLD_COST && complain("Cannot buy a spellbook, not enough gold!") ) | ||||
| 		    || (hero->getArt(ArtifactPosition::SPELLBOOK) && complain("Cannot buy a spellbook, hero already has a one!")) | ||||
| 		    ) | ||||
| @@ -2881,18 +2895,21 @@ bool CGameHandler::buyArtifact( ui32 hid, si32 aid ) | ||||
| 	else if(aid < 7  &&  aid > 3) //war machine | ||||
| 	{ | ||||
| 		int price = VLC->arth->artifacts[aid]->price; | ||||
| 		if((hero->getArt(9+aid) && complain("Hero already has this machine!")) | ||||
| 			|| (!vstd::contains(town->builtBuildings,si32(EBuilding::BLACKSMITH)) && complain("No blackismith!")) | ||||
| 			|| (gs->getPlayer(hero->getOwner())->resources[Res::GOLD] < price  && complain("Not enough gold!"))  //no gold | ||||
| 			|| ((!(town->subID == 6 && vstd::contains(town->builtBuildings,si32(EBuilding::SPECIAL_3) ) ) | ||||
| 			&& town->town->warMachine!= aid ) &&  complain("This machine is unavailable here!"))) | ||||
| 		 | ||||
| 		if(( hero->getArt(9+aid) && complain("Hero already has this machine!")) | ||||
| 		 || (gs->getPlayer(hero->getOwner())->resources[Res::GOLD] < price && complain("Not enough gold!"))) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		giveResource(hero->getOwner(),Res::GOLD,-price); | ||||
| 		giveHeroNewArtifact(hero, VLC->arth->artifacts[aid], 9+aid); | ||||
| 		return true; | ||||
| 		if  ((town->hasBuilt(EBuilding::BLACKSMITH) && town->town->warMachine == aid ) | ||||
| 		 || ((town->hasBuilt(EBuilding::BALLISTA_YARD, ETownType::STRONGHOLD)) && aid == 4)) | ||||
| 		{ | ||||
| 			giveResource(hero->getOwner(),Res::GOLD,-price); | ||||
| 			giveHeroNewArtifact(hero, VLC->arth->artifacts[aid], 9+aid); | ||||
| 			return true; | ||||
| 		} | ||||
| 		else | ||||
| 			COMPLAIN_RET("This machine is unavailable here!"); | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| @@ -3136,7 +3153,7 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, ui8 player) | ||||
|  | ||||
| 	if(t) //tavern in town | ||||
| 	{ | ||||
| 		if(    (!vstd::contains(t->builtBuildings,EBuilding::TAVERN)  && complain("No tavern!")) | ||||
| 		if(    (!t->hasBuilt(EBuilding::TAVERN)  && complain("No tavern!")) | ||||
| 			|| (t->visitingHero  && complain("There is visiting hero - no place!"))) | ||||
| 			return false; | ||||
| 	} | ||||
| @@ -3731,8 +3748,7 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message ) | ||||
| 		{ | ||||
| 			BOOST_FOREACH (auto & build, town->town->buildings) | ||||
| 			{ | ||||
| 				if (!vstd::contains(town->builtBuildings, build.first) | ||||
| 				 && !build.second->Name().empty()) | ||||
| 				if (!town->hasBuilt(build.first) && !build.second->Name().empty()) | ||||
| 				{ | ||||
| 					buildStructure(town->id, build.first, true); | ||||
| 				} | ||||
| @@ -4739,7 +4755,7 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n, std::map< | ||||
| 			} | ||||
|  | ||||
| 			for(std::set<si32>::iterator i = ev->buildings.begin(); i!=ev->buildings.end();i++) | ||||
| 				if ( !vstd::contains(town->builtBuildings, *i)) | ||||
| 				if ( town->hasBuilt(*i)) | ||||
| 				{ | ||||
| 					buildStructure(town->id, *i, true); | ||||
| 					iw.components.push_back(Component(Component::BUILDING, town->subID, *i, 0)); | ||||
| @@ -4902,14 +4918,14 @@ bool CGameHandler::buildBoat( ui32 objid ) | ||||
| 		return false; | ||||
| 	} | ||||
| 	else if(obj->o->ID == GameConstants::TOWNI_TYPE | ||||
| 		&& !vstd::contains((static_cast<const CGTownInstance*>(obj))->builtBuildings,6)) | ||||
| 	        && !static_cast<const CGTownInstance*>(obj)->hasBuilt(EBuilding::SHIPYARD)) | ||||
| 	{ | ||||
| 		complain("Cannot build boat in the town - no shipyard!"); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	//TODO use "real" cost via obj->getBoatCost | ||||
| 	if(getResource(obj->o->tempOwner, 6) < 1000  ||  getResource(obj->o->tempOwner, 0) < 10) | ||||
| 	if(getResource(obj->o->tempOwner, Res::GOLD) < 1000  ||  getResource(obj->o->tempOwner, Res::WOOD) < 10) | ||||
| 	{ | ||||
| 		complain("Not enough resources to build a boat!"); | ||||
| 		return false; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user