/* * Copyright 2007-2009 Ben Boeckel * * 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 . */ // Header include #include "TeamMember.h" // Sigencore includes #include "Arena.h" #include "Containment.h" #include "Overworld.h" #include "Player.h" #include "RunScript.h" #include "Team.h" // Sigscript includes #include "../sigscript/AbilityWrapper.h" #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" #include "../sigscript/StatusWrapper.h" // Sigcore includes #include "../sigcore/Hat.h" // KDE includes #include #include #include // Qt includes #include // C includes #include QMap Sigencore::TeamMember::m_expCache; int Sigencore::actionPriority(TeamMember* teamMember, const TeamMember::Action& action) { int priority = INT_MAX; switch (action.first) { case TeamMember::Attack: { const QString move = action.second.first.toString(); const QString tag = QString("move-priority-%1").arg(move); priority = teamMember->sigmod()->move(move)->priority(); if (teamMember->valueOfType(tag, &priority)) teamMember->removeValue(tag); break; } case TeamMember::Item: priority = INT_MIN / 3; break; case TeamMember::Switch: priority = INT_MIN / 2; break; case TeamMember::Run: priority = INT_MIN; break; case TeamMember::Timeout: priority = INT_MAX; break; default: { bool ok; ObjectMap objects; objects["owner"] = teamMember; objects["sigmod"] = teamMember->sigmod(); Kross::Action* kaction = globalScript(teamMember->sigmod(), QString("battleaction-priority-%1").arg(action.first), QUuid::createUuid().toString(), objects, Kross::Manager::self().actionCollection()); priority = kaction->callFunction("priority").toInt(&ok); if (!ok) priority = 0; break; } } return priority; } Sigencore::TeamMember::TeamMember(Sigscript::SpeciesWrapper* species, const int level, Containment* containment, const bool isWild, const bool suppressInitialization) : Sigscript::Config(containment), m_sigmod(containment->sigmod()), m_containment(containment), m_arena(NULL), 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(isWild); } Sigencore::TeamMember::~TeamMember() { if (m_arena) exitArena(); // TODO: Clean up scripts } QUuid Sigencore::TeamMember::id() const { return m_id; } Sigscript::SigmodWrapper* Sigencore::TeamMember::sigmod() const { return m_sigmod; } void Sigencore::TeamMember::setContainment(Containment* containment) { m_containment->removeMember(this); m_containment = containment; } Sigencore::Containment* Sigencore::TeamMember::containment() const { return m_containment; } Sigscript::SpeciesWrapper* Sigencore::TeamMember::species() const { return m_species; } void Sigencore::TeamMember::setName(const QString& name) { m_name = name; emit(nameChanged(m_name)); } QString Sigencore::TeamMember::name() const { return m_name; } bool Sigencore::TeamMember::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::TeamMember::Gender Sigencore::TeamMember::gender() const { return m_gender; } bool Sigencore::TeamMember::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::TeamMember::giveLevels(const int levels) { return setLevel(m_level + levels); } int Sigencore::TeamMember::level() const { return m_level; } int Sigencore::TeamMember::calcLevel(const Sigmod::Species::Style growth, const long long levelExp) { int level = -1; while (calcLevelExperience(growth, level + 1) < levelExp) ++level; return level; } bool Sigencore::TeamMember::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::TeamMember::giveLevelExperience(const long long levelExp) { return setLevelExperience(m_levelExp + levelExp); } long long Sigencore::TeamMember::levelExperience() const { return m_levelExp; } long long Sigencore::TeamMember::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::TeamMember::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::TeamMember::changeCurrentHp(const int hp) { return setCurrentHp(m_currentHp + hp); } int Sigencore::TeamMember::currentHp() const { return m_currentHp; } bool Sigencore::TeamMember::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::TeamMember::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::TeamMember::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::TeamMember::giveStatExperience(const Sigmod::Stat stat, const long long statExp) { return setStatExperience(stat, m_statExp[stat] + statExp); } bool Sigencore::TeamMember::setStatStage(const Sigmod::Stat stat, const int stage) { if (stat == Sigmod::ST_HP) return false; if (m_sigmod->rules()->maxStages() < qAbs(stage)) return false; m_stages[stat - Sigmod::ST_Attack] = stage; emit(statStageChanged(stat, stage)); return true; } bool Sigencore::TeamMember::giveStatStages(const Sigmod::Stat stat, const int stages) { if (stat == Sigmod::ST_HP) return false; return setStatStage(stat, m_stages[stat - Sigmod::ST_Attack] + stages); } long long Sigencore::TeamMember::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::TeamMember::statStage(const Sigmod::Stat stat) const { int stages; if (stat == Sigmod::ST_HP) return 0; if (valueOfType(QString("stat-stages-%1").arg((m_sigmod->rules()->specialSplit() ? Sigmod::StatGSCStr : Sigmod::StatRBYStr)[stat]), &stages) && (qAbs(stages) <= m_sigmod->rules()->maxStages())) return stages; return m_stages[stat - Sigmod::ST_Attack]; } int Sigencore::TeamMember::statValue(const Sigmod::Stat stat) const { Sigcore::Fraction multiplier; if (stat != Sigmod::ST_HP) { foreach (Sigscript::NatureWrapper* nature, m_natures) multiplier *= nature->stat(stat); } const int stage = statStage(stat); if (0 < stage) multiplier *= Sigcore::Fraction(stage + 2, 2); else if (stage < 0) multiplier *= Sigcore::Fraction(2, stage + 2); return calcStat(m_sigmod, stat, m_level, m_species->baseStat(stat), dv(stat), statExperience(stat), multiplier); } int Sigencore::TeamMember::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::TeamMember::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::TeamMember::recalcStat(const Sigmod::Stat stat) { // FIXME? emit(statValueChanged(stat, statValue(stat))); } bool Sigencore::TeamMember::addAbility(Sigscript::AbilityWrapper* ability) { if ((m_abilities.size() < m_sigmod->rules()->maxAbilities()) && !m_abilities.contains(ability)) { Team* team = qobject_cast(m_containment); if (team) { ObjectMap worldObjects; worldObjects["ability"] = ability; worldObjects["owner"] = this; worldObjects["client"] = team->player(); worldObjects["sigmod"] = m_sigmod; worldObjects["world"] = team->player()->world(); Kross::Action* worldAction = runScript(QUuid::createUuid(), ability->worldScript(), worldObjects, m_abilityWorldScripts); m_abilityWorldScriptsMap[ability] = worldAction; worldAction->trigger(); if (m_arena) { ObjectMap arenaObjects; arenaObjects["ability"] = ability; arenaObjects["owner"] = this; arenaObjects["client"] = team->player(); arenaObjects["sigmod"] = m_sigmod; arenaObjects["arena"] = m_arena; Kross::Action* battleAction = runScript(QUuid::createUuid(), ability->battleScript(), arenaObjects, m_abilityBattleScripts); m_abilityWorldScriptsMap[ability] = battleAction; battleAction->trigger(); } } m_abilities.append(ability); emit(abilityAdded(ability)); return true; } return false; } bool Sigencore::TeamMember::removeAbility(Sigscript::AbilityWrapper* ability) { if (m_abilities.contains(ability)) { delete m_abilityBattleScriptsMap[ability]; m_abilityBattleScriptsMap.remove(ability); delete m_abilityWorldScriptsMap[ability]; m_abilityWorldScriptsMap.remove(ability); m_abilities.removeOne(ability); emit(abilityRemoved(ability)); return true; } return false; } QList Sigencore::TeamMember::abilities() const { return m_abilities; } bool Sigencore::TeamMember::hasAbility(Sigscript::AbilityWrapper* ability) const { return m_abilities.contains(ability); } bool Sigencore::TeamMember::addItems(Sigscript::ItemWrapper* item, const int count, const bool allOrNothing) { // TODO } QList Sigencore::TeamMember::items() const { return m_items; } int Sigencore::TeamMember::hasItem(Sigscript::ItemWrapper* item) const { return m_items.count(item); } bool Sigencore::TeamMember::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::TeamMember::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 Sigencore::TeamMember::moves() const { return m_moves; } bool Sigencore::TeamMember::hasMove(Sigscript::MoveWrapper* move) const { return m_moves.contains(move); } bool Sigencore::TeamMember::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::TeamMember::removeNature(Sigscript::NatureWrapper* nature) { if (m_natures.contains(nature)) { m_natures.removeOne(nature); emit(natureRemoved(nature)); return true; } return false; } QList Sigencore::TeamMember::natures() const { return m_natures; } bool Sigencore::TeamMember::hasNature(Sigscript::NatureWrapper* nature) const { return m_natures.contains(nature); } bool Sigencore::TeamMember::addStatus(Sigscript::StatusWrapper* status) { // TODO: Update scripts m_status.append(status); emit(statusAdded(status)); return true; } bool Sigencore::TeamMember::removeStatus(Sigscript::StatusWrapper* status) { if (m_status.contains(status)) { // TODO: Update scripts m_status.removeOne(status); emit(statusRemoved(status)); return true; } return false; } QList Sigencore::TeamMember::status() const { return m_status; } bool Sigencore::TeamMember::hasStatus(Sigscript::StatusWrapper* status) const { return m_status.contains(status); } void Sigencore::TeamMember::makeActive(Arena* arena) { // TODO Team* team = qobject_cast(this->containment()); foreach (Sigscript::StatusWrapper* status, m_status) { const Sigcore::Script script = status->battleScript(); if (!script.script().isEmpty()) { ObjectMap objects; objects["arena"] = arena; objects["sigmod"] = m_sigmod; if (team) objects["client"] = team->player(); objects["owner"] = this; Kross::Action* action = runScript(QUuid::createUuid().toString(), script, objects, Kross::Manager::self().actionCollection()->collection("status")); action->trigger(); m_statusBattleScripts.append(action); } } foreach (Sigscript::AbilityWrapper* ability, m_abilities) { const Sigcore::Script script = ability->battleScript(); if (!script.script().isEmpty()) { ObjectMap objects; objects["arena"] = arena; objects["sigmod"] = m_sigmod; if (team) objects["client"] = team->player(); objects["owner"] = this; Kross::Action* action = runScript(QUuid::createUuid().toString(), script, objects, Kross::Manager::self().actionCollection()->collection("ability")); m_abilityBattleScriptsMap[ability] = action; action->trigger(); } } m_arena = arena; } void Sigencore::TeamMember::exitArena() { // TODO qDeleteAll(m_statusBattleScripts); m_statusBattleScripts.clear(); qDeleteAll(m_abilityBattleScriptsMap.values()); m_abilityBattleScriptsMap.clear(); for (int i = Sigmod::ST_Attack; i <= Sigmod::ST_Evasion; ++i) { const QString entry = QString("stat-stages-%1").arg((m_sigmod->rules()->specialSplit() ? Sigmod::StatGSCStr : Sigmod::StatRBYStr)[i]); if (options(entry) & Temporary) removeValue(entry, false); } m_arena = NULL; } void Sigencore::TeamMember::completeData(const bool useWild) { // 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); } if (m_sigmod->rules()->specialSplit() && m_sigmod->rules()->specialDVSplit()) { for (int i = Sigmod::ST_HP; i <= Sigmod::ST_SpecialDefense; ++i) m_dv[i] = qrand() & 31; } else { for (int i = Sigmod::ST_Attack; i <= Sigmod::ST_Special; ++i) m_dv[i] = qrand() & 15; m_dv[Sigmod::ST_HP] = ((m_dv[Sigmod::ST_Attack] & 1) << 3) + ((m_dv[Sigmod::ST_Defense] & 1) << 2) + ((m_dv[Sigmod::ST_Speed] & 1) << 1) + (m_dv[Sigmod::ST_Special] & 1); } Sigcore::Hat abilityHat = m_species->abilityHat(); while ((m_abilities.size() < sigmod()->rules()->maxAbilities()) && abilityHat.count()) addAbility(abilityHat.takeAndClear()); Sigcore::Hat 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; } QMap moves; for (i = 0; i < m_species->moveCount(); ++i) { Sigscript::SpeciesMoveWrapper* move = m_species->move(i); const int level = useWild ? move->wild() : move->level(); if ((0 <= level) && (level <= m_level)) moves[level] = move->move(); } // TODO: give the moves to the creature Sigcore::Hat natureHat = sigmod()->natureHat(); while ((m_natures.size() < m_sigmod->rules()->maxNatures()) && natureHat.count()) addNature(natureHat.takeAndClear()); makeConnections(); emit(initialized()); } void Sigencore::TeamMember::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))); connect(this, SIGNAL(statStageChanged(Sigmod::Stat, int)), SLOT(recalcStat(Sigmod::Stat))); }