diff --git a/src/Application/Application.h b/src/Application/Application.h index 2f30b0e..71141b1 100644 --- a/src/Application/Application.h +++ b/src/Application/Application.h @@ -137,6 +137,7 @@ class Application: public Platform::Sdl2Application, public efsw::FileWatchListe void drawEyeColourPicker(); void drawCustomFrameStyles(); void drawArmour(); + void drawBLAttachment(); void drawCustomArmourStyles(); void drawWeapons(); void drawWeaponCategory(Containers::StringView name, Containers::ArrayView weapons_view, @@ -276,9 +277,10 @@ class Application: public Platform::Sdl2Application, public efsw::FileWatchListe bool _bLaunchersDirty = false; bool _eLaunchersDirty = false; + Containers::Optional _selectedArmourSlot{Containers::NullOpt}; Containers::StaticArray<38, std::int32_t> _selectedArmourDecals{ValueInit}; Containers::StaticArray<38, std::int32_t> _selectedArmourAccessories{ValueInit}; - std::int32_t _selectedBLPlacement = 0; + std::uint32_t _selectedBLPlacement = 0; std::int32_t _selectedWeaponPart = 0; std::int32_t _selectedWeaponDecal = 0; std::int32_t _selectedWeaponAccessory = 0; diff --git a/src/Application/Application_MassViewer.cpp b/src/Application/Application_MassViewer.cpp index db74921..d4c86c0 100644 --- a/src/Application/Application_MassViewer.cpp +++ b/src/Application/Application_MassViewer.cpp @@ -88,6 +88,7 @@ Application::drawMassViewer() { _jointsDirty = false; _stylesDirty = false; _eyeFlareDirty = false; + _selectedArmourSlot = Containers::NullOpt; _selectedArmourDecals = Containers::StaticArray<38, std::int32_t>{ValueInit}; _selectedArmourAccessories = Containers::StaticArray<38, std::int32_t>{ValueInit}; _selectedBLPlacement = 0; @@ -126,6 +127,13 @@ Application::drawMassViewer() { ImGui::EndTabItem(); } + if(_currentMass->bulletLauncherAttachmentStyle() != GameObjects::BulletLauncherAttachmentStyle::NotFound && + ImGui::BeginTabItem("Bullet launcher attachment")) + { + drawBLAttachment(); + ImGui::EndTabItem(); + } + if(ImGui::BeginTabItem("Custom armour styles")) { drawCustomArmourStyles(); ImGui::EndTabItem(); diff --git a/src/Application/Application_MassViewer_Armour.cpp b/src/Application/Application_MassViewer_Armour.cpp index 98f6184..9ee33f5 100644 --- a/src/Application/Application_MassViewer_Armour.cpp +++ b/src/Application/Application_MassViewer_Armour.cpp @@ -29,63 +29,77 @@ Application::drawArmour() { return; } - if(ImGui::Button(ICON_FA_UNDO_ALT " Reset all")) { - _currentMass->getArmourParts(); - _currentMass->getBulletLauncherAttachments(); - } - - if(!ImGui::BeginChild("##ArmourParts", {}, ImGuiChildFlags_Border)) { - ImGui::EndChild(); - return; - } - - static Containers::StringView slot_labels[] = { + constexpr static Containers::StringView slot_labels[] = { #define c(enumerator, strenum, name) name, #include "../Maps/ArmourSlots.hpp" #undef c }; - for(std::uint32_t i = 0; i < _currentMass->armourParts().size(); i++) { - ImGui::PushID(int(i)); + auto labels_view = arrayView(slot_labels); - auto& part = _currentMass->armourParts()[i]; + const static float footer_height_to_reserve = ImGui::GetFrameHeightWithSpacing(); - static char header[129] = {'\0'}; + ImGui::BeginGroup(); - std::memset(header, '\0', 129); + if(ImGui::BeginTable("##SlotsTable", 1, + ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_BordersInnerH, + {ImGui::GetContentRegionAvail().x * 0.15f, -footer_height_to_reserve})) + { + ImGui::TableSetupColumn("##Slots", ImGuiTableColumnFlags_WidthStretch); + + for(std::size_t i = 0; i < labels_view.size(); i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if(ImGui::Selectable(labels_view[i].data(), _selectedArmourSlot && (*_selectedArmourSlot) == i, + ImGuiSelectableFlags_SpanAvailWidth)) + { + _selectedArmourSlot = i; + } + } + + ImGui::EndTable(); + } + + if(ImGui::Button(ICON_FA_UNDO_ALT " Reset all")) { + _currentMass->getArmourParts(); + } + + ImGui::EndGroup(); + + ImGui::SameLine(); + + if(!_selectedArmourSlot) { + ImGui::TextUnformatted("No selected armour slot."); + return; + } + + auto& part = _currentMass->armourParts()[*_selectedArmourSlot]; + + ImGui::BeginGroup(); + + if(ImGui::BeginChild("##ArmourEditor", {0.0f, -footer_height_to_reserve})) { + + ImGui::SeparatorText("Part"); if(GameData::armour_sets.find(part.id) != GameData::armour_sets.cend()) { - std::snprintf(header, 128, "%s: %s###%u", - slot_labels[std::uint32_t(part.slot)].data(), - GameData::armour_sets.at(part.id).name.data(), - std::uint32_t(part.slot)); + ImGui::Text("Set name: %s", GameData::armour_sets.at(part.id).name.data()); } else { - std::snprintf(header, 128, "%s: %i###%u", - slot_labels[std::uint32_t(part.slot)].data(), - part.id, - std::uint32_t(part.slot)); + ImGui::Text("Set ID: %u", part.id); } - if(ImGui::CollapsingHeader(header)) { - ImGui::BeginGroup(); + ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x * 0.491f); + if(ImGui::SmallButton("Change")) { + ImGui::OpenPopup("##ArmourPartPopup"); + } + if(ImGui::BeginPopup("##ArmourPartPopup")) { if(ImGui::BeginListBox("##ChangePart")) { - if(std::strncmp("Neck", slot_labels[std::uint32_t(part.slot)].data(), 4) != 0) { - for(auto& set : GameData::armour_sets) { - if(ImGui::Selectable(set.second.name.data(), set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) { - part.id = set.first; - } - } - } - else { - for(auto& set : GameData::armour_sets) { - if(!set.second.neck_compatible) { - continue; - } - - if(ImGui::Selectable(set.second.name.data(), set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) { + for(auto& set : GameData::armour_sets) { + if(part.slot != GameObjects::ArmourPart::Slot::Neck || set.second.neck_compatible) { + if(ImGui::Selectable(set.second.name.data(), set.first == part.id, + ImGuiSelectableFlags_SpanAvailWidth)) + { part.id = set.first; } } @@ -93,208 +107,231 @@ Application::drawArmour() { ImGui::EndListBox(); } - ImGui::EndGroup(); - - ImGui::SameLine(); - - ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); - - ImGui::SameLine(); - - ImGui::BeginGroup(); - - ImGui::TextUnformatted("Styles:"); - - for(std::int32_t j = 0; j < 4; j++) { - drawAlignedText("Slot %d:", j + 1); - - ImGui::SameLine(); - - ImGui::PushID(j); - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 2.0f); - if(ImGui::BeginCombo("##Style", getStyleName(part.styles[j], _currentMass->armourCustomStyles()).data())) { - for(const auto& style : GameData::style_names) { - if(ImGui::Selectable(getStyleName(style.first, _currentMass->armourCustomStyles()).data(), part.styles[j] == style.first)) { - part.styles[j] = style.first; - } - } - - ImGui::EndCombo(); - } - - ImGui::PopID(); - } - - ImGui::EndGroup(); - - ImGui::Separator(); - - ImGui::PushID("Decal"); - - drawAlignedText("Showing/editing decal"); - for(std::uint32_t j = 0; j < part.decals.size(); j++) { - ImGui::SameLine(); - ImGui::RadioButton(std::to_string(j + 1).c_str(), &_selectedArmourDecals[i], int(j)); - } - - drawDecalEditor(part.decals[_selectedArmourDecals[i]]); - - ImGui::PopID(); - - if(!part.accessories.isEmpty()) { - ImGui::Separator(); - - ImGui::PushID("Accessory"); - - drawAlignedText("Showing/editing accessory"); - for(std::uint32_t j = 0; j < part.accessories.size(); j++) { - ImGui::SameLine(); - ImGui::RadioButton(std::string{char(65 + j)}.c_str(), &_selectedArmourAccessories[i], int(j)); - } - - drawAccessoryEditor(part.accessories[_selectedArmourAccessories[i]], _currentMass->armourCustomStyles()); - - ImGui::PopID(); - } - - ImGui::Separator(); - - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { - _modifiedBySaveTool = true; - if(!_currentMass->writeArmourPart(part.slot)) { - _modifiedBySaveTool = false; - _queue.addToast(Toast::Type::Error, _currentMass->lastError()); - } - } + ImGui::EndPopup(); } - ImGui::PopID(); + ImGui::SeparatorText("Styles"); + + for(std::int32_t i = 0; i < 4; i++) { + drawAlignedText("Slot %d:", i + 1); + + ImGui::SameLine(); + + ImGui::PushID(i); + if(ImGui::BeginCombo("##Style", + getStyleName(part.styles[i], _currentMass->armourCustomStyles()).data())) + { + for(const auto& style : GameData::style_names) { + if(ImGui::Selectable(getStyleName(style.first, _currentMass->armourCustomStyles()).data(), + part.styles[i] == style.first)) + { + part.styles[i] = style.first; + } + } + + ImGui::EndCombo(); + } + ImGui::PopID(); + } + + ImGui::SeparatorText("Decals"); + + constexpr static float selectable_width = 25.0f; + + drawAlignedText("Showing/editing decal:"); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f}); + for(std::uint32_t i = 0; i < part.decals.size(); i++) { + ImGui::SameLine(); + if(ImGui::Selectable(std::to_string(i + 1).c_str(), _selectedArmourDecals[*_selectedArmourSlot] == int(i), + ImGuiSelectableFlags_None, {selectable_width, 0.0f})) + { + _selectedArmourDecals[*_selectedArmourSlot] = int(i); + } + if(i != part.decals.size() - 1) { + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + } + } + ImGui::PopStyleVar(); + drawDecalEditor(part.decals[_selectedArmourDecals[*_selectedArmourSlot]]); + + if(!part.accessories.isEmpty()) { + ImGui::SeparatorText("Accessories"); + + drawAlignedText("Showing/editing accessory:"); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f}); + for(std::uint32_t i = 0; i < part.accessories.size(); i++) { + ImGui::SameLine(); + if(ImGui::Selectable((std::string{} + char(i + 65)).c_str(), + _selectedArmourAccessories[*_selectedArmourSlot] == int(i), + ImGuiSelectableFlags_None, {selectable_width, 0.0f})) + { + _selectedArmourAccessories[*_selectedArmourSlot] = int(i); + } + if(i != part.accessories.size() - 1) { + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + } + } + ImGui::PopStyleVar(); + drawAccessoryEditor(part.accessories[_selectedArmourAccessories[*_selectedArmourSlot]], + _currentMass->armourCustomStyles()); + } } - if(_currentMass->bulletLauncherAttachmentStyle() != GameObjects::BulletLauncherAttachmentStyle::NotFound && - ImGui::CollapsingHeader("Bullet launcher placement")) - { - drawAlignedText("Attachment style:"_s); - ImGui::SameLine(); - ImGui::RadioButton("Active one", - _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::ActiveOne); - ImGui::SameLine(); - ImGui::RadioButton("Active one per slot", - _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::ActiveOnePerSlot); - ImGui::SameLine(); - ImGui::RadioButton("All equipped", - _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::AllEquipped); - - ImGui::Separator(); - - drawAlignedText("Launcher slot:"); - ImGui::SameLine(); - ImGui::RadioButton("1", &_selectedBLPlacement, 0); - ImGui::SameLine(); - ImGui::RadioButton("2", &_selectedBLPlacement, 1); - ImGui::SameLine(); - ImGui::RadioButton("3", &_selectedBLPlacement, 2); - ImGui::SameLine(); - ImGui::RadioButton("4", &_selectedBLPlacement, 3); - - auto& placement = _currentMass->bulletLauncherAttachments()[_selectedBLPlacement]; - - static const Containers::StringView socket_labels[] = { - #define c(enumerator, enumstr, name) name, - #include "../Maps/BulletLauncherSockets.hpp" - #undef c - }; - - drawAlignedText("Socket:"); - ImGui::SameLine(); - if(ImGui::BeginCombo("##Socket", socket_labels[std::uint32_t(placement.socket)].data())) { - for(std::uint32_t i = 0; i < (sizeof(socket_labels) / sizeof(socket_labels[0])); i++) { - if(ImGui::Selectable(socket_labels[i].data(), i == std::uint32_t(placement.socket), ImGuiSelectableFlags_SpanAvailWidth)) { - placement.socket = static_cast(i); - } - } - ImGui::EndCombo(); - } - - if(placement.socket != GameObjects::BulletLauncherAttachment::Socket::Auto) { - ImGui::BeginGroup(); - drawAlignedText("Relative position:"); - drawAlignedText("Offset position:"); - drawAlignedText("Relative rotation:"); - drawAlignedText("Offset rotation:"); - drawAlignedText("Scale:"); - ImGui::EndGroup(); - - ImGui::SameLine(); - - ImGui::BeginGroup(); - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::DragFloat("##RelPosX", &placement.relativeLocation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::DragFloat("##RelPosY", &placement.relativeLocation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::DragFloat("##RelPosZ", &placement.relativeLocation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); - ImGui::PopItemWidth(); - - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::SliderFloat("##OffPosX", &placement.offsetLocation.x(), -500.0f, +500.0f, "X: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##OffPosY", &placement.offsetLocation.y(), -500.0f, +500.0f, "Y: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##OffPosZ", &placement.offsetLocation.z(), -500.0f, +500.0f, "Z: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(); - drawHelpMarker("+/-500.0 = +/-250 in-game"); - - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::DragFloat("##RotX", &placement.relativeRotation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "Roll: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::DragFloat("##RotY", &placement.relativeRotation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Yaw: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::DragFloat("##RotZ", &placement.relativeRotation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Pitch: %.3f"); - ImGui::PopItemWidth(); - - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::SliderFloat("##RotOffsetZ", &placement.offsetRotation.z(), -180.0f, +180.0f, "Roll: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##RotOffsetX", &placement.offsetRotation.x(), -30.0f, +30.0f, "Pitch: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##RotOffsetY", &placement.offsetRotation.y(), -30.0f, +30.0f, "Yaw: %.3f"); - ImGui::PopItemWidth(); - - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::SliderFloat("##ScaleX", &placement.relativeScale.x(), 0.5f, 1.5f, "X: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##ScaleY", &placement.relativeScale.y(), 0.5f, 1.5f, "Y: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##ScaleZ", &placement.relativeScale.z(), 0.5f, 1.5f, "Z: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(); - drawHelpMarker("0.5 = 50 in-game\n1.5 = 150 in-game"); - ImGui::EndGroup(); - } + ImGui::EndChild(); + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { _modifiedBySaveTool = true; - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); }) && - !_currentMass->writeBulletLauncherAttachments()) - { + if(!_currentMass->writeArmourPart(part.slot)) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } } - ImGui::EndChild(); + ImGui::EndGroup(); +} + +void +Application::drawBLAttachment() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { + return; + } + + drawAlignedText("Attachment style:"_s); + ImGui::SameLine(); + if(ImGui::RadioButton("Active one", + _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::ActiveOne)) + { + _currentMass->bulletLauncherAttachmentStyle() = GameObjects::BulletLauncherAttachmentStyle::ActiveOne; + } + ImGui::SameLine(); + if(ImGui::RadioButton("Active one per slot", + _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::ActiveOnePerSlot)) + { + _currentMass->bulletLauncherAttachmentStyle() = GameObjects::BulletLauncherAttachmentStyle::ActiveOnePerSlot; + } + ImGui::SameLine(); + if(ImGui::RadioButton("All equipped", + _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::AllEquipped)) + { + _currentMass->bulletLauncherAttachmentStyle() = GameObjects::BulletLauncherAttachmentStyle::ActiveOnePerSlot; + } + + ImGui::Separator(); + + constexpr static float selectable_width = 25.0f; + drawAlignedText("Launcher slot:"); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f}); + for(auto i = 0u; i < _currentMass->bulletLauncherAttachments().size(); i++) { + ImGui::SameLine(); + if(ImGui::Selectable(std::to_string(i).c_str(), _selectedBLPlacement == i, ImGuiSelectableFlags_None, + {selectable_width, 0.0f})) + { + _selectedBLPlacement = i; + } + if(i != _currentMass->bulletLauncherAttachments().size() - 1) { + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + } + } + ImGui::PopStyleVar(); + + auto& placement = _currentMass->bulletLauncherAttachments()[_selectedBLPlacement]; + + static const Containers::StringView socket_labels[] = { + #define c(enumerator, enumstr, name) name, + #include "../Maps/BulletLauncherSockets.hpp" + #undef c + }; + + drawAlignedText("Socket:"); + ImGui::SameLine(); + if(ImGui::BeginCombo("##Socket", socket_labels[std::uint32_t(placement.socket)].data())) { + for(std::uint32_t i = 0; i < (sizeof(socket_labels) / sizeof(socket_labels[0])); i++) { + if(ImGui::Selectable(socket_labels[i].data(), i == std::uint32_t(placement.socket), ImGuiSelectableFlags_SpanAvailWidth)) { + placement.socket = static_cast(i); + } + } + ImGui::EndCombo(); + } + + if(placement.socket != GameObjects::BulletLauncherAttachment::Socket::Auto) { + ImGui::BeginGroup(); + drawAlignedText("Relative position:"); + drawAlignedText("Offset position:"); + drawAlignedText("Relative rotation:"); + drawAlignedText("Offset rotation:"); + drawAlignedText("Scale:"); + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::DragFloat("##RelPosX", &placement.relativeLocation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##RelPosY", &placement.relativeLocation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##RelPosZ", &placement.relativeLocation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); + ImGui::PopItemWidth(); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::SliderFloat("##OffPosX", &placement.offsetLocation.x(), -500.0f, +500.0f, "X: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##OffPosY", &placement.offsetLocation.y(), -500.0f, +500.0f, "Y: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##OffPosZ", &placement.offsetLocation.z(), -500.0f, +500.0f, "Z: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(); + drawHelpMarker("+/-500.0 = +/-250 in-game"); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::DragFloat("##RotX", &placement.relativeRotation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "Roll: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##RotY", &placement.relativeRotation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Yaw: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##RotZ", &placement.relativeRotation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Pitch: %.3f"); + ImGui::PopItemWidth(); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::SliderFloat("##RotOffsetZ", &placement.offsetRotation.z(), -180.0f, +180.0f, "Roll: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##RotOffsetX", &placement.offsetRotation.x(), -30.0f, +30.0f, "Pitch: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##RotOffsetY", &placement.offsetRotation.y(), -30.0f, +30.0f, "Yaw: %.3f"); + ImGui::PopItemWidth(); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::SliderFloat("##ScaleX", &placement.relativeScale.x(), 0.5f, 1.5f, "X: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##ScaleY", &placement.relativeScale.y(), 0.5f, 1.5f, "Y: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##ScaleZ", &placement.relativeScale.z(), 0.5f, 1.5f, "Z: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(); + drawHelpMarker("0.5 = 50 in-game\n1.5 = 150 in-game"); + ImGui::EndGroup(); + } + + _modifiedBySaveTool = true; + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { + _modifiedBySaveTool = true; + if(!_currentMass->writeBulletLauncherAttachments()) { + _modifiedBySaveTool = false; + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + } } void