///////////////////////////////////////////////////////////////////////////// // Name: pokemod/Dialog.cpp // Purpose: Define a dialog in a PokéMod // Author: Ben Boeckel // Modified by: Ben Boeckel // Created: Wed Feb 28 21:05:56 2007 // Copyright: ©2007-2008 Ben Boeckel and Nerdy Productions // Licence: // 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 . ///////////////////////////////////////////////////////////////////////////// #include "Pokemod.h" #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 "Dialog.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 Pokemod* par, const int _id) : Object("Dialog", par, _id), dialog("") { } Dialog::Dialog(const Pokemod* par, const Dialog& d, const int _id) : Object("Dialog", par, _id) { *this = d; } Dialog::Dialog(const Pokemod* par, const QString& fname, const int _id) : Object("Dialog", par, _id) { load(fname, _id); } bool Dialog::validate() const { bool valid = true; pokemod->validationMsg(QString("---Dialog with id %1---").arg(id), Pokemod::V_Msg); if (dialog == "") { pokemod->validationMsg("Dialog is empty"); valid = false; } if (dialog.count('%') & 1) { pokemod->validationMsg("Command delimiter mismatch"); valid = false; } else { int curCmd = End; int numArgs = 0; for (int i = 0; i < dialog.length(); ++i) { switch (dialog.at(i).toAscii()) { case '%': switch (curCmd) { case FlipFlag ... Exit: if (numArgs != CommandNumArgs[curCmd]) { pokemod->validationMsg(QString("Invalid number of arguments for \"%1\". %2 given when %3 needed").arg(CommandStr[curCmd]).arg(numArgs).arg(CommandNumArgs[curCmd])); valid = false; } break; case Menu: if (!(numArgs & 1)) { pokemod->validationMsg("Invalid number of arguments for Menu"); valid = false; } break; case End: QString curCmdStr; for (; (dialog.at(i) != '%') && (dialog.at(i) != '#'); ++i) curCmdStr += dialog.at(i); if (((curCmd = CommandAbbrStr.indexOf(curCmdStr))) == -1) { if (curCmdStr != "") { pokemod->validationMsg(QString("Invalid command \"%1\"").arg(curCmdStr)); valid = false; } curCmd = End; } numArgs = 0; break; } break; case '#': if (curCmd != End) { QString arg; for (; (dialog.at(i) != '%') && (dialog.at(i) != '#'); ++i) arg += 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) { pokemod->validationMsg(QString("Bad flag in \"%1\"").arg(CommandStr[curCmd])); valid = false; } } break; case TestFlag: switch (numArgs) { case 1: if ((arg != "Call") && (arg != "Goto") && (arg != "0") && (arg != "1")) invError = 1; break; case 2: if (!ok) { pokemod->validationMsg("Bad flag in \"Test Flag\""); valid = false; } break; case 3: case 4: if (!ok || (pokemod->getDialogIndex(temp) == -1)) 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 || (pokemod->getDialogIndex(temp) == -1)) 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 || (pokemod->getDialogIndex(temp) == -1)) invError = numArgs; } break; case ItemShop: if (numArgs == 1) { if (!ok || (pokemod->getStoreIndex(temp) == -1)) 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 || (pokemod->getItemIndex(temp) == -1)) invError = 2; break; case 3: case 4: if (!ok || (pokemod->getDialogIndex(temp) == -1)) invError = numArgs; break; } break; case CoinList: if (numArgs == 1) { if (!ok || (pokemod->getCoinListIndex(temp) == -1)) 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 || (pokemod->getMoveIndex(temp) == -1)) invError = 2; break; case 3: case 4: case 5: if (!ok || (pokemod->getDialogIndex(temp) == -1)) 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 || (pokemod->getMoveIndex(temp) == -1)) invError = 3; break; case 4: case 5: if (!ok || (pokemod->getDialogIndex(temp) == -1)) 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 || (pokemod->getSpeciesIndex(temp) ==-1)) invError = 2; break; case 3: if ((arg != "false") && (arg != "true") && (arg != "0") && (arg != "1")) invError = 3; break; case 4: if (ok) { if (pokemod->getRules()->getMaxLevel() < temp) { pokemod->validationMsg("Higher level than allowed in \"Give Pokémon\""); valid = false; } } else invError = 4; break; case 5: if (!ok) invError = 5; break; case 6: case 7: if (!ok || (pokemod->getDialogIndex(temp) == -1)) 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 || (pokemod->getSpeciesIndex(temp) == -1)) invError = 2; break; case 3: case 4: if (!ok || (pokemod->getDialogIndex(temp) == -1)) invError = numArgs; break; } break; case ShowPokemon: if (numArgs == 1) { if (!ok || (pokemod->getSpeciesIndex(temp) == -1)) invError = 1; } break; case GiveMoney: if (numArgs == 1) { if (ok) { if (pokemod->getRules()->getMaxMoney() < temp) { pokemod->validationMsg("More money given than can be held in \"Give Money\"", Pokemod::V_Warn); valid = false; } } 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 (pokemod->getRules()->getMaxMoney() < temp) { pokemod->validationMsg("More money taken than can be held in \"Take Money\""); valid = false; } } else invError = 2; break; case 3: case 4: if (!ok || (pokemod->getDialogIndex(temp) == -1)) 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 || (pokemod->getMapIndex(temp) == -1)) invError = 2; else map = pokemod->getMapByID(temp); break; case 3: if (map) { if (!ok || (map->getEffectIndex(temp) == -1)) invError = 3; } else if ((arg != "Player") && (arg != "-1")) pokemod->validationMsg(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 || (pokemod->getDialogIndex(temp) == -1)) invError = numArgs; break; } break; case TurnEffect: switch (numArgs) { case 1: if (!ok || (pokemod->getMapIndex(temp) == -1)) invError = 1; else map = pokemod->getMapByID(temp); break; case 2: if (map) { if (!ok || (map->getEffectIndex(temp) == -1)) invError = 2; } else if ((arg != "Player") && (arg != "-1")) pokemod->validationMsg("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) ? pokemod->getRules()->getMaxParty() : ((curCmd == CheckLevels) ? pokemod->getRules()->getMaxLevel() : pokemod->getRules()->getMaxMoney())) < temp) { pokemod->validationMsg(QString("More %1 than can be carried in \"%2\"").arg((curCmd == CheckRoster) ? "party members" : ((curCmd == CheckLevels) ? "level" : "money")).arg(CommandStr[curCmd])); valid = false; } } else invError = 3; break; case 4: case 5: if (!ok || (pokemod->getDialogIndex(temp) == -1)) 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 || (pokemod->getDialogIndex(temp) == -1)) 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 || (pokemod->getSpeciesIndex(temp) == -1)) invError = numArgs; case 4: if (!ok) invError = 4; break; case 5: case 6: if (!ok || (pokemod->getDialogIndex(temp) == -1)) invError = numArgs; break; } break; case Daycare: if (numArgs == 1) { if (!ok) invError = 1; } break; case Battle: if (numArgs == 1) { if (!ok || (pokemod->getMapIndex(temp) == -1)) invError = 1; else map = pokemod->getMapByID(temp); } else if (numArgs == 2) { if (map) { if (!ok || (map->getTrainerIndex(temp) == -1)) invError = 2; } else pokemod->validationMsg("Unable to validate argument #2 in \"Battle\""); map = NULL; } break; case Badge: if (numArgs == 1) { if (!ok || (pokemod->getBadgeIndex(temp) == -1)) invError = 1; } break; case Warp: if (numArgs == 1) { if (!ok || (pokemod->getMapIndex(temp) == -1)) invError = 1; else map = pokemod->getMapByID(temp); } else if (numArgs == 2) { if (map) { if (!ok || (map->getWarpIndex(temp) == -1)) invError = 2; } else pokemod->validationMsg("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 (!QFile(QString("%1/music/%2").arg(pokemod->getPath()).arg(arg)).exists()) invError = 1; } break; case SoundEffect: if (numArgs == 1) { if (!QFile(QString("%1/sound/%2").arg(pokemod->getPath()).arg(arg)).exists()) 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 || (pokemod->getDialogIndex(temp) == -1)) invError = 3; break; } break; case WildScope: if (numArgs == 1) { if (!ok) invError = 1; } break; case Safari: switch (numArgs) { case 1: if (ok && (pokemod->getItemIndex(temp) != -1)) { const Item* item = pokemod->getItemByID(temp); bool temp = false; for (int i = 0; (i < item->getEffectCount()) || !temp; ++i) { if (item->getEffect(i)->getEffect() == ItemEffect::E_Ball) temp = true; } if (!temp) { pokemod->validationMsg("Item in argument #1 in \"Safari\" isn\'t a PokéBall"); valid = false; } } else invError = 1; break; case 2: if (!ok) invError = 2; break; case 3: if (!ok || (pokemod->getRules()->getMaxParty() < 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 (pokemod->getDialogIndex(temp) == -1) invError = numArgs; } else invError = numArgs; } else if (arg == "") invError = numArgs; break; } if (invError) { pokemod->validationMsg(QString("Invalid argument #%1 in \"%2\"").arg(invError).arg(CommandStr[curCmd])); valid = false; } } break; } } } return valid; } void Dialog::load(const QString& fname, const int _id) throw(Exception) { Ini ini(fname); if (_id == -1) ini.getValue("id", id); else id = _id; ini.getValue(className, dialog); } void Dialog::save() const throw(Exception) { Ini ini; ini.addField("id", id); ini.addField(className, dialog); ini.save(QString("%1/dialog/%2.pini").arg(pokemod->getPath()).arg(id)); } void Dialog::setDialog(const QString& d) { dialog = d; } QString Dialog::getDialog() const { return dialog; } void Dialog::insertDialogCommand(const QString& cmd, const int pos) throw(BoundsException) { if (dialog.length() < pos) throw(BoundsException(className , "command")); dialog.insert(pos, cmd); } Dialog& Dialog::operator=(const Dialog& rhs) { if (this == &rhs) return *this; dialog = rhs.dialog; return *this; }