summaryrefslogtreecommitdiffstats
path: root/sigencore/Creature.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sigencore/Creature.cpp')
-rw-r--r--sigencore/Creature.cpp662
1 files changed, 662 insertions, 0 deletions
diff --git a/sigencore/Creature.cpp b/sigencore/Creature.cpp
new file mode 100644
index 00000000..941d5649
--- /dev/null
+++ b/sigencore/Creature.cpp
@@ -0,0 +1,662 @@
+/*
+ * Copyright 2007-2009 Ben Boeckel <MathStuf@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Header include
+#include "Creature.h"
+
+// Sigencore includes
+#include "Containment.h"
+
+// Sigscript includes
+#include "../sigscript/ItemWrapper.h"
+#include "../sigscript/MoveWrapper.h"
+#include "../sigscript/NatureWrapper.h"
+#include "../sigscript/RulesWrapper.h"
+#include "../sigscript/SigmodWrapper.h"
+#include "../sigscript/SpeciesWrapper.h"
+#include "../sigscript/SpeciesMoveWrapper.h"
+
+// Sigcore includes
+#include "../sigcore/Hat.h"
+
+// Qt includes
+#include <QtCore/QUuid>
+
+// C includes
+#include <cmath>
+
+QMap<Sigencore::Creature::StyleLevel, long long> Sigencore::Creature::m_expCache;
+
+Sigencore::Creature::Creature(Sigscript::SpeciesWrapper* species, const int level, Containment* containment, const bool suppressInitialization) :
+ Sigscript::Config(containment),
+ m_sigmod(containment->sigmod()),
+ m_containment(containment),
+ m_species(species),
+ m_gender(Undecided),
+ m_level(level),
+ m_levelExp(calcLevelExperience(species->growth(), level)),
+ m_id(QUuid::createUuid())
+{
+ for (int i = 0; i < (Sigmod::ST_SpecialDefense - Sigmod::ST_HP + 1); ++i)
+ {
+ m_dv[i] = -1;
+ m_statExp[i] = -1;
+ }
+ for (int i = 0; i < (Sigmod::ST_SpecialDefense - Sigmod::ST_Attack + 1); ++i)
+ m_stages[i] = 0;
+ if (!suppressInitialization)
+ completeData();
+}
+
+Sigencore::Creature::~Creature()
+{
+ // TODO
+}
+
+QUuid Sigencore::Creature::id() const
+{
+ return m_id;
+}
+
+Sigscript::SigmodWrapper* Sigencore::Creature::sigmod() const
+{
+ return m_sigmod;
+}
+
+void Sigencore::Creature::setContainment(Containment* containment)
+{
+ m_containment->removeMember(this);
+ m_containment = containment;
+}
+
+Sigencore::Containment* Sigencore::Creature::containment() const
+{
+ return m_containment;
+}
+
+Sigscript::SpeciesWrapper* Sigencore::Creature::species() const
+{
+ return m_species;
+}
+
+void Sigencore::Creature::setName(const QString& name)
+{
+ m_name = name;
+ emit(nameChanged(m_name));
+}
+
+QString Sigencore::Creature::name() const
+{
+ return m_name;
+}
+
+bool Sigencore::Creature::setGender(const Gender gender)
+{
+ if (!m_sigmod->rules()->genderAllowed())
+ return false;
+ const Sigcore::Fraction genderFactor = m_species->genderFactor();
+ switch (gender)
+ {
+ case Male:
+ if (1 <= genderFactor)
+ return false;
+ break;
+ case Female:
+ if (genderFactor <= 0)
+ return false;
+ break;
+ case Genderless:
+ if (0 <= genderFactor)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ m_gender = gender;
+ emit(genderChanged(m_gender));
+ return true;
+}
+
+Sigencore::Creature::Gender Sigencore::Creature::gender() const
+{
+ return m_gender;
+}
+
+bool Sigencore::Creature::setLevel(const int level)
+{
+ if ((level <= 0) || (m_sigmod->rules()->maxLevel() < level))
+ return false;
+ m_level = level;
+ m_levelExp = calcLevelExperience(m_species->growth(), m_level);
+ emit(levelChanged(level));
+ emit(levelExperienceChanged(m_levelExp));
+ return true;
+}
+
+bool Sigencore::Creature::giveLevels(const int levels)
+{
+ return setLevel(m_level + levels);
+}
+
+int Sigencore::Creature::level() const
+{
+ return m_level;
+}
+
+int Sigencore::Creature::calcLevel(const Sigmod::Species::Style growth, const long long levelExp)
+{
+ int level = -1;
+ while (calcLevelExperience(growth, level + 1) < levelExp)
+ ++level;
+ return level;
+}
+
+bool Sigencore::Creature::setLevelExperience(const long long levelExp)
+{
+ if (levelExp < 0)
+ return false;
+ const int level = calcLevel(m_species->growth(), levelExp);
+ if ((level <= 0) || (m_sigmod->rules()->maxLevel() < level))
+ return false;
+ m_levelExp = levelExp;
+ if (level != m_level)
+ {
+ m_level = level;
+ emit(levelChanged(level));
+ }
+ emit(levelExperienceChanged(m_levelExp));
+ return true;
+}
+
+bool Sigencore::Creature::giveLevelExperience(const long long levelExp)
+{
+ return setLevelExperience(m_levelExp + levelExp);
+}
+
+long long Sigencore::Creature::levelExperience() const
+{
+ return m_levelExp;
+}
+
+long long Sigencore::Creature::calcLevelExperience(const Sigmod::Species::Style growth, const int level)
+{
+ if (m_expCache.contains(StyleLevel(growth, level)))
+ return m_expCache[StyleLevel(growth, level)];
+ const long long square = level * level;
+ const long long cube = square * level;
+ long long exp = -1;
+ switch (growth)
+ {
+ case Sigmod::Species::Fluctuating:
+ if (level <= 15)
+ exp = cube * ((24 + (level + 1) / 3) / 50.);
+ else if (level <= 35)
+ exp = cube * ((14 + level) / 50.);
+ else if (level <= 100)
+ exp = cube * ((32 + (level / 2)) / 50.);
+ // TODO: better way for further growth?
+ else if (level <= 102)
+ exp = cube * (23 + level) / 75;
+ else
+ exp = 5 * cube / 3;
+ break;
+ case Sigmod::Species::Fading:
+ exp = 6 * cube / 5 - 15 * square + 100 * level - 140;
+ break;
+ case Sigmod::Species::Slow:
+ exp = 5 * cube / 4;
+ break;
+ case Sigmod::Species::Normal:
+ exp = cube;
+ break;
+ case Sigmod::Species::Fast:
+ exp = 4 * cube / 5;
+ break;
+ case Sigmod::Species::Erratic:
+ {
+ const double p[] = {0.000, 0.008, 0.014};
+ if (level <= 50)
+ exp = cube * ((100 - level) / 50.);
+ else if (level <= 68)
+ exp = cube * ((150 - level) / 100.);
+ else if (level <= 98)
+ exp = cube * (1.274 - (level / 3) / 50. - p[level % 3]);
+ else if (level <= 100)
+ exp = cube * ((160 - level) / 100.);
+ // TODO: better way for further growth?
+ else
+ exp = 3 * cube / 5;
+ break;
+ }
+ default:
+ break;
+ }
+ if (0 <= exp)
+ m_expCache[StyleLevel(growth, level)] = exp;
+ return exp;
+}
+
+bool Sigencore::Creature::setCurrentHp(const int hp)
+{
+ if ((hp < 0) || (statValue(Sigmod::ST_HP) < hp))
+ return false;
+ m_currentHp = hp;
+ emit(currentHpChanged(m_currentHp));
+ if (!m_currentHp)
+ emit(knockedOut());
+ return true;
+}
+
+bool Sigencore::Creature::changeCurrentHp(const int hp)
+{
+ return setCurrentHp(m_currentHp + hp);
+}
+
+int Sigencore::Creature::currentHp() const
+{
+ return m_currentHp;
+}
+
+bool Sigencore::Creature::setDv(const Sigmod::Stat stat, const int dv)
+{
+ if ((stat == Sigmod::ST_SpecialDefense) && !(m_sigmod->rules()->specialSplit() && m_sigmod->rules()->specialDVSplit()))
+ return false;
+ m_dv[stat] = dv;
+ emit(dvChanged(stat, dv));
+ return true;
+}
+
+int Sigencore::Creature::dv(const Sigmod::Stat stat) const
+{
+ if ((stat == Sigmod::ST_Accuracy) || (stat == Sigmod::ST_Evasion))
+ return false;
+ Sigmod::Stat actualStat = stat;
+ if (stat == Sigmod::ST_SpecialDefense)
+ {
+ if (m_sigmod->rules()->specialSplit() && m_sigmod->rules()->specialDVSplit())
+ actualStat = Sigmod::ST_Special;
+ else
+ return false;
+ }
+ int dv;
+ if (valueOfType(QString("stat-dv-%1").arg((m_sigmod->rules()->specialSplit() ? Sigmod::StatGSCStr : Sigmod::StatRBYStr)[actualStat]), &dv) && (dv < (m_sigmod->rules()->specialDVSplit() ? 32 : 16)))
+ return dv;
+ return m_dv[actualStat];
+}
+
+bool Sigencore::Creature::setStatExperience(const Sigmod::Stat stat, const long long statExp)
+{
+ if ((stat == Sigmod::ST_Accuracy) || (stat == Sigmod::ST_Evasion) || ((stat == Sigmod::ST_SpecialDefense) && !m_sigmod->rules()->specialSplit()))
+ return false;
+ if (m_sigmod->rules()->effortValuesAllowed())
+ {
+ int evSum = statExp;
+ for (int i = Sigmod::ST_HP; i <= (m_sigmod->rules()->specialSplit() ? Sigmod::ST_Special : Sigmod::ST_SpecialDefense); ++i)
+ {
+ if (i != stat)
+ evSum += m_statExp[i];
+ }
+ if ((m_sigmod->rules()->maxEVPerStat() < statExp) || (m_sigmod->rules()->maxTotalEV() < evSum))
+ return false;
+ }
+ m_statExp[stat] = statExp;
+ emit(statExperienceChanged(stat, m_statExp[stat]));
+ return true;
+}
+
+bool Sigencore::Creature::giveStatExperience(const Sigmod::Stat stat, const long long statExp)
+{
+ return setStatExperience(stat, m_statExp[stat] + statExp);
+}
+
+long long Sigencore::Creature::statExperience(const Sigmod::Stat stat) const
+{
+ long long exp = m_statExp[stat];
+ valueOfType(QString("stat-experience-%1").arg((m_sigmod->rules()->specialSplit() ? Sigmod::StatGSCStr : Sigmod::StatRBYStr)[stat]), &exp);
+ return exp;
+}
+
+int Sigencore::Creature::statValue(const Sigmod::Stat stat) const
+{
+ Sigcore::Fraction multiplier;
+ if (stat != Sigmod::ST_HP)
+ {
+ foreach (Sigscript::NatureWrapper* nature, m_natures)
+ multiplier *= nature->stat(stat);
+ }
+ return calcStat(m_sigmod, stat, m_level, m_species->baseStat(stat), dv(stat), statExperience(stat), multiplier);
+}
+
+int Sigencore::Creature::calcStat(Sigscript::SigmodWrapper* sigmod, const Sigmod::Stat stat, const int level, const int baseStat, const int dv, const int statExp, const Sigcore::Fraction& multiplier)
+{
+ int statValue = statExp;
+ if (!sigmod->rules()->effortValuesAllowed() && statValue)
+ statValue = sqrt(statValue - 1) + 1;
+ statValue >>= 2;
+ statValue += baseStat << 1;
+ if (sigmod->rules()->specialDVSplit())
+ statValue += dv << 1;
+ else
+ statValue += dv;
+ statValue *= double(level) / sigmod->rules()->maxLevel();
+ if (stat == Sigmod::ST_HP)
+ statValue += 10 + level;
+ else
+ statValue += 5;
+ return statValue * multiplier;
+}
+
+void Sigencore::Creature::recalcStats()
+{
+ // FIXME?
+ recalcStat(Sigmod::ST_HP);
+ recalcStat(Sigmod::ST_Attack);
+ recalcStat(Sigmod::ST_Defense);
+ recalcStat(Sigmod::ST_Speed);
+ if (m_sigmod->rules()->specialSplit())
+ {
+ recalcStat(Sigmod::ST_SpecialAttack);
+ recalcStat(Sigmod::ST_SpecialDefense);
+ }
+ else
+ recalcStat(Sigmod::ST_Special);
+}
+
+void Sigencore::Creature::recalcStat(const Sigmod::Stat stat)
+{
+ // FIXME?
+ emit(statValueChanged(stat, statValue(stat)));
+}
+
+bool Sigencore::Creature::addAbility(Sigscript::AbilityWrapper* ability)
+{
+ if ((m_abilities.size() < m_sigmod->rules()->maxAbilities()) && !m_abilities.contains(ability))
+ {
+ m_abilities.append(ability);
+ emit(abilityAdded(ability));
+ return true;
+ }
+ return false;
+}
+
+bool Sigencore::Creature::removeAbility(Sigscript::AbilityWrapper* ability)
+{
+ if (m_abilities.contains(ability))
+ {
+ m_abilities.removeOne(ability);
+ emit(abilityRemoved(ability));
+ return true;
+ }
+ return false;
+}
+
+QList<Sigscript::AbilityWrapper*> Sigencore::Creature::abilities() const
+{
+ return m_abilities;
+}
+
+bool Sigencore::Creature::hasAbility(Sigscript::AbilityWrapper* ability) const
+{
+ return m_abilities.contains(ability);
+}
+
+bool Sigencore::Creature::addItems(Sigscript::ItemWrapper* item, const int count, const bool allOrNothing)
+{
+ const int addWeight = count * item->weight();
+ int end = 0;
+ if (count < 0)
+ {
+ const int diffWeight = itemWeight() + addWeight;
+ const int diffCount = qMax(0, itemCount() + count);
+ if ((0 < diffWeight) && (0 < diffCount))
+ end = count;
+ else if (!allOrNothing)
+ {
+ const int weightUnderflow = qAbs(diffWeight);
+ const int countUnderflow = diffCount;
+ end = count + qMax((weightUnderflow / item->weight()) - !!(weightUnderflow % item->weight()), countUnderflow);
+ }
+ }
+ else if (0 < count)
+ {
+ const int diffWeight = m_species->weight() - (itemWeight() + addWeight);
+ const int diffCount = m_sigmod->rules()->maxHeldItems() - (itemCount() + count);
+ if ((0 < diffWeight) && (0 < diffCount))
+ end = count;
+ else if (!allOrNothing)
+ {
+ const int weightOverflow = qAbs(diffWeight);
+ const int countOverflow = diffCount;
+ end = count - qMax((weightOverflow / item->weight()) - !!(countOverflow % item->weight()), countOverflow);
+ }
+ }
+ end = qBound(qMin(0, count), end, qMax(count, 0));
+ if (end)
+ {
+ m_items[item] += end;
+ if (!m_items[item])
+ m_items.remove(item);
+ emit(itemsAdded(item, end));
+ }
+ return count - end;
+}
+
+QList<Sigscript::ItemWrapper*> Sigencore::Creature::items() const
+{
+ return m_items.keys();
+}
+
+int Sigencore::Creature::itemWeight() const
+{
+ int weight = 0;
+ QList<Sigscript::ItemWrapper*> items = m_items.keys();
+ foreach (Sigscript::ItemWrapper* curItem, items)
+ weight += curItem->weight() * m_items[curItem];
+ return weight;
+}
+
+int Sigencore::Creature::itemCount() const
+{
+ int count = 0;
+ QList<Sigscript::ItemWrapper*> items = m_items.keys();
+ foreach (Sigscript::ItemWrapper* curItem, items)
+ count += m_items[curItem];
+ return count;
+}
+
+int Sigencore::Creature::hasItem(Sigscript::ItemWrapper* item) const
+{
+ return m_items.count(item);
+}
+
+bool Sigencore::Creature::addMove(Sigscript::MoveWrapper* move)
+{
+ bool canLearn = false;
+ for (int i = 0; !canLearn && (i < m_species->moveCount()); ++i)
+ {
+ if (m_species->move(i)->move() == move)
+ canLearn = true;
+ }
+ if (canLearn && (m_moves.size() < m_sigmod->rules()->maxMoves()) && !m_moves.contains(move))
+ {
+ m_moves.append(move);
+ emit(moveAdded(move));
+ return true;
+ }
+ return false;
+}
+
+bool Sigencore::Creature::removeMove(Sigscript::MoveWrapper* move)
+{
+ if (m_moves.contains(move) && (1 < m_moves.size()))
+ {
+ m_moves.removeOne(move);
+ emit(moveRemoved(move));
+ return true;
+ }
+ return false;
+}
+
+QList<Sigscript::MoveWrapper*> Sigencore::Creature::moves() const
+{
+ return m_moves;
+}
+
+bool Sigencore::Creature::hasMove(Sigscript::MoveWrapper* move) const
+{
+ return m_moves.contains(move);
+}
+
+bool Sigencore::Creature::addNature(Sigscript::NatureWrapper* nature)
+{
+ if ((m_natures.size() < m_sigmod->rules()->maxNatures()) && !m_natures.contains(nature))
+ {
+ m_natures.append(nature);
+ emit(natureAdded(nature));
+ return true;
+ }
+ return false;
+}
+
+bool Sigencore::Creature::removeNature(Sigscript::NatureWrapper* nature)
+{
+ if (m_natures.contains(nature))
+ {
+ m_natures.removeOne(nature);
+ emit(natureRemoved(nature));
+ return true;
+ }
+ return false;
+}
+
+QList<Sigscript::NatureWrapper*> Sigencore::Creature::natures() const
+{
+ return m_natures;
+}
+
+bool Sigencore::Creature::hasNature(Sigscript::NatureWrapper* nature) const
+{
+ return m_natures.contains(nature);
+}
+
+bool Sigencore::Creature::addStatus(Sigscript::StatusWrapper* status)
+{
+ if (!m_status.contains(status))
+ {
+ m_status.append(status);
+ emit(statusAdded(status));
+ return true;
+ }
+ return false;
+}
+
+bool Sigencore::Creature::removeStatus(Sigscript::StatusWrapper* status)
+{
+ if (m_status.contains(status))
+ {
+ m_status.removeOne(status);
+ emit(statusRemoved(status));
+ return true;
+ }
+ return false;
+}
+
+QList<Sigscript::StatusWrapper*> Sigencore::Creature::status() const
+{
+ return m_status;
+}
+
+bool Sigencore::Creature::hasStatus(Sigscript::StatusWrapper* status) const
+{
+ return m_status.contains(status);
+}
+
+void Sigencore::Creature::completeData()
+{
+ // TODO
+ if (!m_name.isEmpty())
+ m_name = m_species->name();
+ if (m_gender == Undecided)
+ {
+ if (!m_sigmod->rules()->genderAllowed() || (m_species->genderFactor() < 0))
+ m_gender = Genderless;
+ else
+ m_gender = (m_species->genderFactor().poll() ? Female : Male);
+ }
+ completeStats();
+ completeAbilities();
+ completeItems();
+ completeMoves();
+ completeNatures();
+ makeConnections();
+ emit(initialized());
+}
+
+void Sigencore::Creature::completeAbilities()
+{
+ Sigcore::Hat<Sigscript::AbilityWrapper*> abilityHat = m_species->abilityHat();
+ while ((m_abilities.size() < sigmod()->rules()->maxAbilities()) && abilityHat.count())
+ addAbility(abilityHat.takeAndClear());
+}
+
+void Sigencore::Creature::completeItems()
+{
+ Sigcore::Hat<Sigscript::ItemWrapper*> itemHat = m_species->itemHat();
+ int i = 0;
+ while ((i < m_sigmod->rules()->maxHeldItems()) && itemHat.count())
+ {
+ if (m_species->itemChance().poll())
+ {
+ Sigscript::ItemWrapper* item = itemHat.pick();
+ if (addItems(item, 1))
+ ++i;
+ else
+ itemHat.setCount(item, 0);
+ }
+ else
+ ++i;
+ }
+}
+
+void Sigencore::Creature::completeMoves()
+{
+ QMap<int, Sigscript::MoveWrapper*> moves;
+ for (int i = 0; i < m_species->moveCount(); ++i)
+ {
+ Sigscript::SpeciesMoveWrapper* move = m_species->move(i);
+ const int level = move->level();
+ if ((0 <= level) && (level <= m_level))
+ moves[level] = move->move();
+ }
+ // TODO: give the moves to the creature
+}
+
+void Sigencore::Creature::completeNatures()
+{
+ Sigcore::Hat<Sigscript::NatureWrapper*> natureHat = sigmod()->natureHat();
+ while ((m_natures.size() < m_sigmod->rules()->maxNatures()) && natureHat.count())
+ addNature(natureHat.takeAndClear());
+}
+
+void Sigencore::Creature::makeConnections()
+{
+ connect(this, SIGNAL(levelChanged(int)), SLOT(recalcStats()));
+ connect(this, SIGNAL(natureAdded(Sigscript::NatureWrapper*)), SLOT(recalcStats()));
+ connect(this, SIGNAL(natureRemoved(Sigscript::NatureWrapper*)), SLOT(recalcStats()));
+ connect(this, SIGNAL(dvChanged(Sigmod::Stat, int)), SLOT(recalcStat(Sigmod::Stat)));
+ connect(this, SIGNAL(statExperienceChanged(Sigmod::Stat, long long)), SLOT(recalcStat(Sigmod::Stat)));
+}