PageRenderTime 25ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/src/PokemonInfo/movesetchecker.cpp

https://github.com/pkexp-martin/pokemon-online
C++ | 334 lines | 275 code | 47 blank | 12 comment | 97 complexity | 5ff2d3bd6bc3bd4ef6ddfdefb5ab5494 MD5 | raw file
  1. namespace Pokemon {
  2. class uniqueId;
  3. }
  4. unsigned int qHash (const Pokemon::uniqueId &key);
  5. #include "movesetchecker.h"
  6. #include "pokemoninfo.h"
  7. QHash<Pokemon::uniqueId, QList<QSet<int > > > MoveSetChecker::legalCombinations[NUMBER_GENS];
  8. QHash<Pokemon::uniqueId, QList<QSet<int > > > MoveSetChecker::eventCombinations[NUMBER_GENS];
  9. QHash<Pokemon::uniqueId, QList<QSet<int > > > MoveSetChecker::breedingCombinations[NUMBER_GENS];
  10. QString MoveSetChecker::dir;
  11. bool MoveSetChecker::enforceMinLevels = true;
  12. QString MoveSetChecker::path(const QString &arg)
  13. {
  14. return dir + arg;
  15. }
  16. void MoveSetChecker::loadCombinations(const QString &file, int gen, QHash<Pokemon::uniqueId, QList<QSet<int> > > *set)
  17. {
  18. QFile in(file);
  19. in.open(QIODevice::ReadOnly);
  20. QList<QByteArray> pokes = in.readAll().split('\n');
  21. foreach(QByteArray poke, pokes) {
  22. Pokemon::uniqueId id;
  23. QString data;
  24. if (!id.extract(QString(poke), id, data))
  25. continue;
  26. /* Even if the hash is empty, it proves it's here, otherwise it would be filled by the data of
  27. the base forme */
  28. if (!legalCombinations[gen-GEN_MIN].contains(id))
  29. legalCombinations[gen-GEN_MIN].insert(id, QList<QSet<int > >());
  30. set[gen-GEN_MIN].insert(id, QList<QSet<int > >());
  31. if (data.length() > 0) {
  32. QStringList combs = data.split('|');
  33. foreach(QString comb, combs) {
  34. QStringList moves = comb.split(' ');
  35. QSet<int> toPush;
  36. foreach(QString move, moves) {
  37. toPush.insert(move.toInt());
  38. }
  39. legalCombinations[gen-GEN_MIN][id].push_back(toPush);
  40. set[gen-GEN_MIN][id].push_back(toPush);
  41. }
  42. }
  43. }
  44. }
  45. void MoveSetChecker::init(const QString &dir, bool enf)
  46. {
  47. MoveSetChecker::dir = dir;
  48. MoveSetChecker::enforceMinLevels = enf;
  49. QList<Pokemon::uniqueId> ids = PokemonInfo::AllIds();
  50. for (int gen = GEN_MIN; gen <= GEN_MAX; gen++) {
  51. /* Egg move combinations */
  52. loadCombinations(path("legal_combinations_" + QString::number(gen) + "G.txt"), gen, breedingCombinations);
  53. /* Event move combinations */
  54. loadCombinations(path("event_combinations_" + QString::number(gen) + "G.txt"), gen, eventCombinations);
  55. foreach(Pokemon::uniqueId id, ids) {
  56. if (!PokemonInfo::IsForme(id))
  57. continue;
  58. if (!legalCombinations[gen-GEN_MIN].contains(PokemonInfo::OriginalForme(id)))
  59. continue;
  60. if (legalCombinations[gen-GEN_MIN].contains(id))
  61. continue;
  62. legalCombinations[gen-GEN_MIN][id] = legalCombinations[gen-GEN_MIN][PokemonInfo::OriginalForme(id)];
  63. }
  64. }
  65. }
  66. bool MoveSetChecker::isValid(const Pokemon::uniqueId &pokeid, int gen, int move1, int move2, int move3, int move4, int ability,
  67. int gender, int level, bool maledw, QSet<int> *invalid_moves, QString *error) {
  68. QSet<int> moves;
  69. moves << move1 << move2 << move3 << move4;
  70. return isValid(pokeid, gen, moves, ability, gender, level, maledw, invalid_moves, error);
  71. }
  72. static QString getCombinationS(const QSet<int> &invalid_moves) {
  73. QString s;
  74. bool comma(false);
  75. foreach(int move, invalid_moves) {
  76. if (comma)
  77. s += ", ";
  78. comma = true;
  79. s += MoveInfo::Name(move);
  80. }
  81. return s;
  82. }
  83. bool MoveSetChecker::isValid(const Pokemon::uniqueId &pokeid, int gen, const QSet<int> &moves2, int ability, int gender,
  84. int level, bool maledw, QSet<int> *invalid_moves, QString *error) {
  85. QSet<int> moves = moves2;
  86. moves.remove(0);
  87. if (!enforceMinLevels)
  88. level = 100;
  89. int limit;
  90. if (gen >= 3)
  91. limit = 3;
  92. else
  93. limit = 1;
  94. for (int g = gen; g >= limit; g--) {
  95. if (!PokemonInfo::Exists(pokeid, g)) {
  96. if (PokemonInfo::HasPreEvo(pokeid.pokenum)) {
  97. return MoveSetChecker::isValid(PokemonInfo::PreEvo(pokeid.pokenum), g, moves, 0, gender, level, maledw,
  98. invalid_moves, error);
  99. }
  100. if (invalid_moves) {
  101. *invalid_moves = moves;
  102. }
  103. if (error) {
  104. *error = QObject::tr("%1 can't learn the following moves while being at level %2: %3.")
  105. .arg(PokemonInfo::Name(pokeid), QString::number(level), getCombinationS(moves));
  106. }
  107. return false;
  108. }
  109. if (PokemonInfo::AbsoluteMinLevel(pokeid, g) > level) {
  110. if (invalid_moves) {
  111. *invalid_moves = moves;
  112. }
  113. if (error) {
  114. *error = QObject::tr("%1 can't learn the following moves while being at level %2: %3.")
  115. .arg(PokemonInfo::Name(pokeid), QString::number(level), getCombinationS(moves));
  116. }
  117. return false;
  118. }
  119. if (!PokemonInfo::Moves(pokeid, g).contains(moves)) {
  120. moves.subtract(PokemonInfo::Moves(pokeid, g));
  121. if (invalid_moves) {
  122. *invalid_moves = moves;
  123. }
  124. if (error) {
  125. *error = QObject::tr("%1 can't learn the following moves: %2.")
  126. .arg(PokemonInfo::Name(pokeid), getCombinationS(moves));
  127. }
  128. return false;
  129. }
  130. /* If the pokemon is underleveled, he was caught wild or from a previous gen, anyhow he couldn't have evolved from this gen */
  131. bool nobreeding = PokemonInfo::MinEggLevel(pokeid, g) > level;
  132. if (g < 5 && g >= 3) {
  133. AbilityGroup ab = PokemonInfo::Abilities(pokeid, gen);
  134. if (ability != 0 && ability != ab.ab(0)) {
  135. if (ability != ab.ab(1)) {
  136. if (invalid_moves) {
  137. *invalid_moves = moves;
  138. }
  139. if (error) {
  140. *error = QObject::tr("%1 can't learn the following moves from older generations at the same time as having the ability %2: %3.")
  141. .arg(PokemonInfo::Name(pokeid), AbilityInfo::Name(ability), getCombinationS(moves));
  142. }
  143. return false;
  144. }
  145. /* First stage evolutions can't have 4th gen abilities with 3rd gen moves */
  146. if (g == 3 && gen > 3) {
  147. ab = PokemonInfo::Abilities(pokeid, g);
  148. if (ab.ab(1) != ability) {
  149. if (!PokemonInfo::HasPreEvo(pokeid.pokenum)) {
  150. if (invalid_moves) {
  151. *invalid_moves = moves;
  152. }
  153. if (error) {
  154. *error = QObject::tr("%1 can't learn the following moves from third generation at the same time as having the fourth generation ability %2: %3.")
  155. .arg(PokemonInfo::Name(pokeid), AbilityInfo::Name(ability), getCombinationS(moves));
  156. }
  157. return false;
  158. }
  159. return nobreeding == false &&
  160. isValid(PokemonInfo::PreEvo(pokeid.pokenum), g, moves, 0, gender, level, false, invalid_moves, error);
  161. }
  162. }
  163. }
  164. }
  165. if (maledw && gender == Pokemon::Female) {
  166. return false;
  167. }
  168. /* now we know the pokemon at least knows all moves */
  169. moves.subtract(PokemonInfo::RegularMoves(pokeid, g));
  170. /* In gen 2 we must allow tradebacks. For that we need movesets without gen 2
  171. egg moves or special moves */
  172. if (g == 2) {
  173. if (PokemonInfo::Exists(pokeid, 1)) {
  174. bool ok = true;
  175. foreach(int move, moves) {
  176. if (!MoveInfo::Exists(move, 1)) {
  177. ok = false;
  178. break;
  179. }
  180. }
  181. if (ok)
  182. moves.subtract(PokemonInfo::RegularMoves(pokeid, 1));
  183. } else if (!nobreeding) {
  184. int preevo = PokemonInfo::PreEvo(pokeid.pokenum);
  185. if (preevo != 0 && preevo != pokeid.pokenum && PokemonInfo::Exists(preevo, 1)) {
  186. bool ok = true;
  187. foreach(int move, moves) {
  188. if (!MoveInfo::Exists(move, 1)) {
  189. ok = false;
  190. break;
  191. }
  192. }
  193. if (ok && isValid(preevo, 2, moves))
  194. return true;
  195. }
  196. }
  197. }
  198. if (!nobreeding) {
  199. /* If there's a pre evo move and an old gen move, you must check the pre evo has the combination in the old gen */
  200. QSet<int> moves3 = moves;
  201. moves3.subtract(PokemonInfo::PreEvoMoves(pokeid,g));
  202. if (moves3.size() != moves.size()) {
  203. int pokemon = PokemonInfo::PreEvo(pokeid.pokenum);
  204. int ab2;
  205. AbilityGroup ab = PokemonInfo::Abilities(pokeid);
  206. if (ability == ab.ab(0)) {
  207. ab2 = PokemonInfo::Abilities(pokemon, g).ab(0);
  208. } else if (ability == ab.ab(1)) {
  209. ab2 = PokemonInfo::Abilities(pokemon, g).ab(1);
  210. } else if (ability == ab.ab(2)) {
  211. ab2 = PokemonInfo::Abilities(pokemon, g).ab(2);
  212. } else {
  213. ab2 = 0;
  214. }
  215. if (isValid(pokemon, g, moves,ab2,gender,level,maledw))
  216. return true;
  217. }
  218. }
  219. if (moves.empty())
  220. return true;
  221. if (!maledw) {
  222. if (moves.size() == 1) {
  223. if (!nobreeding) {
  224. if (PokemonInfo::HasMoveInGen(pokeid, *moves.begin(), g)) {
  225. return true;
  226. }
  227. } else {
  228. if (PokemonInfo::SpecialMoves(pokeid, g).contains(*moves.begin())) {
  229. return true;
  230. }
  231. }
  232. }
  233. if (!nobreeding && isAnEggMoveCombination(pokeid, g, moves)) {
  234. return true;
  235. }
  236. }
  237. if (g == 5) {
  238. AbilityGroup ab = PokemonInfo::Abilities(pokeid);
  239. if (ability == ab.ab(2)) {
  240. if (moves.size() == 1 && PokemonInfo::dreamWorldMoves(pokeid).contains(moves)) {
  241. return true;
  242. }
  243. }
  244. }
  245. foreach(int move, moves) {
  246. if (g > 3 && MoveInfo::isHM(move, g-1)) {
  247. if (error) {
  248. *error = QObject::tr("%1 can't have HM %2 inherited from past generations.").arg(PokemonInfo::Name(pokeid), MoveInfo::Name(move));
  249. }
  250. return false;
  251. }
  252. }
  253. }
  254. /* The remaining moves are considered invalid */
  255. if (invalid_moves) {
  256. *invalid_moves = moves;
  257. }
  258. if (error) {
  259. if (moves.size() == 1) {
  260. *error = QObject::tr("%1 can't learn %2 with moves from older generations.").arg(PokemonInfo::Name(pokeid), MoveInfo::Name(*moves.begin()));
  261. } else {
  262. *error = QObject::tr("%1 can't learn the following move combination: %2.").arg(PokemonInfo::Name(pokeid), getCombinationS(moves));
  263. }
  264. }
  265. return false;
  266. }
  267. /* Used by ChainBreeding */
  268. bool MoveSetChecker::isAnEggMoveCombination(const Pokemon::uniqueId &pokeid, int gen, QSet<int> moves)
  269. {
  270. foreach(QSet<int> combination, legalCombinations[gen-GEN_MIN].value(pokeid)) {
  271. if (combination.contains(moves))
  272. return true;
  273. }
  274. return false;
  275. }
  276. QList<QSet<int> > MoveSetChecker::combinationsFor(Pokemon::uniqueId pokenum, int gen)
  277. {
  278. return legalCombinations[gen-GEN_MIN].value(pokenum);
  279. }
  280. QHash<Pokemon::uniqueId, QList<QSet<int> > > MoveSetChecker::eventCombinationsOf(int gen)
  281. {
  282. return eventCombinations[gen-GEN_MIN];
  283. }
  284. QHash<Pokemon::uniqueId, QList<QSet<int> > > MoveSetChecker::breedingCombinationsOf(int gen)
  285. {
  286. return breedingCombinations[gen-GEN_MIN];
  287. }