/* * 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 "Player.h" #include "RunScript.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/SpeciesMoveWrapper.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["arena"] = teamMember->arena(); 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(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; const Sigcore::Script script = m_species->evolution(); if (!script.script().isEmpty()) { ObjectMap objects; objects["owner"] = this; objects["sigmod"] = m_sigmod; runScript("evolution", script, objects, m_scripts)->trigger(); } m_currentHp = statValue(Sigmod::ST_HP); } Sigencore::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()); const QMap& itemMap = teamMember->items(); QList items = itemMap.keys(); foreach (Sigscript::ItemWrapper* item, items) { for (int i = 0; i < itemMap[item]; ++i) m_items.append(item); } 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); } QUuid Sigencore::TeamMember::id() const { return m_id; } Sigscript::SigmodWrapper* Sigencore::TeamMember::sigmod() const { return m_sigmod; } void Sigencore::TeamMember::setContainment(Containment* containment) { // TODO: Unlink m_containment with 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; } 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 == m_level) return false; 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; } 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) { 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; } long long Sigencore::TeamMember::levelExperience() const { return m_levelExp; } int Sigencore::TeamMember::baseStat(const Sigmod::Stat stat) const { bool override; if (valueOfType("overrideBaseStats", &override) && override) return species()->baseStat(stat); return m_species->baseStat(stat); } long long Sigencore::TeamMember::statExperience(const Sigmod::Stat stat) const { long long experience = m_statExp[stat]; valueOfType(QString("statExperience-%1").arg((sigmod()->rules()->specialSplit() ? Sigmod::StatGSCStr : Sigmod::StatRBYStr)[stat]), &experience); return experience; } int Sigencore::TeamMember::dv(const Sigmod::Stat stat) const { int dv; if (valueOfType(QString("dv-%1").arg((sigmod()->rules()->specialSplit() ? Sigmod::StatGSCStr : Sigmod::StatRBYStr)[stat]), &dv)) { if (dv < (sigmod()->rules()->specialDVSplit() ? 32 : 16)) return dv; } return m_dv[stat]; } long long Sigencore::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; Sigcore::Fraction multiplier; foreach (Sigscript::NatureWrapper* nature, m_natures) multiplier *= nature->stat(stat); statValue *= multiplier; } return statValue; } 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::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; } void Sigencore::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 Sigencore::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 Sigencore::TeamMember::giveStatus(Sigscript::StatusWrapper* status) { if (!m_status.contains(status)) { const Sigcore::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 Sigencore::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 Sigencore::TeamMember::takeItem(Sigscript::ItemWrapper* item) { if (m_items.contains(item)) { m_items.removeAt(m_items.indexOf(item)); emit(itemTaken(item)); } } void Sigencore::TeamMember::giveItem(Sigscript::ItemWrapper* item) { if ((m_items.size() < sigmod()->rules()->maxHeldItems()) && checkWeight(item)) { m_items.append(item); emit(itemGiven(item)); } } void Sigencore::TeamMember::forgetMove(Sigscript::MoveWrapper* move) { if (m_moves.contains(move)) { m_moves.removeAll(move); emit(moveForgotten(move)); } } void Sigencore::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)); } void Sigencore::TeamMember::makeActive(Arena* arena) { m_timer = 0; QList statuses = m_status.uniqueKeys(); foreach (Sigscript::StatusWrapper* status, statuses) { const Sigcore::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 Sigcore::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 Sigencore::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 Sigencore::TeamMember::writeBack() { // TODO: write back all (applicable) differences between config and local storage } bool Sigencore::TeamMember::checkWeight(const Sigscript::ItemWrapper* item) { int totalWeight = item->weight(); foreach (Sigscript::ItemWrapper* item, m_items) totalWeight += item->weight(); return (totalWeight <= species()->maxHoldWeight()); } void Sigencore::TeamMember::makeConnections() { // TODO: make connections that are necessary (watching Config changes mainly) } void Sigencore::TeamMember::initAbility(Sigscript::AbilityWrapper* ability) { const Sigcore::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 Sigencore::TeamMember::initItems() { Sigcore::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 Sigencore::TeamMember::initAbilities(const QList& initial) { foreach (Sigscript::AbilityWrapper* ability, initial) initAbility(ability); Sigcore::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 Sigencore::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 Sigencore::TeamMember::initNatures(const QList& initial) { m_natures = initial; Sigcore::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 Sigencore::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); } }