/////////////////////////////////////////////////////////////////////////////
// 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;
}