/* * 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 "Game.h" // Sigmod includes #include "Ability.h" #include "Author.h" #include "Badge.h" #include "CoinList.h" #include "EggGroup.h" #include "GlobalScript.h" #include "Item.h" #include "ItemType.h" #include "Macros.h" #include "Map.h" #include "Move.h" #include "Nature.h" #include "Rules.h" #include "Skin.h" #include "Sound.h" #include "Species.h" #include "Sprite.h" #include "Status.h" #include "Store.h" #include "Tile.h" #include "Time.h" #include "Trainer.h" #include "Type.h" #include "Weather.h" // Qt includes #include using namespace Sigcore; using namespace Sigmod; Game::Game() : Object(NULL, 0), m_title(""), m_version(""), m_description(""), m_singlePlayer(true), m_startScript("", ""), m_typechart(0, 0), m_rules(new Rules(this)) { } Game::Game(const Game& game) : Object(NULL, 0), m_rules(new Rules(this)) { *this = game; } Game::Game(const QDomElement& xml) : Object(NULL, 0), m_rules(new Rules(this)) { load(xml); } Game::~Game() { delete m_rules; clear(); } void Game::validate() { TEST_BEGIN(); if (m_title.isEmpty()) emit(error("Title is empty")); if (m_version.isEmpty()) emit(error("Version is empty")); if (m_description.isEmpty()) emit(warning("Description is empty")); // TODO: check map positioning if ((m_typechart.width() != typeCount()) || (m_typechart.height() != typeCount())) emit(error("Type chart is invalid")); TEST_CHILD(m_rules); QSet idChecker; QSet nameChecker; QSet timeChecker; if (abilityCount() < m_rules->maxAbilities()) emit(error("There are too few abilities")); TEST_SUB_BEGIN(Ability, abilities); TEST_SUB("ability", id); TEST_SUB("ability", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!authorCount()) emit(error("There are no authors")); TEST_SUB_BEGIN(Author, authors); TEST_SUB("author", id); TEST_SUB("author", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!badgeCount()) emit(error("There are no badges")); TEST_SUB_BEGIN(Badge, badges); TEST_SUB("badge", id); TEST_SUB("badge", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!coinListCount()) emit(warning("There are no coin lists")); TEST_SUB_BEGIN(CoinList, coinLists); TEST_SUB("coin list", id); TEST_SUB("coin list", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (m_rules->breedingAllowed()) { if (!eggGroupCount()) emit(error("There are no egg groups")); TEST_SUB_BEGIN(EggGroup, eggGroups); TEST_SUB("egg group", id); TEST_SUB("egg group", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); } if (!globalScriptCount()) emit(warning("There are no global scripts")); TEST_SUB_BEGIN(GlobalScript, globalScripts); TEST_SUB("global script", id); TEST_SUB("global script", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!itemCount()) emit(warning("There are no items")); TEST_SUB_BEGIN(Item, items); TEST_SUB("item", id); TEST_SUB("item", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!itemTypeCount() && itemCount()) emit(error("There are no item types")); TEST_SUB_BEGIN(ItemType, itemTypes); TEST_SUB("item type", id); TEST_SUB("item type", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!mapCount()) emit(error("There are no maps")); TEST_SUB_BEGIN(Map, maps); TEST_SUB("map", id); TEST_SUB("map", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!moveCount()) emit(error("There are no moves")); TEST_SUB_BEGIN(Move, moves); TEST_SUB("move", id); TEST_SUB("move", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (natureCount() < m_rules->maxNatures()) emit(error("There are too few natures")); TEST_SUB_BEGIN(Nature, natures); TEST_SUB("nature", id); TEST_SUB("nature", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); TEST_SUB_BEGIN(Skin, skins); TEST_SUB("skin", id); TEST_SUB("skin", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!soundCount()) emit(warning("There are no sounds")); TEST_SUB_BEGIN(Sound, sounds); TEST_SUB("sound", id); TEST_SUB("sound", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!speciesCount()) emit(error("There are no species")); TEST_SUB_BEGIN(Species, species); TEST_SUB("species", id); TEST_SUB("species", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!statusCount()) emit(error("There are no status effects")); TEST_SUB_BEGIN(Status, status); TEST_SUB("status", id); TEST_SUB("status", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!storeCount()) emit(warning("There are no stores")); TEST_SUB_BEGIN(Store, stores); TEST_SUB("store", id); TEST_SUB("store", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!tileCount()) emit(error("There are no tiles")); TEST_SUB_BEGIN(Tile, tiles); TEST_SUB("tile", id); TEST_SUB("tile", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!trainerCount()) emit(warning("There are no trainers")); TEST_SUB_BEGIN(Trainer, trainers); TEST_SUB("trainer", id); TEST_SUB("trainer", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!timeCount()) emit(error("There are no times")); TEST_SUB_BEGIN(Time, times); TEST_SUB("time", id); TEST_SUB("time", name); if (timeChecker.contains((60 * object->hour()) + object->minute())) emit(error(subclass("time", QString("%1:%2").arg(object->hour(), object->minute())))); timeChecker.insert((60 * object->hour()) + object->minute()); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!typeCount()) emit(error("There are no types")); TEST_SUB_BEGIN(Type, types); TEST_SUB("type", id); TEST_SUB("type", name); TEST_SUB_END(); idChecker.clear(); nameChecker.clear(); if (!weatherCount()) emit(warning("There are no weathers")); TEST_SUB_BEGIN(Weather, weathers); TEST_SUB("weather", id); TEST_SUB("weather", name); TEST_SUB_END(); TEST_END(); } void Game::load(const QDomElement& xml) { LOAD_BEGIN(); LOAD(title); LOAD(version); LOAD(description); LOAD(singlePlayer); LOAD(startScript); m_rules->load(xml.firstChildElement("Rules")); LOAD_SUB(newAbility, Ability); LOAD_SUB(newAuthor, Author); LOAD_SUB(newBadge, Badge); LOAD_SUB(newCoinList, CoinList); LOAD_SUB(newEggGroup, EggGroup); LOAD_SUB(newGlobalScript, GlobalScript); LOAD_SUB(newItem, Item); LOAD_SUB(newItemType, ItemType); LOAD_SUB(newMap, Map); LOAD_SUB(newMove, Move); LOAD_SUB(newNature, Nature); LOAD_SUB(newSkin, Skin); LOAD_SUB(newSound, Sound); LOAD_SUB(newSpecies, Species); LOAD_SUB(newSprite, Sprite); LOAD_SUB(newStatus, Status); LOAD_SUB(newStore, Store); LOAD_SUB(newTile, Tile); LOAD_SUB(newTime, Time); LOAD_SUB(newTrainer, Trainer); LOAD_SUB(newType, Type); LOAD_SUB(newWeather, Weather); LOAD_MAP(mapPosition, position); LOAD_MATRIX(typechart); m_typechart.resize(typeCount(), typeCount()); } QDomElement Game::save() const { QDomDocument doc; QDomElement xml = doc.createElement(className()); SAVE(title); SAVE(version); SAVE(description); SAVE(singlePlayer); SAVE(startScript); SAVE_Rules(rules); SAVE_MAP(mapPosition, position); SAVE_MATRIX(typechart); SAVE_SUB(Ability, abilities); SAVE_SUB(Author, authors); SAVE_SUB(Badge, badges); SAVE_SUB(CoinList, coinLists); SAVE_SUB(EggGroup, eggGroups); SAVE_SUB(GlobalScript, globalScripts); SAVE_SUB(Item, items); SAVE_SUB(ItemType, itemTypes); SAVE_SUB(Map, maps); SAVE_SUB(Move, moves); SAVE_SUB(Nature, natures); SAVE_SUB(Skin, skins); SAVE_SUB(Sound, sounds); SAVE_SUB(Species, species); SAVE_SUB(Sprite, sprites); SAVE_SUB(Status, status); SAVE_SUB(Store, stores); SAVE_SUB(Tile, tiles); SAVE_SUB(Time, times); SAVE_SUB(Trainer, trainers); SAVE_SUB(Type, types); SAVE_SUB(Weather, weathers); return xml; } SETTER(Game, QString&, Title, title) SETTER(Game, QString&, Version, version) SETTER(Game, QString&, Description, description) SETTER(Game, bool, SinglePlayer, singlePlayer) SETTER(Game, Script&, StartScript, startScript) void Game::setMapPosition(const int map, const QPoint& position, const bool remove) { if (remove) { if (m_mapPosition.contains(map)) { m_mapPosition.remove(map); emit(changed()); } } else if (!mapById(map)) EBOUNDS_IDX(map); else if (!m_mapPosition.contains(map) || (position != m_mapPosition[map])) { m_mapPosition[map] = position; emit(changed()); } } SETTER_MATRIX(Game, Fraction&, Typechart, typechart, multiplier) void Game::setRules(const Rules& rules) { *m_rules = rules; } void Game::setRules(const QDomElement& xml) { m_rules->load(xml); } GETTER(Game, QString, title) GETTER(Game, QString, version) GETTER(Game, QString, description) GETTER(Game, bool, singlePlayer) GETTER(Game, Script, startScript) const Matrix* Game::typechart() const { return &m_typechart; } Matrix* Game::typechart() { return &m_typechart; } Fraction Game::typechart(const int attack, const int defense) const { return m_typechart(attack, defense); } QPoint Game::mapPosition(const int map) const { if (m_mapPosition.contains(map)) return m_mapPosition[map]; return QPoint(); } QMap Game::mapPosition() const { return m_mapPosition; } const Rules* Game::rules() const { return m_rules; } Rules* Game::rules() { return m_rules; } CHECK(Game, QString&, title) CHECK(Game, QString&, version) CHECK(Game, QString&, description) CHECK(Game, bool, singlePlayer) CHECK(Game, Script&, startScript) CHECK_BOUNDS(Game, Fraction&, typechart, 0, INT_MAX) CHECK(Game, QPoint&, mapPosition) SSUBCLASS(Game, Ability, ability, abilities) SSUBCLASS(Game, Author, author, authors) SSUBCLASS(Game, Badge, badge, badges) SSUBCLASS(Game, CoinList, coinList, coinLists) SSUBCLASS(Game, EggGroup, eggGroup, eggGroups) SSUBCLASS(Game, GlobalScript, globalScript, globalScripts) SSUBCLASS(Game, Item, item, items) SSUBCLASS(Game, ItemType, itemType, itemTypes) SSUBCLASS(Game, Map, map, maps) SSUBCLASS(Game, Move, move, moves) SSUBCLASS(Game, Nature, nature, natures) SSUBCLASS(Game, Skin, skin, skins) SSUBCLASS(Game, Sound, sound, sounds) SSUBCLASS(Game, Species, species, species) SSUBCLASS(Game, Sprite, sprite, sprites) SSUBCLASS(Game, Status, status, status) SSUBCLASS(Game, Store, store, stores) SSUBCLASS(Game, Tile, tile, tiles) SSUBCLASS(Game, Time, time, times) SSUBCLASS(Game, Trainer, trainer, trainers) const Type* Game::type(const int index) const { if (index < typeCount()) return m_types.at(index); return NULL; } Type* Game::type(const int index) { if (index < typeCount()) return m_types[index]; return NULL; } const Type* Game::typeById(const int id) const { return type(typeIndex(id)); } Type* Game::typeById(const int id) { return type(typeIndex(id)); } int Game::typeIndex(const int id) const { for (int i = 0; i < typeCount(); ++i) { if (m_types[i]->id() == id) return i; } return INT_MAX; } int Game::typeCount() const { return m_types.size(); } Type* Game::newType() { return newType(new Type(this, newTypeId())); } Type* Game::newType(const QDomElement& xml) { return newType(new Type(xml, this, newTypeId())); } Type* Game::newType(const Type& type) { return newType(new Type(type, this, newTypeId())); } Type* Game::newType(Type* type) { m_types.append(type); m_typechart.resize(typeCount(), typeCount(), Sigcore::Fraction(1, 1)); return type; } void Game::deleteType(const int index) { if (index < typeCount()) { delete m_types[index]; m_types.removeAt(index); m_typechart.resize(typeCount(), typeCount(), Sigcore::Fraction(1, 1)); } } void Game::deleteTypeById(const int id) { deleteType(typeIndex(id)); } int Game::newTypeId() const { int i = 0; while ((i < typeCount()) && (typeIndex(i) != INT_MAX)) ++i; return i; } SSUBCLASS(Game, Weather, weather, weathers) Game& Game::operator=(const Game& rhs) { if (this == &rhs) return *this; clear(); COPY(title); COPY(version); COPY(description); COPY(singlePlayer); COPY(startScript); COPY(mapPosition); COPY(typechart); COPY_Rules(rules); COPY_SUB(Ability, abilities); COPY_SUB(Author, authors); COPY_SUB(Badge, badges); COPY_SUB(CoinList, coinLists); COPY_SUB(EggGroup, eggGroups); COPY_SUB(GlobalScript, globalScripts); COPY_SUB(Item, items); COPY_SUB(ItemType, itemTypes); COPY_SUB(Map, maps); COPY_SUB(Move, moves); COPY_SUB(Nature, natures); COPY_SUB(Skin, skins); COPY_SUB(Sound, sounds); COPY_SUB(Species, species); COPY_SUB(Sprite, sprites); COPY_SUB(Status, status); COPY_SUB(Store, stores); COPY_SUB(Tile, tiles); COPY_SUB(Time, times); COPY_SUB(Trainer, trainers); COPY_SUB(Type, types); COPY_SUB(Weather, weathers); m_typechart.resize(typeCount(), typeCount()); return *this; } void Game::clear() { SUBCLASS_CLEAR(abilities); SUBCLASS_CLEAR(authors); SUBCLASS_CLEAR(badges); SUBCLASS_CLEAR(coinLists); SUBCLASS_CLEAR(eggGroups); SUBCLASS_CLEAR(globalScripts); SUBCLASS_CLEAR(items); SUBCLASS_CLEAR(itemTypes); SUBCLASS_CLEAR(maps); SUBCLASS_CLEAR(moves); SUBCLASS_CLEAR(natures); SUBCLASS_CLEAR(skins); SUBCLASS_CLEAR(sounds); SUBCLASS_CLEAR(species); SUBCLASS_CLEAR(sprites); SUBCLASS_CLEAR(status); SUBCLASS_CLEAR(stores); SUBCLASS_CLEAR(tiles); SUBCLASS_CLEAR(times); SUBCLASS_CLEAR(trainers); SUBCLASS_CLEAR(types); SUBCLASS_CLEAR(weathers); m_mapPosition.clear(); m_typechart.clear(); }