From 716544321a0ef9c1d55b6e190ee5e6cb4b3c2764 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Thu, 9 Feb 2023 17:41:24 +0400 Subject: [PATCH] chore: Init --- .gitignore | 2 + QuestWizard.pro | 120 +++++ README.md | 3 + add_ico.rc | 1 + application.qrc | 17 + features/gamefeatures.h | 41 ++ features/qw_abstractdialoguemanager.cpp | 9 + features/qw_abstractdialoguemanager.h | 29 ++ features/qw_dialoguefactory.cpp | 39 ++ features/qw_dialoguefactory.h | 24 + features/qw_eventfactory.cpp | 221 +++++++++ features/qw_eventfactory.h | 48 ++ features/qw_inventorymanager.cpp | 94 ++++ features/qw_inventorymanager.h | 67 +++ features/qw_levelbuilder.cpp | 470 ++++++++++++++++++++ features/qw_levelbuilder.h | 78 ++++ features/qw_soundplayer.cpp | 121 +++++ features/qw_soundplayer.h | 61 +++ features/qw_statemachine.cpp | 15 + features/qw_statemachine.h | 23 + features/qw_textdialoguemanager.cpp | 60 +++ features/qw_textdialoguemanager.h | 37 ++ features/qw_widgetdialoguemanager.cpp | 50 +++ features/qw_widgetdialoguemanager.h | 28 ++ game.cpp | 114 +++++ models/dialogues/qw_abstractgamedialogue.h | 38 ++ models/dialogues/qw_textdialogue.cpp | 48 ++ models/dialogues/qw_textdialogue.h | 35 ++ models/dialogues/qw_widgetdialogue.cpp | 63 +++ models/dialogues/qw_widgetdialogue.h | 45 ++ models/events/qw_abstractevent.h | 43 ++ models/events/qw_abstractinventoryevent.cpp | 20 + models/events/qw_abstractinventoryevent.h | 31 ++ models/events/qw_abstractlevelevent.cpp | 20 + models/events/qw_abstractlevelevent.h | 31 ++ models/events/qw_abstractsceneevent.cpp | 19 + models/events/qw_abstractsceneevent.h | 31 ++ models/events/qw_abstractsoundevent.cpp | 19 + models/events/qw_abstractsoundevent.h | 31 ++ models/events/qw_addtriggerevent.cpp | 49 ++ models/events/qw_addtriggerevent.h | 31 ++ models/events/qw_changelocationevent.cpp | 52 +++ models/events/qw_changelocationevent.h | 32 ++ models/events/qw_endlevelevent.cpp | 25 ++ models/events/qw_endlevelevent.h | 25 ++ models/events/qw_newgameevent.cpp | 45 ++ models/events/qw_newgameevent.h | 27 ++ models/events/qw_pickupitemevent.cpp | 37 ++ models/events/qw_playmusicevent.h | 34 ++ models/events/qw_playsoundevent.cpp | 24 + models/events/qw_playsoundevent.h | 30 ++ models/events/qw_quitgameevent.cpp | 26 ++ models/events/qw_quitgameevent.h | 25 ++ models/events/qw_removetriggerevent.cpp | 49 ++ models/events/qw_removetriggerevent.h | 31 ++ models/events/qw_startdialogueevent.cpp | 36 ++ models/events/qw_startdialogueevent.h | 34 ++ models/events/qw_switcheventsevent.cpp | 75 ++++ models/events/qw_switcheventsevent.h | 48 ++ models/qw_location.cpp | 114 +++++ models/qw_location.h | 51 +++ models/qw_trigger.cpp | 113 +++++ models/qw_trigger.h | 46 ++ qml/test.qml | 21 + qw_globalmetadata.cpp | 42 ++ qw_globalmetadata.h | 30 ++ res/cell.png | Bin 0 -> 12059 bytes res/config.json | 28 ++ res/defaultsave.json | 260 +++++++++++ res/dialogue_panel.jpeg | Bin 0 -> 14968 bytes res/door.png | Bin 0 -> 507 bytes res/inv.jpg | Bin 0 -> 2588 bytes res/levels/level1.json | 111 +++++ res/menu.json | 53 +++ res/osaka.png | Bin 0 -> 4143 bytes view/controls/pushbuttonsound.cpp | 37 ++ view/controls/pushbuttonsound.h | 40 ++ view/controls/qw_abstractscenecontrol.cpp | 13 + view/controls/qw_abstractscenecontrol.h | 25 ++ view/controls/scenedialoguepanel.cpp | 71 +++ view/controls/scenedialoguepanel.h | 45 ++ view/controls/sceneinventorypanel.cpp | 85 ++++ view/controls/sceneinventorypanel.h | 49 ++ view/qw_scene.cpp | 128 ++++++ view/qw_scene.h | 61 +++ view/qw_view.cpp | 9 + view/qw_view.h | 29 ++ 87 files changed, 4442 insertions(+) create mode 100644 .gitignore create mode 100644 QuestWizard.pro create mode 100644 README.md create mode 100644 add_ico.rc create mode 100644 application.qrc create mode 100644 features/gamefeatures.h create mode 100644 features/qw_abstractdialoguemanager.cpp create mode 100644 features/qw_abstractdialoguemanager.h create mode 100644 features/qw_dialoguefactory.cpp create mode 100644 features/qw_dialoguefactory.h create mode 100644 features/qw_eventfactory.cpp create mode 100644 features/qw_eventfactory.h create mode 100644 features/qw_inventorymanager.cpp create mode 100644 features/qw_inventorymanager.h create mode 100644 features/qw_levelbuilder.cpp create mode 100644 features/qw_levelbuilder.h create mode 100644 features/qw_soundplayer.cpp create mode 100644 features/qw_soundplayer.h create mode 100644 features/qw_statemachine.cpp create mode 100644 features/qw_statemachine.h create mode 100644 features/qw_textdialoguemanager.cpp create mode 100644 features/qw_textdialoguemanager.h create mode 100644 features/qw_widgetdialoguemanager.cpp create mode 100644 features/qw_widgetdialoguemanager.h create mode 100644 game.cpp create mode 100644 models/dialogues/qw_abstractgamedialogue.h create mode 100644 models/dialogues/qw_textdialogue.cpp create mode 100644 models/dialogues/qw_textdialogue.h create mode 100644 models/dialogues/qw_widgetdialogue.cpp create mode 100644 models/dialogues/qw_widgetdialogue.h create mode 100644 models/events/qw_abstractevent.h create mode 100644 models/events/qw_abstractinventoryevent.cpp create mode 100644 models/events/qw_abstractinventoryevent.h create mode 100644 models/events/qw_abstractlevelevent.cpp create mode 100644 models/events/qw_abstractlevelevent.h create mode 100644 models/events/qw_abstractsceneevent.cpp create mode 100644 models/events/qw_abstractsceneevent.h create mode 100644 models/events/qw_abstractsoundevent.cpp create mode 100644 models/events/qw_abstractsoundevent.h create mode 100644 models/events/qw_addtriggerevent.cpp create mode 100644 models/events/qw_addtriggerevent.h create mode 100644 models/events/qw_changelocationevent.cpp create mode 100644 models/events/qw_changelocationevent.h create mode 100644 models/events/qw_endlevelevent.cpp create mode 100644 models/events/qw_endlevelevent.h create mode 100644 models/events/qw_newgameevent.cpp create mode 100644 models/events/qw_newgameevent.h create mode 100644 models/events/qw_pickupitemevent.cpp create mode 100644 models/events/qw_playmusicevent.h create mode 100644 models/events/qw_playsoundevent.cpp create mode 100644 models/events/qw_playsoundevent.h create mode 100644 models/events/qw_quitgameevent.cpp create mode 100644 models/events/qw_quitgameevent.h create mode 100644 models/events/qw_removetriggerevent.cpp create mode 100644 models/events/qw_removetriggerevent.h create mode 100644 models/events/qw_startdialogueevent.cpp create mode 100644 models/events/qw_startdialogueevent.h create mode 100644 models/events/qw_switcheventsevent.cpp create mode 100644 models/events/qw_switcheventsevent.h create mode 100644 models/qw_location.cpp create mode 100644 models/qw_location.h create mode 100644 models/qw_trigger.cpp create mode 100644 models/qw_trigger.h create mode 100644 qml/test.qml create mode 100644 qw_globalmetadata.cpp create mode 100644 qw_globalmetadata.h create mode 100644 res/cell.png create mode 100644 res/config.json create mode 100644 res/defaultsave.json create mode 100644 res/dialogue_panel.jpeg create mode 100644 res/door.png create mode 100644 res/inv.jpg create mode 100644 res/levels/level1.json create mode 100644 res/menu.json create mode 100644 res/osaka.png create mode 100644 view/controls/pushbuttonsound.cpp create mode 100644 view/controls/pushbuttonsound.h create mode 100644 view/controls/qw_abstractscenecontrol.cpp create mode 100644 view/controls/qw_abstractscenecontrol.h create mode 100644 view/controls/scenedialoguepanel.cpp create mode 100644 view/controls/scenedialoguepanel.h create mode 100644 view/controls/sceneinventorypanel.cpp create mode 100644 view/controls/sceneinventorypanel.h create mode 100644 view/qw_scene.cpp create mode 100644 view/qw_scene.h create mode 100644 view/qw_view.cpp create mode 100644 view/qw_view.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..432b0ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.pro.user +*.autosave diff --git a/QuestWizard.pro b/QuestWizard.pro new file mode 100644 index 0000000..96d623e --- /dev/null +++ b/QuestWizard.pro @@ -0,0 +1,120 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-02-10T20:30:04 +# +#------------------------------------------------- + + VERSION = 0.0.1 + +CONFIG += c++17 +QMAKE_CXXFLAGS = -Wall -Werror -Wextra -Wpedantic -Wconversion -std=c++17 -O2 -g +QT += core gui multimedia widgets quickwidgets + +# Potato22 is the best vector artist, you can't change my mind. +# The icon is precious! +RC_FILE += add_ico.rc + +TARGET = QuestWizard +TEMPLATE = app + +DEFINES += QT_DEPRECATED_WARNINGS + +SOURCES += main.cpp \ + game.cpp \ + features/qw_abstractdialoguemanager.cpp \ + features/qw_dialoguefactory.cpp \ + features/qw_textdialoguemanager.cpp \ + features/qw_widgetdialoguemanager.cpp \ + features/qw_eventfactory.cpp \ + features/qw_inventorymanager.cpp \ + features/qw_levelbuilder.cpp \ + features/qw_soundplayer.cpp \ + features/qw_statemachine.cpp \ + qw_globalmetadata.cpp \ + \ + models/events/qw_abstractevent.cpp \ + models/events/qw_abstractinventoryevent.cpp \ + models/events/qw_abstractlevelevent.cpp \ + models/events/qw_abstractsceneevent.cpp \ + models/events/qw_abstractsoundevent.cpp \ + models/events/qw_changelocationevent.cpp \ + models/events/qw_changetriggerpropertiesevent.cpp \ + models/events/qw_deletefrominventoryevent.cpp \ + models/events/qw_endlevelevent.cpp \ + models/events/qw_newgameevent.cpp \ + models/events/qw_pickupitemevent.cpp \ + models/events/qw_playmusicevent.cpp \ + models/events/qw_playsoundevent.cpp \ + models/events/qw_quitgameevent.cpp \ + models/events/qw_startdialogueevent.cpp \ + models/events/qw_addtriggerevent.cpp \ + models/events/qw_removetriggerevent.cpp \ + models/events/qw_switcheventsevent.cpp \ + models/dialogues/qw_abstractgamedialogue.cpp \ + models/dialogues/qw_textdialogue.cpp \ + models/dialogues/qw_widgetdialogue.cpp \ + models/qw_tagholder.cpp \ + models/qw_trigger.cpp \ + models/qw_location.cpp \ + \ + view/controls/pushbuttonsound.cpp \ + view/controls/qw_abstractscenecontrol.cpp \ + view/controls/scenedialoguepanel.cpp \ + view/controls/sceneinventorypanel.cpp \ + \ + view/qw_scene.cpp \ + view/qw_view.cpp + +HEADERS += \ + game.h \ + features/qw_abstractdialoguemanager.h \ + features/gamefeatures.h \ + features/qw_dialoguefactory.h \ + features/qw_textdialoguemanager.h \ + features/qw_widgetdialoguemanager.h \ + features/qw_eventfactory.h \ + features/qw_inventorymanager.h \ + features/qw_levelbuilder.h \ + features/qw_soundplayer.h \ + features/qw_statemachine.h \ + qw_globalmetadata.h \ + \ + models/events/qw_abstractevent.h \ + models/events/qw_abstractinventoryevent.h \ + models/events/qw_abstractlevelevent.h \ + models/events/qw_abstractsceneevent.h \ + models/events/qw_abstractsoundevent.h \ + models/events/qw_changelocationevent.h \ + models/events/qw_changetriggerpropertiesevent.h \ + models/events/qw_deletefrominventoryevent.h \ + models/events/qw_endlevelevent.h \ + models/events/qw_newgameevent.h \ + models/events/qw_pickupitemevent.h \ + models/events/qw_playmusicevent.h \ + models/events/qw_playsoundevent.h \ + models/events/qw_quitgameevent.h \ + models/events/qw_startdialogueevent.h \ + models/events/qw_addtriggerevent.h \ + models/events/qw_removetriggerevent.h \ + models/events/qw_switcheventsevent.h \ + models/dialogues/qw_abstractgamedialogue.h \ + models/dialogues/qw_textdialogue.h \ + models/dialogues/qw_widgetdialogue.h \ + models/qw_tagholder.h \ + models/qw_trigger.h \ + models/qw_location.h \ + \ + view/controls/pushbuttonsound.h \ + view/controls/qw_abstractscenecontrol.h \ + view/controls/scenedialoguepanel.h \ + view/controls/sceneinventorypanel.h \ + \ + view/qw_scene.h \ + view/qw_view.h + + +RESOURCES += \ + application.qrc + +DISTFILES += \ + qml/test.qml diff --git a/README.md b/README.md new file mode 100644 index 0000000..85edf8b --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# QuestWizard +I am not really sure what this thing is going to be but anyway here is trello where I try to manage stuff + https://trello.com/b/M3kqOaDB/questwizard-functional-requirements?menu=filter&filter=label:master-merge%201.1 diff --git a/add_ico.rc b/add_ico.rc new file mode 100644 index 0000000..bb10963 --- /dev/null +++ b/add_ico.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icon.ico" \ No newline at end of file diff --git a/application.qrc b/application.qrc new file mode 100644 index 0000000..8f1e21c --- /dev/null +++ b/application.qrc @@ -0,0 +1,17 @@ + + + res/levels/level1.json + res/defaultsave.json + res/menu.json + res/final_background.jpg + res/roomkey_background.jpg + res/spawn_background.jpg + res/cell.png + res/door.png + res/inv.jpg + res/dialogue_panel.jpeg + res/sound.wav + res/osaka.png + res/config.json + + diff --git a/features/gamefeatures.h b/features/gamefeatures.h new file mode 100644 index 0000000..cff3579 --- /dev/null +++ b/features/gamefeatures.h @@ -0,0 +1,41 @@ +#ifndef GAMEFEATURES_H +#define GAMEFEATURES_H + +#include "game.h" + +#include "qw_levelbuilder.h" +#include "qw_soundplayer.h" +#include "qw_inventorymanager.h" +#include "qw_textdialoguemanager.h" +#include "qw_widgetdialoguemanager.h" +#include "view/qw_view.h" + +/* GameFeatures + * The package of all key in-game managers. This way they are easy to transport. */ + +struct GameFeatures final +{ +public: + QWView *ptr_view; + QWScene *ptr_scene; + QWSoundPlayer *ptr_sound_player; + QWInventoryManager *ptr_inventory; + QWLevelBuilder *ptr_builder; + QWTextDialogueManager *ptr_text_dlg; + QWWidgetDialogueManager *ptr_widget_dlg; + + explicit GameFeatures(Game *ptr_game) + { + ptr_scene = new QWScene(1280, 720); + ptr_view = new QWView(ptr_scene); + ptr_sound_player = new QWSoundPlayer(ptr_game); + ptr_inventory = new QWInventoryManager(ptr_scene); + ptr_builder = new QWLevelBuilder(ptr_game); + ptr_text_dlg = new QWTextDialogueManager(ptr_scene); + ptr_widget_dlg = new QWWidgetDialogueManager(ptr_scene); + } + + ~GameFeatures() = default; +}; + +#endif // GAMEFEATURES_H diff --git a/features/qw_abstractdialoguemanager.cpp b/features/qw_abstractdialoguemanager.cpp new file mode 100644 index 0000000..a5698a2 --- /dev/null +++ b/features/qw_abstractdialoguemanager.cpp @@ -0,0 +1,9 @@ +#include "qw_abstractdialoguemanager.h" + +QWAbstractDialogueManager:: QWAbstractDialogueManager(QWScene *scene) : + QObject(scene), + ptr_scene(scene) +{} + +QWAbstractDialogueManager::~QWAbstractDialogueManager() +{} diff --git a/features/qw_abstractdialoguemanager.h b/features/qw_abstractdialoguemanager.h new file mode 100644 index 0000000..c3569a6 --- /dev/null +++ b/features/qw_abstractdialoguemanager.h @@ -0,0 +1,29 @@ +#ifndef QWABSTRACTDIALOGUEMANAGER_H +#define QWABSTRACTDIALOGUEMANAGER_H + +#include +#include "view/qw_scene.h" +#include "models/dialogues/qw_abstractgamedialogue.h" + +class QWAbstractDialogueManager : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QWAbstractDialogueManager) +protected: + QWScene *ptr_scene; + +public: + explicit QWAbstractDialogueManager(QWScene *scene = nullptr); + virtual ~QWAbstractDialogueManager() override = 0; + + virtual void activateDialogue(const std::shared_ptr &dialogue) = 0; + +public slots: + virtual void onClicked(MouseButton mouse_button) = 0; + +signals: + void onEntryDialogueTransition(); + void onLeaveDialogueTransition(); +}; + +#endif // QWABSTRACTDIALOGUEMANAGER_H diff --git a/features/qw_dialoguefactory.cpp b/features/qw_dialoguefactory.cpp new file mode 100644 index 0000000..2c2f3ce --- /dev/null +++ b/features/qw_dialoguefactory.cpp @@ -0,0 +1,39 @@ +#include "qw_dialoguefactory.h" +#include "qw_levelbuilder.h" + +QWDialogueFactory::QWDialogueFactory(QWLevelBuilder *b) : + builder(b) +{} + +std::shared_ptr QWDialogueFactory::createTextDialogue(const QJsonObject &json_object) +{ + std::unique_ptr new_dialogue; + + qDebug() << " Found QWTextDialogue. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("text")); + + QStringList text; + const QJsonArray text_pages = json_object["text"].toArray(); + for (auto const &page : text_pages) + text << page.toString(); + +// add events on leave or something + + new_dialogue = std::make_unique(text); + + return std::shared_ptr{std::move(new_dialogue)}; +} + +std::shared_ptr QWDialogueFactory::createWidgetDialogue(const QJsonObject &json_object) +{ + std::unique_ptr new_dialogue; + + qDebug() << " Found QWWidgetDialogue. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("qml_filename")); + + new_dialogue = std::make_unique(json_object["qml_filename"].toString()); + + return std::shared_ptr{std::move(new_dialogue)}; +} diff --git a/features/qw_dialoguefactory.h b/features/qw_dialoguefactory.h new file mode 100644 index 0000000..2c0e045 --- /dev/null +++ b/features/qw_dialoguefactory.h @@ -0,0 +1,24 @@ +#ifndef QWDIALOGUEFACTORY_H +#define QWDIALOGUEFACTORY_H + +#include "models/dialogues/qw_textdialogue.h" +#include "models/dialogues/qw_widgetdialogue.h" + +/* QWDialogueFactory + * Creates game dialogues of all kinds! */ + +class QWLevelBuilder; + +class QWDialogueFactory final +{ +private: + QWLevelBuilder *builder; + +public: + explicit QWDialogueFactory(QWLevelBuilder *b); + + std::shared_ptr createTextDialogue(const QJsonObject &json_object); + std::shared_ptr createWidgetDialogue(const QJsonObject &json_object); +}; + +#endif // QWDIALOGUEFACTORY_H diff --git a/features/qw_eventfactory.cpp b/features/qw_eventfactory.cpp new file mode 100644 index 0000000..736639b --- /dev/null +++ b/features/qw_eventfactory.cpp @@ -0,0 +1,221 @@ +#include "qw_eventfactory.h" +#include "qw_levelbuilder.h" +#include "qw_widgetdialoguemanager.h" +#include "qw_textdialoguemanager.h" + +QWEventFactory::QWEventFactory(QWLevelBuilder *b) : + builder(b) +{} + +std::shared_ptr QWEventFactory::createDeleteItEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWDeleteFromInventoryEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("target")); + Q_ASSERT(builder->hash_triggers.contains(json_object["target"].toString())); + new_event = std::make_unique(builder->hash_triggers[json_object["target"].toString()]); + new_event->setInventoryManager(builder->ptr_inventory); + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createChangeLocEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWChangeLocationEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("location")); + Q_ASSERT(builder->hash_locations.contains(json_object["location"].toString())); + new_event = std::make_unique(builder->hash_locations[json_object["location"].toString()]); + + new_event->setScene(builder->ptr_scene); + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createChangeTrProperts(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWChangeTriggerPropertiesEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("target")); + Q_ASSERT(builder->hash_triggers.contains(json_object["target"].toString())); + new_event = std::make_unique(builder->hash_triggers[json_object["target"].toString()]); + + if (json_object.contains("x") && json_object.contains("y")) + new_event->setParams(json_object["x"].toDouble(), json_object["y"].toDouble(), + json_object.contains("path") ? json_object["path"].toString() : "$no_pic"); + else + new_event->setParams(json_object.contains("path") ? json_object["path"].toString() : "$no_pic"); + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createSwitchEventsEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWSwitchEventsEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("target")); + Q_ASSERT(builder->hash_triggers.contains(json_object["target"].toString())); + new_event = std::make_unique(builder->hash_triggers[json_object["target"].toString()]); + + Q_ASSERT(json_object.contains("enable_evs") && json_object.contains("disable_evs")); + + // Linking events of enabled state + QJsonArray evs = json_object["enable_evs"].toArray(); + QList> list_events; + + for (const auto obj : evs) { + Q_ASSERT(builder->hash_events.contains(obj.toString())); + list_events.append(builder->hash_events[obj.toString()]); + } + + new_event->setEventsOnEnable(list_events); + + list_events.clear(); + + // Linking events of disabled state + evs = json_object["disable_evs"].toArray(); + + for (const auto obj : evs) { + Q_ASSERT(builder->hash_events.contains(obj.toString())); + list_events.append(builder->hash_events[obj.toString()]); + } + + new_event->setEventsOnDisable(list_events); + new_event->init(); + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createPickupItEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWPickupItemEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("target")); + Q_ASSERT(builder->hash_triggers.contains(json_object["target"].toString())); + new_event = std::make_unique(builder->hash_triggers[json_object["target"].toString()]); + new_event->setInventoryManager(builder->ptr_inventory); + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createEndLevelEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWEndLevelEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("new_level")); + new_event = std::make_unique(json_object["target"].toString()); + new_event->setLevelBuilder(builder); + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createPlaySoundEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWPlaySoundEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("sound")); + new_event = std::make_unique(json_object["sound"].toString()); + new_event->setSoundPlayer(builder->ptr_soundplayer); + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createNewGameEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWNewGameEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("save_file")); + new_event = std::make_unique(json_object["save_file"].toString()); + new_event->setLevelBuilder(builder); + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createQuitGameEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWQuitGameEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("save_game")); + new_event = std::make_unique(json_object["save_game"].toBool()); + new_event->setLevelBuilder(builder); + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createStartDlgEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWStartTextDialogueEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("dialogue")); + Q_ASSERT(json_object.contains("dialogue_type")); + Q_ASSERT(builder->hash_dialogues.contains(json_object["dialogue"].toString())); + new_event = std::make_unique(builder->hash_dialogues[json_object["dialogue"].toString()]); + + switch(json_object["dialogue_type"].toInt()) { + case 1: + new_event->setDialogueManager(builder->ptr_widget_dlg); + break; + case 0: + new_event->setDialogueManager(builder->ptr_text_dlg); + break; + default: + Q_ASSERT(false); + } + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createAddTrEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWAddTriggerEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("trigger")); + Q_ASSERT(builder->hash_triggers.contains(json_object["trigger"].toString())); + new_event = std::make_unique(builder->hash_triggers[json_object["trigger"].toString()]); + + Q_ASSERT(json_object.contains("location")); + Q_ASSERT(builder->hash_locations.contains(json_object["location"].toString())); + new_event->setLocation(builder->hash_locations[json_object["location"].toString()]); + + return std::shared_ptr{std::move(new_event)}; +} + +std::shared_ptr QWEventFactory::createRemoveTrEvent(const QJsonObject &json_object) +{ + std::unique_ptr new_event; + + qDebug() << " Found QWRemoveTriggerEvent. " << json_object["id"].toString() << "."; + + Q_ASSERT(json_object.contains("trigger")); + Q_ASSERT(builder->hash_triggers.contains(json_object["trigger"].toString())); + new_event = std::make_unique(builder->hash_triggers[json_object["trigger"].toString()]); + + Q_ASSERT(json_object.contains("location")); + Q_ASSERT(builder->hash_locations.contains(json_object["location"].toString())); + new_event->setLocation(builder->hash_locations[json_object["location"].toString()]); + + return std::shared_ptr{std::move(new_event)}; +} + diff --git a/features/qw_eventfactory.h b/features/qw_eventfactory.h new file mode 100644 index 0000000..73684ba --- /dev/null +++ b/features/qw_eventfactory.h @@ -0,0 +1,48 @@ +#ifndef EVENTFACTORY_H +#define EVENTFACTORY_H + +#include +#include + +#include "models/events/qw_abstractevent.h" +#include "models/events/qw_changelocationevent.h" +#include "models/events/qw_switcheventsevent.h" +#include "models/events/qw_deletefrominventoryevent.h" +#include "models/events/qw_pickupitemevent.h" +#include "models/events/qw_endlevelevent.h" +#include "models/events/qw_playsoundevent.h" +#include "models/events/qw_changetriggerpropertiesevent.h" +#include "models/events/qw_newgameevent.h" +#include "models/events/qw_quitgameevent.h" +#include "models/events/qw_startdialogueevent.h" +#include "models/events/qw_addtriggerevent.h" +#include "models/events/qw_removetriggerevent.h" + +/* QWEventFactory + * Creates game events of all kinds! */ + +class QWLevelBuilder; + +class QWEventFactory final +{ +private: + QWLevelBuilder *builder; + +public: + explicit QWEventFactory(QWLevelBuilder *b); + + std::shared_ptr createDeleteItEvent(const QJsonObject &json_object); + std::shared_ptr createChangeLocEvent(const QJsonObject &json_object); + std::shared_ptr createChangeTrProperts(const QJsonObject &json_object); + std::shared_ptr createSwitchEventsEvent(const QJsonObject &json_object); + std::shared_ptr createPickupItEvent(const QJsonObject &json_object); + std::shared_ptr createEndLevelEvent(const QJsonObject &json_object); + std::shared_ptr createPlaySoundEvent(const QJsonObject &json_object); + std::shared_ptr createNewGameEvent(const QJsonObject &json_object); + std::shared_ptr createQuitGameEvent(const QJsonObject &json_object); + std::shared_ptr createStartDlgEvent(const QJsonObject &json_object); + std::shared_ptr createAddTrEvent(const QJsonObject &json_object); + std::shared_ptr createRemoveTrEvent(const QJsonObject &json_object); +}; + +#endif // EVENTFACTORY_H diff --git a/features/qw_inventorymanager.cpp b/features/qw_inventorymanager.cpp new file mode 100644 index 0000000..4980a93 --- /dev/null +++ b/features/qw_inventorymanager.cpp @@ -0,0 +1,94 @@ +#include "qw_inventorymanager.h" +#include "qw_globalmetadata.h" +#include "view/controls/sceneinventorypanel.h" +#include "view/qw_scene.h" +#include "models/qw_trigger.h" + +QWInventoryManager::QWInventoryManager(QWScene *sc) : + ptr_scene(sc), + index_freepan(0) +{ + // Loading metadata + metadata.length_wall = QWGlobalMetadata::valueBy("InventoryManager:length_wall").toInt(); + metadata.length_cell = QWGlobalMetadata::valueBy("InventoryManager:length_cell").toInt(); + metadata.max_amount = QWGlobalMetadata::valueBy("InventoryManager:max_amount").toInt(); + metadata.first_wall = QWGlobalMetadata::valueBy("InventoryManager:first_wall").toInt(); + metadata.clr_sel = Qt::GlobalColor(QWGlobalMetadata::valueBy("InventoryManager:clr_sel").toInt()); + metadata.clr_sth = QWGlobalMetadata::valueBy("InventoryManager:clr_str").toReal(); + + item_selected_effect = new QGraphicsColorizeEffect(); + item_selected_effect->setColor(metadata.clr_sel); + item_selected_effect->setStrength(metadata.clr_sth); + +} + +void QWInventoryManager::setInventoryPanel(const std::shared_ptr &panel) noexcept +{ + ptr_panel = panel; +} + +int QWInventoryManager::freePanel() const noexcept +{ + return index_freepan; +} + +void QWInventoryManager::freePanelToRight() +{ + ++index_freepan; + Q_ASSERT(index_freepan <= metadata.max_amount); +} + +void QWInventoryManager::freePanelToLeft() +{ + --index_freepan; + Q_ASSERT(index_freepan >= 0); +} + +void QWInventoryManager::addInventoryIcon(std::shared_ptr &inventory_icon) +{ + inventory_icon->setParentItem(ptr_panel.get()); + list_inventory_icons.append(inventory_icon); + inventory_icon->setPos(metadata.first_wall + + ((metadata.length_wall + metadata.length_cell) + * freePanel()) + , 0); + freePanelToRight(); +} + +void QWInventoryManager::removeInventoryIcon(std::shared_ptr &inventory_icon) +{ + Index panel = metadata.max_amount + 1; + for (Index i = 0; i < list_inventory_icons.size(); ++i) + if (inventory_icon == list_inventory_icons[i]) + panel = i; + + Q_ASSERT(panel <= metadata.max_amount); + ptr_scene->removeItem(inventory_icon.get()); + list_inventory_icons.removeAt(panel); + freePanelToLeft(); + reorganizeItems(panel); +} + +void QWInventoryManager::reorganizeItems(Index free_panel) +{ + // When we remove item from the center we also should carefully move all the right items to the left + for (Index j = free_panel; j < list_inventory_icons.size(); ++j) + { + const std::shared_ptr &t = list_inventory_icons.at(j); + t->moveBy(-(metadata.length_cell + metadata.length_wall), 0); + + if (j > 0) + list_inventory_icons[j-1] = list_inventory_icons[j]; + } +} + +void QWInventoryManager::onClicked() +{ + for (auto &tr : list_inventory_icons) + if (tr->isUnderMouse()) { + tr->setGraphicsEffect(tr->graphicsEffect() ? nullptr : item_selected_effect); + tr->activate(); + } else { + tr->setGraphicsEffect(nullptr); + } +} diff --git a/features/qw_inventorymanager.h b/features/qw_inventorymanager.h new file mode 100644 index 0000000..1106dd4 --- /dev/null +++ b/features/qw_inventorymanager.h @@ -0,0 +1,67 @@ +#ifndef INVENTORYMANAGER_H +#define INVENTORYMANAGER_H + +#include +#include + +/* QWInventoryManager + * Controls everything related to inventory slots and their items. */ + +class QGraphicsWidget; +class QWScene; +class QWTrigger; + +class QWInventoryManager final : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QWInventoryManager) +private: + QList> list_inventory_icons; + std::shared_ptr ptr_panel; + + QWScene *ptr_scene; + + QRect rect_hidden; + QRect rect_visible; + + using Index = QList>::size_type; + Index index_freepan; + + QGraphicsColorizeEffect *item_selected_effect; + + struct metadata + { + // the separator thinkness + int length_wall; + // the cell side + int length_cell; + // the maximum amount of items to hold + int max_amount; + // the left wall of first item slot + int first_wall; + // the color for selected items + QColor clr_sel; + // the color for selected items + qreal clr_sth; + } metadata; + + void reorganizeItems(Index i); + +public: + + explicit QWInventoryManager(QWScene *sc); + + void setInventoryPanel(const std::shared_ptr &panel) noexcept; + + inline int freePanel() const noexcept; + inline void freePanelToRight(); + inline void freePanelToLeft(); + + void addInventoryIcon(std::shared_ptr &inventory_icon); + void removeInventoryIcon(std::shared_ptr &inventory_icon); + +public slots: + void onClicked(); +}; + +#endif // INVENTORYMANAGER_H diff --git a/features/qw_levelbuilder.cpp b/features/qw_levelbuilder.cpp new file mode 100644 index 0000000..4583427 --- /dev/null +++ b/features/qw_levelbuilder.cpp @@ -0,0 +1,470 @@ +#include +#include "gamefeatures.h" +#include "qw_levelbuilder.h" +#include "qw_eventfactory.h" +#include "qw_dialoguefactory.h" +#include "qw_globalmetadata.h" +#include "view/qw_scene.h" +#include "models/qw_location.h" +#include "models/qw_trigger.h" + +QWLevelBuilder::QWLevelBuilder(QObject *parent) : + QObject(parent), + str_current_level(strInitLevel()) +{ + // Dir for local save files + if (!QDir("save").exists()) { + QDir().mkdir("save"); + } + save_dir.setPath("save"); +} + +template +QJsonArray writeEntitesToJsonArray(const QHash> &hash_models) +{ + // Writing in active save file all the game elements and their state + QJsonArray json_array; + for (const auto &model : hash_models) + { + QJsonObject model_data; + model->writeToJson(model_data); + + json_array.append(model_data); + } + return json_array; +} + +void QWLevelBuilder::initMenuLevel() +{ + str_current_level = "menu"; + + QFile file(":/res/menu.json"); + + Q_ASSERT(file.open(QIODevice::ReadOnly)); + const QByteArray json_arr = file.readAll(); + file.close(); + + QJsonObject savefile = QJsonDocument::fromJson(json_arr).object(); + init(savefile); +} + +void QWLevelBuilder::initSaveProfile(const QString &filename, const QString &profilename) +{ + Q_UNUSED(profilename) + + // Creating inital save file after beginning the new game: + str_profile_filename = filename; + str_current_level = strInitLevel(); + + QFile file; + + str_profile_filename.remove(".json"); + + // We move default game description to player's save file + + file.setFileName(save_dir.path() + "/" + str_profile_filename + ".json"); + + qDebug() << save_dir.path() + "/" + str_profile_filename + ".json"; + + if (!file.exists()) { + qDebug() << "copy file"; + QFile::copy(":/res/defaultsave.json", file.fileName()); + } + + // Now we modify default json file to add the chosen profile name + + /*Q_ASSERT(file.open(QIODevice::ReadOnly)); + const QByteArray json_arr = file.readAll(); + file.close(); + + QJsonObject savefile = QJsonDocument::fromJson(json_arr).object(); + + // Pull the saved string from new game dialogue + str_current_profile = profilename; + savefile.insert("save_profile", str_current_profile); + + // Write it and resave + Q_ASSERT(file.open(QIODevice::WriteOnly)); + file.write(QJsonDocument(savefile).toJson()); + file.close();*/ +} + +void QWLevelBuilder::saveGame(/*QString filename*/) +{ + qDebug() << "Save current game!"; + + QJsonObject savejson; + QJsonArray json_entites; + + /* The first Trigger which moves player into its + * needed location where the game starts */ + savejson.insert("init", "spawn"); + savejson.insert("save_profile", str_current_profile); + + qDebug() << "Save triggers!"; + json_entites = writeEntitesToJsonArray(hash_triggers); + savejson.insert("triggers", json_entites); + + qDebug() << "Save dialogues!"; + json_entites = writeEntitesToJsonArray(hash_dialogues); + savejson.insert("dialogues", json_entites); + + qDebug() << "Save locations!"; + json_entites = writeEntitesToJsonArray(hash_locations); + savejson.insert("locations", json_entites); + + qDebug() << "Save events!"; + json_entites = writeEntitesToJsonArray(hash_events); + + // Creating the inital event where player must appear after game loading + QJsonObject init_spawn_data; + + QStringList trs; + for (auto & tr : ptr_scene->currentLocation()->triggers()) + trs << tr->tag(); + + QList> list_triggers; + init_spawn_data.insert("id", "spawn"); + init_spawn_data.insert("type", 0); + init_spawn_data.insert("trs", QJsonArray::fromStringList(trs)); + json_entites.append(init_spawn_data); + + savejson.insert("events", json_entites); + // - - - // + + QFile file(save_dir.dirName() + "/" + str_profile_filename + ".json"); + Q_ASSERT(file.open(QIODevice::ReadOnly)); + const QByteArray json_arr = file.readAll(); + file.close(); + QJsonObject savefile = QJsonDocument::fromJson(json_arr).object(); + + savefile[str_current_level] = savejson; + + Q_ASSERT(file.open(QIODevice::WriteOnly)); + file.write(QJsonDocument(savejson).toJson()); + file.close(); +} + +void QWLevelBuilder::loadGame(/*QString filename*/) +{ + QFile file; + + hash_events.clear(); + hash_triggers.clear(); + hash_dialogues.clear(); + hash_locations.clear(); + init_trigger.reset(); + + file.setFileName(save_dir.dirName() + "/" + str_profile_filename + ".json"); + file.open(QIODevice::ReadOnly); + const QByteArray json_arr = file.readAll(); + file.close(); + QJsonObject savefile = QJsonDocument::fromJson(json_arr).object(); + + str_current_level = savefile["init_level"].toString(); + + init(savefile); +} + +void QWLevelBuilder::initLevel(const QString &lvlname) +{ + str_current_level = lvlname; + + QFile file(save_dir.dirName() + "/" + str_profile_filename + ".json"); + + Q_ASSERT(file.open(QIODevice::ReadOnly)); + const QByteArray json_arr = file.readAll(); + file.close(); + + emit ptr_scene->signalLeaveMenu(); + + QJsonObject savefile = QJsonDocument::fromJson(json_arr).object(); + init(savefile); +} + +void QWLevelBuilder::init(const QJsonObject & savefile) +{ + qDebug() << "Init " << str_current_level << " level!"; + + hash_events.clear(); + hash_triggers.clear(); + init_trigger.reset(); + + level = savefile[str_current_level].toObject(); + + Q_ASSERT(!level.isEmpty()); + + // Building level from current json + + if (level.contains("triggers")) + setupTriggers(level["triggers"].toArray()); + + if (level.contains("dialogues")) + setupDialogues(level["dialogues"].toArray()); + + if (level.contains("locations")) + setupLocations(level["locations"].toArray()); + + if (level.contains("events")) + setupEvents(level["events"].toArray()); + + linkEvents(level["triggers"].toArray()); + + init_trigger = level.contains("init") ? hash_triggers[level["init"].toString()] : nullptr; + + // Start the level + Q_ASSERT(init_trigger); + init_trigger->activate(); +} + +QString QWLevelBuilder::strInitLevel() noexcept +{ + return "start"; +} + +void QWLevelBuilder::setGameFeatures(std::unique_ptr &features) +{ + ptr_scene = features->ptr_scene; + ptr_inventory = features->ptr_inventory; + ptr_soundplayer = features->ptr_sound_player; + ptr_text_dlg = features->ptr_text_dlg; + ptr_widget_dlg = features->ptr_widget_dlg; +} + +void QWLevelBuilder::linkEvents(const QJsonArray &array) +{ + qDebug() << "Link the events to triggers!"; + + for (const QJsonValue &json_value : array) + { + QJsonObject json_object(json_value.toObject()); + if (json_object.contains("evs")) + { + qDebug() << " Trigger " << json_object["id"].toString() << " contains events!"; + + QJsonArray evs = json_object["evs"].toArray(); + + for (const QJsonValue event : evs) + { + QString obj = event.toString(); + qDebug() << " Inserting " << obj << " to its trigger!"; + hash_triggers[json_object["id"].toString()]->addEvents(hash_events[obj]); + qDebug() << " Success!"; + } + } + + if (json_object.contains("examine_dialogue")) + { + qDebug() << " Trigger " << json_object["id"].toString() << " contains examination dialogue!"; + + const QString dialogue_tag = json_object["examine_dialogue"].toString(); + hash_triggers[json_object["id"].toString()]->setExaminationDialogueEvent(hash_events[dialogue_tag]); + } + } +} + +// * * * * * * * * * * * * * * SETUP STUFF * * * * * * * * * * * * * * +// +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +void QWLevelBuilder::setupTriggers(const QJsonArray &array) +{ + qDebug() << "Setup the triggers!"; + + QJsonObject json_object; + + for (const QJsonValue &json_value : array) + { + json_object = json_value.toObject(); + + std::shared_ptr new_trigger; + + // check if the picture exists! + Q_ASSERT(json_object.contains("id")); + QString mask = json_object.contains("mask") ? json_object["mask"].toString() : json_object["id"].toString(); + new_trigger = std::make_shared(mask); + new_trigger->setTag(json_object["id"].toString()); + qDebug() << "! The trigger with id " << json_object["id"].toString() << " was created."; + + // setup the position on the scene + if (json_object.contains("x") && json_object.contains("y")) { + new_trigger->setPos(json_object["x"].toDouble(), json_object["y"].toDouble()); + qDebug() << " The trigger was moved to " + << new_trigger->x() << " " << new_trigger->y(); + } + + hash_triggers.insert(json_object["id"].toString(), new_trigger); + qDebug() << " The trigger was inserted into its map.\n - - -"; + } +} + +void QWLevelBuilder::setupEvents(const QJsonArray &array) +{ + qDebug() << "Setup the events!"; + + std::unique_ptr factory = std::make_unique(this); + + QJsonObject json_object; + QList list_later_events; + + for (const QJsonValue &json_value : array) + { + json_object = json_value.toObject(); + + std::shared_ptr new_event; + + // check if the id exists! + Q_ASSERT(json_object.contains("id")); + qDebug() << "! The event with id " << json_object["id"].toString() << " is about to create."; + + // independent events + Q_ASSERT(json_object.contains("type")); + int type = json_object["type"].toInt(); + + switch (type) + { + case (EVENT_TYPE::CHANGE_LOCATION): + new_event = factory->createChangeLocEvent(json_object); + break; + case (EVENT_TYPE::DELETE_FROM_INVENTORY): + new_event = factory->createDeleteItEvent(json_object); + break; + case (EVENT_TYPE::PICKUP_ITEM): + new_event = factory->createPickupItEvent(json_object); + break; + case (EVENT_TYPE::END_LEVEL): + new_event = factory->createEndLevelEvent(json_object); + break; + case (EVENT_TYPE::CHANGE_TRIGGER_PROPERTIES): + new_event = factory->createChangeTrProperts(json_object); + break; + case (EVENT_TYPE::NEW_GAME): + new_event = factory->createNewGameEvent(json_object); + break; + case (EVENT_TYPE::QUIT_GAME): + new_event = factory->createQuitGameEvent(json_object); + break; + case (EVENT_TYPE::START_DIALOGUE): + new_event = factory->createStartDlgEvent(json_object); + break; + case (EVENT_TYPE::ADD_TRIGGER): + new_event = factory->createAddTrEvent(json_object); + break; + case (EVENT_TYPE::REMOVE_TRIGGER): + new_event = factory->createRemoveTrEvent(json_object); + break; + default: + list_later_events.append(json_object); + continue; + } + + new_event->setTag(json_object["id"].toString()); + hash_events.insert(json_object["id"].toString(), new_event); + } + + // Some events work with other events, so we need to initialize all + // independent events earlier than these + for (const auto &later_event : list_later_events) { + + int type = later_event["type"].toInt(); + std::shared_ptr new_event; + + switch (type) + { + case (EVENT_TYPE::SWITCH_EVENTS): // switch events + new_event = factory->createSwitchEventsEvent(later_event); + break; + default: + Q_ASSERT(false); + continue; + } + + new_event->setTag(later_event["id"].toString()); + hash_events.insert(later_event["id"].toString(), new_event); + } +} + +void QWLevelBuilder::setupDialogues(const QJsonArray &array) +{ + qDebug() << "Setup the dialogues!"; + + std::unique_ptr factory = std::make_unique(this); + + QJsonObject json_object; + + for (const QJsonValue &json_value : array) + { + json_object = json_value.toObject(); + + std::shared_ptr new_dialogue; + + // check if the id exists! + Q_ASSERT(json_object.contains("id")); + qDebug() << "! The dialogue with id " << json_object["id"].toString() << " is about to create."; + + Q_ASSERT(json_object.contains("type")); + int type = json_object["type"].toInt(); + + switch (type) + { + case (DIALOGUE_TYPE::TEXT): + new_dialogue = factory->createTextDialogue(json_object); + break; + case (DIALOGUE_TYPE::WIDGET): + new_dialogue = factory->createWidgetDialogue(json_object); + break; + default: + Q_ASSERT(false); + } + + new_dialogue->setTag(json_object["id"].toString()); + hash_dialogues.insert(json_object["id"].toString(), new_dialogue); + } +} + +void QWLevelBuilder::setupLocations(const QJsonArray &array) +{ + qDebug() << "Setup the locations!"; + + QJsonObject json_object; + + for (const QJsonValue & json_value : array) + { + json_object = json_value.toObject(); + + std::shared_ptr new_location; + + Q_ASSERT(json_object.contains("id")); + qDebug() << "! The level with id " << json_object["id"].toString() << " was created."; + new_location = std::make_shared(); + new_location->setTag(json_object["id"].toString()); + + // check and add the pixmap triggers + Q_ASSERT(json_object.contains("triggers")); + if (json_object.contains("triggers")) { + + qDebug() << " Found triggers!"; + QJsonArray loc_triggers = json_value["triggers"].toArray(); + + for (const QJsonValue trg : loc_triggers) + { + qDebug() << " Added the trigger " << trg.toString(); + new_location->addTriggers( hash_triggers[trg.toString()] ); + } + } + + if (json_object.contains("triggers")) + new_location->setDiscovered(json_object["discovered"].toBool()); + + // check if it has a background + if (json_object.contains("background")) { + qDebug() << " Here is a background."; + QString b = json_value["background"].toString(); + new_location->addTriggers(hash_triggers[b]); + qDebug() << " Background was added."; + } + + hash_locations.insert(json_object["id"].toString(), new_location); + qDebug() << " The level was inserted into its map.\n - - -"; + } +} diff --git a/features/qw_levelbuilder.h b/features/qw_levelbuilder.h new file mode 100644 index 0000000..1933460 --- /dev/null +++ b/features/qw_levelbuilder.h @@ -0,0 +1,78 @@ +#ifndef LEVELBUILDER_H +#define LEVELBUILDER_H + +#include +#include +#include +#include +#include +#include +#include + +/* QWLevelBuilder + * Saves and loads game sessions by managing dependent json files. */ + +struct GameFeatures; + +class QWScene; +class QWAbstractEvent; +class QWAbstractGameDialogue; +class QWInventoryManager; +class QWTextDialogueManager; +class QWWidgetDialogueManager; +class QWSoundPlayer; +class QWTrigger; +class QWLocation; + +class QWLevelBuilder final : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QWLevelBuilder) +friend class QWEventFactory; +friend class QWDialogueFactory; +private: + QDir save_dir; + + QString str_profile_filename; + QString str_current_profile; + QString str_current_level; + + QJsonObject level; + + QWInventoryManager *ptr_inventory; + QWScene *ptr_scene; + QWSoundPlayer *ptr_soundplayer; + QWTextDialogueManager *ptr_text_dlg; + QWWidgetDialogueManager *ptr_widget_dlg; + + QHash> hash_events; + QHash> hash_triggers; + QHash> hash_dialogues; + QHash> hash_locations; + + std::shared_ptr init_trigger; + + void setupTriggers(const QJsonArray &array); + void setupEvents(const QJsonArray &array); + void linkEvents(const QJsonArray &array); + void setupDialogues(const QJsonArray &array); + void setupLocations(const QJsonArray &array); + + void init(const QJsonObject & savefile); + +public: + explicit QWLevelBuilder(QObject *parent = nullptr); + + void setGameFeatures(std::unique_ptr &features); + void initLevel(const QString &lvlname); + void initSaveProfile(const QString &filename, const QString &profilename); + void initMenuLevel(); + + static QString strInitLevel() noexcept; + +public slots: + void saveGame( ); + void loadGame(/*int on_id*/); +}; + +#endif // LEVELBUILDER_H diff --git a/features/qw_soundplayer.cpp b/features/qw_soundplayer.cpp new file mode 100644 index 0000000..5762aaa --- /dev/null +++ b/features/qw_soundplayer.cpp @@ -0,0 +1,121 @@ +#include "qw_soundplayer.h" + +QWSoundPlayer::QWSoundPlayer(QObject *parent) : + QObject(parent), + i_volume(100), + b_muted(false), + player_loop(new QMediaPlayer(this)), + playlist_loop(new QMediaPlaylist(this)), + player_single(new QMediaPlayer(this)), + player_music(new QMediaPlayer(this)), + playlist_music(new QMediaPlaylist(this)) +{ + // Music + playlist_music->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); + playlist_music->setCurrentIndex(0); + + player_music->setAudioRole(QAudio::MusicRole); + player_music->setPlaylist(playlist_music); + QObject::connect(this, + SIGNAL(transferSetMuteness(bool)), + player_music, + SLOT(setMuted(bool))); + QObject::connect(this, + SIGNAL(transferAdjustVolume(int)), + player_music, + SLOT(setVolume(int))); + QObject::connect(this, + SIGNAL(playMusic()), + player_music, + SLOT(play())); + + // Loop sounds + playlist_loop->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); + playlist_loop->setCurrentIndex(0); + + player_loop->setAudioRole(QAudio::GameRole); + player_loop->setPlaylist(playlist_loop); + QObject::connect(this, + SIGNAL(transferSetMuteness(bool)), + player_loop, + SLOT(setMuted(bool))); + QObject::connect(this, + SIGNAL(transferAdjustVolume(int)), + player_loop, + SLOT(setVolume(int))); + QObject::connect(this, + SIGNAL(playLoop()), + player_loop, + SLOT(play())); + + // Single sounds + player_single->setAudioRole(QAudio::GameRole); + QObject::connect(this, + SIGNAL(transferSetMuteness(bool)), + player_single, + SLOT(setMuted(bool))); + QObject::connect(this, + SIGNAL(transferAdjustVolume(int)), + player_single, + SLOT(setVolume(int))); + QObject::connect(this, + SIGNAL(playSingle()), + player_single, + SLOT(play())); +} + +int QWSoundPlayer::addMusic(const QString &path) +{ + playlist_music->addMedia(QUrl::fromLocalFile(path)); + playlist_music->setCurrentIndex(playlist_music->currentIndex() + 1); + + return playlist_music->currentIndex() - 1; +} + +bool QWSoundPlayer::isMuted() const noexcept +{ + return b_muted; +} + +int QWSoundPlayer::volume() const noexcept +{ + return i_volume; +} + +void QWSoundPlayer::playSound(QMediaContent *sound) +{ + player_single->stop(); + player_single->setMedia(*sound); + emit playSingle(); +} + +void QWSoundPlayer::playMusic(const int index) +{ + playlist_music->setCurrentIndex(index); + emit playMusic(); +} + +void QWSoundPlayer::stopMusic() +{ + player_music->stop(); +} + +//////////////////////// + +void QWSoundPlayer::setMuteness(bool mute) noexcept +{ + b_muted = mute; + emit transferSetMuteness(b_muted); +} + +void QWSoundPlayer::adjustVolume(int vol) noexcept +{ + i_volume = vol; + emit transferAdjustVolume(i_volume); +} + +void QWSoundPlayer::onEndLevel() noexcept +{ + playlist_music->clear(); + playlist_music->setCurrentIndex(0); +} diff --git a/features/qw_soundplayer.h b/features/qw_soundplayer.h new file mode 100644 index 0000000..637776f --- /dev/null +++ b/features/qw_soundplayer.h @@ -0,0 +1,61 @@ +#ifndef SOUNDPLAYER_H +#define SOUNDPLAYER_H + +#include +#include +#include + +///////////////////////////////////////////////////////////////// +//// DOESN'T WORK! Has to be reworked in master-merge 1.2 +//// https://trello.com/c/KFUhEbYh/62-reimplement-sound-system + +class QWSoundPlayer final : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QWSoundPlayer) +private: + int i_volume; + bool b_muted; + + //////////////////////// + + //////// + //// Background sounds in loop. + QMediaPlayer *player_loop; + QMediaPlaylist *playlist_loop; + //////// + //// Sounds which should play once per call. + QMediaPlayer *player_single; + //////// + //// Music is music. + QMediaPlayer *player_music; + QMediaPlaylist *playlist_music; + +public: + explicit QWSoundPlayer(QObject *parent = nullptr); + + int addMusic(const QString &path); + + inline bool isMuted() const noexcept; + inline int volume() const noexcept; + void playSound(QMediaContent *sound); + void playMusic(const int index); + void stopMusic(); + +//////////////////////// + +public slots: + void setMuteness(bool mute) noexcept; + void adjustVolume(int vol) noexcept; + void onEndLevel() noexcept; + +signals: + void transferSetMuteness(bool); + void transferAdjustVolume(int); + void playLoop(); + void playSingle(); + void playMusic(); + +}; + +#endif // SOUNDPLAYER_H diff --git a/features/qw_statemachine.cpp b/features/qw_statemachine.cpp new file mode 100644 index 0000000..2b37598 --- /dev/null +++ b/features/qw_statemachine.cpp @@ -0,0 +1,15 @@ +#include "qw_statemachine.h" + +QWStateMachine::QWStateMachine(QObject *parent) : + QStateMachine(parent) +{} + +void QWStateMachine::registerState(QString &&str, QState *state) noexcept +{ + hash_states.insert(str, state); +} + +QState *QWStateMachine::stateByKey(QString &&str) noexcept +{ + return hash_states[str]; +} diff --git a/features/qw_statemachine.h b/features/qw_statemachine.h new file mode 100644 index 0000000..af27e88 --- /dev/null +++ b/features/qw_statemachine.h @@ -0,0 +1,23 @@ +#ifndef STATEMACHINE_H +#define STATEMACHINE_H + +#include + +/* QWStateMachine + * Inherited realization of qt state machine for simpler state managment. */ + +class QWStateMachine final : public QStateMachine +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QWStateMachine) +private: + QHash hash_states; + +public: + explicit QWStateMachine(QObject *parent = nullptr); + + void registerState(QString &&str, QState *state) noexcept; + QState *stateByKey(QString &&str) noexcept; +}; + +#endif // STATEMACHINE_H diff --git a/features/qw_textdialoguemanager.cpp b/features/qw_textdialoguemanager.cpp new file mode 100644 index 0000000..a9ee120 --- /dev/null +++ b/features/qw_textdialoguemanager.cpp @@ -0,0 +1,60 @@ +#include "models/dialogues/qw_textdialogue.h" +#include "qw_textdialoguemanager.h" + +QWTextDialogueManager::QWTextDialogueManager(QWScene *scene) : + QWAbstractDialogueManager(scene), + p_frametext(new QGraphicsSimpleTextItem) +{ + p_frametext->setFont(QFont("Arial", 25)); + p_frametext->setBrush(QBrush(Qt::white)); + p_frametext->setPos(12, 12); + scene->addItem(p_frametext); +} + +QWTextDialogueManager::~QWTextDialogueManager() +{ + delete p_frametext; +} + +void QWTextDialogueManager::activateDialogue(const std::shared_ptr &dialogue) +{ + /* Moving to text dialogue state. + * Make the panel with text visible. + * Game freezes until when player reads all + * the frames. */ + + ptr_text_dialogue = std::dynamic_pointer_cast(dialogue); + + connect(ptr_scene, SIGNAL(signalClickDialogue(MouseButton)), this, SLOT(onClicked(MouseButton))); + + emit onEntryDialogueTransition(); + + p_frametext->show(); + + // Show the first page of active dialogue. + p_frametext->setText(ptr_text_dialogue->currentText()); +} + +//////////////////////// + +void QWTextDialogueManager::setDialoguePanel(const std::shared_ptr &panel) noexcept +{ + p_textbox = panel; +} + +void QWTextDialogueManager::onClicked(MouseButton mouse_button) +{ + const bool has_next_pages = ptr_text_dialogue->toNextPage(); + + if (!has_next_pages || mouse_button == MouseButton::RIGHT) { + p_frametext->hide(); + ptr_text_dialogue->resetPage(); + ptr_text_dialogue->onExit(static_cast(mouse_button)); + + disconnect(ptr_scene, SIGNAL(signalClickDialogue(MouseButton)), this, SLOT(onClicked(MouseButton))); + + emit onLeaveDialogueTransition(); + + } else + p_frametext->setText(ptr_text_dialogue->currentText()); +} diff --git a/features/qw_textdialoguemanager.h b/features/qw_textdialoguemanager.h new file mode 100644 index 0000000..0513f0a --- /dev/null +++ b/features/qw_textdialoguemanager.h @@ -0,0 +1,37 @@ +#ifndef DIALOGUEMANAGER_H +#define DIALOGUEMANAGER_H + +#include "qw_abstractdialoguemanager.h" +#include +#include +#include + +/* QWTextDialogueManager + * Controls text game-freezing dialogues. */ + +class QWTextDialogue; + +class QWTextDialogueManager final : public QWAbstractDialogueManager +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QWTextDialogueManager) +private: + std::shared_ptr ptr_text_dialogue; + std::shared_ptr p_textbox; + QGraphicsSimpleTextItem *p_frametext; + +public: + explicit QWTextDialogueManager(QWScene *scene = nullptr); + virtual ~QWTextDialogueManager() override; + + virtual void activateDialogue(const std::shared_ptr &dialogue) override; + + //////////////////////// + + void setDialoguePanel(const std::shared_ptr &panel) noexcept; + +public slots: + virtual void onClicked(MouseButton mouse_button) override; +}; + +#endif // DIALOGUEMANAGER_H diff --git a/features/qw_widgetdialoguemanager.cpp b/features/qw_widgetdialoguemanager.cpp new file mode 100644 index 0000000..cfa5304 --- /dev/null +++ b/features/qw_widgetdialoguemanager.cpp @@ -0,0 +1,50 @@ +#include "models/dialogues/qw_widgetdialogue.h" +#include "qw_widgetdialoguemanager.h" +#include "qw_globalmetadata.h" +#include +#include +#include + +QWWidgetDialogueManager::QWWidgetDialogueManager(QWScene *scene) : + QWAbstractDialogueManager(scene) +{} + +QWWidgetDialogueManager::~QWWidgetDialogueManager() +{} + +void QWWidgetDialogueManager::activateDialogue(const std::shared_ptr &dialogue) +{ + widget_dialogue = std::dynamic_pointer_cast(dialogue); + + connect(ptr_scene, SIGNAL(signalClickDialogue(MouseButton)), this, SLOT(onClicked(MouseButton))); + + emit onEntryDialogueTransition(); + + quick_view = new QQuickWidget; + // Moving dialogue model into qml widget to handle the properties from inside + quick_view->engine()->rootContext()->setContextProperty("dialogue", widget_dialogue.get()); + quick_view->setSource(QUrl::fromLocalFile(widget_dialogue->qmlPath())); + + QObject::connect(quick_view, SIGNAL(destroyed()), this, SLOT(onDestroyingWidget())); +} + +void QWWidgetDialogueManager::onClicked(MouseButton mouse_button) +{ + if (mouse_button == MouseButton::RIGHT) + quick_view->close(); +} + +void QWWidgetDialogueManager::onDestroyingWidget() +{ + // Testing + qDebug() << "Exit code:" << widget_dialogue->exitCode(); + + disconnect(ptr_scene, SIGNAL(signalClickDialogue(MouseButton)), this, SLOT(onClicked(MouseButton))); + + if (widget_dialogue->isCustomStringToSave()) + QWGlobalMetadata::setValue(widget_dialogue->tag(), widget_dialogue->customString()); + + widget_dialogue->onExit(widget_dialogue->exitCode()); + + emit onLeaveDialogueTransition(); +} diff --git a/features/qw_widgetdialoguemanager.h b/features/qw_widgetdialoguemanager.h new file mode 100644 index 0000000..2ee4cde --- /dev/null +++ b/features/qw_widgetdialoguemanager.h @@ -0,0 +1,28 @@ +#ifndef QWWIDGETDIALOGUEMANAGER_H +#define QWWIDGETDIALOGUEMANAGER_H + +#include "qw_abstractdialoguemanager.h" + +class QWWidgetDialogue; +class QQuickWidget; + +class QWWidgetDialogueManager final : public QWAbstractDialogueManager +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QWWidgetDialogueManager) +private: + std::shared_ptr widget_dialogue; + QQuickWidget *quick_view; + +public: + explicit QWWidgetDialogueManager(QWScene *scene = nullptr); + virtual ~QWWidgetDialogueManager() override; + + virtual void activateDialogue(const std::shared_ptr &dialogue) override; + +public slots: + virtual void onClicked(MouseButton mouse_button) override; + void onDestroyingWidget(); +}; + +#endif // QWWIDGETDIALOGUEMANAGER_H diff --git a/game.cpp b/game.cpp new file mode 100644 index 0000000..cccf22f --- /dev/null +++ b/game.cpp @@ -0,0 +1,114 @@ +#include "game.h" +#include "features/gamefeatures.h" +#include "features/qw_levelbuilder.h" +#include "features/qw_statemachine.h" + +Game::Game() +{ + game_features = std::make_unique(this); + + auto &[view, scene, sound_player, inventory, builder, text_dlg, widget_dlg] = *game_features; + + builder->setGameFeatures(game_features); + + // Connecting all the UI controls to their features + for (auto &widg : scene->inventoryWidgets()) + widg->onConnect(game_features); + + connect(scene, SIGNAL(signalClickInventory()), inventory, SLOT(onClicked())); + + view->setFixedSize(static_cast(scene->sceneRect().width() ), + static_cast(scene->sceneRect().height())); + + builder->initSaveProfile("test", "test"); + builder->initLevel(QWLevelBuilder::strInitLevel()); + buildStateMachine(); + buildHotKeys(); +} + +Game::~Game() = default; + +void Game::buildStateMachine() +{ + auto *view = game_features->ptr_view; + auto *scene = game_features->ptr_scene; + + QWStateMachine *state_machine = new QWStateMachine(view); + + // Connecting UI elements to global state machine + for (auto &widg : scene->inventoryWidgets()) + widg->onBuildingStateMachine(state_machine, game_features); + + QState *state_menu = new QState; + state_machine->registerState("state_menu", state_menu); + connect(state_menu, SIGNAL(entered()), game_features->ptr_scene, SLOT(onEntryMenu())); + + const auto &state_gameplay = state_machine->stateByKey("state_gameplay"); + + QSignalTransition *enter_gameplay_transition = new QSignalTransition(scene, SIGNAL(signalLeaveMenu())); + enter_gameplay_transition->setTargetState(state_gameplay); + state_menu->addTransition(enter_gameplay_transition); + + QSignalTransition *leave_gameplay_transition = new QSignalTransition(scene, SIGNAL(signalEnterMenu())); + leave_gameplay_transition->setTargetState(state_menu); + state_gameplay->addTransition(leave_gameplay_transition); + + QState *state_examination = new QState; + state_machine->registerState("state_examination", state_examination); + + QSignalTransition *enter_examination_transition = new QSignalTransition(scene, SIGNAL(signalEnterExamination())); + enter_examination_transition->setTargetState(state_examination); + state_gameplay->addTransition(enter_examination_transition); + + QSignalTransition *leave_examination_transition = new QSignalTransition(scene, SIGNAL(signalLeaveExamination())); + leave_examination_transition->setTargetState(state_gameplay); + state_examination->addTransition(leave_examination_transition); + + QState *state_widget_dialogue = new QState; + state_machine->registerState("state_widget_dialogue", state_widget_dialogue); + connect(state_widget_dialogue, SIGNAL(entered()), game_features->ptr_scene, SLOT(onEntryDialogue())); + + QSignalTransition *enter_widget_dialogue_transition = new QSignalTransition(game_features->ptr_widget_dlg, SIGNAL(onEntryDialogueTransition())); + enter_widget_dialogue_transition->setTargetState(state_widget_dialogue); + state_menu->addTransition(enter_widget_dialogue_transition); + state_gameplay->addTransition(enter_widget_dialogue_transition); + + QSignalTransition *leave_widget_dialogue_transition = new QSignalTransition(game_features->ptr_widget_dlg, SIGNAL(onLeaveDialogueTransition())); + leave_widget_dialogue_transition->setTargetState(state_gameplay); + state_widget_dialogue->addTransition(leave_widget_dialogue_transition); + + state_machine->addState(state_menu); + state_machine->addState(state_widget_dialogue); + state_machine->addState(state_examination); + state_machine->setInitialState(state_gameplay); + state_machine->start(); +} + +void Game::buildHotKeys() +{ + auto *builder = game_features->ptr_builder; + auto *view = game_features->ptr_view; + + /* For quick exit */ + auto quit_action = new QAction(tr("E&xit"), this); + quit_action->setShortcut(tr("Ctrl+Q")); + connect(quit_action, SIGNAL(triggered()), qApp, SLOT(quit())); + view->addAction(quit_action); + + /* For quick save */ + auto save_action = new QAction(tr("S&ave"), this); + save_action->setShortcut(tr("Ctrl+S")); + connect(save_action, SIGNAL(triggered()), builder, SLOT(saveGame())); + view->addAction(save_action); + + /* For quick load */ + auto load_action = new QAction(tr("L&oad"), this); + load_action->setShortcut(tr("Ctrl+L")); + connect(load_action, SIGNAL(triggered()), builder, SLOT(loadGame())); + view->addAction(load_action); +} + +void Game::start() +{ + game_features->ptr_view->show(); +} diff --git a/models/dialogues/qw_abstractgamedialogue.h b/models/dialogues/qw_abstractgamedialogue.h new file mode 100644 index 0000000..e680506 --- /dev/null +++ b/models/dialogues/qw_abstractgamedialogue.h @@ -0,0 +1,38 @@ +#ifndef QWABSTRACTGAMEDIALOGUE_H +#define QWABSTRACTGAMEDIALOGUE_H + +#include + +#include "models/events/qw_abstractevent.h" + +/* QWAbstractGameDialogue + * Interface for all in-game dialogues. */ + +class QWAbstractGameDialogue : public QWTagHolder +{ +protected: + QList>> list_events; + +public: + QWAbstractGameDialogue(); + virtual ~QWAbstractGameDialogue() override = 0; + + virtual void onExit(int code); + virtual void setAllEvents(const QList>> &evs); + virtual void addEventsList(const QList> &evs); + + //////////////////////// + + virtual void writeToJson(QJsonObject &savejson) override = 0; +}; + +enum DIALOGUE_TYPE +{ + // Pure visual-novel-like text + TEXT, + + // External widget with inner logic described with qml + WIDGET +}; + +#endif // QWABSTRACTGAMEDIALOGUE_H diff --git a/models/dialogues/qw_textdialogue.cpp b/models/dialogues/qw_textdialogue.cpp new file mode 100644 index 0000000..a7b32a1 --- /dev/null +++ b/models/dialogues/qw_textdialogue.cpp @@ -0,0 +1,48 @@ +#include "qw_textdialogue.h" + +QWTextDialogue::QWTextDialogue(const QStringList &pages) +{ + setPages(pages); +} + +//////////////////////// + +bool QWTextDialogue::toNextPage() noexcept +{ + const bool has_next = ((it_current_page + 1) != list_pages.end()); + + if (has_next) + ++it_current_page; + + return has_next; +} + +void QWTextDialogue::resetPage() noexcept +{ + it_current_page = list_pages.begin(); +} + +void QWTextDialogue::setPages(const QStringList &pages) noexcept +{ + list_pages = pages; + Q_ASSERT(!list_pages.empty()); + + it_current_page = list_pages.begin(); +} + +QStringList QWTextDialogue::pages() const noexcept +{ + return list_pages; +} + +QString QWTextDialogue::currentText() const noexcept +{ + return *it_current_page; +} + +//////////////////////// + +void QWTextDialogue::writeToJson(QJsonObject &savejson) +{ + Q_UNUSED(savejson) +} diff --git a/models/dialogues/qw_textdialogue.h b/models/dialogues/qw_textdialogue.h new file mode 100644 index 0000000..aea8ad6 --- /dev/null +++ b/models/dialogues/qw_textdialogue.h @@ -0,0 +1,35 @@ +#ifndef QWTEXTDIALOGUE_H +#define QWTEXTDIALOGUE_H + +#include +#include "qw_abstractgamedialogue.h" + +/* QWTextDialogue + * Provides text visual-novel-lide dialogue. */ + +class QWTextDialogue : public QWAbstractGameDialogue +{ +protected: + QStringList list_pages; + QStringList::iterator it_current_page; + +public: + QWTextDialogue(const QStringList &pages); + virtual ~QWTextDialogue() override {} + + //////////////////////// + + bool toNextPage() noexcept; + void resetPage() noexcept; + + void setPages(const QStringList &pages) noexcept; + QStringList pages() const noexcept; + + QString currentText() const noexcept; + + //////////////////////// + + virtual void writeToJson(QJsonObject &savejson) override; +}; + +#endif // QWTEXTDIALOGUE_H diff --git a/models/dialogues/qw_widgetdialogue.cpp b/models/dialogues/qw_widgetdialogue.cpp new file mode 100644 index 0000000..455a8b0 --- /dev/null +++ b/models/dialogues/qw_widgetdialogue.cpp @@ -0,0 +1,63 @@ +#include "qw_widgetdialogue.h" + +QWWidgetDialogue::QWWidgetDialogue(const QString &path) : + qml_path(path) +{ + Q_ASSERT(!qml_path.isNull()); + // Filepath fixes + if (!qml_path.endsWith(".qml")) + qml_path += ".qml"; + if (!qml_path.startsWith("qml/")) + qml_path = "qml/" + qml_path; +} + +void QWWidgetDialogue::onExit(int code) +{ + Q_UNUSED(code) +} + +//////////////////////// + +QString QWWidgetDialogue::qmlPath() const +{ + return qml_path; +} + +QString QWWidgetDialogue::customString() const +{ + return custom_string; +} + +void QWWidgetDialogue::setCustomString(const QString &str) +{ + custom_string = str; +} + +int QWWidgetDialogue::exitCode() const +{ + return exit_code; +} + +void QWWidgetDialogue::setExitCode(int code) +{ + if (code != exit_code) + exit_code = code; +} + +bool QWWidgetDialogue::isCustomStringToSave() const +{ + return to_save_string; +} + +void QWWidgetDialogue::setCustomStringToSave(bool to_save) +{ + to_save_string = to_save; +} + + +//////////////////////// + +void QWWidgetDialogue::writeToJson(QJsonObject &savejson) +{ + Q_UNUSED(savejson) +} diff --git a/models/dialogues/qw_widgetdialogue.h b/models/dialogues/qw_widgetdialogue.h new file mode 100644 index 0000000..fd82261 --- /dev/null +++ b/models/dialogues/qw_widgetdialogue.h @@ -0,0 +1,45 @@ +#ifndef WIDGETDIALOGUE_H +#define WIDGETDIALOGUE_H + +#include "qw_abstractgamedialogue.h" +#include + +class QWWidgetDialogue : public QObject, public QWAbstractGameDialogue +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QWWidgetDialogue) + Q_PROPERTY(int exit_code READ exitCode WRITE setExitCode) + Q_PROPERTY(QString custom_string READ customString WRITE setCustomString) + Q_PROPERTY(bool to_save_string READ isCustomStringToSave WRITE setCustomStringToSave) +private: + QString qml_path; + + QString custom_string; + int exit_code; + bool to_save_string; + +public: + explicit QWWidgetDialogue(const QString &path); + virtual ~QWWidgetDialogue() override {} + + void onExit(int code) override; + + //////////////////////// + + QString qmlPath() const; + + QString customString() const; + void setCustomString(const QString &str); + + int exitCode() const; + void setExitCode(int code); + + bool isCustomStringToSave() const; + void setCustomStringToSave(bool to_save); + + //////////////////////// + + void writeToJson(QJsonObject &savejson) override; +}; + +#endif // WIDGETDIALOGUE_H diff --git a/models/events/qw_abstractevent.h b/models/events/qw_abstractevent.h new file mode 100644 index 0000000..0f132cb --- /dev/null +++ b/models/events/qw_abstractevent.h @@ -0,0 +1,43 @@ +#ifndef QUESTEVENT_H +#define QUESTEVENT_H + +#include +#include + +#include +#include "../qw_tagholder.h" + +/* QWAbstractEvent + * Interface for all in-game events. The core of game logic process. */ + +class QWAbstractEvent : public QWTagHolder +{ +public: + QWAbstractEvent(); + virtual ~QWAbstractEvent() override = 0; + + virtual void execute() = 0; + + //////////////////////// + + virtual void writeToJson(QJsonObject &savejson) override = 0; +}; + +enum EVENT_TYPE +{ + CHANGE_LOCATION, + SWITCH_EVENTS, // <- requires other events to be initialized + DELETE_FROM_INVENTORY, + END_LEVEL, + PICKUP_ITEM, + PLAY_SOUND, + PLAY_MUSIC, + CHANGE_TRIGGER_PROPERTIES, + NEW_GAME, + QUIT_GAME, + START_DIALOGUE, + ADD_TRIGGER, + REMOVE_TRIGGER +}; + +#endif // QUESTEVENT_H diff --git a/models/events/qw_abstractinventoryevent.cpp b/models/events/qw_abstractinventoryevent.cpp new file mode 100644 index 0000000..6224ea9 --- /dev/null +++ b/models/events/qw_abstractinventoryevent.cpp @@ -0,0 +1,20 @@ +#include "qw_abstractinventoryevent.h" + +QWAbstractInventoryEvent:: QWAbstractInventoryEvent() +{} + +QWAbstractInventoryEvent::~QWAbstractInventoryEvent() +{} + +//////////////////////// + +void QWAbstractInventoryEvent::setInventoryManager(QWInventoryManager *const inv) noexcept +{ + ptr_inventory = inv; +} + +QWInventoryManager *QWAbstractInventoryEvent::inventoryManager() const noexcept +{ + return ptr_inventory; +} + diff --git a/models/events/qw_abstractinventoryevent.h b/models/events/qw_abstractinventoryevent.h new file mode 100644 index 0000000..a7a8942 --- /dev/null +++ b/models/events/qw_abstractinventoryevent.h @@ -0,0 +1,31 @@ +#ifndef QUESTABSTRACTINVENTORYEVENT_H +#define QUESTABSTRACTINVENTORYEVENT_H + +#include "features/qw_inventorymanager.h" +#include "qw_abstractevent.h" + +/* QWAbstractInventoryEvent + * Interface for all in-game inventory events. */ + +class QWAbstractInventoryEvent : public QWAbstractEvent +{ +protected: + QWInventoryManager *ptr_inventory; + +public: + QWAbstractInventoryEvent(); + virtual ~QWAbstractInventoryEvent() override = 0; + + virtual void execute() override = 0; + + //////////////////////// + + void setInventoryManager(QWInventoryManager *const inv) noexcept; + QWInventoryManager *inventoryManager() const noexcept; + + //////////////////////// + + virtual void writeToJson(QJsonObject &savejson) override = 0; +}; + +#endif // QUESTABSTRACTINVENTORYEVENT_H diff --git a/models/events/qw_abstractlevelevent.cpp b/models/events/qw_abstractlevelevent.cpp new file mode 100644 index 0000000..f018571 --- /dev/null +++ b/models/events/qw_abstractlevelevent.cpp @@ -0,0 +1,20 @@ +#include "qw_abstractlevelevent.h" + +QWAbstractLevelEvent:: QWAbstractLevelEvent() +{} + +QWAbstractLevelEvent::~QWAbstractLevelEvent() +{} + +//////////////////////// + +void QWAbstractLevelEvent::setLevelBuilder(QWLevelBuilder *const buil) noexcept +{ + ptr_builder = buil; +} + +QWLevelBuilder *QWAbstractLevelEvent::levelBuilder() const noexcept +{ + return ptr_builder; +} + diff --git a/models/events/qw_abstractlevelevent.h b/models/events/qw_abstractlevelevent.h new file mode 100644 index 0000000..8216b12 --- /dev/null +++ b/models/events/qw_abstractlevelevent.h @@ -0,0 +1,31 @@ +#ifndef QUESTABSTRACTLEVELEVENT_H +#define QUESTABSTRACTLEVELEVENT_H + +#include "features/qw_levelbuilder.h" +#include "qw_abstractevent.h" + +/* QWAbstractLevelEvent + * Interface for all in-game events which control save files and levels. */ + +class QWAbstractLevelEvent : public QWAbstractEvent +{ +protected: + QWLevelBuilder *ptr_builder; + +public: + QWAbstractLevelEvent(); + virtual ~QWAbstractLevelEvent() override = 0; + + virtual void execute() override = 0; + + //////////////////////// + + void setLevelBuilder(QWLevelBuilder *const buil) noexcept; + QWLevelBuilder *levelBuilder() const noexcept; + + //////////////////////// + + virtual void writeToJson(QJsonObject &savejson) override = 0; +}; + +#endif // QUESTABSTRACTLEVELEVENT_H diff --git a/models/events/qw_abstractsceneevent.cpp b/models/events/qw_abstractsceneevent.cpp new file mode 100644 index 0000000..d32b9b1 --- /dev/null +++ b/models/events/qw_abstractsceneevent.cpp @@ -0,0 +1,19 @@ +#include "qw_abstractsceneevent.h" + +QWAbstractSceneEvent:: QWAbstractSceneEvent() +{} + +QWAbstractSceneEvent::~QWAbstractSceneEvent() +{} + +//////////////////////// + +void QWAbstractSceneEvent::setScene(QWScene *const scene) noexcept +{ + ptr_scene = scene; +} + +QWScene *QWAbstractSceneEvent::scene() const noexcept +{ + return ptr_scene; +} diff --git a/models/events/qw_abstractsceneevent.h b/models/events/qw_abstractsceneevent.h new file mode 100644 index 0000000..e35f505 --- /dev/null +++ b/models/events/qw_abstractsceneevent.h @@ -0,0 +1,31 @@ +#ifndef QUESTABSTRACTSCENEEVENT_H +#define QUESTABSTRACTSCENEEVENT_H + +#include "qw_abstractevent.h" +#include "view/qw_scene.h" + +/* QWAbstractSceneEvent + * Interface for all in-game scene events. */ + +class QWAbstractSceneEvent : public QWAbstractEvent +{ +protected: + QWScene *ptr_scene; + +public: + QWAbstractSceneEvent(); + virtual ~QWAbstractSceneEvent() override = 0; + + virtual void execute() override = 0; + + //////////////////////// + + void setScene(QWScene *const scene) noexcept; + QWScene *scene() const noexcept; + + //////////////////////// + + virtual void writeToJson(QJsonObject &savejson) override = 0; +}; + +#endif // QUESTABSTRACTSCENEEVENT_H diff --git a/models/events/qw_abstractsoundevent.cpp b/models/events/qw_abstractsoundevent.cpp new file mode 100644 index 0000000..3e2e904 --- /dev/null +++ b/models/events/qw_abstractsoundevent.cpp @@ -0,0 +1,19 @@ +#include "qw_abstractsoundevent.h" + +QWAbstractSoundEvent:: QWAbstractSoundEvent() +{} + +QWAbstractSoundEvent::~QWAbstractSoundEvent() +{} + +//////////////////////// + +void QWAbstractSoundEvent::setSoundPlayer(QWSoundPlayer *const soundplayer) noexcept +{ + ptr_soundplayer = soundplayer; +} + +QWSoundPlayer *QWAbstractSoundEvent::soundPlayer() const noexcept +{ + return ptr_soundplayer; +} diff --git a/models/events/qw_abstractsoundevent.h b/models/events/qw_abstractsoundevent.h new file mode 100644 index 0000000..fb36dcf --- /dev/null +++ b/models/events/qw_abstractsoundevent.h @@ -0,0 +1,31 @@ +#ifndef QUESTABSTRACTSOUNDEVENT_H +#define QUESTABSTRACTSOUNDEVENT_H + +#include "features/qw_soundplayer.h" +#include "qw_abstractevent.h" + +/* QWAbstractSoundEvent + * Interface for all in-game audio events. */ + +class QWAbstractSoundEvent : public QWAbstractEvent +{ +protected: + QWSoundPlayer *ptr_soundplayer; + +public: + QWAbstractSoundEvent(); + virtual ~QWAbstractSoundEvent() override = 0; + + virtual void execute() override = 0; + + //////////////////////// + + void setSoundPlayer(QWSoundPlayer *const soundplayer) noexcept; + QWSoundPlayer *soundPlayer() const noexcept; + + //////////////////////// + + virtual void writeToJson(QJsonObject &savejson) override = 0; +}; + +#endif // QUESTABSTRACTSOUNDEVENT_H diff --git a/models/events/qw_addtriggerevent.cpp b/models/events/qw_addtriggerevent.cpp new file mode 100644 index 0000000..f49eb21 --- /dev/null +++ b/models/events/qw_addtriggerevent.cpp @@ -0,0 +1,49 @@ +#include "qw_addtriggerevent.h" +#include "models/qw_trigger.h" + +QWAddTriggerEvent::QWAddTriggerEvent(const std::shared_ptr &tr) : + trigger(tr) +{ + Q_ASSERT(trigger); +} + +void QWAddTriggerEvent::execute() +{ + Q_ASSERT(location); + + if (location->triggers().contains(trigger)) + return; + + location->addTriggers(trigger); + + if (location == ptr_scene->currentLocation()) + ptr_scene->addItem(trigger.get()); +} + +//////////////////////// + +void QWAddTriggerEvent::setLocation(const std::shared_ptr &loc) noexcept +{ + location = loc; + Q_ASSERT(location); +} + +void QWAddTriggerEvent::setTrigger(const std::shared_ptr &tr) noexcept +{ + trigger = tr; + Q_ASSERT(trigger); +} + +//////////////////////// + +void QWAddTriggerEvent::writeToJson(QJsonObject &event_data) +{ + event_data.insert("id", tag()); + event_data.insert("type", EVENT_TYPE::ADD_TRIGGER); + event_data.insert("location", location->tag()); + event_data.insert("trigger", trigger->tag()); + + qDebug() << " The add_trigger event:\n" << " id " << tag() + << "\n location :" << location->tag() + << "\n trigger :" << trigger->tag(); +} diff --git a/models/events/qw_addtriggerevent.h b/models/events/qw_addtriggerevent.h new file mode 100644 index 0000000..dabf375 --- /dev/null +++ b/models/events/qw_addtriggerevent.h @@ -0,0 +1,31 @@ +#ifndef QWADDTRIGGER_H +#define QWADDTRIGGER_H + +#include "qw_abstractsceneevent.h" + +/* QWAddTriggerEvent + * Adds a trigger to its location list. */ + +class QWAddTriggerEvent : public QWAbstractSceneEvent +{ +private: + std::shared_ptr trigger; // What to add. + std::shared_ptr location; // Where to add. + +public: + explicit QWAddTriggerEvent(const std::shared_ptr &tr); + virtual ~QWAddTriggerEvent() override {} + + void execute() override; + + //////////////////////// + + void setLocation(const std::shared_ptr &loc) noexcept; + void setTrigger(const std::shared_ptr &tr) noexcept; + + //////////////////////// + + void writeToJson(QJsonObject &event_data) override; +}; + +#endif // QWADDTRIGGER_H diff --git a/models/events/qw_changelocationevent.cpp b/models/events/qw_changelocationevent.cpp new file mode 100644 index 0000000..127816d --- /dev/null +++ b/models/events/qw_changelocationevent.cpp @@ -0,0 +1,52 @@ +#include "qw_changelocationevent.h" +#include "../qw_location.h" +#include "../qw_trigger.h" + +QWChangeLocationEvent::QWChangeLocationEvent(const std::shared_ptr &location) : + target_location(location) +{ + Q_ASSERT(target_location); +} + +void QWChangeLocationEvent::execute() +{ + const QList> &triggers = target_location->triggers(); + + // We clear all the triggers from previous location + ptr_scene->clearLocation(); + ptr_scene->setCurrentLocation(target_location); + + if (const auto & tr = target_location->firstVisitTrigger(); tr && !target_location->discovered()) + tr->activate(); + + target_location->setDiscovered(true); + + int z = 0; + + // The first item will be below the others. The last item - above. + // Triggers[0] must be background. + for (auto &trigger : triggers) + { + trigger->setZValue(-1 * ++z); + ptr_scene->addItem(trigger.get()); + } +} + +//////////////////////// + +void QWChangeLocationEvent::setLocation(const std::shared_ptr &location) noexcept +{ + target_location = location; +} + +//////////////////////// + +void QWChangeLocationEvent::writeToJson(QJsonObject &event_data) +{ + event_data.insert("id", tag()); + event_data.insert("type", EVENT_TYPE::CHANGE_LOCATION); + event_data.insert("location", target_location->tag()); + + qDebug() << " The move_to_location event:\n" << " id " << tag() + << "\n location :" << target_location->tag(); +} diff --git a/models/events/qw_changelocationevent.h b/models/events/qw_changelocationevent.h new file mode 100644 index 0000000..0d092b1 --- /dev/null +++ b/models/events/qw_changelocationevent.h @@ -0,0 +1,32 @@ +#ifndef QUESTCHANGELOCATIONEVENT_H +#define QUESTCHANGELOCATIONEVENT_H + +#include "qw_abstractsceneevent.h" + +/* QWChangeLocationEvent + * Moves player to new location by replacing scene triggers. */ + +class QWTrigger; +class QWLocation; + +class QWChangeLocationEvent : public QWAbstractSceneEvent +{ +private: + std::shared_ptr target_location; + +public: + explicit QWChangeLocationEvent(const std::shared_ptr &location); + virtual ~QWChangeLocationEvent() override {} + + void execute() override; + + //////////////////////// + + void setLocation(const std::shared_ptr &location) noexcept; + + //////////////////////// + + void writeToJson(QJsonObject &event_data) override; +}; + +#endif // QUESTCHANGELOCATIONEVENT_H diff --git a/models/events/qw_endlevelevent.cpp b/models/events/qw_endlevelevent.cpp new file mode 100644 index 0000000..021d099 --- /dev/null +++ b/models/events/qw_endlevelevent.cpp @@ -0,0 +1,25 @@ +#include "qw_endlevelevent.h" + +QWEndLevelEvent::QWEndLevelEvent(const QString &level) : + str_nlevel(level) +{ + Q_ASSERT(!str_nlevel.isEmpty()); +} + +void QWEndLevelEvent::execute() +{ + ptr_builder->saveGame(); + ptr_builder->initLevel(str_nlevel); +} + +//////////////////////// + +void QWEndLevelEvent::writeToJson(QJsonObject &event_data) +{ + event_data.insert("id", tag()); + event_data.insert("type", EVENT_TYPE::END_LEVEL); + event_data.insert("new_level", str_nlevel); + + qDebug() << " The end_level event:\n" << " id " << tag() + << "\n new_level " << str_nlevel; +} diff --git a/models/events/qw_endlevelevent.h b/models/events/qw_endlevelevent.h new file mode 100644 index 0000000..69ec755 --- /dev/null +++ b/models/events/qw_endlevelevent.h @@ -0,0 +1,25 @@ +#ifndef QUESTENDLEVELEVENT_H +#define QUESTENDLEVELEVENT_H + +#include "qw_abstractlevelevent.h" + +/* QWEndLevelEvent + * Sends its signal to QWLevelBuilder whenever it has to load next level. */ + +class QWEndLevelEvent : public QWAbstractLevelEvent +{ +private: + QString str_nlevel; + +public: + explicit QWEndLevelEvent(const QString &level); + virtual ~QWEndLevelEvent() override {} + + void virtual execute() override; + + //////////////////////// + + void writeToJson(QJsonObject &event_data) override; +}; + +#endif // QUESTENDLEVELEVENT_H diff --git a/models/events/qw_newgameevent.cpp b/models/events/qw_newgameevent.cpp new file mode 100644 index 0000000..539ec05 --- /dev/null +++ b/models/events/qw_newgameevent.cpp @@ -0,0 +1,45 @@ +#include +#include "qw_newgameevent.h" +#include "qw_globalmetadata.h" + +QWNewGameEvent::QWNewGameEvent(const QString &savefile) : + str_savefile(savefile) +{ + Q_ASSERT(!str_savefile.isEmpty()); +} + +void QWNewGameEvent::execute() +{ + const QString profile_name = QWGlobalMetadata::valueBy("new_game_dialogue").toString(); + str_savefile = composeFileName(profile_name); + + Q_ASSERT(!profile_name.isEmpty()); + Q_ASSERT(!str_savefile.isEmpty()); + + ptr_builder->initSaveProfile(str_savefile, profile_name); + ptr_builder->initLevel(QWLevelBuilder::strInitLevel()); +} +//////////////////////// + +QString QWNewGameEvent::composeFileName(const QString &str) +{ + //Turn the chosen by user savename into file name. + QString file_name = str; + const QRegExp regexp("[^0-9a-z]+"); + + file_name = file_name.toLower().remove(regexp); + + return file_name; +} + +//////////////////////// + +void QWNewGameEvent::writeToJson(QJsonObject &event_data) +{ + event_data.insert("id", tag()); + event_data.insert("type", EVENT_TYPE::NEW_GAME); + event_data.insert("save_file", str_savefile); + + qDebug() << " The new_game event:\n" << " id " << tag() + << "\n save_file " << str_savefile; +} diff --git a/models/events/qw_newgameevent.h b/models/events/qw_newgameevent.h new file mode 100644 index 0000000..88bf7c7 --- /dev/null +++ b/models/events/qw_newgameevent.h @@ -0,0 +1,27 @@ +#ifndef QUESTNEWGAMEEVENT_H +#define QUESTNEWGAMEEVENT_H + +#include "qw_abstractlevelevent.h" + +/* QWNewGameEvent + * Creates new savefile and begins the game from clear sheet. */ + +class QWNewGameEvent : public QWAbstractLevelEvent +{ +private: + QString str_savefile; + + QString composeFileName(const QString &str); + +public: + explicit QWNewGameEvent(const QString &savefile); + virtual ~QWNewGameEvent() override {} + + void virtual execute() override; + + //////////////////////// + + void writeToJson(QJsonObject &event_data) override; +}; + +#endif // QUESTNEWGAMEEVENT_H diff --git a/models/events/qw_pickupitemevent.cpp b/models/events/qw_pickupitemevent.cpp new file mode 100644 index 0000000..7906f13 --- /dev/null +++ b/models/events/qw_pickupitemevent.cpp @@ -0,0 +1,37 @@ +#include "qw_pickupitemevent.h" +#include "../qw_trigger.h" + +QWPickupItemEvent::QWPickupItemEvent(const std::shared_ptr &tr) : + ptr_inventory_trigger(tr) +{ + Q_ASSERT(ptr_inventory_trigger); +} + +void QWPickupItemEvent::execute() +{ + ptr_inventory->addInventoryIcon(ptr_inventory_trigger); +} + +//////////////////////// + +void QWPickupItemEvent::setInventoryTrigger(const std::shared_ptr &tr) noexcept +{ + ptr_inventory_trigger = tr; +} + +std::shared_ptr QWPickupItemEvent::inventoryTrigger() const noexcept +{ + return ptr_inventory_trigger; +} + +//////////////////////// + +void QWPickupItemEvent::writeToJson(QJsonObject &event_data) +{ + event_data.insert("id", tag()); + event_data.insert("type", EVENT_TYPE::PICKUP_ITEM); + event_data.insert("target", ptr_inventory_trigger->tag()); + + qDebug() << " The pickup_item event:\n" << " id " << tag() + << "\n target " << ptr_inventory_trigger->tag(); +} diff --git a/models/events/qw_playmusicevent.h b/models/events/qw_playmusicevent.h new file mode 100644 index 0000000..fe4decb --- /dev/null +++ b/models/events/qw_playmusicevent.h @@ -0,0 +1,34 @@ +#ifndef QUESTPLAYMUSICEVENT_H +#define QUESTPLAYMUSICEVENT_H + +#include "qw_abstractsoundevent.h" + +///////////////////////////////////////////////////////////////// +//// DOESN'T WORK! Has to be reworked in master-merge 1.2 +//// https://trello.com/c/KFUhEbYh/62-reimplement-sound-system + +class QWPlayMusicEvent : public QWAbstractSoundEvent +{ +private: + QString str_musicpath; + int index; + +public: + explicit QWPlayMusicEvent(const QString &path); + virtual ~QWPlayMusicEvent() override {} + + void execute() override; + + //////////////////////// + + inline void setMusicpath(const QString &path) noexcept; + inline QString musicpath() const noexcept; + + void attachMusic(); + + //////////////////////// + + void writeToJson(QJsonObject &event_data) override; +}; + +#endif // QUESTPLAYMUSICEVENT_H diff --git a/models/events/qw_playsoundevent.cpp b/models/events/qw_playsoundevent.cpp new file mode 100644 index 0000000..48adbbc --- /dev/null +++ b/models/events/qw_playsoundevent.cpp @@ -0,0 +1,24 @@ +#include "qw_playsoundevent.h" + +QWPlaySoundEvent::QWPlaySoundEvent(const QString &path) : + media_sound(new QMediaContent(QUrl("qrc:/res/" + path + ".wav"))) +{ + Q_ASSERT(!media_sound->isNull()); +} + +void QWPlaySoundEvent::execute() +{ + ptr_soundplayer->playSound(media_sound); +} + +//////////////////////// + +void QWPlaySoundEvent::writeToJson(QJsonObject &event_data) +{ + event_data.insert("id", tag()); + event_data.insert("type", EVENT_TYPE::PLAY_SOUND); + //event_data.insert("sound", media_sound->canonicalUrl().toString()); + + //qDebug() << " The play_sound event:\n" << " id " << tag() + //<< "\n sound " << media_sound->canonicalUrl().toString(); +} diff --git a/models/events/qw_playsoundevent.h b/models/events/qw_playsoundevent.h new file mode 100644 index 0000000..4f55326 --- /dev/null +++ b/models/events/qw_playsoundevent.h @@ -0,0 +1,30 @@ +#ifndef QUESTPLAYSOUNDEVENT_H +#define QUESTPLAYSOUNDEVENT_H + +#include +#include +#include "qw_abstractsoundevent.h" + +///////////////////////////////////////////////////////////////// +//// DOESN'T WORK! Has to be reworked in master-merge 1.2 +//// https://trello.com/c/KFUhEbYh/62-reimplement-sound-system + +class QSoundEffect; + +class QWPlaySoundEvent : public QWAbstractSoundEvent +{ +private: + QMediaContent *media_sound; + +public: + explicit QWPlaySoundEvent(const QString &path); + virtual ~QWPlaySoundEvent() override {} + + void execute() override; + + //////////////////////// + + void writeToJson(QJsonObject &event_data) override; +}; + +#endif // QUESTPLAYSOUNDEVENT_H diff --git a/models/events/qw_quitgameevent.cpp b/models/events/qw_quitgameevent.cpp new file mode 100644 index 0000000..7e9a9bb --- /dev/null +++ b/models/events/qw_quitgameevent.cpp @@ -0,0 +1,26 @@ +#include +#include "qw_quitgameevent.h" + +QWQuitGameEvent::QWQuitGameEvent(bool savegame) : + b_savegame(savegame) +{} + +void QWQuitGameEvent::execute() +{ + if (b_savegame) + ptr_builder->saveGame(); + + QCoreApplication::quit(); +} + +//////////////////////// + +void QWQuitGameEvent::writeToJson(QJsonObject &event_data) +{ + event_data.insert("id", tag()); + event_data.insert("type", EVENT_TYPE::QUIT_GAME); + event_data.insert("save_game", b_savegame); + + qDebug() << " The quit_game event:\n" << " id " << tag() + << "\n save_game " << b_savegame; +} diff --git a/models/events/qw_quitgameevent.h b/models/events/qw_quitgameevent.h new file mode 100644 index 0000000..0b22295 --- /dev/null +++ b/models/events/qw_quitgameevent.h @@ -0,0 +1,25 @@ +#ifndef QUESTQUITGAMEEVENT_H +#define QUESTQUITGAMEEVENT_H + +#include "qw_abstractlevelevent.h" + +/* QWQuitGameEvent + * Immediately terminates current session. */ + +class QWQuitGameEvent : public QWAbstractLevelEvent +{ +private: + bool b_savegame; + +public: + explicit QWQuitGameEvent(bool savegame = false); + virtual ~QWQuitGameEvent() override {} + + void virtual execute() override; + + //////////////////////// + + void writeToJson(QJsonObject &event_data) override; +}; + +#endif // QUESTQUITGAMEEVENT_H diff --git a/models/events/qw_removetriggerevent.cpp b/models/events/qw_removetriggerevent.cpp new file mode 100644 index 0000000..662fb42 --- /dev/null +++ b/models/events/qw_removetriggerevent.cpp @@ -0,0 +1,49 @@ +#include "qw_removetriggerevent.h" +#include "models/qw_trigger.h" + +QWRemoveTriggerEvent::QWRemoveTriggerEvent(const std::shared_ptr &tr) : + trigger(tr) +{ + Q_ASSERT(trigger); +} + +void QWRemoveTriggerEvent::execute() +{ + Q_ASSERT(location); + + if (!location->triggers().contains(trigger)) + return; + + location->removeTrigger(trigger); + + if (location == ptr_scene->currentLocation()) + ptr_scene->removeItem(trigger.get()); +} + +//////////////////////// + +void QWRemoveTriggerEvent::setLocation(const std::shared_ptr &loc) noexcept +{ + location = loc; + Q_ASSERT(location); +} + +void QWRemoveTriggerEvent::setTrigger(const std::shared_ptr &tr) noexcept +{ + trigger = tr; + Q_ASSERT(trigger); +} + +//////////////////////// + +void QWRemoveTriggerEvent::writeToJson(QJsonObject &event_data) +{ + event_data.insert("id", tag()); + event_data.insert("type", EVENT_TYPE::REMOVE_TRIGGER); + event_data.insert("location", location->tag()); + event_data.insert("trigger", trigger->tag()); + + qDebug() << " The remove_trigger event:\n" << " id " << tag() + << "\n location :" << location->tag() + << "\n trigger :" << trigger->tag(); +} diff --git a/models/events/qw_removetriggerevent.h b/models/events/qw_removetriggerevent.h new file mode 100644 index 0000000..7cac63b --- /dev/null +++ b/models/events/qw_removetriggerevent.h @@ -0,0 +1,31 @@ +#ifndef QWREMOVETRIGGER_H +#define QWREMOVETRIGGER_H + +#include "qw_abstractsceneevent.h" + +/* QWRemoveTriggerEvent + * Removes a trigger from its location list. */ + +class QWRemoveTriggerEvent : public QWAbstractSceneEvent +{ +private: + std::shared_ptr trigger; // What to add. + std::shared_ptr location; // Where to add. + +public: + explicit QWRemoveTriggerEvent(const std::shared_ptr &tr); + virtual ~QWRemoveTriggerEvent() override {} + + void execute() override; + + //////////////////////// + + void setLocation(const std::shared_ptr &loc) noexcept; + void setTrigger(const std::shared_ptr &tr) noexcept; + + //////////////////////// + + void writeToJson(QJsonObject &event_data) override; +}; + +#endif // QWREMOVETRIGGER_H diff --git a/models/events/qw_startdialogueevent.cpp b/models/events/qw_startdialogueevent.cpp new file mode 100644 index 0000000..9f9bd18 --- /dev/null +++ b/models/events/qw_startdialogueevent.cpp @@ -0,0 +1,36 @@ +#include "models/dialogues/qw_abstractgamedialogue.h" +#include "features/qw_abstractdialoguemanager.h" +#include "qw_startdialogueevent.h" + +QWStartDialogueEvent::QWStartDialogueEvent(const std::shared_ptr &dialogue) : + ptr_dialogue(dialogue) +{} + +void QWStartDialogueEvent::execute() +{ + ptr_dialogue_manager->activateDialogue(ptr_dialogue); +} + +//////////////////////// + +void QWStartDialogueEvent::setDialogueManager(QWAbstractDialogueManager *dialogman) noexcept +{ + ptr_dialogue_manager = dialogman; +} + +QWAbstractDialogueManager *QWStartDialogueEvent::dialogueManager() const noexcept +{ + return ptr_dialogue_manager; +} + +//////////////////////// + +void QWStartDialogueEvent::writeToJson(QJsonObject &event_data) +{ + event_data.insert("id", tag()); + event_data.insert("type", EVENT_TYPE::START_DIALOGUE); + event_data.insert("dialogue", ptr_dialogue->tag()); + + qDebug() << " The start_dialogue event:\n" << " id " << tag() + << "\n dialogue " << ptr_dialogue->tag(); +} diff --git a/models/events/qw_startdialogueevent.h b/models/events/qw_startdialogueevent.h new file mode 100644 index 0000000..7763f42 --- /dev/null +++ b/models/events/qw_startdialogueevent.h @@ -0,0 +1,34 @@ +#ifndef QWSTARTDIALOGUEEVENT_H +#define QWSTARTDIALOGUEEVENT_H + +#include "qw_abstractevent.h" + +/* QWStartDialogueEvent + * Starts a game-freezing dialogue by moving QWScene from gameplay state. */ + +class QWAbstractDialogueManager; +class QWAbstractGameDialogue; + +class QWStartDialogueEvent : public QWAbstractEvent +{ +private: + QWAbstractDialogueManager *ptr_dialogue_manager; + std::shared_ptr ptr_dialogue; + +public: + explicit QWStartDialogueEvent(const std::shared_ptr &dialogue); + virtual ~QWStartDialogueEvent() override {} + + void execute() override; + + //////////////////////// + + void setDialogueManager(QWAbstractDialogueManager *dialogman) noexcept; + QWAbstractDialogueManager *dialogueManager() const noexcept; + + //////////////////////// + + void writeToJson(QJsonObject &event_data) override; +}; + +#endif // QWSTARTDIALOGUEEVENT_H diff --git a/models/events/qw_switcheventsevent.cpp b/models/events/qw_switcheventsevent.cpp new file mode 100644 index 0000000..3986af5 --- /dev/null +++ b/models/events/qw_switcheventsevent.cpp @@ -0,0 +1,75 @@ +#include "../qw_trigger.h" +#include "qw_switcheventsevent.h" + +QWSwitchEventsEvent::QWSwitchEventsEvent(const std::shared_ptr &tr) : + ptr_target(tr), + b_active(false) +{ + Q_ASSERT(ptr_target); +} + +void QWSwitchEventsEvent::execute() +{ + /* QWActivateItemEvent changes its state, + * Now we send other list of events for the linked trigger. */ + b_active = !b_active; + ptr_target->setEvents(b_active ? list_enabled_events : list_disabled_events); +} + +void QWSwitchEventsEvent::init() +{ + ptr_target->setEvents(list_disabled_events); +} + +//////////////////////// + +void QWSwitchEventsEvent::setTarget(const std::shared_ptr &tr) noexcept +{ + ptr_target = tr; +} + +std::shared_ptr QWSwitchEventsEvent::target() const noexcept +{ + return ptr_target; +} + +void QWSwitchEventsEvent::setEventsOnEnable(const QList> &evs) noexcept +{ + list_enabled_events = evs; +} + +void QWSwitchEventsEvent::setEventsOnDisable(const QList> &evs) noexcept +{ + list_disabled_events = evs; +} + +//////////////////////// + +void QWSwitchEventsEvent::writeToJson(QJsonObject & event_data) +{ + event_data.insert("id", tag()); + event_data.insert("type", EVENT_TYPE::SWITCH_EVENTS); + event_data.insert("target", ptr_target->tag()); + + QJsonArray trs_en, trs_dis; + + for (const auto & ev : list_enabled_events) { + trs_en.append(ev->tag()); + } + + event_data.insert("enable_evs", trs_en); + + for (const auto & ev : list_disabled_events) { + trs_dis.append(ev->tag()); + } + + event_data.insert("disable_evs", trs_dis); + + qDebug() << " The switch_events event:\n" << " id " << tag() + << "\n trs_en :"; + for (const auto tr : trs_en) + qDebug() << " " << tr.toString(); + qDebug() << "\n trs_dis :"; + for (const auto tr : trs_dis) + qDebug() << " " << tr.toString(); +} diff --git a/models/events/qw_switcheventsevent.h b/models/events/qw_switcheventsevent.h new file mode 100644 index 0000000..038d95a --- /dev/null +++ b/models/events/qw_switcheventsevent.h @@ -0,0 +1,48 @@ +#ifndef QUESTACTIVATEITEMEVENT_H +#define QUESTACTIVATEITEMEVENT_H + +#include +#include +#include "qw_abstractevent.h" + +/* QWSwitchEventsEvent + * Changes events of its linked QWTrigger. + * For example: a door is being closed until we pick up a key. + * It's initial state "to print message "go find a key you punk". + * When we activate its key, execute() changes door event to "go through". */ + +class QWTrigger; + +class QWSwitchEventsEvent : public QWAbstractEvent +{ +private: + std::shared_ptr ptr_target; + + bool b_active; + + QList> list_disabled_events; + QList> list_enabled_events; + +public: + explicit QWSwitchEventsEvent(const std::shared_ptr &tr); + virtual ~QWSwitchEventsEvent() override {} + + void execute() override; + + //////////////////////// + + void setTarget(const std::shared_ptr &tr) noexcept; + std::shared_ptr target() const noexcept; + + void setEventsOnDisable(const QList> &evs) noexcept; + void setEventsOnEnable(const QList> &evs) noexcept; + + // init() sets an inital state of ptr_trigger. + void init(); + + //////////////////////// + + void writeToJson(QJsonObject &event_data) override; +}; + +#endif // QUESTACTIVATEITEMEVENT_H diff --git a/models/qw_location.cpp b/models/qw_location.cpp new file mode 100644 index 0000000..7776d99 --- /dev/null +++ b/models/qw_location.cpp @@ -0,0 +1,114 @@ +#include "qw_location.h" +#include "qw_trigger.h" + +QWLocation::QWLocation() : + flag_discovered(false) +{} + +void QWLocation::clearTriggers() noexcept +{ + list_triggers.clear(); +} + +void QWLocation::setTriggers(std::initializer_list> &&trs) noexcept +{ + list_triggers.clear(); + + /* For some reason QList(::begin(), ::end()) + * doesn't work for MSVC */ + +#if defined(_MSC_VER) + list_triggers = QList>::fromStdList(std::move(trs)); +#else + list_triggers = QList(trs.begin(), trs.end()); +#endif + +} + +void QWLocation::setTriggers(const QList> &trs) noexcept +{ + list_triggers.clear(); + list_triggers = trs; +} + +void QWLocation::addTriggers(std::initializer_list> &&trs) noexcept +{ + list_triggers.append(std::move(QList>(trs))); +} + +void QWLocation::addTriggers(const std::shared_ptr &tr) noexcept +{ + list_triggers.append(tr); +} + +void QWLocation::removeTrigger(const std::shared_ptr &tr) noexcept +{ + list_triggers.removeOne(tr); +} + +QList> QWLocation::triggers() const +{ + return list_triggers; +} + +void QWLocation::setFirstVisitTrigger(const std::shared_ptr &tr) noexcept +{ + first_visit_trigger = tr; + Q_ASSERT(first_visit_trigger); +} + +std::shared_ptr QWLocation::firstVisitTrigger() const noexcept +{ + return first_visit_trigger; +} + +void QWLocation::setDiscovered(bool discovered) noexcept +{ + flag_discovered = discovered; +} + +bool QWLocation::discovered() const noexcept +{ + return flag_discovered; +} + +void QWLocation::setMusicPath(const QString &path) noexcept +{ + music_path = path; + Q_ASSERT(!music_path.isEmpty()); +} + +void QWLocation::removeMusic() noexcept +{ + music_path.clear(); +} + +QString QWLocation::musicPath() const noexcept +{ + return music_path; +} + +//////////////////////// + +void QWLocation::writeToJson(QJsonObject &location_data) +{ + location_data.insert("id", tag()); + + if (flag_discovered) + location_data.insert("discovered", flag_discovered); + + if (!music_path.isEmpty()) + location_data.insert("music_path", music_path); + + qDebug() << " The location:\n" << " id " << tag() + << "\n discovered " << flag_discovered + << "\n music_path " << music_path; + + // - Location triggers - // + QJsonArray json_triggers; + for (const auto &trigger : triggers()) { + json_triggers.append(trigger->tag()); + qDebug() << " trigger: " << trigger->tag(); + } + location_data.insert("trs", json_triggers); +} diff --git a/models/qw_location.h b/models/qw_location.h new file mode 100644 index 0000000..a440cd1 --- /dev/null +++ b/models/qw_location.h @@ -0,0 +1,51 @@ +#ifndef QWLOCATION_H +#define QWLOCATION_H + +#include +#include + +#include "events/qw_abstractevent.h" + +/* QWLocation + * Represents any game location. */ + +class QWTrigger; + +class QWLocation final : public QWTagHolder +{ +private: + QList> list_triggers; + std::shared_ptr first_visit_trigger; + bool flag_discovered; + + QString music_path; + +public: + explicit QWLocation(); + + void clearTriggers() noexcept; + void setTriggers(std::initializer_list> &&trs) noexcept; + void setTriggers(const QList> &trs) noexcept; + void addTriggers(std::initializer_list> &&trs) noexcept; + void addTriggers(const std::shared_ptr &tr) noexcept; + void removeTrigger(const std::shared_ptr &tr) noexcept; + QList> triggers() const; + + // Should be activated on first visit. + void setFirstVisitTrigger(const std::shared_ptr &tr) noexcept; + std::shared_ptr firstVisitTrigger() const noexcept; + + void setDiscovered(bool discovered) noexcept; + bool discovered() const noexcept; + + void setMusicPath(const QString &path) noexcept; + void removeMusic() noexcept; + QString musicPath() const noexcept; + + + //////////////////////// + + void writeToJson(QJsonObject &location_data) override; +}; + +#endif // QWLOCATION_H diff --git a/models/qw_trigger.cpp b/models/qw_trigger.cpp new file mode 100644 index 0000000..e27f4d9 --- /dev/null +++ b/models/qw_trigger.cpp @@ -0,0 +1,113 @@ +#include "qw_trigger.h" +#include +#include +#include + +QWTrigger::QWTrigger(const QString &path) +{ + setPos(0, 0); + setPixmap(QPixmap(":/res/" + path)); +} + +void QWTrigger::clearEvents() noexcept +{ + list_events.clear(); +} + +void QWTrigger::setEvents(std::initializer_list> &&evs) noexcept +{ + list_events.clear(); + + /* For some reason QList(::begin(), ::end()) + * doesn't work for MSVC */ + +#if defined(_MSC_VER) + list_events = QList>::fromStdList(std::move(evs)); +#else + list_events = QList(evs.begin(), evs.end()); +#endif + +} + +void QWTrigger::setEvents(const QList> &evs) noexcept +{ + list_events.clear(); + list_events = evs; +} + +void QWTrigger::addEvents(std::initializer_list> &&evs) noexcept +{ + list_events.append(std::move(QList>(evs))); +} + +void QWTrigger::addEvents(const std::shared_ptr &ev) noexcept +{ + list_events.append(ev); +} + +QList> QWTrigger::events() const +{ + return list_events; +} + +void QWTrigger::setCover(qreal x, qreal y, const QString &path) noexcept +{ + pix_path = path; + setPixmap(QPixmap(":/res/" + path)); + setPos(x, y); +} + +void QWTrigger::setCover(const QString &path) noexcept +{ + pix_path = path; + setPixmap(QPixmap(":/res/" + path)); +} + +QString QWTrigger::path() const noexcept +{ + return pix_path; +} + +void QWTrigger::setExaminationDialogueEvent(const std::shared_ptr &dialogue_event) +{ + examination_event = dialogue_event; + Q_ASSERT(examination_event); +} + +void QWTrigger::startExaminationDialogue() const +{ + if (examination_event) + examination_event->execute(); +} + +void QWTrigger::activate() const +{ + for (const auto &ev : list_events) { + Q_ASSERT(ev); + ev->execute(); + } +} + +//////////////////////// + +void QWTrigger::writeToJson(QJsonObject &trigger_data) +{ + trigger_data.insert("id", tag()); + trigger_data.insert("mask", path()); + trigger_data.insert("x", x()); + trigger_data.insert("y", y()); + trigger_data.insert("examine_dialogue", examination_event->tag()); + + qDebug() << " The trigger:\n" << " id " << tag() + << "\n mask " << tag() + << "\n xy " << x() << " " << y() + << "\n examine_dialogue " << examination_event->tag(); + + // - Trigger events - // + QJsonArray json_trevents; + for (const auto &tr_event : events()) { + json_trevents.append(tr_event->tag()); + qDebug() << " event: " << tr_event->tag(); + } + trigger_data.insert("evs", json_trevents); +} diff --git a/models/qw_trigger.h b/models/qw_trigger.h new file mode 100644 index 0000000..e1b76e8 --- /dev/null +++ b/models/qw_trigger.h @@ -0,0 +1,46 @@ +#ifndef TRIGGER_H +#define TRIGGER_H + +#include +#include +#include "events/qw_abstractevent.h" + +/* QWTrigger + * Represents an interactive in-game element. */ + +class QWStartDialogueEvent; + +class QWTrigger : public QGraphicsPixmapItem, public QWTagHolder +{ +private: + QList> list_events; + std::shared_ptr examination_event; + QString pix_path; + +public: + explicit QWTrigger(const QString &path); + + void clearEvents() noexcept; + void setEvents(std::initializer_list> &&evs) noexcept; + void setEvents(const QList> &evs) noexcept; + void addEvents(std::initializer_list> &&evs) noexcept; + void addEvents(const std::shared_ptr &ev) noexcept; + QList> events() const; + + void setCover(qreal x, qreal y, const QString &path) noexcept; + void setCover(const QString &path) noexcept; + inline QString path() const noexcept; + + void setExaminationDialogueEvent(const std::shared_ptr &dialogue_event); + void startExaminationDialogue() const; + + void activate() const; + + //////////////////////// + + void writeToJson(QJsonObject &trigger_data) override; +}; + + + +#endif // TRIGGER_H diff --git a/qml/test.qml b/qml/test.qml new file mode 100644 index 0000000..144e108 --- /dev/null +++ b/qml/test.qml @@ -0,0 +1,21 @@ +import QtQuick 2.6 +import QtQuick.Controls 2.0 + +ApplicationWindow { + id: root + width: 300 + height: 480 + visible: true + +/* This absolutely does not make any practical sense, + * I just try to make it work and see what's + * gonna happen */ + + TextField { + text: dialogue.exit_code + placeholderText: qsTr("exit code") + anchors.centerIn: parent + + onTextChanged: dialogue.exit_code = text + } +} diff --git a/qw_globalmetadata.cpp b/qw_globalmetadata.cpp new file mode 100644 index 0000000..e4ed575 --- /dev/null +++ b/qw_globalmetadata.cpp @@ -0,0 +1,42 @@ +#include "qw_globalmetadata.h" +#include +#include +#include +#include +#include + +QJsonObject QWGlobalMetadata::config = QJsonObject(); + +void QWGlobalMetadata::load() +{ + QFile file("config.json"); + + if (!file.exists()) + QFile::copy(":/res/config.json", file.fileName()); + + Q_ASSERT(file.open(QIODevice::ReadOnly)); + const QByteArray json_arr = file.readAll(); + file.close(); + config = QJsonDocument::fromJson(json_arr).object(); +} + +void QWGlobalMetadata::save() +{ + QFile file("config.json"); + + Q_ASSERT(file.open(QIODevice::WriteOnly)); + file.write(QJsonDocument(config).toJson()); + file.close(); +} + +//////////////////////// + +QVariant QWGlobalMetadata::valueBy(QString &&key) +{ + return config[key].toVariant(); +} + +void QWGlobalMetadata::setValue(QString &&key, const QVariant &val) noexcept +{ + config.insert(key, val.toJsonValue()); +} diff --git a/qw_globalmetadata.h b/qw_globalmetadata.h new file mode 100644 index 0000000..d9a86b1 --- /dev/null +++ b/qw_globalmetadata.h @@ -0,0 +1,30 @@ +#ifndef GLOBALMETADATA_H +#define GLOBALMETADATA_H + +#include + +/* QWGlobalMetadata + * Provides config options and metadata for all application classes. */ + +class QJsonObject; +class QString; + +class QWGlobalMetadata final +{ +private: + static QJsonObject config; + +public: + explicit QWGlobalMetadata() = delete; + ~QWGlobalMetadata() = delete; + + static void load(); + static void save(); + + //////////////////////// + + static QVariant valueBy(QString &&key); + static void setValue(QString &&key, const QVariant &val) noexcept; +}; + +#endif // GLOBALMETADATA_H diff --git a/res/cell.png b/res/cell.png new file mode 100644 index 0000000000000000000000000000000000000000..ffe9b375b24c172a0266773c45653cc1d27dbd6b GIT binary patch literal 12059 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 literal 0 HcmV?d00001 diff --git a/res/door.png b/res/door.png new file mode 100644 index 0000000000000000000000000000000000000000..37b4d83fa1525c1c19126563111fdd285356ff03 GIT binary patch literal 507 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 F^?!9E&C37) literal 0 HcmV?d00001 diff --git a/view/controls/pushbuttonsound.cpp b/view/controls/pushbuttonsound.cpp new file mode 100644 index 0000000..29790f4 --- /dev/null +++ b/view/controls/pushbuttonsound.cpp @@ -0,0 +1,37 @@ +#include "pushbuttonsound.h" +#include "features/gamefeatures.h" + +PushButtonSound::PushButtonSound(const QPixmap &pixmap_off, QGraphicsItem *parent) : + b_on(false), + pix_on(pixmap_off), + pix_off(pixmap_off) +{ + setParentItem(parent); + Q_ASSERT(!pixmap_off.isNull()); +} + +void PushButtonSound::onClick() +{ + b_on = !b_on; + emit signalChangeSound(b_on); +} + +void PushButtonSound::onConnect(std::unique_ptr &game_features) +{ + QObject::connect(this, //sender + SIGNAL(signalChangeSound(bool)), + game_features->ptr_sound_player, //receiver + SLOT(setMuteness(bool))); +} + +//////////////////////// + +void PushButtonSound::setPixmapOn(const QPixmap &pixmap_on) noexcept +{ + pix_on = pixmap_on; +} + +void PushButtonSound::setPixmapOff(const QPixmap &pixmap_off) noexcept +{ + pix_off = pixmap_off; +} diff --git a/view/controls/pushbuttonsound.h b/view/controls/pushbuttonsound.h new file mode 100644 index 0000000..14d4fb9 --- /dev/null +++ b/view/controls/pushbuttonsound.h @@ -0,0 +1,40 @@ +#ifndef PUSHBUTTONSOUND_H +#define PUSHBUTTONSOUND_H + +#include +#include "qw_abstractscenecontrol.h" + +/* PushButtonSound + * Switches current sound state. */ + +class PushButtonSound : public QWAbstractSceneControl +{ + Q_OBJECT +private: + bool b_on; + QPixmap pix_on; + QPixmap pix_off; + +protected: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) override + { + painter->fillRect(rect(), QBrush(b_on ? pix_on : pix_off)); + } + +public: + explicit PushButtonSound(const QPixmap &pixmap_off, QGraphicsItem *parent = nullptr); + virtual ~PushButtonSound() override {} + void onClick() override; + void onConnect(std::unique_ptr &game_features) override; + + //////////////////////// + + inline void setPixmapOn(const QPixmap &pixmap_on) noexcept; + inline void setPixmapOff(const QPixmap &pixmap_off) noexcept; + +signals: + void signalChangeSound(bool); +}; + +#endif // PUSHBUTTONSOUND_H diff --git a/view/controls/qw_abstractscenecontrol.cpp b/view/controls/qw_abstractscenecontrol.cpp new file mode 100644 index 0000000..b7e1847 --- /dev/null +++ b/view/controls/qw_abstractscenecontrol.cpp @@ -0,0 +1,13 @@ +#include "qw_abstractscenecontrol.h" + +QWAbstractSceneControl:: QWAbstractSceneControl() +{} + +QWAbstractSceneControl::~QWAbstractSceneControl() +{} + +void QWAbstractSceneControl::onBuildingStateMachine(QWStateMachine *state_machine, std::unique_ptr &game_features) +{ + Q_UNUSED(state_machine) + Q_UNUSED(game_features) +} diff --git a/view/controls/qw_abstractscenecontrol.h b/view/controls/qw_abstractscenecontrol.h new file mode 100644 index 0000000..2bf19d5 --- /dev/null +++ b/view/controls/qw_abstractscenecontrol.h @@ -0,0 +1,25 @@ +#ifndef ABSTRACTSCENECONTROL_H +#define ABSTRACTSCENECONTROL_H + +#include +#include + +/* QWAbstractSceneControl + * Interface for scene graphics elements which have + * to manipulate non-logic game processes like sound, view, etc. */ + +struct GameFeatures; +class QWStateMachine; + +class QWAbstractSceneControl : public QGraphicsWidget +{ +public: + QWAbstractSceneControl(); + ~QWAbstractSceneControl() = 0; + + virtual void onClick() = 0; + virtual void onConnect(std::unique_ptr &game_features) = 0; + virtual void onBuildingStateMachine(QWStateMachine *state_machine, std::unique_ptr &game_features); +}; + +#endif // ABSTRACTSCENECONTROL_H diff --git a/view/controls/scenedialoguepanel.cpp b/view/controls/scenedialoguepanel.cpp new file mode 100644 index 0000000..dde0e6e --- /dev/null +++ b/view/controls/scenedialoguepanel.cpp @@ -0,0 +1,71 @@ +#include "scenedialoguepanel.h" +#include "features/gamefeatures.h" +#include "features/qw_statemachine.h" +#include "qw_globalmetadata.h" + +SceneDialoguePanel::SceneDialoguePanel() +{ + metadata.pixmap_path = QWGlobalMetadata::valueBy("DialoguePanel:pixmap_path").toString(); + + metadata.on_hid = QRect(QPoint(QWGlobalMetadata::valueBy("DialoguePanel:on_hid:bot_left_x" ).toInt(), // x1 + QWGlobalMetadata::valueBy("DialoguePanel:on_hid:bot_left_x" ).toInt()), // y1 + QPoint(QWGlobalMetadata::valueBy("DialoguePanel:on_hid:top_right_x").toInt(), // x4 + QWGlobalMetadata::valueBy("DialoguePanel:on_hid:top_right_y").toInt())); // y4 + + metadata.on_shw = QRect(QPoint(QWGlobalMetadata::valueBy("DialoguePanel:on_shw:bot_left_x" ).toInt(), // x1 + QWGlobalMetadata::valueBy("DialoguePanel:on_shw:bot_left_x" ).toInt()), // y1 + QPoint(QWGlobalMetadata::valueBy("DialoguePanel:on_shw:top_right_x").toInt(), // x4 + QWGlobalMetadata::valueBy("DialoguePanel:on_shw:top_right_y").toInt())); // y4 + /* / + * 2-------4 / + * | | <- + * 1-------3 + */ + setPixmap(QPixmap(metadata.pixmap_path)); + Q_ASSERT(!pix_rect.isNull()); +} + +void SceneDialoguePanel::onClick() +{} + +void SceneDialoguePanel::onConnect(std::unique_ptr &game_features) +{ + game_features->ptr_text_dlg->setDialoguePanel(shared_from_this()); +} + +void SceneDialoguePanel::onBuildingStateMachine(QWStateMachine *state_machine, std::unique_ptr &game_features) +{ + // Establisihing animation for dialogue panel + + const auto &state_gameplay = state_machine->stateByKey("state_gameplay"); + + QState *state_dialogue = new QState; + state_machine->registerState("state_dialogue", state_dialogue); + connect(state_dialogue, SIGNAL(entered()), game_features->ptr_scene, SLOT(onEntryDialogue())); + + QSignalTransition *enter_dialogue_transition = new QSignalTransition(game_features->ptr_text_dlg, SIGNAL(onEntryDialogueTransition())); + enter_dialogue_transition->setTargetState(state_dialogue); + state_gameplay->addTransition(enter_dialogue_transition); + + QSignalTransition *leave_dialogue_transition = new QSignalTransition(game_features->ptr_text_dlg, SIGNAL(onLeaveDialogueTransition())); + leave_dialogue_transition->setTargetState(state_gameplay); + state_dialogue->addTransition(leave_dialogue_transition); + + state_gameplay->assignProperty(this, "geometry", metadata.on_hid); + state_dialogue->assignProperty(this, "geometry", metadata.on_shw); + + state_machine->addState(state_dialogue); +} + +//////////////////////// + +void SceneDialoguePanel::setPixmap(const QPixmap &pix) noexcept +{ + pix_rect = pix; + Q_ASSERT(!pix_rect.isNull()); +} + +QPixmap SceneDialoguePanel::pixmap() const noexcept +{ + return pix_rect; +} diff --git a/view/controls/scenedialoguepanel.h b/view/controls/scenedialoguepanel.h new file mode 100644 index 0000000..f05cc33 --- /dev/null +++ b/view/controls/scenedialoguepanel.h @@ -0,0 +1,45 @@ +#ifndef SCENEDIALOGUEPANEL_H +#define SCENEDIALOGUEPANEL_H + +#include +#include "qw_abstractscenecontrol.h" + +/* SceneDialoguePanel + * The view for dialogue processes like text, thoughts, etc. */ + +class SceneDialoguePanel : public QWAbstractSceneControl, public std::enable_shared_from_this +{ +private: + QPixmap pix_rect; + + struct metadata + { + // path to panel pixmap + QString pixmap_path; + // rect when panel is hidden + QRect on_hid; + // rect when panel is shown + QRect on_shw; + } metadata; + +protected: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) override + { + painter->fillRect(rect(), QBrush(pix_rect)); + } + +public: + explicit SceneDialoguePanel(); + virtual ~SceneDialoguePanel() override {} + void onClick() override; + void onConnect(std::unique_ptr &game_features) override; + void onBuildingStateMachine(QWStateMachine *state_machine, std::unique_ptr &game_features) override; + + //////////////////////// + + inline void setPixmap(const QPixmap &pix) noexcept; + inline QPixmap pixmap() const noexcept; +}; + +#endif // SCENEDIALOGUEPANEL_H diff --git a/view/controls/sceneinventorypanel.cpp b/view/controls/sceneinventorypanel.cpp new file mode 100644 index 0000000..d1da460 --- /dev/null +++ b/view/controls/sceneinventorypanel.cpp @@ -0,0 +1,85 @@ +#include "sceneinventorypanel.h" +#include "features/gamefeatures.h" +#include "features/qw_statemachine.h" +#include "qw_globalmetadata.h" + +SceneInventoryPanel::SceneInventoryPanel() +{ + metadata.pixmap_path = QWGlobalMetadata::valueBy("InventoryPanel:pixmap_path").toString(); + + metadata.on_hid = QRect(QPoint(QWGlobalMetadata::valueBy("InventoryPanel:on_hid:bot_left_x" ).toInt(), // x1 + QWGlobalMetadata::valueBy("InventoryPanel:on_hid:bot_left_y" ).toInt()), // y1 + QPoint(QWGlobalMetadata::valueBy("InventoryPanel:on_hid:top_right_x").toInt(), // x4 + QWGlobalMetadata::valueBy("InventoryPanel:on_hid:top_right_y").toInt())); // y4 + + metadata.on_shw = QRect(QPoint(QWGlobalMetadata::valueBy("InventoryPanel:on_shw:bot_left_x" ).toInt(), // x1 + QWGlobalMetadata::valueBy("InventoryPanel:on_shw:bot_left_y" ).toInt()), // y1 + QPoint(QWGlobalMetadata::valueBy("InventoryPanel:on_shw:top_right_x").toInt(), // x4 + QWGlobalMetadata::valueBy("InventoryPanel:on_shw:top_right_y").toInt())); // y4 + /* / + * 2-------4 / + * | | <- + * 1-------3 + */ + setPixmap(QPixmap(metadata.pixmap_path)); +} + +void SceneInventoryPanel::onClick() +{ + emit signalOnInventory(); +} + +void SceneInventoryPanel::onConnect(std::unique_ptr &game_features) +{ + game_features->ptr_inventory->setInventoryPanel(shared_from_this()); + QObject::connect(this, //sender + SIGNAL(signalOnInventory()), + game_features->ptr_inventory, // receiver + SLOT(onClicked())); +} + +void SceneInventoryPanel::onBuildingStateMachine(QWStateMachine *state_machine, std::unique_ptr &game_features) +{ + // Establisihing animation for inventory panel + + QPropertyAnimation *anim = new QPropertyAnimation(shared_from_this().get(), "geometry"); + anim->setDuration(200); + anim->setEasingCurve(QEasingCurve::InBack); + + QState *state_gameplay = new QState; + state_machine->registerState("state_gameplay", state_gameplay); + connect(state_gameplay, SIGNAL(entered()), game_features->ptr_scene, SLOT(onEntryGameplay())); + + QState *state_inventory = new QState; + state_machine->registerState("state_inventory", state_inventory); + connect(state_inventory, SIGNAL(entered()), game_features->ptr_scene, SLOT(onEntryInventory())); + + QEventTransition *enter_inventory_transition = new QEventTransition(game_features->ptr_view, QEvent::Wheel); + enter_inventory_transition->setTargetState(state_inventory); + enter_inventory_transition->addAnimation(anim); + state_gameplay->addTransition(enter_inventory_transition); + + QEventTransition *leave_inventory_transition = new QEventTransition(game_features->ptr_view, QEvent::Wheel); + leave_inventory_transition->setTargetState(state_gameplay); + leave_inventory_transition->addAnimation(anim); + state_inventory->addTransition(leave_inventory_transition); + + state_gameplay->assignProperty(this, "geometry", metadata.on_hid); + state_inventory->assignProperty(this, "geometry", metadata.on_shw); + + state_machine->addState(state_gameplay); + state_machine->addState(state_inventory); +} + +//////////////////////// + +void SceneInventoryPanel::setPixmap(const QPixmap &pix) noexcept +{ + pix_rect = pix; + Q_ASSERT(!pix_rect.isNull()); +} + +QPixmap SceneInventoryPanel::pixmap() const noexcept +{ + return pix_rect; +} diff --git a/view/controls/sceneinventorypanel.h b/view/controls/sceneinventorypanel.h new file mode 100644 index 0000000..b047755 --- /dev/null +++ b/view/controls/sceneinventorypanel.h @@ -0,0 +1,49 @@ +#ifndef SCENEINVENTORYPANEL_H +#define SCENEINVENTORYPANEL_H + +#include +#include "qw_abstractscenecontrol.h" + +/* SceneInventoryPanel + * The view for inventory and all its items on the scene. */ + +class SceneInventoryPanel : public QWAbstractSceneControl, public std::enable_shared_from_this +{ + Q_OBJECT +private: + QPixmap pix_rect; + + struct metadata + { + // path to panel pixmap + QString pixmap_path; + // rect when panel is hidden + QRect on_hid; + // rect when panel is shown + QRect on_shw; + } metadata; + +protected: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, + QWidget *) override + { + painter->fillRect(rect(), QBrush(pix_rect)); + } + +public: + explicit SceneInventoryPanel(); + virtual ~SceneInventoryPanel() override {} + void onClick() override; + void onConnect(std::unique_ptr &game_features) override; + void onBuildingStateMachine(QWStateMachine *state_machine, std::unique_ptr &game_features) override; + + //////////////////////// + + inline void setPixmap(const QPixmap &pix) noexcept; + inline QPixmap pixmap() const noexcept; + +signals: + void signalOnInventory(); +}; + +#endif // SCENEINVENTORYPANEL_H diff --git a/view/qw_scene.cpp b/view/qw_scene.cpp new file mode 100644 index 0000000..a513800 --- /dev/null +++ b/view/qw_scene.cpp @@ -0,0 +1,128 @@ +#include "qw_scene.h" +#include "models/qw_trigger.h" +#include "controls/sceneinventorypanel.h" +#include "controls/pushbuttonsound.h" +#include "controls/scenedialoguepanel.h" + +QWScene::QWScene(int x, int y) : + QGraphicsScene(0, 0, x, y) +{ + QLinkedList> widgt_list = + { std::make_shared(), + std::make_shared() + //std::make_shared(QPixmap(":/res/cell.png")/*, ptr_inventory_panel.get()*/), + }; + + foreach (std::shared_ptr widgt, widgt_list) { + addItem(widgt.get()); + list_on_inventory_widgets.append(std::move(widgt)); + } +} + +QLinkedList> QWScene::inventoryWidgets() const noexcept +{ + return list_on_inventory_widgets; +} + +void QWScene::onEntryGameplay() noexcept +{ + status = GAMEPLAY; +} + +void QWScene::onEntryInventory() noexcept +{ + status = INVENTORY; +} + +void QWScene::onEntryMenu() noexcept +{ + status = MENU; + emit signalEnterMenu(); +} + +void QWScene::onEntryDialogue() noexcept +{ + status = DIALOGUE; +} + +void QWScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + switch (status) + { + case MENU: + case GAMEPLAY: + // On GAMEPLAY and MENU state we check all the in-game entities. + foreach (std::shared_ptr tr, location->triggers()) { + if (tr->isUnderMouse() && event->button() == Qt::LeftButton) + tr->activate(); + } + break; + + case INVENTORY: + // On INVENTORY state we check all the system controls like buttons for sound, etc. + foreach (std::shared_ptr tr, list_on_inventory_widgets) { + if (tr->isUnderMouse() && event->button() == Qt::LeftButton) + tr->onClick(); + } + break; + + case DIALOGUE: + // On DIALOGUE state we interact with current dialogue only. + switch (event->button()) { + case Qt::LeftButton: + emit signalClickDialogue(MouseButton::LEFT); + break; + case Qt::RightButton: + emit signalClickDialogue(MouseButton::RIGHT); + break; + default: + break; + } + break; + + case EXAMINATION: + // On EXAMINATION state we activate examination events of triggers + foreach (std::shared_ptr tr, location->triggers()) { + if (tr->isUnderMouse() && event->button() == Qt::LeftButton) + emit signalLeaveExamination(); + tr->startExaminationDialogue(); + } + break; + } +} + +void QWScene::keyReleaseEvent(QKeyEvent *event) +{ + switch (status) + { + case GAMEPLAY: + if (event->key() == Qt::Key_E) + emit signalEnterExamination(); + break; + + default: + return; + } +} + +void QWScene::clearLocation() +{ + if (!location) + return; + + // Moving to another location by erasing current + foreach (const std::shared_ptr trigger, location->triggers()) + removeItem(trigger.get()); + + location = nullptr; +} + +void QWScene::setCurrentLocation(const std::shared_ptr &loc) noexcept +{ + location = loc; +} + +std::shared_ptr QWScene::currentLocation() const noexcept +{ + return location; +} diff --git a/view/qw_scene.h b/view/qw_scene.h new file mode 100644 index 0000000..376e5ad --- /dev/null +++ b/view/qw_scene.h @@ -0,0 +1,61 @@ +#ifndef QUESTSCENE_H +#define QUESTSCENE_H + +#include +#include +#include +#include +#include + +#include "models/qw_location.h" +#include "controls/qw_abstractscenecontrol.h" + +/* QWScene + * The game scene itself. */ + +enum class MouseButton { LEFT, RIGHT }; + +class QWDialogueFrame; + +class QWScene final : public QGraphicsScene +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QWScene) +private: + std::shared_ptr location; + QLinkedList> list_on_inventory_widgets; + + enum SceneStatus { GAMEPLAY, INVENTORY, MENU, DIALOGUE, EXAMINATION }; + SceneStatus status; + +protected: + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + +public: + explicit QWScene(int x, int y); + + QLinkedList> inventoryWidgets() const noexcept; + + void setCurrentLocation(const std::shared_ptr &loc) noexcept; + std::shared_ptr currentLocation() const noexcept; + void clearLocation(); + + void onEndLevel(const QString &str) noexcept; + +signals: + void signalClickInventory(); + void signalClickDialogue(MouseButton); + void signalEnterMenu(); + void signalLeaveMenu(); + void signalEnterExamination(); + void signalLeaveExamination(); + +public slots: + void onEntryGameplay() noexcept; + void onEntryInventory() noexcept; + void onEntryMenu() noexcept; + void onEntryDialogue() noexcept; +}; + +#endif // QUESTSCENE_H diff --git a/view/qw_view.cpp b/view/qw_view.cpp new file mode 100644 index 0000000..def8430 --- /dev/null +++ b/view/qw_view.cpp @@ -0,0 +1,9 @@ +#include "qw_view.h" + +QWView::QWView(QWScene *sc) : + scene(sc) +{ + setScene(sc); + //setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + //setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +} diff --git a/view/qw_view.h b/view/qw_view.h new file mode 100644 index 0000000..a769171 --- /dev/null +++ b/view/qw_view.h @@ -0,0 +1,29 @@ +#ifndef QUESTVIEW_H +#define QUESTVIEW_H + +#include +#include "qw_scene.h" + +/* QWView + * The same QGraphicsView but this one ignores wheel events, + * because wheels are for loosers. */ + +class QWView final : public QGraphicsView +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QWView) +private: + QWScene *scene; + +protected: + + void wheelEvent(QWheelEvent *ev) override + { + ev->ignore(); + } + +public: + explicit QWView(QWScene *sc); +}; + +#endif // QUESTVIEW_H