/* * Copyright 2007-2008 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" // Sigbattle includes #include "Arena.h" #include "Containment.h" #include "Player.h" // Sigscript includes #include "../sigscript/AbilityWrapper.h" #include "../sigscript/ItemWrapper.h" #include "../sigscript/MapTrainerTeamMemberWrapper.h" #include "../sigscript/MoveWrapper.h" #include "../sigscript/NatureWrapper.h" #include "../sigscript/RulesWrapper.h" #include "../sigscript/SigmodWrapper.h" #include "../sigscript/StatusWrapper.h" #include "../sigscript/SpeciesWrapper.h" #include "../sigscript/SpeciesAbilityWrapper.h" #include "../sigscript/SpeciesItemWrapper.h" #include "../sigscript/SpeciesMoveWrapper.h" // Sigmod includes #include "../sigmod/Hat.h" // KDE includes #include #include #include // Qt includes #include // C includes #include int Sigbattle::actionPriority(TeamMember* teamMember, const TeamMember::Action& action) { int priority = INT_MAX; switch (action.first) { case TeamMember::Attack: { QString move = action.second.first.toString(); if (teamMember->hasValueOfType(QString("move-priority-%1").arg(move))) { priority = teamMember->valueOfType(QString("move-priority-%1").arg(move)); teamMember->removeValue(QString("move-priority-%1").arg(move)); } else priority = teamMember->sigmod()->move(move)->priority(); 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::Skip: priority = INT_MAX - 2; break; case TeamMember::Timeout: priority = INT_MAX; break; case TeamMember::Invalid: priority = INT_MAX - 1; break; } return priority; } Sigbattle::TeamMember::TeamMember(const int speciesId, const QString& name, const int level, Containment* containment, const bool suppressItems) : Sigscript::Config(containment), m_containment(containment), m_id(QUuid::createUuid()) { makeConnections(); setSpecies(sigmod()->species(speciesId)); if (name.isEmpty()) setName(m_species->name()); else setName(name); setLevel(level); if (!suppressItems) initItems(); initAbilities(); initMoves(); initNatures(); initStats(); if (m_species->genderFactor() <= 1) m_gender = (m_species->genderFactor().poll() ? Male : Female); else m_gender = Genderless; for (int i = 0; i <= Sigmod::ST_SpecialDefense; ++i) m_statExp[i] = 0; if (m_containment->isMutable()) { const Sigmod::Script script = m_species->evolution(); if (!script.script().isEmpty()) { Kross::Action* evolution = new Kross::Action(Kross::Manager::self().actionCollection()->collection("evolutions"), QUuid::createUuid().toString()); evolution->setInterpreter(script.interpreter()); evolution->setCode(script.script().toUtf8()); evolution->addObject(this, "owner"); evolution->trigger(); } } m_currentHp = statValue(Sigmod::ST_HP); } Sigbattle::TeamMember::TeamMember(Sigscript::MapTrainerTeamMemberWrapper* teamMember, Containment* containment) : Sigscript::Config(containment), m_containment(containment), m_id(QUuid::createUuid()) { makeConnections(); setSpecies(teamMember->species()); setName(m_species->name()); setLevel(teamMember->level()); m_items = teamMember->items(); initAbilities(teamMember->abilities()); initMoves(teamMember->moves()); initNatures(teamMember->natures()); initStats(); if (m_species->genderFactor() <= 1) m_gender = (m_species->genderFactor().poll() ? Male : Female); else m_gender = Genderless; for (int i = 0; i <= Sigmod::ST_SpecialDefense; ++i) m_statExp[i] = 0; m_currentHp = statValue(Sigmod::ST_HP); } Sigbattle::Containment* Sigbattle::TeamMember::containment() const { return m_containment; } QUuid Sigbattle::TeamMember::id() const { return m_id; } QString Sigbattle::TeamMember::name() const { if (hasValueOfType("name")) return valueOfType("name"); return m_name; } long long Sigbattle::TeamMember::currentHp() const { return m_currentHp; } Sigscript::SpeciesWrapper* Sigbattle::TeamMember::species() const { if (hasValueOfType("species")) return valueOfType("species"); return m_species; } int Sigbattle::TeamMember::level() const { return m_level; } Sigbattle::TeamMember::Gender Sigbattle::TeamMember::gender() const { if (hasValueOfType("gender")) return valueOfType("gender"); return m_gender; } long long Sigbattle::TeamMember::levelExperience() const { if (hasValueOfType("levelExperience")) return valueOfType("levelExperience"); return m_levelExp; } int Sigbattle::TeamMember::baseStat(const Sigmod::Stat stat) const { if (hasValueOfType("overrideBaseStats") && valueOfType("overrideBaseStats")) return species()->baseStat(stat); return m_species->baseStat(stat); } long long Sigbattle::TeamMember::statExperience(const Sigmod::Stat stat) const { const QString valueName = QString("statExperience-%1").arg((sigmod()->rules()->specialSplit() ? Sigmod::StatGSCStr : Sigmod::StatRBYStr)[stat]); if (hasValueOfType(valueName)) return valueOfType(valueName); return m_statExp[stat]; } int Sigbattle::TeamMember::dv(const Sigmod::Stat stat) const { const QString valueName = QString("dv-%1").arg((sigmod()->rules()->specialSplit() ? Sigmod::StatGSCStr : Sigmod::StatRBYStr)[stat]); if (hasValueOfType(valueName)) { const int dv = valueOfType(valueName); if (dv < (sigmod()->rules()->specialDVSplit() ? 32 : 16)) return dv; } return m_dv[stat]; } long long Sigbattle::TeamMember::statValue(const Sigmod::Stat stat, const long long exp) const { long long statValue; if (exp < 0) statValue = statExperience(stat); else statValue = exp; if (!sigmod()->rules()->effortValuesAllowed() && statValue) statValue = sqrt(statValue - 1) + 1; statValue >>= 2; statValue += baseStat(stat) << 1; if (sigmod()->rules()->specialDVSplit()) { if (stat == Sigmod::ST_SpecialDefense) statValue += dv(Sigmod::ST_Special) << 1; else statValue += dv(stat) << 1; } else statValue += dv(stat); statValue *= double(m_level) / sigmod()->rules()->maxLevel(); if (stat == Sigmod::ST_HP) statValue += 10 + m_level; else { statValue += 5; Sigmod::Fraction multiplier; foreach (Sigscript::NatureWrapper* nature, m_natures) multiplier *= nature->stat(stat); statValue *= multiplier; } return statValue; } long long Sigbattle::TeamMember::calcExp(int level) const { if (level < 0) level = m_level; const long long square = level * level; const long long cube = square * level; switch (m_species->growth()) { case Sigmod::Species::Fluctuating: if (level <= 15) return cube * ((24 + (level + 1) / 3) / 50.0); else if (level <= 35) return cube * ((14 + level) / 50.0); else if (level <= 100) return cube * ((32 + (level / 2)) / 50.0); // TODO: better way for further growth? else if (level <= 102) return cube * (23 + level) / 75; else return 5 * cube / 3; case Sigmod::Species::Fading: return 6 * cube / 5 - 15 * square + 100 * level - 140; case Sigmod::Species::Slow: return 5 * cube / 4; case Sigmod::Species::Normal: return cube; case Sigmod::Species::Fast: return 4 * cube / 5; case Sigmod::Species::Erratic: { const double p[] = {0.000, 0.008, 0.014}; if (level <= 50) return cube * ((100 - level) / 50.0); else if (level <= 68) return cube * ((150 - level) / 100.0); else if (level <= 98) return cube * (1.274 - (level / 3) / 50.0 - p[level % 3]); else if (level <= 100) return cube * ((160 - level) / 100.0); // TODO: better way for further growth? else return 3 * cube / 5; } default: break; } return -1; } bool Sigbattle::TeamMember::canLearnMove(Sigscript::MoveWrapper* move) const { for (int i = 0; i < m_species->moveCount(); ++i) { if (move->id() == m_species->move(i)->move()->id()) return true; } return false; } long long Sigbattle::TeamMember::timer() const { return m_timer; } void Sigbattle::TeamMember::boostLevels(const int levels) { if ((m_level + levels) < sigmod()->rules()->maxLevel()) { for (int i = 0; i < levels; ++i) setLevel(m_level + 1); } } void Sigbattle::TeamMember::evolveInto(Sigscript::SpeciesWrapper* newSpecies) { emit(evolveStart()); int oldStats[Sigmod::ST_SpecialDefense - Sigmod::ST_HP + 1] = {}; int newStats[Sigmod::ST_SpecialDefense - Sigmod::ST_HP + 1] = {}; oldStats[Sigmod::ST_Attack] = statValue(Sigmod::ST_Attack); oldStats[Sigmod::ST_Defense] = statValue(Sigmod::ST_Defense); oldStats[Sigmod::ST_Speed] = statValue(Sigmod::ST_Speed); if (sigmod()->rules()->specialSplit()) { oldStats[Sigmod::ST_SpecialAttack] = statValue(Sigmod::ST_SpecialAttack); oldStats[Sigmod::ST_SpecialDefense] = statValue(Sigmod::ST_SpecialDefense); } else oldStats[Sigmod::ST_Special] = statValue(Sigmod::ST_Special); setSpecies(newSpecies); newStats[Sigmod::ST_Attack] = statValue(Sigmod::ST_Attack); newStats[Sigmod::ST_Defense] = statValue(Sigmod::ST_Defense); newStats[Sigmod::ST_Speed] = statValue(Sigmod::ST_Speed); if (sigmod()->rules()->specialSplit()) { newStats[Sigmod::ST_SpecialAttack] = statValue(Sigmod::ST_SpecialAttack); newStats[Sigmod::ST_SpecialDefense] = statValue(Sigmod::ST_SpecialDefense); } else newStats[Sigmod::ST_Special] = statValue(Sigmod::ST_Special); if (oldStats[Sigmod::ST_Attack] != newStats[Sigmod::ST_Attack]) emit(statChanged(Sigmod::ST_Attack, newStats[Sigmod::ST_Attack])); if (oldStats[Sigmod::ST_Defense] != newStats[Sigmod::ST_Defense]) emit(statChanged(Sigmod::ST_Defense, newStats[Sigmod::ST_Defense])); if (oldStats[Sigmod::ST_Speed] != newStats[Sigmod::ST_Speed]) emit(statChanged(Sigmod::ST_Speed, newStats[Sigmod::ST_Speed])); if (sigmod()->rules()->specialSplit()) { if (oldStats[Sigmod::ST_SpecialAttack] != newStats[Sigmod::ST_SpecialAttack]) emit(statChanged(Sigmod::ST_SpecialAttack, newStats[Sigmod::ST_SpecialAttack])); if (oldStats[Sigmod::ST_SpecialDefense] != newStats[Sigmod::ST_SpecialDefense]) emit(statChanged(Sigmod::ST_SpecialDefense, newStats[Sigmod::ST_SpecialDefense])); } else { if (oldStats[Sigmod::ST_Special] != newStats[Sigmod::ST_Special]) emit(statChanged(Sigmod::ST_Special, newStats[Sigmod::ST_Special])); } emit(evolveEnd()); } void Sigbattle::TeamMember::setName(const QString& name) { if (m_name != name) { const QString oldName = m_name; m_name = name; emit(nameChanged(oldName, m_name)); } } void Sigbattle::TeamMember::cureStatus(Sigscript::StatusWrapper* status) { if (m_status.contains(status)) { QList actions = m_status.values(status); qDeleteAll(actions); m_status.remove(status); emit(statusCured(status)); } } void Sigbattle::TeamMember::giveStatus(Sigscript::StatusWrapper* status) { if (!m_status.contains(status)) { const Sigmod::Script script = status->worldScript(); if (!script.script().isEmpty()) { Kross::Action* statusAction = new Kross::Action(Kross::Manager::self().actionCollection()->collection("status"), QUuid::createUuid().toString()); statusAction->setInterpreter(script.interpreter()); statusAction->setCode(script.script().toUtf8()); statusAction->addObject(this, "owner"); statusAction->trigger(); m_status.insert(status, statusAction); emit(statusInflicted(status)); } } } void Sigbattle::TeamMember::giveLevelExp(const int exp) { if (m_level == sigmod()->rules()->maxLevel()) return; const int expNeeded = calcExp(m_level + 1) - calcExp(); if (exp < expNeeded) { m_levelExp += exp; emit(expGained(exp)); } else { setLevel(m_level + 1); emit(expGained(expNeeded)); giveLevelExp(exp - expNeeded); } } void Sigbattle::TeamMember::giveStatExp(const Sigmod::Stat stat, const int exp) { const int oldStat = statValue(stat); int expToGive = exp; if (sigmod()->rules()->effortValuesAllowed()) { int totalEV = 0; for (int i = 0; i <= Sigmod::ST_SpecialDefense; ++i) totalEV += m_statExp[i]; while (expToGive && (totalEV < sigmod()->rules()->maxTotalEV()) && (m_statExp[stat] < sigmod()->rules()->maxEVPerStat())) { --expToGive; ++totalEV; ++m_statExp[stat]; } } else { while (expToGive && (m_statExp[stat] < INT_MAX)) { --expToGive; ++m_statExp[stat]; } } const int newStat = statValue(stat); if (oldStat != newStat) emit(statChanged(stat, newStat)); } void Sigbattle::TeamMember::takeItem(Sigscript::ItemWrapper* item) { if (m_items.contains(item)) { m_items.removeAt(m_items.indexOf(item)); emit(itemTaken(item)); } } void Sigbattle::TeamMember::giveItem(Sigscript::ItemWrapper* item) { if ((m_items.size() < sigmod()->rules()->maxHeldItems()) && checkWeight(item)) { m_items.append(item); emit(itemGiven(item)); } } void Sigbattle::TeamMember::forgetMove(Sigscript::MoveWrapper* move) { if (m_moves.contains(move)) { m_moves.removeAll(move); emit(moveForgotten(move)); } } void Sigbattle::TeamMember::teachMove(Sigscript::MoveWrapper* move) { if (canLearnMove(move)) { if (m_moves.size() < sigmod()->rules()->maxMoves()) { m_moves.append(move); emit(moveLearned(move)); } emit(movesFull(move)); } emit(unlearnableMove(move)); } Sigbattle::TeamMember::Action Sigbattle::TeamMember::requestAction() { Player* player = qobject_cast(m_containment); m_lastAction = player ? player->requestAction(this) : Action(Invalid, ActionData()); return m_lastAction; } Sigbattle::TeamMember::Action Sigbattle::TeamMember::latestAction() const { return m_lastAction; Player* player = qobject_cast(m_containment); if (player) return player->requestAction(this); return Action(Invalid, ActionData()); } void Sigbattle::TeamMember::makeActive(Arena* arena) { m_timer = 0; QList statuses = m_status.uniqueKeys(); foreach (Sigscript::StatusWrapper* status, statuses) { const Sigmod::Script script = status->battleScript(); if (!script.script().isEmpty()) { Kross::Action* action = new Kross::Action(Kross::Manager::self().actionCollection()->collection("status"), QUuid::createUuid().toString()); action->setInterpreter(script.interpreter()); action->setCode(script.script().toUtf8()); action->addObject(arena, "arena"); action->addObject(this, "owner"); action->trigger(); m_statusBattleScripts.append(action); } } QList abilities = m_abilities.keys(); foreach (Sigscript::AbilityWrapper* ability, abilities) { const Sigmod::Script script = ability->battleScript(); if (!script.script().isEmpty()) { Kross::Action* action = new Kross::Action(Kross::Manager::self().actionCollection()->collection("ability"), QUuid::createUuid().toString()); action->setInterpreter(script.interpreter()); action->setCode(script.script().toUtf8()); action->addObject(arena, "arena"); action->addObject(this, "owner"); action->trigger(); m_abilityBattleScripts.append(action); } } } void Sigbattle::TeamMember::leaveArena() { Kross::ActionCollection* collection = Kross::Manager::self().actionCollection()->collection("status"); foreach (Kross::Action* action, m_statusBattleScripts) collection->removeAction(action); collection = Kross::Manager::self().actionCollection()->collection("ability"); foreach (Kross::Action* action, m_abilityBattleScripts) collection->removeAction(action); } void Sigbattle::TeamMember::advanceTimer(const long long jump) { m_timer += jump; } void Sigbattle::TeamMember::writeBack() { // TODO: write back all (applicable) differences between config and local storage } Sigscript::SigmodWrapper* Sigbattle::TeamMember::sigmod() const { return m_containment->sigmod(); } void Sigbattle::TeamMember::setSpecies(Sigscript::SpeciesWrapper* species) { m_species = species; emit(speciesChanged(m_species)); } void Sigbattle::TeamMember::setLevel(const int level) { emit(levelAboutToGrow()); m_level = level; emit(levelGrown(level)); m_levelExp = calcExp(); } void Sigbattle::TeamMember::levelGrown() { for (int i = 0; i < m_species->moveCount(); ++i) { Sigscript::SpeciesMoveWrapper* move = m_species->move(i); if (move->level() == m_level) teachMove(move->move()); } } bool Sigbattle::TeamMember::checkWeight(const Sigscript::ItemWrapper* item) { int totalWeight = item->weight(); foreach (Sigscript::ItemWrapper* item, m_items) totalWeight += item->weight(); return (totalWeight <= species()->maxHoldWeight()); } void Sigbattle::TeamMember::makeConnections() { // TODO: make connections that are necessary (watching Config changes mainly) } void Sigbattle::TeamMember::initAbility(Sigscript::AbilityWrapper* ability) { const Sigmod::Script script = ability->battleScript(); if (!script.script().isEmpty()) { Kross::Action* action = new Kross::Action(Kross::Manager::self().actionCollection()->collection("ability"), QUuid::createUuid().toString()); action->setInterpreter(script.interpreter()); action->setCode(script.script().toUtf8()); action->addObject(this, "owner", Kross::ChildrenInterface::AutoConnectSignals); action->trigger(); m_abilities[ability] = action; } } void Sigbattle::TeamMember::initItems() { Sigmod::Hat hat = m_species->itemHat(); for (int i = 0; i < sigmod()->rules()->maxHeldItems(); ++i) { if (m_species->itemChance().poll()) { Sigscript::ItemWrapper* item = hat.pick(); if (checkWeight(item)) m_items.append(item); } } } void Sigbattle::TeamMember::initAbilities(const QList& initial) { foreach (Sigscript::AbilityWrapper* ability, initial) initAbility(ability); Sigmod::Hat hat = m_species->abilityHat(); while (m_abilities.size() < sigmod()->rules()->maxAbilities()) { Sigscript::AbilityWrapper* ability = hat.takeAndClear(); if (!m_abilities.contains(ability)) initAbility(ability); } } void Sigbattle::TeamMember::initMoves(const QList& initial) { m_moves = initial; for (int i = 0; (i < m_species->moveCount()) && (m_moves.size() < sigmod()->rules()->maxMoves()); ++i) { Sigscript::SpeciesMoveWrapper* move = m_species->move(i); if (!m_moves.contains(move->move()) && (((move->level() < m_level) && move->level()) || (!m_containment->isMutable() && (move->wild() < m_level)))) m_moves.append(move->move()); } } void Sigbattle::TeamMember::initNatures(const QList& initial) { m_natures = initial; Sigmod::Hat hat = sigmod()->natureHat(); while (m_natures.size() < sigmod()->rules()->maxNatures()) { Sigscript::NatureWrapper* nature = hat.takeAndClear(); if (!m_natures.contains(nature)) m_natures.append(nature); } } void Sigbattle::TeamMember::initStats() { if (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); } }