| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 | #include "StdInc.h"#include "CHeroHandler.h"#include "CLodHandler.h"#include "../lib/VCMI_Lib.h"#include "../lib/JsonNode.h"#include "GameConstants.h"#include <boost/version.hpp>#include "BattleHex.h"extern CLodHandler * bitmaph;void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);/* * CHeroHandler.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */CHeroClass::CHeroClass(){	skillLimit = 8;}CHeroClass::~CHeroClass(){}int CHeroClass::chooseSecSkill(const std::set<int> & possibles) const //picks secondary skill out from given possibilities{	if(possibles.size()==1)		return *possibles.begin();	int totalProb = 0;	for(std::set<int>::const_iterator i=possibles.begin(); i!=possibles.end(); i++)	{		totalProb += proSec[*i];	}	int ran = rand()%totalProb;	for(std::set<int>::const_iterator i=possibles.begin(); i!=possibles.end(); i++)	{		ran -= proSec[*i];		if(ran<0)			return *i;	}	throw std::runtime_error("Cannot pick secondary skill!");}EAlignment::EAlignment CHeroClass::getAlignment(){	return (EAlignment::EAlignment)alignment;}std::vector<BattleHex> CObstacleInfo::getBlocked(BattleHex hex) const{	std::vector<BattleHex> ret;	if(isAbsoluteObstacle)	{		assert(!hex.isValid());		range::copy(blockedTiles, std::back_inserter(ret));		return ret;	}	BOOST_FOREACH(int offset, blockedTiles)	{		BattleHex toBlock = hex + offset;		if((hex.getY() & 1) && !(toBlock.getY() & 1))			toBlock += BattleHex::LEFT;		if(!toBlock.isValid())			tlog1 << "Misplaced obstacle!\n";		else			ret.push_back(toBlock);	}	return ret;}bool CObstacleInfo::isAppropriate(int terrainType, int specialBattlefield /*= -1*/) const{	if(specialBattlefield != -1)		return vstd::contains(allowedSpecialBfields, specialBattlefield);	return vstd::contains(allowedTerrains, terrainType);}CHeroHandler::~CHeroHandler(){	for (int i = 0; i < heroes.size(); i++)		heroes[i].dellNull();	for (int i = 0; i < heroClasses.size(); i++)		delete heroClasses[i];}CHeroHandler::CHeroHandler(){}void CHeroHandler::loadObstacles(){	auto loadObstacles = [](const JsonNode &node, bool absolute, std::map<int, CObstacleInfo> &out)	{		BOOST_FOREACH(const JsonNode &obs, node.Vector()) 		{			int ID = obs["id"].Float();			CObstacleInfo & obi = out[ID];			obi.ID = ID;			obi.defName = obs["defname"].String();			obi.width = obs["width"].Float();			obi.height = obs["height"].Float();			obi.allowedTerrains = obs["allowedTerrain"].StdVector<ui8>();			obi.allowedSpecialBfields = obs["specialBattlefields"].StdVector<ui8>();			obi.blockedTiles = obs["blockedTiles"].StdVector<si16>();			obi.isAbsoluteObstacle = absolute;		}	};	const JsonNode config(GameConstants::DATA_DIR + "/config/obstacles.json");	loadObstacles(config["obstacles"], false, obstacles);	loadObstacles(config["absoluteObstacles"], true, absoluteObstacles);}void CHeroHandler::loadPuzzleInfo(){	const JsonNode config(GameConstants::DATA_DIR + "/config/puzzle_map.json");	int faction = 0;	BOOST_FOREACH(const JsonNode &puzzle, config["puzzles"].Vector()) 	{		int idx = 0;		BOOST_FOREACH(const JsonNode &piece, puzzle.Vector()) 		{			SPuzzleInfo spi;			spi.x = piece["x"].Float();			spi.y = piece["y"].Float();			spi.whenUncovered = piece["order"].Float();			spi.number = idx;							// filename calculation			std::ostringstream suffix;			suffix << std::setfill('0') << std::setw(2);			suffix << idx << ".BMP";			static const std::string factionToInfix[GameConstants::F_NUMBER] = {"CAS", "RAM", "TOW", "INF", "NEC", "DUN", "STR", "FOR", "ELE"};			spi.filename = "PUZ" + factionToInfix[faction] + suffix.str();			puzzleInfo[faction].push_back(spi);			idx ++;		}		assert(idx == PUZZLES_PER_FACTION);		faction ++;	}	assert(faction == GameConstants::F_NUMBER);}void CHeroHandler::loadHeroes(){	VLC->heroh = this;	std::string buf = bitmaph->getTextFile("HOTRAITS.TXT");	int it=0;	std::string dump;	for(int i=0; i<2; ++i)	{		loadToIt(dump,buf,it,3);	}	int numberOfCurrentClassHeroes = 0;	int currentClass = 0;	int additHero = 0;	CHero::EHeroClasses addTab[12];	addTab[0] = CHero::KNIGHT;	addTab[1] = CHero::WITCH;	addTab[2] = CHero::KNIGHT;	addTab[3] = CHero::WIZARD;	addTab[4] = CHero::RANGER;	addTab[5] = CHero::BARBARIAN;	addTab[6] = CHero::DEATHKNIGHT;	addTab[7] = CHero::WARLOCK;	addTab[8] = CHero::KNIGHT;	addTab[9] = CHero::WARLOCK;	addTab[10] = CHero::BARBARIAN;	addTab[11] = CHero::DEMONIAC;		for (int i=0; i<GameConstants::HEROES_QUANTITY; i++)	{		CHero * nher = new CHero;		if(currentClass<18)		{			nher->heroType = static_cast<CHero::EHeroClasses>(currentClass);			++numberOfCurrentClassHeroes;			if(numberOfCurrentClassHeroes==8)			{				numberOfCurrentClassHeroes = 0;				++currentClass;			}		}		else		{			nher->heroType = addTab[additHero++];		}		std::string pom ;		loadToIt(nher->name,buf,it,4);		for(int x=0;x<3;x++)		{			loadToIt(pom,buf,it,4);			nher->lowStack[x] = atoi(pom.c_str());			loadToIt(pom,buf,it,4);			nher->highStack[x] = atoi(pom.c_str());			loadToIt(nher->refTypeStack[x],buf,it,(x==2) ? (3) : (4));			int hlp = nher->refTypeStack[x].find_first_of(' ',0);			if(hlp>=0)				nher->refTypeStack[x].replace(hlp,1,"");		}			nher->ID = heroes.size();		heroes.push_back(nher);	}	// Load heroes information	const JsonNode config(GameConstants::DATA_DIR + "/config/heroes.json");	BOOST_FOREACH(const JsonNode &hero, config["heroes"].Vector()) {		int hid = hero["id"].Float();		const JsonNode *value;		// sex: 0=male, 1=female		heroes[hid]->sex = !!hero["female"].Bool();		BOOST_FOREACH(const JsonNode &set, hero["skill_set"].Vector()) {			heroes[hid]->secSkillsInit.push_back(std::make_pair(set["skill"].Float(), set["level"].Float()));		}		value = &hero["spell"];		if (!value->isNull()) {			heroes[hid]->startingSpell = value->Float();		}		value = &hero["specialties"];		if (!value->isNull()) {			BOOST_FOREACH(const JsonNode &specialty, value->Vector()) {				SSpecialtyInfo dummy;				dummy.type = specialty["type"].Float();				dummy.val = specialty["val"].Float();				dummy.subtype = specialty["subtype"].Float();				dummy.additionalinfo = specialty["info"].Float();				heroes[hid]->spec.push_back(dummy); //put a copy of dummy			}		}	}	loadHeroClasses();	initHeroClasses();	expPerLevel.push_back(0);	expPerLevel.push_back(1000);	expPerLevel.push_back(2000);	expPerLevel.push_back(3200);	expPerLevel.push_back(4600);	expPerLevel.push_back(6200);	expPerLevel.push_back(8000);	expPerLevel.push_back(10000);	expPerLevel.push_back(12200);	expPerLevel.push_back(14700);	expPerLevel.push_back(17500);	expPerLevel.push_back(20600);	expPerLevel.push_back(24320);	expPerLevel.push_back(28784);	expPerLevel.push_back(34140);	while (expPerLevel[expPerLevel.size() - 1] > expPerLevel[expPerLevel.size() - 2])	{		int i = expPerLevel.size() - 1;		expPerLevel.push_back (expPerLevel[i] + (expPerLevel[i] - expPerLevel[i-1]) * 1.2);	}	expPerLevel.pop_back();//last value is broken	//ballistics info	buf = bitmaph->getTextFile("BALLIST.TXT");	it = 0;	for(int i=0; i<22; ++i)	{		loadToIt(dump,buf,it,4);	}	for(int lvl=0; lvl<4; ++lvl)	{		CHeroHandler::SBallisticsLevelInfo bli;		si32 tempNum;		loadToIt(tempNum,buf,it,4);		bli.keep = tempNum;		loadToIt(tempNum,buf,it,4);		bli.tower = tempNum;		loadToIt(tempNum,buf,it,4);		bli.gate = tempNum;		loadToIt(tempNum,buf,it,4);		bli.wall = tempNum;		loadToIt(tempNum,buf,it,4);		bli.shots = tempNum;		loadToIt(tempNum,buf,it,4);		bli.noDmg = tempNum;		loadToIt(tempNum,buf,it,4);		bli.oneDmg = tempNum;		loadToIt(tempNum,buf,it,4);		bli.twoDmg = tempNum;		loadToIt(tempNum,buf,it,4);		bli.sum = tempNum;		if(lvl!=3)		{			loadToIt(dump,buf,it,4);		}		ballistics.push_back(bli);	}}void CHeroHandler::loadHeroClasses(){	std::istringstream str(bitmaph->getTextFile("HCTRAITS.TXT")); //we'll be reading from it	const int BUFFER_SIZE = 5000;	char buffer[BUFFER_SIZE+1];	for(int i=0; i<3; ++i) str.getline(buffer, BUFFER_SIZE); //omitting rubbish	for(int ss=0; ss<18; ++ss) //18 classes of hero (including conflux)	{		CHeroClass * hc = new CHeroClass;		hc->alignment = ss / 6;		char name[BUFFER_SIZE+1];		str.get(name, BUFFER_SIZE, '\t');		hc->name = name;		//workaround for locale issue (different localisations use different decimal separator)		int intPart,fracPart;		str >> intPart;		str.ignore();//ignore decimal separator		str >> fracPart;		hc->aggression = intPart + fracPart/100.0;				str >> hc->initialAttack;		str >> hc->initialDefence;		str >> hc->initialPower;		str >> hc->initialKnowledge;		hc->primChance.resize(GameConstants::PRIMARY_SKILLS);		for(int x=0; x<GameConstants::PRIMARY_SKILLS; ++x)		{			str >> hc->primChance[x].first;		}		for(int x=0; x<GameConstants::PRIMARY_SKILLS; ++x)		{			str >> hc->primChance[x].second;		}		hc->proSec.resize(GameConstants::SKILL_QUANTITY);		for(int dd=0; dd<GameConstants::SKILL_QUANTITY; ++dd)		{			str >> hc->proSec[dd];		}		for(int dd=0; dd<ARRAY_COUNT(hc->selectionProbability); ++dd)		{			str >> hc->selectionProbability[dd];		}		heroClasses.push_back(hc);		str.getline(buffer, BUFFER_SIZE); //removing end of line characters	}}void CHeroHandler::initHeroClasses(){	for(int gg=0; gg<heroes.size(); ++gg)	{		heroes[gg]->heroClass = heroClasses[heroes[gg]->heroType];	}	loadTerrains();}ui32 CHeroHandler::level (ui64 experience) const{	int i;	if (experience <= expPerLevel.back())	{		for (i = expPerLevel.size()-1; experience < expPerLevel[i]; i--);		return i + 1;	}	else	{		i = expPerLevel.size() - 1;		while (experience > reqExp (i))			i++;		return i;	}}ui64 CHeroHandler::reqExp (ui32 level) const{	if(!level)		return 0;	if (level <= expPerLevel.size())	{		return expPerLevel[level-1];	}	else	{		tlog3 << "A hero has reached unsupported amount of experience\n";		return expPerLevel[expPerLevel.size()-1];	}}void CHeroHandler::loadTerrains(){	int faction = 0;	const JsonNode config(GameConstants::DATA_DIR + "/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);}CHero::CHero(){	startingSpell = -1;	sex = 0xff;}CHero::~CHero(){}
 |