diff options
Diffstat (limited to 'sigencore/Creature.cpp')
| -rw-r--r-- | sigencore/Creature.cpp | 662 |
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))); +} |
