diff --git a/include/config.h b/include/config.h index a48c7e45..c5c0e66b 100644 --- a/include/config.h +++ b/include/config.h @@ -148,7 +148,7 @@ public: this->enableHiddenItemQuantity = false; this->enableHiddenItemRequiresItemfinder = false; this->enableHealLocationRespawnData = false; - this->enableObjectEventInConnection = false; + this->enableEventCloneObject = false; this->enableFloorNumber = false; this->createMapTextFile = false; this->enableTripleLayerMetatiles = false; @@ -178,8 +178,8 @@ public: bool getHiddenItemRequiresItemfinderEnabled(); void setHealLocationRespawnDataEnabled(bool enable); bool getHealLocationRespawnDataEnabled(); - void setObjectEventInConnectionEnabled(bool enable); - bool getObjectEventInConnectionEnabled(); + void setEventCloneObjectEnabled(bool enable); + bool getEventCloneObjectEnabled(); void setFloorNumberEnabled(bool enable); bool getFloorNumberEnabled(); void setCreateMapTextFileEnabled(bool enable); @@ -206,7 +206,7 @@ private: bool enableHiddenItemQuantity; bool enableHiddenItemRequiresItemfinder; bool enableHealLocationRespawnData; - bool enableObjectEventInConnection; + bool enableEventCloneObject; bool enableFloorNumber; bool createMapTextFile; bool enableTripleLayerMetatiles; diff --git a/include/core/event.h b/include/core/event.h index cce095d0..c6ecb5fd 100644 --- a/include/core/event.h +++ b/include/core/event.h @@ -14,6 +14,7 @@ class EventType { public: static QString Object; + static QString CloneObject; static QString Warp; static QString Trigger; static QString WeatherTrigger; @@ -23,6 +24,16 @@ public: static QString HealLocation; }; +class EventGroup +{ +public: + static QString Object; + static QString Warp; + static QString Coord; + static QString Bg; + static QString Heal; +}; + class DraggablePixmapItem; class Project; class Event @@ -68,6 +79,7 @@ public: static Event* createNewEvent(QString, QString, Project*); static Event* createNewObjectEvent(Project*); + static Event* createNewCloneObjectEvent(Project*, QString); static Event* createNewWarpEvent(QString); static Event* createNewHealLocationEvent(QString); static Event* createNewTriggerEvent(Project*); @@ -75,8 +87,11 @@ public: static Event* createNewSignEvent(Project*); static Event* createNewHiddenItemEvent(Project*); static Event* createNewSecretBaseEvent(Project*); + static bool isValidType(QString event_type); + static QString typeToGroup(QString event_type); OrderedJson::object buildObjectEventJSON(); + OrderedJson::object buildCloneObjectEventJSON(const QMap &); OrderedJson::object buildWarpEventJSON(const QMap &); OrderedJson::object buildTriggerEventJSON(); OrderedJson::object buildWeatherTriggerEventJSON(); @@ -86,7 +101,7 @@ public: void setPixmapFromSpritesheet(QImage, int, int, bool); int getPixelX(); int getPixelY(); - QMap getExpectedFields(); + QStringList getExpectedFields(); void readCustomValues(QJsonObject values); void addCustomValuesTo(OrderedJson::object *obj); void setFrameFromMovement(QString); diff --git a/include/project.h b/include/project.h index ec1ffe6b..6fac21c9 100644 --- a/include/project.h +++ b/include/project.h @@ -60,7 +60,7 @@ public: QMap mapSectionNameToValue; QMap mapSectionValueToName; QMap eventGraphicsMap; - QStringList gfxNames; + QMap gfxDefines; QStringList songNames; QStringList itemNames; QStringList flagNames; diff --git a/include/ui/neweventtoolbutton.h b/include/ui/neweventtoolbutton.h index 1168d447..fa2405df 100644 --- a/include/ui/neweventtoolbutton.h +++ b/include/ui/neweventtoolbutton.h @@ -11,6 +11,7 @@ public: explicit NewEventToolButton(QWidget *parent = nullptr); QString getSelectedEventType(); QAction *newObjectAction; + QAction *newCloneObjectAction; QAction *newWarpAction; QAction *newHealLocationAction; QAction *newTriggerAction; @@ -20,6 +21,7 @@ public: QAction *newSecretBaseAction; public slots: void newObject(); + void newCloneObject(); void newWarp(); void newHealLocation(); void newTrigger(); diff --git a/src/config.cpp b/src/config.cpp index f9597b4e..32cf00e7 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -522,11 +522,11 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { if (!ok) { logWarn(QString("Invalid config value for enable_heal_location_respawn_data: '%1'. Must be 0 or 1.").arg(value)); } - } else if (key == "enable_object_event_in_connection") { + } else if (key == "enable_event_clone_object") { bool ok; - this->enableObjectEventInConnection = value.toInt(&ok); + this->enableEventCloneObject = value.toInt(&ok); if (!ok) { - logWarn(QString("Invalid config value for enable_object_event_in_connection: '%1'. Must be 0 or 1.").arg(value)); + logWarn(QString("Invalid config value for enable_event_clone_object: '%1'. Must be 0 or 1.").arg(value)); } } else if (key == "enable_floor_number") { bool ok; @@ -570,7 +570,7 @@ void ProjectConfig::setUnreadKeys() { if (!readKeys.contains("enable_hidden_item_quantity")) this->enableHiddenItemQuantity = isPokefirered; if (!readKeys.contains("enable_hidden_item_requires_itemfinder")) this->enableHiddenItemRequiresItemfinder = isPokefirered; if (!readKeys.contains("enable_heal_location_respawn_data")) this->enableHealLocationRespawnData = isPokefirered; - if (!readKeys.contains("enable_object_event_in_connection")) this->enableObjectEventInConnection = isPokefirered; + if (!readKeys.contains("enable_event_clone_object")) this->enableEventCloneObject = isPokefirered; if (!readKeys.contains("enable_floor_number")) this->enableFloorNumber = isPokefirered; if (!readKeys.contains("create_map_text_file")) this->createMapTextFile = (this->baseGameVersion != BaseGameVersion::pokeemerald); } @@ -587,7 +587,7 @@ QMap ProjectConfig::getKeyValueMap() { map.insert("enable_hidden_item_quantity", QString::number(this->enableHiddenItemQuantity)); map.insert("enable_hidden_item_requires_itemfinder", QString::number(this->enableHiddenItemRequiresItemfinder)); map.insert("enable_heal_location_respawn_data", QString::number(this->enableHealLocationRespawnData)); - map.insert("enable_object_event_in_connection", QString::number(this->enableObjectEventInConnection)); + map.insert("enable_event_clone_object", QString::number(this->enableEventCloneObject)); map.insert("enable_floor_number", QString::number(this->enableFloorNumber)); map.insert("create_map_text_file", QString::number(this->createMapTextFile)); map.insert("enable_triple_layer_metatiles", QString::number(this->enableTripleLayerMetatiles)); @@ -628,7 +628,7 @@ void ProjectConfig::onNewConfigFileCreated() { this->enableHiddenItemQuantity = isPokefirered; this->enableHiddenItemRequiresItemfinder = isPokefirered; this->enableHealLocationRespawnData = isPokefirered; - this->enableObjectEventInConnection = isPokefirered; + this->enableEventCloneObject = isPokefirered; this->enableFloorNumber = isPokefirered; this->createMapTextFile = (this->baseGameVersion != BaseGameVersion::pokeemerald); this->useEncounterJson = true; @@ -739,13 +739,13 @@ bool ProjectConfig::getHealLocationRespawnDataEnabled() { return this->enableHealLocationRespawnData; } -void ProjectConfig::setObjectEventInConnectionEnabled(bool enable) { - this->enableObjectEventInConnection = enable; +void ProjectConfig::setEventCloneObjectEnabled(bool enable) { + this->enableEventCloneObject = enable; this->save(); } -bool ProjectConfig::getObjectEventInConnectionEnabled() { - return this->enableObjectEventInConnection; +bool ProjectConfig::getEventCloneObjectEnabled() { + return this->enableEventCloneObject; } void ProjectConfig::setFloorNumberEnabled(bool enable) { diff --git a/src/core/editcommands.cpp b/src/core/editcommands.cpp index b4c96883..f755da6e 100644 --- a/src/core/editcommands.cpp +++ b/src/core/editcommands.cpp @@ -9,18 +9,16 @@ int getEventTypeMask(QList events) { int eventTypeMask = 0; for (auto event : events) { - if (event->get("event_type") == EventType::Object) { + QString groupType = event->get("event_group_type"); + if (groupType == EventGroup::Object) { eventTypeMask |= IDMask_EventType_Object; - } else if (event->get("event_type") == EventType::Warp) { + } else if (groupType == EventGroup::Warp) { eventTypeMask |= IDMask_EventType_Warp; - } else if (event->get("event_type") == EventType::Trigger || - event->get("event_type") == EventType::WeatherTrigger) { + } else if (groupType == EventGroup::Coord) { eventTypeMask |= IDMask_EventType_Trigger; - } else if (event->get("event_type") == EventType::Sign || - event->get("event_type") == EventType::HiddenItem || - event->get("event_type") == EventType::SecretBase) { + } else if (groupType == EventGroup::Bg) { eventTypeMask |= IDMask_EventType_BG; - } else if (event->get("event_type") == EventType::HealLocation) { + } else if (groupType == EventGroup::Heal) { eventTypeMask |= IDMask_EventType_Heal; } } diff --git a/src/core/event.cpp b/src/core/event.cpp index 9de6a02f..39dcd8e8 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -10,6 +10,7 @@ QString EventGroup::Coord = "coord_event_group"; QString EventGroup::Bg = "bg_event_group"; QString EventType::Object = "event_object"; +QString EventType::CloneObject = "event_clone_object"; QString EventType::Warp = "event_warp"; QString EventType::Trigger = "event_trigger"; QString EventType::WeatherTrigger = "event_weather_trigger"; @@ -20,6 +21,7 @@ QString EventType::HealLocation = "event_healspot"; const QMap EventTypeTable = { {EventType::Object, EventGroup::Object}, + {EventType::CloneObject, EventGroup::Object}, {EventType::Warp, EventGroup::Warp}, {EventType::Trigger, EventGroup::Coord}, {EventType::WeatherTrigger, EventGroup::Coord}, @@ -58,6 +60,8 @@ Event* Event::createNewEvent(QString event_type, QString map_name, Project *proj if (event_type == EventType::Object) { event = createNewObjectEvent(project); event->setFrameFromMovement(event->get("movement_type")); + } else if (event_type == EventType::CloneObject) { + event = createNewCloneObjectEvent(project, map_name); } else if (event_type == EventType::Warp) { event = createNewWarpEvent(map_name); } else if (event_type == EventType::HealLocation) { @@ -77,6 +81,8 @@ Event* Event::createNewEvent(QString event_type, QString map_name, Project *proj event = new Event; } + event->put("event_type", event_type); + event->put("event_group_type", typeToGroup(event_type)); event->setX(0); event->setY(0); return event; @@ -85,13 +91,8 @@ Event* Event::createNewEvent(QString event_type, QString map_name, Project *proj Event* Event::createNewObjectEvent(Project *project) { Event *event = new Event; - event->put("event_group_type", EventGroup::Object); - event->put("event_type", EventType::Object); - event->put("sprite", project->gfxNames.first()); + event->put("sprite", project->gfxDefines.keys().first()); event->put("movement_type", project->movementTypes.first()); - if (projectConfig.getObjectEventInConnectionEnabled()) { - event->put("in_connection", false); - } event->put("radius_x", 0); event->put("radius_y", 0); event->put("script_label", "NULL"); @@ -103,11 +104,18 @@ Event* Event::createNewObjectEvent(Project *project) return event; } +Event* Event::createNewCloneObjectEvent(Project *project, QString map_name) +{ + Event *event = new Event; + event->put("sprite", project->gfxDefines.keys().first()); + event->put("target_local_id", 1); + event->put("target_map", map_name); + return event; +} + Event* Event::createNewWarpEvent(QString map_name) { Event *event = new Event; - event->put("event_group_type", EventGroup::Warp); - event->put("event_type", EventType::Warp); event->put("destination_warp", 0); event->put("destination_map_name", map_name); event->put("elevation", 0); @@ -117,8 +125,6 @@ Event* Event::createNewWarpEvent(QString map_name) Event* Event::createNewHealLocationEvent(QString map_name) { Event *event = new Event; - event->put("event_group_type", EventGroup::Heal); - event->put("event_type", EventType::HealLocation); event->put("loc_name", QString(Map::mapConstantFromName(map_name)).remove(0,4)); event->put("id_name", map_name.replace(QRegularExpression("([a-z])([A-Z])"), "\\1_\\2").toUpper()); event->put("elevation", 3); @@ -132,8 +138,6 @@ Event* Event::createNewHealLocationEvent(QString map_name) Event* Event::createNewTriggerEvent(Project *project) { Event *event = new Event; - event->put("event_group_type", EventGroup::Coord); - event->put("event_type", EventType::Trigger); event->put("script_label", "NULL"); event->put("script_var", project->varNames.first()); event->put("script_var_value", "0"); @@ -144,8 +148,6 @@ Event* Event::createNewTriggerEvent(Project *project) Event* Event::createNewWeatherTriggerEvent(Project *project) { Event *event = new Event; - event->put("event_group_type", EventGroup::Coord); - event->put("event_type", EventType::WeatherTrigger); event->put("weather", project->coordEventWeatherNames.first()); event->put("elevation", 0); return event; @@ -154,8 +156,6 @@ Event* Event::createNewWeatherTriggerEvent(Project *project) Event* Event::createNewSignEvent(Project *project) { Event *event = new Event; - event->put("event_group_type", EventGroup::Bg); - event->put("event_type", EventType::Sign); event->put("player_facing_direction", project->bgEventFacingDirections.first()); event->put("script_label", "NULL"); event->put("elevation", 0); @@ -165,8 +165,6 @@ Event* Event::createNewSignEvent(Project *project) Event* Event::createNewHiddenItemEvent(Project *project) { Event *event = new Event; - event->put("event_group_type", EventGroup::Bg); - event->put("event_type", EventType::HiddenItem); event->put("item", project->itemNames.first()); event->put("flag", project->flagNames.first()); event->put("elevation", 3); @@ -182,8 +180,6 @@ Event* Event::createNewHiddenItemEvent(Project *project) Event* Event::createNewSecretBaseEvent(Project *project) { Event *event = new Event; - event->put("event_group_type", EventGroup::Bg); - event->put("event_type", EventType::SecretBase); event->put("secret_base_id", project->secretBaseIds.first()); event->put("elevation", 0); return event; @@ -199,81 +195,76 @@ int Event::getPixelY() return (this->y() * 16) - qMax(0, this->spriteHeight - 16); } -static QMap expectedObjectFields { - {"graphics_id", true}, - {"x", true}, - {"y", true}, - {"elevation", true}, - {"movement_type", true}, - {"movement_range_x", true}, - {"movement_range_y", true}, - {"trainer_type", true}, - {"trainer_sight_or_berry_tree_id", true}, - {"script", true}, - {"flag", true}, +const QStringList expectedObjectFields = { + "graphics_id", + "elevation", + "movement_type", + "movement_range_x", + "movement_range_y", + "trainer_type", + "trainer_sight_or_berry_tree_id", + "script", + "flag", }; -static QMap expectedWarpFields { - {"x", true}, - {"y", true}, - {"elevation", true}, - {"dest_map", true}, - {"dest_warp_id", true}, +const QStringList expectedCloneObjectFields = { + "type", + "graphics_id", + "target_local_id", + "target_map", }; -static QMap expectedTriggerFields { - {"type", true}, - {"x", true}, - {"y", true}, - {"elevation", true}, - {"var", true}, - {"var_value", true}, - {"script", true}, +const QStringList expectedWarpFields = { + "elevation", + "dest_map", + "dest_warp_id", }; -static QMap expectedWeatherTriggerFields { - {"type", true}, - {"x", true}, - {"y", true}, - {"elevation", true}, - {"weather", true}, +const QStringList expectedTriggerFields = { + "type", + "elevation", + "var", + "var_value", + "script", }; -static QMap expectedSignFields { - {"type", true}, - {"x", true}, - {"y", true}, - {"elevation", true}, - {"player_facing_dir", true}, - {"script", true}, +const QStringList expectedWeatherTriggerFields = { + "type", + "elevation", + "weather", }; -static QMap expectedHiddenItemFields { - {"type", true}, - {"x", true}, - {"y", true}, - {"elevation", true}, - {"item", true}, - {"flag", true}, +const QStringList expectedSignFields = { + "type", + "elevation", + "player_facing_dir", + "script", }; -static QMap expectedSecretBaseFields { - {"type", true}, - {"x", true}, - {"y", true}, - {"elevation", true}, - {"secret_base_id", true}, +const QStringList expectedHiddenItemFields = { + "type", + "elevation", + "item", + "flag", }; -QMap Event::getExpectedFields() +const QStringList expectedSecretBaseFields = { + "type", + "elevation", + "secret_base_id", +}; + +QStringList Event::getExpectedFields() { QString type = this->get("event_type"); - QMap expectedFields = QMap(); + QStringList expectedFields = QStringList(); if (type == EventType::Object) { expectedFields = expectedObjectFields; - if (projectConfig.getObjectEventInConnectionEnabled()) { - expectedFields.insert("in_connection", true); + if (projectConfig.getEventCloneObjectEnabled()) { + expectedFields.append("type"); } + } else if (type == EventType::CloneObject) { + expectedFields = expectedCloneObjectFields; } else if (type == EventType::Warp) { expectedFields = expectedWarpFields; } else if (type == EventType::Trigger) { @@ -285,23 +276,24 @@ QMap Event::getExpectedFields() } else if (type == EventType::HiddenItem) { expectedFields = expectedHiddenItemFields; if (projectConfig.getHiddenItemQuantityEnabled()) { - expectedFields.insert("quantity", true); + expectedFields.append("quantity"); } if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) { - expectedFields.insert("underfoot", true); + expectedFields.append("underfoot"); } } else if (type == EventType::SecretBase) { expectedFields = expectedSecretBaseFields; } + expectedFields << "x" << "y"; return expectedFields; }; void Event::readCustomValues(QJsonObject values) { this->customValues.clear(); - QMap expectedValues = this->getExpectedFields(); + QStringList expectedFields = this->getExpectedFields(); for (QString key : values.keys()) { - if (!expectedValues.contains(key)) { + if (!expectedFields.contains(key)) { this->customValues[key] = values[key].toString(); } } @@ -318,24 +310,38 @@ void Event::addCustomValuesTo(OrderedJson::object *obj) OrderedJson::object Event::buildObjectEventJSON() { - OrderedJson::object eventObj; - eventObj["graphics_id"] = this->get("sprite"); - if (projectConfig.getObjectEventInConnectionEnabled()) { - eventObj["in_connection"] = this->getInt("in_connection") > 0 || this->get("in_connection") == "TRUE"; + OrderedJson::object objectObj; + if (projectConfig.getEventCloneObjectEnabled()) { + objectObj["type"] = "object"; } - eventObj["x"] = this->getS16("x"); - eventObj["y"] = this->getS16("y"); - eventObj["elevation"] = this->getInt("elevation"); - eventObj["movement_type"] = this->get("movement_type"); - eventObj["movement_range_x"] = this->getInt("radius_x"); - eventObj["movement_range_y"] = this->getInt("radius_y"); - eventObj["trainer_type"] = this->get("trainer_type"); - eventObj["trainer_sight_or_berry_tree_id"] = this->get("sight_radius_tree_id"); - eventObj["script"] = this->get("script_label"); - eventObj["flag"] = this->get("event_flag"); - this->addCustomValuesTo(&eventObj); + objectObj["graphics_id"] = this->get("sprite"); + objectObj["x"] = this->getS16("x"); + objectObj["y"] = this->getS16("y"); + objectObj["elevation"] = this->getInt("elevation"); + objectObj["movement_type"] = this->get("movement_type"); + objectObj["movement_range_x"] = this->getInt("radius_x"); + objectObj["movement_range_y"] = this->getInt("radius_y"); + objectObj["trainer_type"] = this->get("trainer_type"); + objectObj["trainer_sight_or_berry_tree_id"] = this->get("sight_radius_tree_id"); + objectObj["script"] = this->get("script_label"); + objectObj["flag"] = this->get("event_flag"); + this->addCustomValuesTo(&objectObj); - return eventObj; + return objectObj; +} + +OrderedJson::object Event::buildCloneObjectEventJSON(const QMap &mapNamesToMapConstants) +{ + OrderedJson::object cloneObj; + cloneObj["type"] = "clone"; + cloneObj["graphics_id"] = this->get("sprite"); + cloneObj["x"] = this->getS16("x"); + cloneObj["y"] = this->getS16("y"); + cloneObj["target_local_id"] = this->getInt("target_local_id"); + cloneObj["target_map"] = mapNamesToMapConstants.value(this->get("target_map")); + this->addCustomValuesTo(&cloneObj); + + return cloneObj; } OrderedJson::object Event::buildWarpEventJSON(const QMap &mapNamesToMapConstants) diff --git a/src/editor.cpp b/src/editor.cpp index 2a4f4ed9..d15b3bbc 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -2064,7 +2064,7 @@ DraggablePixmapItem* Editor::addNewEvent(QString event_type) { bool Editor::eventLimitReached(Map *map, QString event_type) { if (project && map && !event_type.isEmpty()) { - if (event_type == EventType::Object) + if (Event::typeToGroup(event_type) == EventGroup::Object) return map->events.value(EventGroup::Object).length() >= project->getMaxObjectEvents(); } return false; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 190719d0..f3133137 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -389,23 +389,13 @@ void MainWindow::setProjectSpecificUIVisibility() break; } - if (projectConfig.getEventWeatherTriggerEnabled()) { - ui->newEventToolButton->newWeatherTriggerAction->setVisible(true); - } else { - ui->newEventToolButton->newWeatherTriggerAction->setVisible(false); - } - if (projectConfig.getEventSecretBaseEnabled()) { - ui->newEventToolButton->newSecretBaseAction->setVisible(true); - } else { - ui->newEventToolButton->newSecretBaseAction->setVisible(false); - } - if (projectConfig.getFloorNumberEnabled()) { - ui->spinBox_FloorNumber->setVisible(true); - ui->label_FloorNumber->setVisible(true); - } else { - ui->spinBox_FloorNumber->setVisible(false); - ui->label_FloorNumber->setVisible(false); - } + ui->newEventToolButton->newWeatherTriggerAction->setVisible(projectConfig.getEventWeatherTriggerEnabled()); + ui->newEventToolButton->newSecretBaseAction->setVisible(projectConfig.getEventSecretBaseEnabled()); + ui->newEventToolButton->newCloneObjectAction->setVisible(projectConfig.getEventCloneObjectEnabled()); + + bool floorNumEnabled = projectConfig.getFloorNumberEnabled(); + ui->spinBox_FloorNumber->setVisible(floorNumEnabled); + ui->label_FloorNumber->setVisible(floorNumEnabled); } void MainWindow::mapSortOrder_changed(QAction *action) @@ -1859,7 +1849,7 @@ void MainWindow::addNewEvent(QString event_type) } else { QMessageBox msgBox(this); msgBox.setText("Failed to add new event"); - if (event_type == EventType::Object) { + if (Event::typeToGroup(event_type) == EventGroup::Object) { msgBox.setInformativeText(QString("The limit for object events (%1) has been reached.\n\n" "This limit can be adjusted with OBJECT_EVENT_TEMPLATES_COUNT in 'include/constants/global.h'.") .arg(editor->project->getMaxObjectEvents())); @@ -1879,57 +1869,20 @@ void MainWindow::updateObjects() { selectedHealspot = nullptr; ui->tabWidget_EventType->clear(); - bool hasObjects = false; - bool hasWarps = false; - bool hasTriggers = false; - bool hasBGs = false; - bool hasHealspots = false; - - for (DraggablePixmapItem *item : editor->getObjects()) - { - QString event_type = item->event->get("event_type"); - - if (event_type == EventType::Object) { - hasObjects = true; - } - else if (event_type == EventType::Warp) { - hasWarps = true; - } - else if (event_type == EventType::Trigger || event_type == EventType::WeatherTrigger) { - hasTriggers = true; - } - else if (event_type == EventType::Sign || event_type == EventType::HiddenItem || event_type == EventType::SecretBase) { - hasBGs = true; - } - else if (event_type == EventType::HealLocation) { - hasHealspots = true; - } - } - - if (hasObjects) - { + if (editor->map->events.value(EventGroup::Object).length()) ui->tabWidget_EventType->addTab(eventTabObjectWidget, "Objects"); - } - if (hasWarps) - { + if (editor->map->events.value(EventGroup::Warp).length()) ui->tabWidget_EventType->addTab(eventTabWarpWidget, "Warps"); - } - if (hasTriggers) - { + if (editor->map->events.value(EventGroup::Coord).length()) ui->tabWidget_EventType->addTab(eventTabTriggerWidget, "Triggers"); - } - if (hasBGs) - { + if (editor->map->events.value(EventGroup::Bg).length()) ui->tabWidget_EventType->addTab(eventTabBGWidget, "BGs"); - } - if (hasHealspots) - { + if (editor->map->events.value(EventGroup::Heal).length()) ui->tabWidget_EventType->addTab(eventTabHealspotWidget, "Healspots"); - } updateSelectedObjects(); } @@ -1956,7 +1909,6 @@ void MainWindow::updateSelectedObjects() { QList frames; - bool inConnectionEnabled = projectConfig.getObjectEventInConnectionEnabled(); bool quantityEnabled = projectConfig.getHiddenItemQuantityEnabled(); bool underfootEnabled = projectConfig.getHiddenItemRequiresItemfinderEnabled(); bool respawnDataEnabled = projectConfig.getHealLocationRespawnDataEnabled(); @@ -2021,7 +1973,6 @@ void MainWindow::updateSelectedObjects() { field_labels["radius_y"] = "Movement Radius Y"; field_labels["trainer_type"] = "Trainer Type"; field_labels["sight_radius_tree_id"] = "Sight Radius / Berry Tree ID"; - field_labels["in_connection"] = "In Connection"; field_labels["destination_warp"] = "Destination Warp"; field_labels["destination_map_name"] = "Destination Map"; field_labels["script_var"] = "Var"; @@ -2035,13 +1986,15 @@ void MainWindow::updateSelectedObjects() { field_labels["secret_base_id"] = "Secret Base Id"; field_labels["respawn_map"] = "Respawn Map"; field_labels["respawn_npc"] = "Respawn NPC"; + field_labels["target_local_id"] = "Target Local Id"; + field_labels["target_map"] = "Target Map"; QStringList fields; if (event_type == EventType::Object) { frame->ui->sprite->setVisible(true); - frame->ui->comboBox_sprite->addItems(editor->project->gfxNames); + frame->ui->comboBox_sprite->addItems(editor->project->gfxDefines.keys()); frame->ui->comboBox_sprite->setCurrentIndex(frame->ui->comboBox_sprite->findText(item->event->get("sprite"))); connect(frame->ui->comboBox_sprite, &QComboBox::currentTextChanged, item, &DraggablePixmapItem::set_sprite); connect(frame->ui->comboBox_sprite, &QComboBox::currentTextChanged, this, &MainWindow::markMapEdited); @@ -2063,7 +2016,17 @@ void MainWindow::updateSelectedObjects() { fields << "event_flag"; fields << "trainer_type"; fields << "sight_radius_tree_id"; - if (inConnectionEnabled) fields << "in_connection"; + } + else if (event_type == EventType::CloneObject) { + frame->ui->sprite->setVisible(true); + frame->ui->comboBox_sprite->setEnabled(false); + frame->ui->comboBox_sprite->addItem(item->event->get("sprite")); + + frame->ui->spinBox_z->setVisible(false); + frame->ui->label_z->setVisible(false); + + fields << "target_local_id"; + fields << "target_map"; } else if (event_type == EventType::Warp) { fields << "destination_map_name"; @@ -2101,8 +2064,8 @@ void MainWindow::updateSelectedObjects() { } // Some keys shouldn't use a combobox - QStringList spinKeys = {"quantity", "respawn_npc"}; - QStringList checkKeys = {"underfoot", "in_connection"}; + QStringList spinKeys = {"quantity", "respawn_npc", "target_local_id"}; + QStringList checkKeys = {"underfoot"}; for (QString key : fields) { QString value = item->event->get(key); QWidget *widget = new QWidget(frame); @@ -2226,8 +2189,6 @@ void MainWindow::updateSelectedObjects() { combo->setToolTip("The maximum sight range of a trainer,\n" "OR the unique id of the berry tree."); combo->setMinimumContentsLength(4); - } else if (key == "in_connection") { - check->setToolTip("Check if object is positioned in the connection to another map."); } else if (key == "respawn_map") { if (!editor->project->mapNames.contains(value)) { combo->addItem(value); @@ -2239,6 +2200,27 @@ void MainWindow::updateSelectedObjects() { "upon respawning after whiteout."); spin->setMinimum(1); spin->setMaximum(126); + } else if (key == "target_local_id") { + spin->setToolTip("event_object ID of the object being cloned."); + spin->setMinimum(1); + spin->setMaximum(126); + connect(spin, QOverload::of(&NoScrollSpinBox::valueChanged), [item, frame](int value) { + item->event->put("target_local_id", value); + item->updatePixmap(); + frame->ui->comboBox_sprite->setItemText(0, item->event->get("sprite")); + }); + } else if (key == "target_map") { + if (!editor->project->mapNames.contains(value)) { + combo->addItem(value); + } + combo->addItems(editor->project->mapNames); + combo->setCurrentIndex(combo->findText(value)); + combo->setToolTip("The name of the map that the object being cloned is on."); + connect(combo, static_cast(&QComboBox::currentTextChanged), this, [item, frame](QString value){ + item->event->put("target_map", value); + item->updatePixmap(); + frame->ui->comboBox_sprite->setItemText(0, item->event->get("sprite")); + }); } else { combo->addItem(value); } diff --git a/src/project.cpp b/src/project.cpp index 98bc2cd5..aedfdf64 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -213,26 +213,51 @@ bool Project::loadMapData(Map* map) { // Events map->events[EventGroup::Object].clear(); QJsonArray objectEventsArr = mapObj["object_events"].toArray(); + bool hasCloneObjects = projectConfig.getEventCloneObjectEnabled(); for (int i = 0; i < objectEventsArr.size(); i++) { QJsonObject event = objectEventsArr[i].toObject(); - Event *object = new Event(event, EventType::Object); - object->put("map_name", map->name); - object->put("sprite", event["graphics_id"].toString()); - if (projectConfig.getObjectEventInConnectionEnabled()) { - object->put("in_connection", event["in_connection"].toBool()); + // If clone objects are not enabled then no type field is present + QString type = hasCloneObjects ? event["type"].toString() : "object"; + if (type == "object") { + Event *object = new Event(event, EventType::Object); + object->put("map_name", map->name); + object->put("sprite", event["graphics_id"].toString()); + object->put("x", QString::number(event["x"].toInt())); + object->put("y", QString::number(event["y"].toInt())); + object->put("elevation", QString::number(event["elevation"].toInt())); + object->put("movement_type", event["movement_type"].toString()); + object->put("radius_x", QString::number(event["movement_range_x"].toInt())); + object->put("radius_y", QString::number(event["movement_range_y"].toInt())); + object->put("trainer_type", event["trainer_type"].toString()); + object->put("sight_radius_tree_id", event["trainer_sight_or_berry_tree_id"].toString()); + object->put("script_label", event["script"].toString()); + object->put("event_flag", event["flag"].toString()); + object->put("event_group_type", EventGroup::Object); + map->events[EventGroup::Object].append(object); + } else if (type == "clone") { + Event *object = new Event(event, EventType::CloneObject); + object->put("map_name", map->name); + object->put("sprite", event["graphics_id"].toString()); + object->put("x", QString::number(event["x"].toInt())); + object->put("y", QString::number(event["y"].toInt())); + object->put("target_local_id", QString::number(event["target_local_id"].toInt())); + + // Ensure the target map constant is valid before adding it to the events. + QString mapConstant = event["target_map"].toString(); + if (mapConstantsToMapNames.contains(mapConstant)) { + object->put("target_map", mapConstantsToMapNames.value(mapConstant)); + object->put("event_group_type", EventGroup::Object); + map->events[EventGroup::Object].append(object); + } else if (mapConstant == NONE_MAP_CONSTANT) { + object->put("target_map", NONE_MAP_NAME); + object->put("event_group_type", EventGroup::Object); + map->events[EventGroup::Object].append(object); + } else { + logError(QString("Destination map constant '%1' is invalid").arg(mapConstant)); + } + } else { + logError(QString("Map %1 object_event %2 has invalid type '%3'. Must be 'object' or 'clone'.").arg(map->name).arg(i).arg(type)); } - object->put("x", QString::number(event["x"].toInt())); - object->put("y", QString::number(event["y"].toInt())); - object->put("elevation", QString::number(event["elevation"].toInt())); - object->put("movement_type", event["movement_type"].toString()); - object->put("radius_x", QString::number(event["movement_range_x"].toInt())); - object->put("radius_y", QString::number(event["movement_range_y"].toInt())); - object->put("trainer_type", event["trainer_type"].toString()); - object->put("sight_radius_tree_id", event["trainer_sight_or_berry_tree_id"].toString()); - object->put("script_label", event["script"].toString()); - object->put("event_flag", event["flag"].toString()); - object->put("event_group_type", EventGroup::Object); - map->events[EventGroup::Object].append(object); } map->events[EventGroup::Warp].clear(); @@ -1334,9 +1359,15 @@ void Project::saveMap(Map *map) { // Object events OrderedJson::array objectEventsArr; for (int i = 0; i < map->events[EventGroup::Object].length(); i++) { - Event *object_event = map->events[EventGroup::Object].value(i); - OrderedJson::object eventObj = object_event->buildObjectEventJSON(); - objectEventsArr.push_back(eventObj); + Event *event = map->events[EventGroup::Object].value(i); + QString event_type = event->get("event_type"); + OrderedJson::object jsonObj; + if (event_type == EventType::Object) { + jsonObj = event->buildObjectEventJSON(); + } else if (event_type == EventType::CloneObject) { + jsonObj = event->buildCloneObjectEventJSON(mapNamesToMapConstants); + } + objectEventsArr.push_back(jsonObj); } mapObj["object_events"] = objectEventsArr; @@ -2248,9 +2279,8 @@ bool Project::readObjEventGfxConstants() { QStringList objEventGfxPrefixes("\\bOBJ_EVENT_GFX_"); QString filename = "include/constants/event_objects.h"; fileWatcher.addPath(root + "/" + filename); - QMap gfxDefines = parser.readCDefines(filename, objEventGfxPrefixes); - this->gfxNames = gfxDefines.keys(); - if (this->gfxNames.isEmpty()) { + this->gfxDefines = parser.readCDefines(filename, objEventGfxPrefixes); + if (this->gfxDefines.isEmpty()) { logError(QString("Failed to read object event graphics constants from %1.").arg(filename)); return false; } @@ -2380,25 +2410,48 @@ void Project::setEventPixmap(Event * event, bool forceLoad) { event->spriteHeight = 16; event->usingSprite = false; - QString event_type = event->get("event_type"); - if (event_type == EventType::Object) { - QString gfxName = event->get("sprite"); + QString group_type = event->get("event_group_type"); + if (group_type == EventGroup::Object) { + QString gfxName; + QString movement; + QString event_type = event->get("event_type"); + if (event_type == EventType::CloneObject) { + // Try to get the targeted object to clone + int eventIndex = event->getInt("target_local_id") - 1; + Map * clonedMap = getMap(event->get("target_map")); + Event * clonedEvent = clonedMap ? clonedMap->events[EventGroup::Object].value(eventIndex, nullptr) : nullptr; + if (clonedEvent && clonedEvent->get("event_type") == EventType::Object) { + // Get graphics data from cloned object + gfxName = clonedEvent->get("sprite"); + movement = clonedEvent->get("movement_type"); + } else { + // Invalid object specified, use default graphics data (as would be shown in-game) + gfxName = gfxDefines.key(0); + movement = movementTypes.first(); + } + // Update clone object's sprite text to match target object + event->put("sprite", gfxName); + } else { + // Get graphics data of regular object + gfxName = event->get("sprite"); + movement = event->get("movement_type"); + } EventGraphics * eventGfx = eventGraphicsMap.value(gfxName, nullptr); if (!eventGfx || eventGfx->spritesheet.isNull()) { // No sprite associated with this gfx constant. // Use default sprite instead. event->pixmap = QPixmap(":/images/Entities_16x16.png").copy(0, 0, 16, 16); } else { - event->setFrameFromMovement(facingDirections.value(event->get("movement_type"))); + event->setFrameFromMovement(facingDirections.value(movement)); event->setPixmapFromSpritesheet(eventGfx->spritesheet, eventGfx->spriteWidth, eventGfx->spriteHeight, eventGfx->inanimate); } - } else if (event_type == EventType::Warp) { + } else if (group_type == EventGroup::Warp) { event->pixmap = QPixmap(":/images/Entities_16x16.png").copy(16, 0, 16, 16); - } else if (event_type == EventType::Trigger || event_type == EventType::WeatherTrigger) { + } else if (group_type == EventGroup::Coord) { event->pixmap = QPixmap(":/images/Entities_16x16.png").copy(32, 0, 16, 16); - } else if (event_type == EventType::Sign || event_type == EventType::HiddenItem || event_type == EventType::SecretBase) { + } else if (group_type == EventGroup::Bg) { event->pixmap = QPixmap(":/images/Entities_16x16.png").copy(48, 0, 16, 16); - } else if (event_type == EventType::HealLocation) { + } else if (group_type == EventGroup::Heal) { event->pixmap = QPixmap(":/images/Entities_16x16.png").copy(64, 0, 16, 16); } } @@ -2413,7 +2466,8 @@ bool Project::readEventGraphics() { qDeleteAll(eventGraphicsMap); eventGraphicsMap.clear(); - for (QString gfxName : this->gfxNames) { + QStringList gfxNames = gfxDefines.keys(); + for (QString gfxName : gfxNames) { EventGraphics * eventGraphics = new EventGraphics; QString info_label = pointerHash[gfxName].replace("&", ""); diff --git a/src/ui/neweventtoolbutton.cpp b/src/ui/neweventtoolbutton.cpp index 58510a2d..94db8807 100644 --- a/src/ui/neweventtoolbutton.cpp +++ b/src/ui/neweventtoolbutton.cpp @@ -18,6 +18,10 @@ void NewEventToolButton::init() this->newObjectAction->setIcon(QIcon(":/icons/add.ico")); connect(this->newObjectAction, &QAction::triggered, this, &NewEventToolButton::newObject); + this->newCloneObjectAction = new QAction("New Object Clone", this); + this->newCloneObjectAction->setIcon(QIcon(":/icons/add.ico")); + connect(this->newCloneObjectAction, &QAction::triggered, this, &NewEventToolButton::newCloneObject); + this->newWarpAction = new QAction("New Warp", this); this->newWarpAction->setIcon(QIcon(":/icons/add.ico")); connect(this->newWarpAction, &QAction::triggered, this, &NewEventToolButton::newWarp); @@ -50,6 +54,7 @@ void NewEventToolButton::init() QMenu *alignMenu = new QMenu(); alignMenu->addAction(this->newObjectAction); + alignMenu->addAction(this->newCloneObjectAction); alignMenu->addAction(this->newWarpAction); //alignMenu->addAction(this->newHealLocationAction); alignMenu->addAction(this->newTriggerAction); @@ -72,6 +77,12 @@ void NewEventToolButton::newObject() emit newEventAdded(this->selectedEventType); } +void NewEventToolButton::newCloneObject() +{ + this->selectedEventType = EventType::CloneObject; + emit newEventAdded(this->selectedEventType); +} + void NewEventToolButton::newWarp() { this->selectedEventType = EventType::Warp;