CHeroHandler.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. #include "StdInc.h"
  2. #include "CHeroHandler.h"
  3. #include "CLodHandler.h"
  4. #include "../lib/VCMI_Lib.h"
  5. #include "../lib/JsonNode.h"
  6. #include "GameConstants.h"
  7. #include <boost/version.hpp>
  8. #if BOOST_VERSION >= 103800
  9. #include <boost/spirit/include/classic.hpp>
  10. #else
  11. #include <boost/spirit.hpp>
  12. #endif
  13. using namespace boost::spirit;
  14. extern CLodHandler * bitmaph;
  15. void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);
  16. /*
  17. * CHeroHandler.cpp, part of VCMI engine
  18. *
  19. * Authors: listed in file AUTHORS in main folder
  20. *
  21. * License: GNU General Public License v2.0 or later
  22. * Full text of license available in license.txt file, in main folder
  23. *
  24. */
  25. CHeroClass::CHeroClass()
  26. {
  27. skillLimit = 8;
  28. }
  29. CHeroClass::~CHeroClass()
  30. {
  31. }
  32. int CHeroClass::chooseSecSkill(const std::set<int> & possibles) const //picks secondary skill out from given possibilities
  33. {
  34. if(possibles.size()==1)
  35. return *possibles.begin();
  36. int totalProb = 0;
  37. for(std::set<int>::const_iterator i=possibles.begin(); i!=possibles.end(); i++)
  38. {
  39. totalProb += proSec[*i];
  40. }
  41. int ran = rand()%totalProb;
  42. for(std::set<int>::const_iterator i=possibles.begin(); i!=possibles.end(); i++)
  43. {
  44. ran -= proSec[*i];
  45. if(ran<0)
  46. return *i;
  47. }
  48. throw std::string("Cannot pick secondary skill!");
  49. }
  50. EAlignment::EAlignment CHeroClass::getAlignment()
  51. {
  52. return (EAlignment::EAlignment)alignment;
  53. }
  54. int CObstacleInfo::getWidth() const
  55. {
  56. int ret = 1;
  57. int line = 1;
  58. for(int h=0; h<blockmap.size(); ++h)
  59. {
  60. int cur = - line/2;
  61. switch(blockmap[h])
  62. {
  63. case 'X' : case 'N':
  64. ++cur;
  65. break;
  66. case 'L':
  67. if(cur > ret)
  68. ret = cur;
  69. ++line;
  70. break;
  71. }
  72. }
  73. return ret;
  74. }
  75. int CObstacleInfo::getHeight() const
  76. {
  77. int ret = 1;
  78. for(int h=0; h<blockmap.size(); ++h)
  79. {
  80. if(blockmap[h] == 'L')
  81. {
  82. ++ret;
  83. }
  84. }
  85. return ret;
  86. }
  87. std::vector<BattleHex> CObstacleInfo::getBlocked(BattleHex hex) const
  88. {
  89. std::vector<BattleHex> ret;
  90. int cur = hex; //currently browsed hex
  91. int curBeg = hex; //beginning of current line
  92. for(int h=0; h<blockmap.size(); ++h)
  93. {
  94. switch(blockmap[h])
  95. {
  96. case 'X':
  97. ret.push_back(cur);
  98. ++cur;
  99. break;
  100. case 'L':
  101. cur = curBeg + GameConstants::BFIELD_WIDTH;
  102. if((cur/GameConstants::BFIELD_WIDTH)%2 != 1)
  103. {
  104. cur--;
  105. }
  106. curBeg = cur;
  107. break;
  108. case 'N':
  109. ++cur;
  110. break;
  111. }
  112. }
  113. return ret;
  114. }
  115. BattleHex CObstacleInfo::getMaxBlocked(BattleHex hex) const
  116. {
  117. std::vector<BattleHex> blocked = getBlocked(hex);
  118. return *std::max_element(blocked.begin(), blocked.end());
  119. }
  120. CHeroHandler::~CHeroHandler()
  121. {
  122. for (int i = 0; i < heroes.size(); i++)
  123. heroes[i].dellNull();
  124. for (int i = 0; i < heroClasses.size(); i++)
  125. delete heroClasses[i];
  126. }
  127. CHeroHandler::CHeroHandler()
  128. {}
  129. void CHeroHandler::loadObstacles()
  130. {
  131. const JsonNode config(GameConstants::DATA_DIR + "/config/obstacles.json");
  132. BOOST_FOREACH(const JsonNode &obs, config["obstacles"].Vector()) {
  133. CObstacleInfo obi;
  134. obi.ID = obs["id"].Float();
  135. obi.defName = obs["defname"].String();
  136. obi.blockmap = obs["blockmap"].String();
  137. obi.allowedTerrains = obs["terrains"].String();
  138. assert(obi.allowedTerrains.size() >= 25);
  139. obi.posShift.first = obs["shift_x"].Float();
  140. obi.posShift.second = obs["shift_y"].Float();
  141. obstacles[obi.ID] = obi;
  142. }
  143. }
  144. void CHeroHandler::loadPuzzleInfo()
  145. {
  146. const JsonNode config(GameConstants::DATA_DIR + "/config/puzzle_map.json");
  147. int faction = 0;
  148. BOOST_FOREACH(const JsonNode &puzzle, config["puzzles"].Vector()) {
  149. int idx = 0;
  150. BOOST_FOREACH(const JsonNode &piece, puzzle.Vector()) {
  151. SPuzzleInfo spi;
  152. spi.x = piece["x"].Float();
  153. spi.y = piece["y"].Float();
  154. spi.whenUncovered = piece["order"].Float();
  155. spi.number = idx;
  156. // filename calculation
  157. std::ostringstream suffix;
  158. suffix << std::setfill('0') << std::setw(2);
  159. suffix << idx << ".BMP";
  160. static const std::string factionToInfix[GameConstants::F_NUMBER] = {"CAS", "RAM", "TOW", "INF", "NEC", "DUN", "STR", "FOR", "ELE"};
  161. spi.filename = "PUZ" + factionToInfix[faction] + suffix.str();
  162. puzzleInfo[faction].push_back(spi);
  163. idx ++;
  164. }
  165. assert(idx == PUZZLES_PER_FACTION);
  166. faction ++;
  167. }
  168. assert(faction == GameConstants::F_NUMBER);
  169. }
  170. void CHeroHandler::loadHeroes()
  171. {
  172. VLC->heroh = this;
  173. std::string buf = bitmaph->getTextFile("HOTRAITS.TXT");
  174. int it=0;
  175. std::string dump;
  176. for(int i=0; i<2; ++i)
  177. {
  178. loadToIt(dump,buf,it,3);
  179. }
  180. int numberOfCurrentClassHeroes = 0;
  181. int currentClass = 0;
  182. int additHero = 0;
  183. CHero::EHeroClasses addTab[12];
  184. addTab[0] = CHero::KNIGHT;
  185. addTab[1] = CHero::WITCH;
  186. addTab[2] = CHero::KNIGHT;
  187. addTab[3] = CHero::WIZARD;
  188. addTab[4] = CHero::RANGER;
  189. addTab[5] = CHero::BARBARIAN;
  190. addTab[6] = CHero::DEATHKNIGHT;
  191. addTab[7] = CHero::WARLOCK;
  192. addTab[8] = CHero::KNIGHT;
  193. addTab[9] = CHero::WARLOCK;
  194. addTab[10] = CHero::BARBARIAN;
  195. addTab[11] = CHero::DEMONIAC;
  196. for (int i=0; i<GameConstants::HEROES_QUANTITY; i++)
  197. {
  198. CHero * nher = new CHero;
  199. if(currentClass<18)
  200. {
  201. nher->heroType = static_cast<CHero::EHeroClasses>(currentClass);
  202. ++numberOfCurrentClassHeroes;
  203. if(numberOfCurrentClassHeroes==8)
  204. {
  205. numberOfCurrentClassHeroes = 0;
  206. ++currentClass;
  207. }
  208. }
  209. else
  210. {
  211. nher->heroType = addTab[additHero++];
  212. }
  213. std::string pom ;
  214. loadToIt(nher->name,buf,it,4);
  215. for(int x=0;x<3;x++)
  216. {
  217. loadToIt(pom,buf,it,4);
  218. nher->lowStack[x] = atoi(pom.c_str());
  219. loadToIt(pom,buf,it,4);
  220. nher->highStack[x] = atoi(pom.c_str());
  221. loadToIt(nher->refTypeStack[x],buf,it,(x==2) ? (3) : (4));
  222. int hlp = nher->refTypeStack[x].find_first_of(' ',0);
  223. if(hlp>=0)
  224. nher->refTypeStack[x].replace(hlp,1,"");
  225. }
  226. nher->ID = heroes.size();
  227. heroes.push_back(nher);
  228. }
  229. // Load heroes information
  230. const JsonNode config(GameConstants::DATA_DIR + "/config/heroes.json");
  231. BOOST_FOREACH(const JsonNode &hero, config["heroes"].Vector()) {
  232. int hid = hero["id"].Float();
  233. const JsonNode *value;
  234. // sex: 0=male, 1=female
  235. heroes[hid]->sex = !!hero["female"].Bool();
  236. BOOST_FOREACH(const JsonNode &set, hero["skill_set"].Vector()) {
  237. heroes[hid]->secSkillsInit.push_back(std::make_pair(set["skill"].Float(), set["level"].Float()));
  238. }
  239. value = &hero["spell"];
  240. if (!value->isNull()) {
  241. heroes[hid]->startingSpell = value->Float();
  242. }
  243. value = &hero["specialties"];
  244. if (!value->isNull()) {
  245. BOOST_FOREACH(const JsonNode &specialty, value->Vector()) {
  246. SSpecialtyInfo dummy;
  247. dummy.type = specialty["type"].Float();
  248. dummy.val = specialty["val"].Float();
  249. dummy.subtype = specialty["subtype"].Float();
  250. dummy.additionalinfo = specialty["info"].Float();
  251. heroes[hid]->spec.push_back(dummy); //put a copy of dummy
  252. }
  253. }
  254. }
  255. loadHeroClasses();
  256. initHeroClasses();
  257. expPerLevel.push_back(0);
  258. expPerLevel.push_back(1000);
  259. expPerLevel.push_back(2000);
  260. expPerLevel.push_back(3200);
  261. expPerLevel.push_back(4600);
  262. expPerLevel.push_back(6200);
  263. expPerLevel.push_back(8000);
  264. expPerLevel.push_back(10000);
  265. expPerLevel.push_back(12200);
  266. expPerLevel.push_back(14700);
  267. expPerLevel.push_back(17500);
  268. expPerLevel.push_back(20600);
  269. expPerLevel.push_back(24320);
  270. expPerLevel.push_back(28784);
  271. expPerLevel.push_back(34140);
  272. while (expPerLevel[expPerLevel.size() - 1] > expPerLevel[expPerLevel.size() - 2])
  273. {
  274. int i = expPerLevel.size() - 1;
  275. expPerLevel.push_back (expPerLevel[i] + (expPerLevel[i] - expPerLevel[i-1]) * 1.2);
  276. }
  277. expPerLevel.pop_back();//last value is broken
  278. //ballistics info
  279. buf = bitmaph->getTextFile("BALLIST.TXT");
  280. it = 0;
  281. for(int i=0; i<22; ++i)
  282. {
  283. loadToIt(dump,buf,it,4);
  284. }
  285. for(int lvl=0; lvl<4; ++lvl)
  286. {
  287. CHeroHandler::SBallisticsLevelInfo bli;
  288. si32 tempNum;
  289. loadToIt(tempNum,buf,it,4);
  290. bli.keep = tempNum;
  291. loadToIt(tempNum,buf,it,4);
  292. bli.tower = tempNum;
  293. loadToIt(tempNum,buf,it,4);
  294. bli.gate = tempNum;
  295. loadToIt(tempNum,buf,it,4);
  296. bli.wall = tempNum;
  297. loadToIt(tempNum,buf,it,4);
  298. bli.shots = tempNum;
  299. loadToIt(tempNum,buf,it,4);
  300. bli.noDmg = tempNum;
  301. loadToIt(tempNum,buf,it,4);
  302. bli.oneDmg = tempNum;
  303. loadToIt(tempNum,buf,it,4);
  304. bli.twoDmg = tempNum;
  305. loadToIt(tempNum,buf,it,4);
  306. bli.sum = tempNum;
  307. if(lvl!=3)
  308. {
  309. loadToIt(dump,buf,it,4);
  310. }
  311. ballistics.push_back(bli);
  312. }
  313. }
  314. void CHeroHandler::loadHeroClasses()
  315. {
  316. std::istringstream str(bitmaph->getTextFile("HCTRAITS.TXT")); //we'll be reading from it
  317. const int BUFFER_SIZE = 5000;
  318. char buffer[BUFFER_SIZE+1];
  319. for(int i=0; i<3; ++i) str.getline(buffer, BUFFER_SIZE); //omitting rubbish
  320. for(int ss=0; ss<18; ++ss) //18 classes of hero (including conflux)
  321. {
  322. CHeroClass * hc = new CHeroClass;
  323. hc->alignment = ss / 6;
  324. char name[BUFFER_SIZE+1];
  325. str.get(name, BUFFER_SIZE, '\t');
  326. hc->name = name;
  327. //workaround for locale issue (different localisations use different decimal separator)
  328. int intPart,fracPart;
  329. str >> intPart;
  330. str.ignore();//ignore decimal separator
  331. str >> fracPart;
  332. hc->aggression = intPart + fracPart/100.0;
  333. str >> hc->initialAttack;
  334. str >> hc->initialDefence;
  335. str >> hc->initialPower;
  336. str >> hc->initialKnowledge;
  337. hc->primChance.resize(GameConstants::PRIMARY_SKILLS);
  338. for(int x=0; x<GameConstants::PRIMARY_SKILLS; ++x)
  339. {
  340. str >> hc->primChance[x].first;
  341. }
  342. for(int x=0; x<GameConstants::PRIMARY_SKILLS; ++x)
  343. {
  344. str >> hc->primChance[x].second;
  345. }
  346. hc->proSec.resize(GameConstants::SKILL_QUANTITY);
  347. for(int dd=0; dd<GameConstants::SKILL_QUANTITY; ++dd)
  348. {
  349. str >> hc->proSec[dd];
  350. }
  351. for(int dd=0; dd<ARRAY_COUNT(hc->selectionProbability); ++dd)
  352. {
  353. str >> hc->selectionProbability[dd];
  354. }
  355. heroClasses.push_back(hc);
  356. str.getline(buffer, BUFFER_SIZE); //removing end of line characters
  357. }
  358. }
  359. void CHeroHandler::initHeroClasses()
  360. {
  361. for(int gg=0; gg<heroes.size(); ++gg)
  362. {
  363. heroes[gg]->heroClass = heroClasses[heroes[gg]->heroType];
  364. }
  365. loadTerrains();
  366. }
  367. ui32 CHeroHandler::level (ui64 experience) const
  368. {
  369. int i;
  370. if (experience <= expPerLevel.back())
  371. {
  372. for (i = expPerLevel.size()-1; experience < expPerLevel[i]; i--);
  373. return i + 1;
  374. }
  375. else
  376. {
  377. i = expPerLevel.size() - 1;
  378. while (experience > reqExp (i))
  379. i++;
  380. return i;
  381. }
  382. }
  383. ui64 CHeroHandler::reqExp (ui32 level) const
  384. {
  385. if(!level)
  386. return 0;
  387. if (level <= expPerLevel.size())
  388. {
  389. return expPerLevel[level-1];
  390. }
  391. else
  392. {
  393. tlog3 << "A hero has reached unsupported amount of experience\n";
  394. return expPerLevel[expPerLevel.size()-1];
  395. }
  396. }
  397. void CHeroHandler::loadTerrains()
  398. {
  399. int faction = 0;
  400. const JsonNode config(GameConstants::DATA_DIR + "/config/terrains.json");
  401. nativeTerrains.resize(GameConstants::F_NUMBER);
  402. BOOST_FOREACH(const JsonNode &terrain, config["terrains"].Vector()) {
  403. BOOST_FOREACH(const JsonNode &cost, terrain["costs"].Vector()) {
  404. int curCost = cost.Float();
  405. heroClasses[2*faction]->terrCosts.push_back(curCost);
  406. heroClasses[2*faction+1]->terrCosts.push_back(curCost);
  407. }
  408. nativeTerrains[faction] = terrain["native"].Float();
  409. faction ++;
  410. }
  411. assert(faction == GameConstants::F_NUMBER);
  412. }
  413. CHero::CHero()
  414. {
  415. startingSpell = -1;
  416. sex = 0xff;
  417. }
  418. CHero::~CHero()
  419. {
  420. }