/* * 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 "Dialog.h" // Pokemod includes #include "Item.h" #include "ItemEffect.h" #include "Map.h" #include "MapEffect.h" #include "MapTrainer.h" #include "MapWarp.h" #include "Move.h" #include "Species.h" #include "Pokemod.h" const QStringList Dialog::CommandStr = QStringList() << "Flip Flag" << "Set Flag" << "Unset Flag" << "Randomize Flag" << "Test Flag" << "Dialog" << "Yes/No" << "Item Shop" << "Give Item" << "Take Item" << "Check Item" << "Coin List" << "Teach Move" << "Delete Move" << "Give Pokemon" << "Take Pokemon" << "Show Pokemon" << "View Pokemon" << "Give Money" << "Take Money" << "Move Effect" << "Turn Effect" << "Check Direction" << "Check Roster" << "Check Levels" << "Check Species" << "Check Held Items" << "Check Money" << "Trade" << "Daycare" << "Battle" << "Badge" << "Warp" << "Name" << "Music" << "Sound Effect" << "Timer" << "Map Sign" << "Wild Scope" << "Safari" << "Heal Party" << "Refresh" << "Clear" << "Pause" << "New Line" << "Exit" << "Menu"; const QStringList Dialog::CommandAbbrStr = QStringList() << "FF" << "SF" << "UF" << "RF" << "TF" << "D" << "YN" << "ItS" << "GIt" << "TIt" << "CIt" << "CL" << "TMv" << "DMv" << "GPk" << "TPk" << "SPk" << "VPk" << "G$" << "T$" << "MvEf" << "TEf" << "CD" << "CR" << "CLv" << "CS" << "CHIt" << "C$" << "T" << "Dc" << "Bat" << "Bdg" << "W" << "N" << "Ms" << "SFX" << "Tmr" << "MS" << "WS" << "S" << "HP" << "R" << "C" << "P" << "NL" << "X" << "M"; const QList Dialog::CommandNumArgs = QList() << 1 << 1 << 1 << 1 << 3 << 2 << 2 << 1 << 4 << 4 << 4 << 1 << 5 << 3 << 7 << 4 << 1 << 4 << 1 << 4 << 6 << 3 << 6 << 5 << 5 << 4 << 4 << 5 << 6 << 1 << 2 << 1 << 2 << 1 << 2 << 1 << 3 << 1 << 1 << 3 << 0 << 0 << 0 << 0 << 0 << 0; Dialog::Dialog(const Dialog& dialog) : Object("Dialog", dialog.parent(), dialog.id()) { *this = dialog; } Dialog::Dialog(const Pokemod* parent, const int id) : Object("Dialog", parent, id), m_dialog("") { } Dialog::Dialog(const Dialog& dialog, const Pokemod* parent, const int id) : Object("Dialog", parent, id) { *this = dialog; } Dialog::Dialog(const QDomElement& xml, const Pokemod* parent, const int id) : Object("Dialog", parent, id) { load(xml, id); } void Dialog::validate() { if (m_dialog.isEmpty()) emit(error("Dialog is empty")); if (m_dialog.count('%') & 1) emit(error("Command delimiter mismatch")); else { int curCmd = End; int numArgs = 0; for (int i = 0; i < m_dialog.length(); ++i) { switch (m_dialog.at(i).toAscii()) { case '%': switch (curCmd) { case FlipFlag ... Exit: if (numArgs != CommandNumArgs[curCmd]) emit(error(QString("Invalid number of arguments for \"%1\". %2 given when %3 needed"))); break; case Menu: if (!(numArgs & 1)) emit(error("Invalid number of arguments for Menu")); break; case End: QString curCmdStr; for (; (m_dialog.at(i) != '%') && (m_dialog.at(i) != '#'); ++i) curCmdStr += m_dialog.at(i); if (((curCmd = CommandAbbrStr.indexOf(curCmdStr))) == INT_MAX) { if (!curCmdStr.isEmpty()) emit(error(QString("Invalid command \"%1\"").arg(curCmdStr))); curCmd = End; } numArgs = 0; break; } break; case '#': if (curCmd != End) { QString arg; for (; (m_dialog.at(i) != '%') && (m_dialog.at(i) != '#'); ++i) arg += m_dialog.at(i); bool ok; int temp = arg.toInt(&ok); int invError = 0; const Map* map = NULL; ++numArgs; switch (curCmd) { case FlipFlag: case SetFlag: case UnsetFlag: case RandomizeFlag: if (numArgs == 1) { if (!ok) emit(error(QString("Bad flag in \"%1\"").arg(CommandStr[curCmd]))); } break; case TestFlag: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if (!ok) emit(error("Bad flag in \"Test Flag\"")); break; case 3: case 4: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; } break; case DialogC: if (numArgs == 1) { if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; } else if (numArgs == 2) { if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = 2; } break; case YesNo: case DeleteMove: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: case 3: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; } break; case ItemShop: if (numArgs == 1) { if (!ok || (static_cast(pokemod())->storeIndex(temp) == INT_MAX)) invError = 1; } break; case GiveItem: case TakeItem: case CheckItem: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if (!ok || (static_cast(pokemod())->itemIndex(temp) == INT_MAX)) invError = 2; break; case 3: case 4: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; break; } break; case CoinList: if (numArgs == 1) { if (!ok || (static_cast(pokemod())->coinListIndex(temp) == INT_MAX)) invError = 1; } break; case TeachMove: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if (!ok || (static_cast(pokemod())->moveIndex(temp) == INT_MAX)) invError = 2; break; case 3: case 4: case 5: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; break; } break; case CheckMove: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if ((arg != "Lead") && (arg != "All") && (arg != "0") && (arg != "1")) invError = 2; break; case 3: if (!ok || (static_cast(pokemod())->moveIndex(temp) == INT_MAX)) invError = 3; break; case 4: case 5: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; break; } break; case GivePokemon: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if (!ok || (static_cast(pokemod())->speciesIndex(temp) ==INT_MAX)) invError = 2; break; case 3: if ((arg != "false") && (arg != "true") && (arg != "0") && (arg != "1")) invError = 3; break; case 4: if (ok) { if (static_cast(pokemod())->rules()->maxLevel() < temp) emit(error("Higher level than allowed in \"Give Pokémon\"")); } else invError = 4; break; case 5: if (!ok) invError = 5; break; case 6: case 7: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; break; } break; case TakePokemon: case ViewPokemon: case CheckSpecies: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if (!ok || (static_cast(pokemod())->speciesIndex(temp) == INT_MAX)) invError = 2; break; case 3: case 4: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; break; } break; case ShowPokemon: if (numArgs == 1) { if (!ok || (static_cast(pokemod())->speciesIndex(temp) == INT_MAX)) invError = 1; } break; case GiveMoney: if (numArgs == 1) { if (ok) { if (static_cast(pokemod())->rules()->maxMoney() < temp) emit(warning("More money given than can be held in \"Give Money\"")); } else invError = 1; } break; case TakeMoney: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if (ok) { if (static_cast(pokemod())->rules()->maxMoney() < temp) emit(error("More money taken than can be held in \"Take Money\"")); } else invError = 2; break; case 3: case 4: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; break; } break; case MoveEffect: case CheckDirection: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if (!ok || (static_cast(pokemod())->mapIndex(temp) == INT_MAX)) invError = 2; else map = static_cast(pokemod())->mapById(temp); break; case 3: if (map) { if (!ok || (map->effectIndex(temp) == INT_MAX)) invError = 3; } else if ((arg != "Player") && (arg != "INT_MAX")) emit(error(QString("Unable to validate argument #3 in \"%1\"").arg(CommandStr[curCmd]))); break; case 4: if ((arg != "Up") && (arg != "Down") && (arg != "Left") && (arg != "Right") && ((arg != "Random") || (curCmd == CheckDirection)) && (arg != "0") && (arg != "1") && (arg != "2") && (arg != "3") && ((arg != "4") || (curCmd == CheckDirection))) invError = 4; break; case 5: case 6: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; break; } break; case TurnEffect: switch (numArgs) { case 1: if (!ok || (static_cast(pokemod())->mapIndex(temp) == INT_MAX)) invError = 1; else map = static_cast(pokemod())->mapById(temp); break; case 2: if (map) { if (!ok || (map->effectIndex(temp) == INT_MAX)) invError = 2; } else if ((arg != "Player") && (arg != "INT_MAX")) emit(error("Unable to validate argument #2 in \"Turn Effect\"")); map = NULL; break; case 3: if ((arg != "Up") && (arg != "Down") && (arg != "Left") && (arg != "Right") && (arg != "Random") && (arg != "0") && (arg != "1") && (arg != "2") && (arg != "3") && (arg != "4")) invError = 3; break; } break; case CheckRoster: case CheckLevels: case CheckMoney: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if ((arg != "<") && (arg != ">") && (arg != "=")) invError = 2; break; case 3: if (ok) { if (((curCmd == CheckRoster) ? static_cast(pokemod())->rules()->maxParty() : ((curCmd == CheckLevels) ? static_cast(pokemod())->rules()->maxLevel() : static_cast(pokemod())->rules()->maxMoney())) < temp) emit(error(QString("More %1 than can be carried in \"%2\"").arg((curCmd == CheckRoster) ? "party members" : ((curCmd == CheckLevels) ? "level" : "money")).arg(CommandStr[curCmd]))); } else invError = 3; break; case 4: case 5: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; break; } break; case CheckHeldItems: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: case 3: case 4: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; } break; case Trade: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: case 3: if (!ok || (static_cast(pokemod())->speciesIndex(temp) == INT_MAX)) invError = numArgs; case 4: if (!ok) invError = 4; break; case 5: case 6: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = numArgs; break; } break; case Daycare: if (numArgs == 1) { if (!ok) invError = 1; } break; case Battle: if (numArgs == 1) { if (!ok || (static_cast(pokemod())->mapIndex(temp) == INT_MAX)) invError = 1; else map = static_cast(pokemod())->mapById(temp); } else if (numArgs == 2) { if (map) { if (!ok || (map->trainerIndex(temp) == INT_MAX)) invError = 2; } else emit(error("Unable to validate argument #2 in \"Battle\"")); map = NULL; } break; case Badge: if (numArgs == 1) { if (!ok || (static_cast(pokemod())->badgeIndex(temp) == INT_MAX)) invError = 1; } break; case Warp: if (numArgs == 1) { if (!ok || (static_cast(pokemod())->mapIndex(temp) == INT_MAX)) invError = 1; else map = static_cast(pokemod())->mapById(temp); } else if (numArgs == 2) { if (map) { if (!ok || (map->warpIndex(temp) == INT_MAX)) invError = 2; } else emit(error("Unable to validate argument #2 in \"Warp\"")); map = NULL; } break; case Name: case MapSign: if (numArgs == 1) { if (arg == "") invError = 1; } break; case Music: if (numArgs == 1) { if (!ok || (static_cast(pokemod())->soundIndex(temp) == INT_MAX)) invError = 1; } break; case SoundEffect: if (numArgs == 1) { if (!ok || (static_cast(pokemod())->soundIndex(temp) == INT_MAX)) invError = 1; } else if (numArgs == 2) { if (!ok) invError = 2; } break; case Timer: switch (numArgs) { case 1: if ((arg != "Time") && (arg != "Step") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if (!ok) invError = 2; break; case 3: if (!ok || (static_cast(pokemod())->dialogIndex(temp) == INT_MAX)) invError = 3; break; } break; case WildScope: if (numArgs == 1) { if (!ok) invError = 1; } break; case Safari: switch (numArgs) { case 1: if (ok && (static_cast(pokemod())->itemIndex(temp) != INT_MAX)) { const Item* item = static_cast(pokemod())->itemById(temp); bool temp = false; for (int i = 0; (i < item->effectCount()) || !temp; ++i) { if (item->effect(i)->effect() == ItemEffect::E_Ball) temp = true; } if (!temp) emit(error("Item in argument #1 in \"Safari\" isn\'t a PokéBall")); } else invError = 1; break; case 2: if (!ok) invError = 2; break; case 3: if (!ok || (static_cast(pokemod())->rules()->maxParty() < temp)) invError = 3; break; } break; case Menu: if (numArgs == 1) { if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; } else if (numArgs & 1) { if (ok) { if (static_cast(pokemod())->dialogIndex(temp) == INT_MAX) invError = numArgs; } else invError = numArgs; } else if (arg == "") invError = numArgs; break; } if (invError) emit(error(QString("Invalid argument #%1 in \"%2\"").arg(invError).arg(CommandStr[curCmd]))); } break; } } } } void Dialog::load(const QDomElement& xml, int id) { LOAD_ID(); LOAD(QString, dialog); } QDomElement Dialog::save() const { SAVE_CREATE(); SAVE(QString, dialog); return xml; } void Dialog::setDialog(const QString& dialog) { m_dialog = dialog; emit(changed()); } QString Dialog::dialog() const { return m_dialog; } void Dialog::insertCommand(const int position, const QString& command) { if (m_dialog.length() < position) { emit(error(bounds("command"))); return; } m_dialog.insert(position, command); emit(changed()); } Dialog& Dialog::operator=(const Dialog& rhs) { if (this == &rhs) return *this; COPY(dialog); return *this; }