diff --git a/src/Core/Crawl/claster.cpp b/src/Core/Crawl/claster.cpp index 0253c98..5f37fa2 100644 --- a/src/Core/Crawl/claster.cpp +++ b/src/Core/Crawl/claster.cpp @@ -23,7 +23,7 @@ Claster::~Claster() { void Claster::add(ClasterItem *object) { _objects.insert(object->guiId(), object); - if (auto singlClasterObject = dynamic_cast(object)) { + if (auto singlClasterObject = dynamic_cast(object)) { singlClasterObject->setClaster(this); } } diff --git a/src/Core/Crawl/clasteritem.cpp b/src/Core/Crawl/clasteritem.cpp index 9ec2d99..6294640 100644 --- a/src/Core/Crawl/clasteritem.cpp +++ b/src/Core/Crawl/clasteritem.cpp @@ -16,8 +16,8 @@ ClasterItem::ClasterItem(const QString &name, } ClasterItem::~ClasterItem() { - if (parentClasters().size() && *parentClasters().begin()) { - (*parentClasters().begin())->remove(this); + for (auto claster : qAsConst(_parentClasters)) { + claster->remove(this); } } diff --git a/src/Core/Crawl/iworld.cpp b/src/Core/Crawl/iworld.cpp index 8f819dc..b5fa62e 100644 --- a/src/Core/Crawl/iworld.cpp +++ b/src/Core/Crawl/iworld.cpp @@ -82,7 +82,7 @@ void IWorld::setPlayer(QObject *newPlayer) { } if (_player) { - removeIAtomictem(_player->guiId()); + removeIAtomicItem(_player->guiId()); } _player = newPlayerObject; @@ -91,6 +91,10 @@ void IWorld::setPlayer(QObject *newPlayer) { emit playerChanged(); } +IWorldItem *IWorld::generate(const QString &objectType) const { + return _registeredTypes.value(objectType, [](){return nullptr;}).operator()(); +} + bool IWorld::stop() { start(); @@ -177,13 +181,14 @@ void IWorld::removeItem(int id, QList *removedObjectsList) { // Work wih claster if (auto claster = dynamic_cast(obj)) { - for (auto item : claster->objects()) { - if (!item || item->parentClastersCount()) + auto copyOfObjectsList = claster->objects(); + for (auto item : copyOfObjectsList) { + if (!item || !item->parentClastersCount()) continue; int id = item->guiId(); - removeIAtomictem(item); + removeIAtomicItem(item); if (removedObjectsList) removedObjectsList->push_back(id); @@ -191,7 +196,8 @@ void IWorld::removeItem(int id, QList *removedObjectsList) { } addAtomicItem(obj); - removedObjectsList->push_back(obj->guiId()); + if (removedObjectsList) + removedObjectsList->push_back(obj->guiId()); } @@ -230,7 +236,7 @@ void IWorld::addAtomicItem(IWorldItem* obj) { _itemsGroup.insert(obj->className(), obj->guiId()); } -bool IWorld::removeIAtomictem(int id) { +bool IWorld::removeIAtomicItem(int id) { auto obj = _items.value(id); if (!obj) { @@ -245,7 +251,7 @@ bool IWorld::removeIAtomictem(int id) { return true; } -bool IWorld::removeIAtomictem(IWorldItem *obj) { +bool IWorld::removeIAtomicItem(IWorldItem *obj) { if (!obj) { return false; } @@ -341,3 +347,4 @@ void IWorld::setWorldStatus(int newWorldStatus) { _worldStatus = newWorldStatus; emit worldStatusChanged(); } + diff --git a/src/Core/Crawl/iworld.h b/src/Core/Crawl/iworld.h index 0d5a282..3eeee9a 100644 --- a/src/Core/Crawl/iworld.h +++ b/src/Core/Crawl/iworld.h @@ -63,11 +63,27 @@ public: /** * @brief initWorldRules The implementation of this interface must be retun initialized list of the world rules. * For more information see the WorldRules map. + * + * Example of use : + * + * ```cpp + * WorldRule *World::initWorldRules() { + * return new WorldRule { + * { + * 0, {{registerObject(), 10}}, + * 100, {{registerObject(), 10}, {registerObject(), 1}}, + * } + * }; + * } + * ``` + * * @return a raw pointer to world a rules map. * @note The Palyer object will be deleted when wold distroed. * So do not delete your created player pbject yuorself. + * + * */ - virtual WorldRule* initWorldRules() const = 0; + virtual WorldRule* initWorldRules() = 0; /** * @brief initUserInterface This method should be return point to userInterface object. @@ -253,25 +269,22 @@ protected: /** * @brief generate This method shold be generate object from the @a objectType. * Override this method for add support yourown objects. + * @note If your objects not requre custom setting then use the default implementation of the generate method. * @param objectType This is string type name of the object, * @return pointer to the object. * * **Example** * ```cpp * IWorldItem* generate(const QString& objectType)) const { - * if (auto obj = IWorld::generate(objectType)) { - * return obj; + * auto registeredObject = IWorld::generate(objectType); + * if (registeredObject) { + * // process creating of object. * } - * - * ... - * // custom implementation - * ... - * - * return nullptr; + * return registeredObject; * } * ``` */ - virtual IWorldItem* generate(const QString& objectType) const = 0; + virtual IWorldItem* generate(const QString& objectType) const; /** * @brief setCameraReleativePosition This method update camera position @@ -285,6 +298,40 @@ protected: */ void setCameraRatation(const QQuaternion &newCameraRatation); + template + + /** + * @brief registerObject This method will register object type for generation on the world. + * + * Example of use: + * + * ```cpp + * ... + * QString className = registerObject(); + * ... + * ``` + * + * @return name of registered class. + */ + QString registerObject() { + + static_assert(std::is_base_of_v, + "You try register no IWorldItem class. Please inherit of IWorldItem class and try again"); + + QString type = Type().className(); + + if (!_registeredTypes.contains(type)) { + + auto wraper = []() { + return new Type(); + }; + + _registeredTypes.insert(type, wraper); + } + + return type; + } + private slots: /** @@ -331,14 +378,14 @@ private: * @param id This is id of removed objects. * @return return true if object remove successul */ - bool removeIAtomictem(int id); + bool removeIAtomicItem(int id); /** - * @brief removeIAtomictem This method remove object @a obj. This method work with atomic objects only. If you rty remove claster objects then it will be ramoved witohout child objects. + * @brief removeIAtomicItem This method remove object @a obj. This method work with atomic objects only. If you rty remove claster objects then it will be ramoved witohout child objects. * @param obj This is id of removed objects. * @return return true if object remove successul */ - bool removeIAtomictem(IWorldItem *obj); + bool removeIAtomicItem(IWorldItem *obj); /** * @brief removeAnyItemFromGroup This method remove any object from group and return id of removed object. If The objec are claster then this method remove all child objects. @@ -359,9 +406,15 @@ private: IPlayer *_player = nullptr; IControl *_userInterface = nullptr; IAI *_backgroundAI = nullptr; - - friend class Engine; int _worldStatus = 0; + QHash> _registeredTypes; + + // engine + friend class Engine; + + // testing + friend class ClastersTest; + }; #endif // IWORLD_H diff --git a/src/Core/Crawl/singleclasterworlditem.cpp b/src/Core/Crawl/singleclasterworlditem.cpp index 418b630..6bada5e 100644 --- a/src/Core/Crawl/singleclasterworlditem.cpp +++ b/src/Core/Crawl/singleclasterworlditem.cpp @@ -19,8 +19,6 @@ SingleClasterWorldItem::SingleClasterWorldItem(const QString &name, void SingleClasterWorldItem::setClaster(Claster *claster) { if (parentClasters().size() > 0) { - debug_assert(parentClasters().size() > 1, "Internal error occured, The singleClaster object have multiple claster parents!!"); - Claster* parent = *parentClasters().begin(); parent->remove(this); removeClaster(parent); diff --git a/tests/units/clasterstest.cpp b/tests/units/clasterstest.cpp index cb989e6..78151f4 100644 --- a/tests/units/clasterstest.cpp +++ b/tests/units/clasterstest.cpp @@ -7,12 +7,137 @@ #include "clasterstest.h" #include +#include +#include +#include +#include -ClastersTest::ClastersTest() -{ +// Test claster object. +class TestClasterObject: public Claster { + +public: + TestClasterObject(): Claster("TestClaster"){}; +protected: + void onIntersects(const IWorldItem *) {}; +}; + +// Test single claster item +class TestClasterSingleItem: public SingleClasterWorldItem { +public: + TestClasterSingleItem(): SingleClasterWorldItem("TestClasterSingleItem"){}; +protected: + void onIntersects(const IWorldItem *) {}; +}; + +// Test claster item +class TestClasterItem: public ClasterItem { +public: + TestClasterItem(): ClasterItem("TestClasterItem"){}; +protected: + void onIntersects(const IWorldItem *) {}; +}; + +// Test world of clasters +class TestWorld: public IWorld { +public: + TestWorld(): IWorld(){}; + + // IWorld interface +public: + IPlayer *initPlayer() const {return nullptr;}; + WorldRule *initWorldRules() {return nullptr;}; + QString initHdrBackGround() const {return "";}; + QString description() const {return "";}; + QString imagePreview() const {return "";}; + QString name() const {return "TestWorld";}; + int costToUnlock() const {return 0;}; + + friend class ClastersTest; +}; + +ClastersTest::ClastersTest() { } void ClastersTest::test() { - QVERIFY(false); + // test clasters + testOneClaster(); + testClastersWithWorld(); +} + +void ClastersTest::testOneClaster() const { + TestClasterItem item; + TestClasterSingleItem singleItem; + + // life of the clasters + { + TestClasterObject claster, claster2; + + // Add items to first clasters + claster.add(&item); + claster.add(&singleItem); + + // in the first claster should be 2 objects + QVERIFY(claster.objects().size() == 2); + + // item sould be contains oly one parents claster object. + QVERIFY(item.parentClastersCount() == 1); + QVERIFY(singleItem.parentClastersCount() == 1); + + // Add items into second claster object + claster2.add(&item); + claster2.add(&singleItem); + + // in the second claster should be 2 objects + QVERIFY(claster2.objects().size() == 2); + // but first claster should be contains only one claster item because the singleclater item cannot are child of the two clasters. + QVERIFY(claster.objects().size() == 1); + + // item sould be contains two parents claster object. + + QVERIFY(item.parentClastersCount() == 2); + // item sould be contains oly one parents claster object. + QVERIFY(singleItem.parentClastersCount() == 1); + } + + // after distrou clasters objects the parents list size should be equals 0 + QVERIFY(item.parentClastersCount() == 0); + QVERIFY(singleItem.parentClastersCount() == 0); + +} + +void ClastersTest::testClastersWithWorld() const { + TestWorld world; + TestClasterObject *claster = new TestClasterObject(), + *claster2 = new TestClasterObject(); + + TestClasterItem *item = new TestClasterItem(), + *item2 = new TestClasterItem; + + // Add to first claster two items. + claster->add(item); + claster->add(item2); + + // The claster 2 contains item 2 only. + claster2->add(item2); + + // Add claster item to world + world.addItem(claster); + + // After adding claster object ito world child objects should be addeed automaticaly + QVERIFY(world._items.size() == 3); + + // add second claster ot world + world.addItem(claster2); + + QVERIFY(world._items.size() == 4); + + // remove claster with 2 items from world + world.removeItem(claster->guiId()); + + // after removing claster ovject child items should be removed too. but ony items that contains one parent claster. + QVERIFY(world._items.size() == 2); + + world.removeItem(claster2->guiId()); + } diff --git a/tests/units/clasterstest.h b/tests/units/clasterstest.h index 34f1e3e..436326f 100644 --- a/tests/units/clasterstest.h +++ b/tests/units/clasterstest.h @@ -18,10 +18,18 @@ class ClastersTest: public Test, protected TestUtils { public: ClastersTest(); - - // Test interface -public: void test() override; +private: + + /** + * @brief testOneClaster This test check functionality of the claster class. + */ + void testOneClaster() const; + + /** + * @brief testClastersWithWorld This test check clasters integrarion on the world + */ + void testClastersWithWorld() const; }; #endif // CLASTERSTEST_H