Peoplemon  0.1.0
Peoplemon 3 game source documentation
Item.cpp
Go to the documentation of this file.
1 #include <Core/Items/Item.hpp>
2 
3 #include <BLIB/Logging.hpp>
6 #include <Core/Properties.hpp>
7 #include <unordered_map>
8 
9 namespace core
10 {
11 namespace item
12 {
13 namespace
14 {
15 std::vector<Id> ids;
16 const std::string UnknownName = "<Unknown Item>";
17 const std::string UnknownDescription = "<This item is not known to Peoplemon>";
18 
19 void refreshIds(std::unordered_map<Id, std::string>* names) {
20  ids.clear();
21  ids.reserve(names->size());
22  for (const auto& p : *names) { ids.emplace_back(p.first); }
23  std::sort(ids.begin(), ids.end());
24 }
25 } // namespace
26 
27 std::unordered_map<Id, std::string>* Item::names = nullptr;
28 std::unordered_map<Id, std::string>* Item::descriptions = nullptr;
29 std::unordered_map<Id, int>* Item::values = nullptr;
30 
31 Id Item::cast(unsigned int id) {
32  const Id result = static_cast<Id>(id);
33 
34  if (names->find(result) != names->end()) { return result; }
35  else {
36  BL_LOG_WARN << "Unknown item id: " << id;
37  return Id::Unknown;
38  }
39 }
40 
42  names = &db.names;
43  descriptions = &db.descriptions;
44  values = &db.values;
45 
46  refreshIds(names);
47 }
48 
50  const unsigned int id = static_cast<unsigned int>(item);
51  if (item == Id::Unknown) return Category::Unknown;
52  if (id <= 100) {
53  switch (item) {
54  case Id::Terriball:
55  case Id::Peopleball:
60  case Id::Intelligiball:
61  case Id::Gulliball:
63  case Id::BullyBall:
64  case Id::CloneBall:
66  case Id::LegendBall:
67  return Category::Peopleball;
68  default:
69  return Category::Regular;
70  }
71  }
72  if (id <= 200) return Category::Key;
73  return Category::TM;
74 }
75 
77  const unsigned int id = static_cast<unsigned int>(item);
78  if (id >= 1 && id <= 4) return Type::TargetPeoplemon;
79  if (id >= 5 && id <= 17) return Type::Peopleball;
80  if (id >= 18 && id <= 27) return Type::TargetPeoplemon;
81  if (id >= 28 && id <= 32) return Type::EvolveStone;
82  if (id == 33 || id == 34 || id == 44) return Type::Useless;
83  if (id >= 35 && id <= 37) return Type::PlayerModifier;
84  if (id >= 38 && id <= 43) return Type::TargetPeoplemon;
85  if (id >= 50 && id <= 62) return Type::HoldItem;
86  if (id >= 101 && id <= 138) return Type::KeyItem;
87  if (id >= 200) return Type::TM;
88  return Type::Unknown;
89 }
90 
91 const std::string& Item::getName(Id item) {
92  const auto it = names->find(item);
93  if (it == names->end()) {
94  const pplmn::MoveId mid = getTmMove(item);
95  if (mid != pplmn::MoveId::Unknown) {
96  static std::string tmName;
97  tmName = "TM " + std::to_string(static_cast<int>(mid));
98  return tmName;
99  }
100  return UnknownName;
101  }
102  return it->second;
103 }
104 
105 const std::string& Item::getDescription(Id item) {
106  const auto it = descriptions->find(item);
107  if (it == descriptions->end()) {
108  const pplmn::MoveId mid = getTmMove(item);
109  if (mid != pplmn::MoveId::Unknown) {
110  static std::string tmDesc;
111  tmDesc = "This TM teaches " + pplmn::Move::name(mid) + " to a Peoplemon";
112  return tmDesc;
113  }
114  return UnknownDescription;
115  }
116  return it->second;
117 }
118 
119 int Item::getValue(Id item) {
120  const auto it = values->find(item);
121  if (it == values->end()) { return getCategory(item) == Category::TM ? 2500 : 0; }
122  return it->second;
123 }
124 
125 const std::vector<Id>& Item::validIds() {
126 #ifdef PEOPLEMON_DEBUG
127  refreshIds(names);
128 #endif
129 
130  return ids;
131 }
132 
134  const Type t = getType(id);
135  switch (t) {
137  case Type::Peopleball:
138  return true;
139  default:
140  return false;
141  }
142 }
143 
145  const auto checkStat = [&ppl](pplmn::Stat stat) -> bool {
146  return ppl.currentEVs().sum() < pplmn::Stats::MaxEVSum &&
147  ppl.currentEVs().get(stat) < pplmn::Stats::MaxEVStat;
148  };
149 
150  switch (item) {
151  case Id::Potion:
152  case Id::SuperPotion:
153  case Id::MegaPotion:
154  return ppl.currentHp() < ppl.currentStats().hp && ppl.currentHp() > 0;
156  return (ppl.currentHp() < ppl.currentStats().hp ||
158  ppl.currentHp() > 0;
159 
160  case Id::PpPack:
161  case Id::SuperPpPack:
162  for (int i = 0; i < 4; ++i) {
163  if (ppl.knownMoves()[i].id != pplmn::MoveId::Unknown &&
164  ppl.knownMoves()[i].curPP < ppl.knownMoves()[i].maxPP) {
165  return true;
166  }
167  }
168  return false;
169  case Id::Pp6Pack:
170  case Id::SuperPp6Pack:
171  return true; // just let them use it, dont check whole team
172  case Id::PpRaiser:
173  for (int i = 0; i < 4; ++i) {
174  const auto& m = ppl.knownMoves()[i];
175  if (m.id != pplmn::MoveId::Unknown) {
176  if (m.maxPP < pplmn::Move::pp(m.id) * 8 / 5) { return true; }
177  }
178  }
179  return false;
180 
181  case Id::KegOfProtein:
182  return checkStat(pplmn::Stat::Attack);
183  case Id::SuperPowerJuice:
184  return checkStat(pplmn::Stat::SpecialAttack);
185  case Id::TubOfIcedCream:
186  return checkStat(pplmn::Stat::SpecialDefense);
188  return checkStat(pplmn::Stat::Defense);
189  case Id::SuperSpeedJuice:
190  return checkStat(pplmn::Stat::Speed);
191  case Id::Compliments:
192  return checkStat(pplmn::Stat::HP);
193 
194  case Id::UnAnnoyerSoda:
195  return ppl.currentAilment() == pplmn::Ailment::Annoyed;
198  case Id::WakeUpSoda:
199  return ppl.currentAilment() == pplmn::Ailment::Sleep;
200  case Id::UnStickySoda:
201  return ppl.currentAilment() == pplmn::Ailment::Sticky;
202  case Id::UnFreezeSoda:
203  return ppl.currentAilment() == pplmn::Ailment::Frozen;
204 
205  default:
206  return false;
207  }
208 }
209 
211  const battle::Battler& battler) {
212  switch (item) {
214  if (battler.getSubstate().ailments != pplmn::PassiveAilment::None) return true;
215  break;
216  default:
217  break;
218  }
219  return hasEffectOnPeoplemon(item, ppl.base());
220 }
221 
223  std::vector<pplmn::OwnedPeoplemon>* team,
224  std::vector<pplmn::BattlePeoplemon>* battleTeam) {
225  if (!hasEffectOnPeoplemon(item, ppl)) return;
226 
227  const auto restorePP = [](pplmn::OwnedPeoplemon& ppl, int pp) {
228  for (int i = 0; i < 4; ++i) {
229  auto& m = ppl.knownMoves()[i];
230  m.curPP = std::min(m.curPP + pp, m.maxPP);
231  }
232  };
233 
234  switch (item) {
235  case Id::Potion:
236  ppl.giveHealth(20);
237  break;
238  case Id::SuperPotion:
239  ppl.giveHealth(50);
240  break;
241  case Id::MegaPotion:
242  ppl.giveHealth(200);
243  break;
245  ppl.giveHealth(9001);
247  break;
248 
249  case Id::PpPack:
250  restorePP(ppl, 10);
251  break;
252  case Id::SuperPpPack:
253  restorePP(ppl, 1000);
254  break;
255  case Id::Pp6Pack:
256  case Id::SuperPp6Pack: {
257  const int pp = item == Id::Pp6Pack ? 10 : 1000;
258  if (battleTeam) {
259  for (pplmn::BattlePeoplemon& p : *battleTeam) { restorePP(p.base(), pp); }
260  }
261  else if (team) {
262  for (pplmn::OwnedPeoplemon& p : *team) { restorePP(p, pp); }
263  }
264  else {
265  BL_LOG_ERROR << "Used PP 6 pack but not given team";
266  }
267  break;
268  }
269  case Id::PpRaiser:
270  for (int i = 0; i < 4; ++i) {
271  auto& m = ppl.knownMoves()[i];
272  if (m.id != pplmn::MoveId::Unknown) {
273  const int npp = std::min(m.maxPP + 1, pplmn::Move::pp(m.id) * 8 / 5);
274  const int ppp = npp - m.maxPP;
275  m.maxPP = npp;
276  m.curPP += ppp;
277  }
278  }
279  break;
280 
281  case Id::UnAnnoyerSoda:
282  case Id::UnFreezeSoda:
284  case Id::WakeUpSoda:
285  case Id::UnStickySoda:
287  break;
288 
289  case Id::KegOfProtein:
291  break;
292  case Id::SuperPowerJuice:
294  break;
295  case Id::TubOfIcedCream:
297  break;
300  break;
301  case Id::SuperSpeedJuice:
303  break;
304  case Id::Compliments:
306  break;
307 
308  default:
309  break;
310  }
311 }
312 
314  if (!hasEffectOnPeoplemon(item, ppl, battler)) return;
315 
316  useOnPeoplemon(item, ppl.base(), nullptr, &battler.peoplemon());
317 
318  switch (item) {
320  battler.getSubstate().ailments = pplmn::PassiveAilment::None;
321  break;
322  default:
323  break;
324  }
325 }
326 
327 std::string Item::getUseLine(Id item, const pplmn::OwnedPeoplemon& ppl) {
328  switch (item) {
329  case Id::Potion:
330  case Id::SuperPotion:
331  case Id::MegaPotion:
332  return ppl.name() + " had it's HP restored.";
334  return ppl.name() + " was healed completely.";
335 
336  case Id::PpPack:
337  case Id::SuperPpPack:
338  return ppl.name() + " had it's PP restored!";
339  case Id::Pp6Pack:
340  case Id::SuperPp6Pack:
341  return ppl.name() + " and all their friends had their PP restored.";
342  case Id::PpRaiser:
343  return ppl.name() + " had their PP raised.";
344 
345  case Id::KegOfProtein:
346  return ppl.name() + " had their " + pplmn::Stats::statToString(pplmn::Stat::Attack) +
347  " increased";
348  case Id::SuperPowerJuice:
349  return ppl.name() + " had their " + pplmn::Stats::statToString(pplmn::Stat::SpecialAttack) +
350  " increased";
351  case Id::TubOfIcedCream:
352  return ppl.name() + " had their " +
355  return ppl.name() + " had their " + pplmn::Stats::statToString(pplmn::Stat::Defense) +
356  " increased";
357  case Id::SuperSpeedJuice:
358  return ppl.name() + " had their " + pplmn::Stats::statToString(pplmn::Stat::Speed) +
359  " increased";
360  case Id::Compliments:
361  return ppl.name() + " had their " + pplmn::Stats::statToString(pplmn::Stat::HP) +
362  " increased";
363 
364  case Id::UnAnnoyerSoda:
365  return ppl.name() + " is no longer Annoyed.";
367  return ppl.name() + " is no longer Frustrated.";
368  case Id::WakeUpSoda:
369  return ppl.name() + " is no longer Sleeping.";
370  case Id::UnStickySoda:
371  return ppl.name() + " is no longer Sticky.";
372  case Id::UnFreezeSoda:
373  return ppl.name() + " is no longer Frozen.";
374 
375  default:
376  return "ERROR: " + getName(item) + " does not generate a useLine on a peoplemon.";
377  }
378 }
379 
380 bool Item::hasEffectOnPlayer(Id item, const player::State& state) {
381  switch (item) {
382  case Id::GoAwaySpray:
385  return state.repelSteps == 0;
386  default:
387  return false;
388  }
389 }
390 
391 void Item::useOnPlayer(Id item, player::State& state) {
392  switch (item) {
393  case Id::GoAwaySpray:
394  state.repelSteps = 100;
395  break;
397  state.repelSteps = 250;
398  break;
400  state.repelSteps = 2000;
401  break;
402  default:
403  break;
404  }
405 }
406 
407 std::string Item::getUseLine(Id item) {
408  switch (item) {
409  case Id::GoAwaySpray:
412  return "Now you smell bad enough to keep Peoplemon away.";
413  default:
414  return "ERROR: " + getName(item) + " does not generate a useLine on the player.";
415  }
416 }
417 
419  using T = std::underlying_type_t<Id>;
420  const T id = static_cast<T>(tm);
421  if (id < 200) return pplmn::MoveId::Unknown;
422  const T mid = id - 200;
423  return pplmn::Move::cast(mid);
424 }
425 
426 float Item::getPeopleballRate(Id ball, pplmn::Id ppl, int turnNumber, float levelRatio) {
427  if (ppl == pplmn::Id::Ben || ppl == pplmn::Id::BenToo) {
428  if (ball != Id::LegendBall) { return 0.f; }
429  }
430 
431  switch (ball) {
432  case Id::Terriball:
433  return 0.25f;
434  case Id::Peopleball:
436  return 1.f;
437  case Id::GreatPeopleball:
438  return 1.5f;
439  case Id::UltraPeopleball:
441  return 2.f;
442  case Id::Gulliball:
443  return turnNumber == 1 ? 4.f : 1.f;
444  case Id::BullyBall:
445  if (levelRatio >= 4.f) return 8.f;
446  if (levelRatio >= 2.f) return 4.f;
447  if (levelRatio > 1.f) return 2.f;
448  return 1.f;
449  case Id::ClockPeopleball:
450  return (static_cast<float>(turnNumber) + 10.f) / 10.f;
451  case Id::Intelligiball:
452  return pplmn::Peoplemon::type(ppl) == pplmn::Type::Intelligent ? 2.5f : 1.f;
453  case Id::LegendBall:
454  return 5.f;
455  case Id::CloneBall:
457  BL_LOG_WARN << "Got a clone or masterball unexpectedly";
458  return 1.f;
459  default:
460  BL_LOG_ERROR << "Got a non-peopleball: " << ball;
461  return 1.f;
462  }
463 }
464 
465 } // namespace item
466 } // namespace core
Type
The type classification of an item. This is used to determine when an item may be used and how to use...
Definition: Type.hpp:17
Category
Represents a category that an item can belong to. Used for bag sorting.
Definition: Category.hpp:16
Id
Represents an item in its simplist form.
Definition: Id.hpp:24
@ EvolveStone
The item is used to evolve peoplemon.
@ Useless
Items who's only use is to be sold.
@ Peopleball
The item is used to catch peoplemon.
@ Unknown
Invalid type for Unknown items.
@ KeyItem
The item is a key item.
@ TargetPeoplemon
The item is used on a peoplemon.
@ TM
The item is a TM.
@ PlayerModifier
The item modifiers player state (ie repel)
@ HoldItem
The item may be held by Peoplemon.
@ Peopleball
Peopleballs.
@ Unknown
Unknown item category.
@ Key
Key items. Covers ids [101, 200].
@ TM
TM's. Covers ids [201, 499].
@ Regular
Regular items. Covers ids [1, 100].
Stat
Represents a single stat. Used as an offset to access Stats as an array.
Definition: Stat.hpp:16
MoveId
The id of a move.
Definition: MoveId.hpp:16
Id
The id of a peoplemon.
Definition: Id.hpp:16
Core classes and functionality for both the editor and game.
Base class for battlers in the game. This provides storage for peoplemon and turn choices.
Definition: Battler.hpp:23
Loads and stores metadata surrounding items in the game.
Definition: ItemDB.hpp:24
std::unordered_map< item::Id, std::string > names
Definition: ItemDB.hpp:65
std::unordered_map< item::Id, std::string > descriptions
Definition: ItemDB.hpp:66
std::unordered_map< item::Id, std::int32_t > values
Definition: ItemDB.hpp:67
static std::string getUseLine(Id item, const pplmn::OwnedPeoplemon &ppl)
Returns the text to display when the item is used on the given peoplemon.
Definition: Item.cpp:327
static void setDataSource(file::ItemDB &source)
Set the data source for the item methods.
Definition: Item.cpp:41
static const std::string & getDescription(item::Id item)
Returns the description of the given item.
Definition: Item.cpp:105
static Category getCategory(Id item)
Returns the category of the given item.
Definition: Item.cpp:49
static pplmn::MoveId getTmMove(Id tm)
Returns the move taught by the TM.
Definition: Item.cpp:418
static void useOnPlayer(Id item, player::State &state)
Uses the given item on the player.
Definition: Item.cpp:391
static const std::string & getName(item::Id item)
Returns the name of the given item.
Definition: Item.cpp:91
static bool canUseInBattle(Id item)
Returns whether or not the item can be used in battle.
Definition: Item.cpp:133
static Type getType(Id item)
Returns the type of the given item.
Definition: Item.cpp:76
static void useOnPeoplemon(Id item, pplmn::OwnedPeoplemon &ppl, std::vector< pplmn::OwnedPeoplemon > *team=nullptr, std::vector< pplmn::BattlePeoplemon > *battleTeam=nullptr)
Applies the given item to the peoplemon.
Definition: Item.cpp:222
static float getPeopleballRate(Id ball, pplmn::Id ppl, int turnNumber, float levelRatio)
Returns the catch rate for the given peopleball.
Definition: Item.cpp:426
static const std::vector< Id > & validIds()
Returns the list of valid item ids.
Definition: Item.cpp:125
static int getValue(item::Id item)
Returns the value of the given item.
Definition: Item.cpp:119
static Id cast(unsigned int id)
Helper function to cast a raw id to an item Id.
Definition: Item.cpp:31
static bool hasEffectOnPeoplemon(Id item, const pplmn::OwnedPeoplemon &ppl)
Returns whether or not the given item will affect the peoplemon.
Definition: Item.cpp:144
static bool hasEffectOnPlayer(Id item, const player::State &state)
Returns whether or not the given item will affect the player.
Definition: Item.cpp:380
Represents a Peoplemon in battle. Stores some extra state that only exists in battle....
OwnedPeoplemon & base()
Returns the wrapped peoplemon.
static unsigned int pp(MoveId move)
Returns the max pp of the given move.
Definition: Move.cpp:101
static const std::string & name(MoveId move)
Returns the name of the given move.
Definition: Move.cpp:71
static MoveId cast(unsigned int id)
Converts the given raw id to a move id, or Unknown if invalid.
Definition: Move.cpp:37
unsigned int maxPP
The pp to restore to. Increased max pp is stored here.
Definition: OwnedMove.hpp:25
unsigned int curPP
The current pp of the move.
Definition: OwnedMove.hpp:22
MoveId id
The id of the move.
Definition: OwnedMove.hpp:19
Represents an instance of a peoplemon. Can be a wild peoplemon, trainer, or player peoplemon....
const Stats & currentEVs() const
Returns the current EVs of this peoplemon.
Ailment & currentAilment()
Access the current ailment of this peoplemon.
std::uint16_t & currentHp()
Access the current HP.
void awardEVs(const Stats &evs)
Award EVs to this peoplemon.
Stats currentStats() const
Returns the computed stats for the peoplemon. Does not take into account changes during battle.
void giveHealth(int hp)
Restores HP and ensures it does not go over max HP.
const OwnedMove * knownMoves() const
Returns the moves known by this Peoplemon.
const std::string & name() const
Returns the name of this peoplemon, custom or defualt.
static Type type(Id id)
Returns the type of the Peoplemon.
Definition: Peoplemon.cpp:136
Stats for Peoplemon. This struct is used for base stats, EVs, IVs, battle increases/decreases,...
Definition: Stats.hpp:19
static constexpr int MaxEVStat
The maximum amount that a single EV can be.
Definition: Stats.hpp:110
int & get(Stat stat)
Returns a reference to the given stat.
Definition: Stats.cpp:39
static const char * statToString(Stat stat)
Converts the given stat to its string representation.
Definition: Stats.cpp:74
static constexpr int MaxEVSum
The maximum amount that EVs or IVs can sum to.
Definition: Stats.hpp:107
int sum() const
Returns the sum of the 6 stats that are used for EV calculations.
Definition: Stats.cpp:25
Stores all player state.
Definition: State.hpp:21
unsigned int repelSteps
Definition: State.hpp:54