// MassBuilderSaveTool // Copyright (C) 2021-2022 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include #include "../Maps/Accessories.h" #include "../Maps/ArmourSets.h" #include "../Maps/StyleNames.h" #include "../FontAwesome/IconsFontAwesome5.h" #include "SaveTool.h" static Weapon* current_weapon = nullptr; void SaveTool::drawMassViewer() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { _currentMass = nullptr; current_weapon = nullptr; _uiState = UiState::MainManager; _queue.addToast(Toast::Type::Error, "The selected M.A.S.S. isn't valid anymore."); return; } ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always); ImGui::SetNextWindowSize({Float(windowSize().x()), Float(windowSize().y()) - ImGui::GetItemRectSize().y}, ImGuiCond_Always); if(!ImGui::Begin("##MassViewer", nullptr, ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_NoMove| ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoBringToFrontOnFocus)) { ImGui::End(); return; } if(ImGui::BeginChild("##MassInfo", {0.0f, 0.0f}, true, ImGuiWindowFlags_MenuBar)) { if(ImGui::BeginMenuBar()) { if(ImGui::BeginTable("##MassViewerMenuTable", 4)) { ImGui::TableSetupColumn("##MassName"); ImGui::TableSetupColumn("##Spacer", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("##Updates"); ImGui::TableSetupColumn("##Close", ImGuiTableColumnFlags_WidthFixed); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("M.A.S.S.: %s", (*_currentMass->name()).c_str()); drawTooltip(_currentMass->filename().c_str()); ImGui::TableSetColumnIndex(2); if(_currentMass->dirty()) { ImGui::TextUnformatted("External changes detected"); ImGui::SameLine(); if(ImGui::SmallButton(ICON_FA_SYNC_ALT " Refresh")) { _currentMass->refreshValues(); _currentMass->setDirty(false); _jointsDirty = false; _stylesDirty = false; _eyeFlareDirty = false; } } ImGui::TableSetColumnIndex(3); if(ImGui::SmallButton(ICON_FA_TIMES)) { current_weapon = nullptr; _currentMass = nullptr; _uiState = UiState::MainManager; _jointsDirty = false; _stylesDirty = false; _eyeFlareDirty = false; _selectedArmourDecals = Containers::StaticArray<38, Int>{ValueInit}; _selectedArmourAccessories = Containers::StaticArray<38, Int>{ValueInit}; _selectedWeaponPart = 0; _selectedWeaponDecal = 0; _selectedWeaponAccessory = 0; }; ImGui::EndTable(); } ImGui::EndMenuBar(); } ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::TextWrapped("WARNING: Colours in this app may look different from in-game colours, due to unavoidable differences in the rendering pipeline."); ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::TextWrapped("Real-time updates are disabled on this screen."); if(_currentMass) { if(ImGui::BeginTabBar("##MassTabBar")) { if(ImGui::BeginTabItem("Frame")) { drawFrameInfo(); ImGui::EndTabItem(); } if(ImGui::BeginTabItem("Custom frame styles")) { drawCustomFrameStyles(); ImGui::EndTabItem(); } if(ImGui::BeginTabItem("Armour parts")) { drawArmour(); ImGui::EndTabItem(); } if(ImGui::BeginTabItem("Custom armour styles")) { drawCustomArmourStyles(); ImGui::EndTabItem(); } if(ImGui::BeginTabItem("Weapons (WIP)")) { drawWeapons(); ImGui::EndTabItem(); } if(_currentMass->globalStyles().size() != 0 && ImGui::BeginTabItem("Global styles")) { drawGlobalStyles(); ImGui::EndTabItem(); } if(ImGui::BeginTabItem("Tuning (WIP)")) { drawTuning(); ImGui::EndTabItem(); } ImGui::EndTabBar(); } } } ImGui::EndChild(); ImGui::End(); } void SaveTool::drawFrameInfo() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; } if(!ImGui::BeginChild("##FrameInfo")) { ImGui::EndChild(); return; } ImGui::BeginGroup(); if(ImGui::BeginChild("##JointSliders", {(ImGui::GetContentRegionAvailWidth() / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 300.0f}, true, ImGuiWindowFlags_MenuBar)) { if(ImGui::BeginMenuBar()) { ImGui::TextUnformatted("Joint sliders"); ImGui::EndMenuBar(); } drawJointSliders(); } ImGui::EndChild(); if(ImGui::BeginChild("##FrameStyles", {(ImGui::GetContentRegionAvailWidth() / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 0.0f}, true, ImGuiWindowFlags_MenuBar)) { if(ImGui::BeginMenuBar()) { ImGui::TextUnformatted("Frame styles"); ImGui::EndMenuBar(); } drawFrameStyles(); } ImGui::EndChild(); ImGui::EndGroup(); ImGui::SameLine(); if(ImGui::BeginChild("##EyeFlare", {0.0f, 0.0f}, true, ImGuiWindowFlags_MenuBar)) { if(ImGui::BeginMenuBar()) { ImGui::TextUnformatted("Eye flare colour"); drawHelpMarker("Right-click the picker for more options.", 250.0f); ImGui::EndMenuBar(); } drawEyeColourPicker(); } ImGui::EndChild(); ImGui::EndChild(); } void SaveTool::drawJointSliders() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; } ImGui::TextWrapped("In-game values are multiplied by 100.\nFor example, 0.500 here is equal to 50 in-game."); if(ImGui::BeginTable("##JointSliderTable", 2, ImGuiTableFlags_Borders)) { ImGui::TableSetupColumn("##SliderLabel", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("##Sliders", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Neck"); ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##NeckSlider", &_currentMass->jointSliders().neck, 0.0f, 1.0f)) { _jointsDirty = true; } ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Body"); ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##BodySlider", &_currentMass->jointSliders().body, 0.0f, 1.0f)) { _jointsDirty = true; } ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Shoulders"); ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##ShouldersSlider", &_currentMass->jointSliders().shoulders, 0.0f, 1.0f)) { _jointsDirty = true; } ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Hips"); ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##HipsSlider", &_currentMass->jointSliders().hips, 0.0f, 1.0f)) { _jointsDirty = true; } ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Arms"); ImGui::TableSetColumnIndex(1); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2.0f, 1.0f}); if(ImGui::BeginTable("##UpperLowerArmsLayoutTable", 2)) { ImGui::TableSetupColumn("##UpperArms", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("##LowerArms", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##UpperArmsSlider", &_currentMass->jointSliders().upperArms, 0.0f, 1.0f, "Upper: %.3f")) { _jointsDirty = true; } ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##LowerArmsSlider", &_currentMass->jointSliders().lowerArms, 0.0f, 1.0f, "Lower: %.3f")) { _jointsDirty = true; } ImGui::EndTable(); } ImGui::PopStyleVar(); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Legs"); ImGui::TableSetColumnIndex(1); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2.0f, 1.0f}); if(ImGui::BeginTable("##UpperLowerLegsLayoutTable", 2)) { ImGui::TableSetupColumn("##UpperLegs", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("##LowerLegs", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##UpperLegsSlider", &_currentMass->jointSliders().upperLegs, 0.0f, 1.0f, "Upper: %.3f")) { _jointsDirty = true; } ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##LowerLegsSlider", &_currentMass->jointSliders().lowerLegs, 0.0f, 1.0f, "Lower: %.3f")) { _jointsDirty = true; } ImGui::EndTable(); } ImGui::PopStyleVar(); ImGui::EndTable(); } if(!_jointsDirty) { ImGui::BeginDisabled(); ImGui::Button(ICON_FA_SAVE " Save"); ImGui::SameLine(); ImGui::Button(ICON_FA_UNDO " Reset"); ImGui::EndDisabled(); } else { if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { if(!_currentMass->writeJointSliders()) { _queue.addToast(Toast::Type::Error, "Error: " + Mass::lastError()); } _jointsDirty = false; } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO " Reset")) { _currentMass->getJointSliders(); _jointsDirty = false; } } } void SaveTool::drawFrameStyles() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; } for(Int i = 0; i < 4; i++) { ImGui::AlignTextToFramePadding(); ImGui::Text("Slot %d:", i + 1); ImGui::SameLine(); ImGui::PushID(i); if(ImGui::BeginCombo("##Style", getStyleName(_currentMass->frameStyles()[i], _currentMass->frameCustomStyles()))) { for(const auto& style : style_names) { if(ImGui::Selectable(getStyleName(style.first, _currentMass->frameCustomStyles()), _currentMass->frameStyles()[i] == style.first)) { _currentMass->frameStyles()[i] = style.first; _stylesDirty = true; } } ImGui::EndCombo(); } ImGui::PopID(); } if(!_stylesDirty) { ImGui::BeginDisabled(); ImGui::Button(ICON_FA_SAVE " Save"); ImGui::SameLine(); ImGui::Button(ICON_FA_UNDO " Reset"); ImGui::EndDisabled(); } else { if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { if(!_currentMass->writeFrameStyles()) { _queue.addToast(Toast::Type::Error, "Error: " + Mass::lastError()); } _stylesDirty = false; } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO " Reset")) { _currentMass->getFrameStyles(); _stylesDirty = false; } } } void SaveTool::drawEyeColourPicker() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; } if(ImGui::ColorPicker3("##EyeFlarePicker", &_currentMass->eyeFlareColour().x())) { _eyeFlareDirty = true; } if(!_eyeFlareDirty) { ImGui::BeginDisabled(); ImGui::Button(ICON_FA_SAVE " Save"); ImGui::SameLine(); ImGui::Button(ICON_FA_UNDO " Reset"); ImGui::EndDisabled(); } else { if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { if(!_currentMass->writeEyeFlareColour()) { _queue.addToast(Toast::Type::Error, "Error writing the eye flare colour."); } _eyeFlareDirty = false; } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO " Reset")) { _currentMass->getEyeFlareColour(); _eyeFlareDirty = false; } } } void SaveTool::drawCustomFrameStyles() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; } if(!ImGui::BeginChild("##FrameStyles")) { ImGui::EndChild(); return; } ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game."); for(UnsignedInt i = 0; i < _currentMass->frameCustomStyles().size(); i++) { ImGui::PushID(i); DCSResult result; result = drawCustomStyle(_currentMass->frameCustomStyles()[i]); switch(result) { case DCS_ResetStyle: _currentMass->getFrameCustomStyles(); break; case DCS_Save: _currentMass->writeFrameCustomStyle(i); break; default: break; } ImGui::PopID(); } ImGui::EndChild(); } void SaveTool::drawArmour() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; } if(ImGui::Button(ICON_FA_UNDO_ALT " Reset all")) { _currentMass->getArmourParts(); } if(!ImGui::BeginChild("##ArmourParts")) { ImGui::EndChild(); return; } static const char* slot_labels[] = { #define c(enumerator, strenum, name) name, #include "../Maps/ArmourSlots.hpp" #undef c }; for(UnsignedInt i = 0; i < _currentMass->armourParts().size(); i++) { ImGui::PushID(i); auto& part = _currentMass->armourParts()[i]; static char header[129] = {'\0'}; std::memset(header, '\0', 129); if(armour_sets.find(part.id) != armour_sets.cend()) { std::snprintf(header, 128, "%s: %s###%u", slot_labels[UnsignedInt(part.slot)], armour_sets.at(part.id).name, UnsignedInt(part.slot)); } else { std::snprintf(header, 128, "%s: %i###%u", slot_labels[UnsignedInt(part.slot)], part.id, UnsignedInt(part.slot)); } if(ImGui::CollapsingHeader(header)) { ImGui::BeginGroup(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth() * 0.491f); if(ImGui::BeginListBox("##ChangePart")) { if(std::strncmp("Neck", slot_labels[UnsignedInt(part.slot)], 4) != 0) { for(auto& set : armour_sets) { if(ImGui::Selectable(set.second.name, set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) { part.id = set.first; } } } else { for(auto& set : armour_sets) { if(!set.second.neck_compatible) { continue; } if(ImGui::Selectable(set.second.name, set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) { part.id = set.first; } } } ImGui::EndListBox(); } ImGui::EndGroup(); ImGui::SameLine(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SameLine(); ImGui::BeginGroup(); ImGui::TextUnformatted("Styles:"); for(Int j = 0; j < 4; j++) { ImGui::AlignTextToFramePadding(); ImGui::Text("Slot %d:", j + 1); ImGui::SameLine(); ImGui::PushID(j); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth() - 2.0f); if(ImGui::BeginCombo("##Style", getStyleName(part.styles[j], _currentMass->armourCustomStyles()))) { for(const auto& style : style_names) { if(ImGui::Selectable(getStyleName(style.first, _currentMass->armourCustomStyles()), part.styles[j] == style.first)) { part.styles[j] = style.first; } } ImGui::EndCombo(); } ImGui::PopID(); } ImGui::EndGroup(); ImGui::Separator(); ImGui::PushID("Decal"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Showing/editing decal"); for(UnsignedLong j = 0; j < part.decals.size(); j++) { ImGui::SameLine(); ImGui::RadioButton(std::to_string(j + 1).c_str(), &_selectedArmourDecals[i], j); } drawDecalEditor(part.decals[_selectedArmourDecals[i]]); ImGui::PopID(); if(!part.accessories.size()) { ImGui::Separator(); ImGui::PushID("Accessory"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Showing/editing accessory"); for(UnsignedInt j = 0; j < part.accessories.size(); j++) { ImGui::SameLine(); ImGui::RadioButton(std::string{char(65 + j)}.c_str(), &_selectedArmourAccessories[i], j); } drawAccessoryEditor(part.accessories[_selectedArmourAccessories[i]], _currentMass->armourCustomStyles()); ImGui::PopID(); } ImGui::Separator(); if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { _currentMass->writeArmourPart(part.slot); } } ImGui::PopID(); } ImGui::EndChild(); } void SaveTool::drawCustomArmourStyles() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; } if(!ImGui::BeginChild("##ArmourStyles")) { ImGui::EndChild(); return; } ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game."); for(UnsignedInt i = 0; i < _currentMass->armourCustomStyles().size(); i++) { ImGui::PushID(i); DCSResult result; result = drawCustomStyle(_currentMass->armourCustomStyles()[i]); switch(result) { case DCS_ResetStyle: _currentMass->getArmourCustomStyles(); break; case DCS_Save: _currentMass->writeArmourCustomStyle(i); break; default: break; } ImGui::PopID(); } ImGui::EndChild(); } void SaveTool::drawWeapons() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { current_weapon = nullptr; return; } const Float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); ImGui::BeginGroup(); if(!ImGui::BeginTable("##WeaponsList", 1, ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_BordersInnerH, {ImGui::GetContentRegionAvailWidth() * 0.2f, -footer_height_to_reserve})) { return; } ImGui::TableSetupColumn("Weapon"); int id = 0; #define weapcat(header, array, dirty, payload_type, payload_tooltip) \ ImGui::TableNextRow(ImGuiTableRowFlags_Headers); \ ImGui::TableNextColumn(); \ ImGui::TextUnformatted(header); \ \ for(UnsignedInt i = 0; i < _currentMass->array().size(); i++) { \ auto& weapon = _currentMass->array()[i]; \ \ ImGui::TableNextRow(); \ ImGui::TableNextColumn(); \ ImGui::PushID(id); \ if(ImGui::Selectable(weapon.name.c_str(), current_weapon == &weapon)) { \ current_weapon = &weapon; \ } \ if(ImGui::BeginDragDropSource()) { \ ImGui::SetDragDropPayload(payload_type, &i, sizeof(UnsignedInt)); \ if(ImGui::GetIO().KeyCtrl) { \ ImGui::Text(payload_tooltip " %i - %s (copy)", i + 1, weapon.name.c_str()); \ } \ else { \ ImGui::Text(payload_tooltip " %i - %s", i + 1, weapon.name.c_str()); \ } \ ImGui::EndDragDropSource(); \ } \ if(ImGui::BeginDragDropTarget()) { \ if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(payload_type)) { \ int index = *static_cast(payload->Data); \ \ if(!ImGui::GetIO().KeyCtrl) { \ if(current_weapon == &_currentMass->array()[index]) { \ current_weapon = &_currentMass->array()[i]; \ } \ else if (current_weapon == &_currentMass->array()[i]) { \ current_weapon = &_currentMass->array()[index]; \ } \ \ std::swap(_currentMass->array()[index], _currentMass->array()[i]); \ } \ else { \ _currentMass->array()[i] = _currentMass->array()[index]; \ } \ (dirty) = true; \ } \ \ ImGui::EndDragDropTarget(); \ } \ \ ImGui::PopID(); \ id++; \ \ if(weapon.attached == true) { \ ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0x1F008CFFu); \ } \ } weapcat("Melee weapons", meleeWeapons, _meleeDirty, "MeleeWeapon", "Melee weapon") weapcat("Shield", shields, _shieldsDirty, "Shield", "Shield") weapcat("Bullet shooters", bulletShooters, _bShootersDirty, "BShooter", "Bullet shooter") weapcat("Energy shooters", energyShooters, _eShootersDirty, "EShooter", "Energy shooter") weapcat("Bullet launchers", bulletLaunchers, _bLaunchersDirty, "BLauncher", "Bullet launcher") weapcat("Energy launchers", energyLaunchers, _eLaunchersDirty, "ELauncher", "Energy launcher") #undef weapcat ImGui::EndTable(); bool dirty = _meleeDirty || _shieldsDirty || _bShootersDirty || _eShootersDirty || _bLaunchersDirty || _eLaunchersDirty; if(!dirty) { ImGui::BeginDisabled(); } if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { if(_meleeDirty) { _currentMass->writeMeleeWeapons(); _meleeDirty = false; } if(_shieldsDirty) { _currentMass->writeShields(); _shieldsDirty = false; } if(_bShootersDirty) { _currentMass->writeBulletShooters(); _bShootersDirty = false; } if(_eShootersDirty) { _currentMass->writeEnergyShooters(); _eShootersDirty = false; } if(_bLaunchersDirty) { _currentMass->writeBulletLaunchers(); _bLaunchersDirty = false; } if(_eLaunchersDirty) { _currentMass->writeEnergyLaunchers(); _eLaunchersDirty = false; } } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO_ALT " Reset")) { if(_meleeDirty) { _currentMass->getMeleeWeapons(); _meleeDirty = false; } if(_shieldsDirty) { _currentMass->getShields(); _shieldsDirty = false; } if(_bShootersDirty) { _currentMass->getBulletShooters(); _bShootersDirty = false; } if(_eShootersDirty) { _currentMass->getEnergyShooters(); _eShootersDirty = false; } if(_bLaunchersDirty) { _currentMass->getBulletLaunchers(); _bLaunchersDirty = false; } if(_eLaunchersDirty) { _currentMass->getEnergyLaunchers(); _eLaunchersDirty = false; } } if(!dirty) { ImGui::EndDisabled(); } ImGui::EndGroup(); ImGui::SameLine(); if(!current_weapon) { ImGui::TextUnformatted("No weapon selected."); return; } ImGui::BeginGroup(); if(!ImGui::BeginChild("##WeaponChild", {0.0f, -footer_height_to_reserve})) { ImGui::EndChild(); return; } drawWeaponEditor(*current_weapon); ImGui::EndChild(); ImGui::Separator(); if(drawUnsafeWidget([](){ return ImGui::Button(ICON_FA_SAVE " Save changes to weapon category"); })) { switch(current_weapon->type) { case WeaponType::Melee: if(!_currentMass->writeMeleeWeapons()) { _queue.addToast(Toast::Type::Error, "Couldn't save melee weapons"); } break; case WeaponType::Shield: if(!_currentMass->writeShields()) { _queue.addToast(Toast::Type::Error, "Couldn't save shields"); } break; case WeaponType::BulletShooter: if(!_currentMass->writeBulletShooters()) { _queue.addToast(Toast::Type::Error, "Couldn't save bullet shooters"); } break; case WeaponType::EnergyShooter: if(!_currentMass->writeEnergyShooters()) { _queue.addToast(Toast::Type::Error, "Couldn't save energy shooters"); } break; case WeaponType::BulletLauncher: if(!_currentMass->writeBulletLaunchers()) { _queue.addToast(Toast::Type::Error, "Couldn't save bullet launchers"); } break; case WeaponType::EnergyLauncher: if(!_currentMass->writeEnergyLaunchers()) { _queue.addToast(Toast::Type::Error, "Couldn't save energy launchers"); } break; default: _queue.addToast(Toast::Type::Error, "Unknown weapon type"); } } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO_ALT " Reset weapon category")) { switch(current_weapon->type) { case WeaponType::Melee: _currentMass->getMeleeWeapons(); break; case WeaponType::Shield: _currentMass->getShields(); break; case WeaponType::BulletShooter: _currentMass->getBulletShooters(); break; case WeaponType::EnergyShooter: _currentMass->getEnergyShooters(); break; case WeaponType::BulletLauncher: _currentMass->getBulletLaunchers(); break; case WeaponType::EnergyLauncher: _currentMass->getEnergyLaunchers(); break; default: _queue.addToast(Toast::Type::Error, "Unknown weapon type"); } } ImGui::EndGroup(); } void SaveTool::drawWeaponEditor(Weapon& weapon) { if(!_currentMass || _currentMass->state() != Mass::State::Valid || !current_weapon) { return; } ImGui::AlignTextToFramePadding(); static const char* labels[] { #define c(enumerator, strenum, name) name, #include "../Maps/WeaponTypes.hpp" #undef c }; ImGui::Text("%s: %s", labels[UnsignedInt(weapon.type)], weapon.name.c_str()); ImGui::SameLine(); static Containers::StaticArray<33, char> name_buf{ValueInit}; if(ImGui::Button(ICON_FA_EDIT " Rename")) { for(auto& c : name_buf) { c = '\0'; } std::strncpy(name_buf.data(), weapon.name.c_str(), 32); ImGui::OpenPopup("name_edit"); } if(drawRenamePopup(name_buf)) { weapon.name = name_buf.data(); } ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Equipped:"); if(weapon.type != WeaponType::Shield) { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Damage type:"); } if(weapon.type == WeaponType::Melee) { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Dual-wield:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Custom effect mode:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Custom effect colour:"); } ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); ImGui::Checkbox("##EquippedCheckbox", &weapon.attached); if(weapon.type != WeaponType::Shield) { if(weapon.type == WeaponType::Melee && ImGui::RadioButton("Physical##NoElement", weapon.damageType == DamageType::Physical)) { weapon.damageType = DamageType::Physical; } else if((weapon.type == WeaponType::BulletShooter || weapon.type == WeaponType::BulletLauncher) && ImGui::RadioButton("Piercing##NoElement", weapon.damageType == DamageType::Piercing)) { weapon.damageType = DamageType::Piercing; } else if((weapon.type == WeaponType::EnergyShooter || weapon.type == WeaponType::EnergyLauncher) && ImGui::RadioButton("Plasma##NoElement", weapon.damageType == DamageType::Plasma)) { weapon.damageType = DamageType::Plasma; } ImGui::SameLine(); if(ImGui::RadioButton("Heat##Heat", weapon.damageType == DamageType::Heat)) { weapon.damageType = DamageType::Heat; } ImGui::SameLine(); if(ImGui::RadioButton("Freeze##Freeze", weapon.damageType == DamageType::Freeze)) { weapon.damageType = DamageType::Freeze; } ImGui::SameLine(); if(ImGui::RadioButton("Shock##Shock", weapon.damageType == DamageType::Freeze)) { weapon.damageType = DamageType::Freeze; } } if(weapon.type == WeaponType::Melee) { ImGui::Checkbox("##DualWield", &weapon.dualWield); if(ImGui::RadioButton("Default##Default", weapon.effectColourMode == EffectColourMode::Default)) { weapon.effectColourMode = EffectColourMode::Default; } ImGui::SameLine(); if(ImGui::RadioButton("Custom##Custom", weapon.effectColourMode == EffectColourMode::Custom)) { weapon.effectColourMode = EffectColourMode::Custom; } bool custom_effect = (weapon.effectColourMode == EffectColourMode::Custom); if(!custom_effect) { ImGui::BeginDisabled(); } ImGui::ColorEdit3("##CustomEffectColourPicker", &weapon.effectColour.x(), ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_Float); if(!custom_effect) { ImGui::EndDisabled(); } } ImGui::EndGroup(); ImGui::Separator(); if(ImGui::CollapsingHeader("Weapon parts")) { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Viewing/editing part:"); for(Int i = 0; UnsignedLong(i) < weapon.parts.size(); i++) { if(UnsignedLong(_selectedWeaponPart) >= weapon.parts.size()) { _selectedWeaponPart = 0; } ImGui::SameLine(); ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponPart, i); } auto& part = weapon.parts[_selectedWeaponPart]; ImGui::Text("ID: %i", part.id); if(ImGui::BeginChild("##PartDetails", {0.0f, 0.0f}, true)) { ImGui::TextUnformatted("Styles:"); for(Int i = 0; i < 4; i++) { ImGui::AlignTextToFramePadding(); ImGui::Text("Slot %d:", i + 1); ImGui::SameLine(); ImGui::PushID(i); if(ImGui::BeginCombo("##Style", getStyleName(part.styles[i], weapon.customStyles))) { for(const auto& style: style_names) { if(ImGui::Selectable(getStyleName(style.first, weapon.customStyles), part.styles[i] == style.first)) { part.styles[i] = style.first; } } ImGui::EndCombo(); } ImGui::PopID(); } ImGui::Separator(); ImGui::PushID("Decal"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Showing/editing decal"); for(UnsignedLong i = 0; i < part.decals.size(); i++) { ImGui::SameLine(); ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponDecal, i); } drawDecalEditor(part.decals[_selectedWeaponDecal]); ImGui::PopID(); if(part.accessories.size() != 0) { ImGui::Separator(); ImGui::PushID("Accessory"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Showing/editing accessory"); for(UnsignedLong i = 0; i < part.accessories.size(); i++) { ImGui::SameLine(); ImGui::RadioButton(std::string{char(65 + i)}.c_str(), &_selectedWeaponAccessory, i); } drawAccessoryEditor(part.accessories[_selectedWeaponAccessory], weapon.customStyles); ImGui::PopID(); } } ImGui::EndChild(); } } void SaveTool::drawGlobalStyles() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; } if(!ImGui::BeginChild("##GlobalStyles")) { ImGui::EndChild(); return; } ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game."); for(UnsignedInt i = 0; i < _currentMass->globalStyles().size(); i++) { ImGui::PushID(i); DCSResult result; result = drawCustomStyle(_currentMass->globalStyles()[i]); switch(result) { case DCS_ResetStyle: _currentMass->getGlobalStyles(); break; case DCS_Save: _currentMass->writeGlobalStyle(i); break; default: break; } ImGui::PopID(); } ImGui::EndChild(); } void SaveTool::drawTuning() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; } if(!ImGui::BeginTable("##TuningTable", 3)) { return; } ImGui::TableSetupColumn("##EngineColumn"); ImGui::TableSetupColumn("##OSColumn"); ImGui::TableSetupColumn("##ArchitectureColumn"); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); if(ImGui::BeginTable("##EngineTable", 1, ImGuiTableFlags_Borders)) { ImGui::TableSetupColumn("##Engine"); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextColumn(); ImGui::TextUnformatted("Engine"); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%i", _currentMass->engine()); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextColumn(); ImGui::TextUnformatted("Gears"); for(UnsignedInt i = 0; i < _currentMass->gears().size(); i++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%i", _currentMass->gears()[i]); } ImGui::EndTable(); } ImGui::TableSetColumnIndex(1); if(ImGui::BeginTable("##OSTable", 1, ImGuiTableFlags_Borders)) { ImGui::TableSetupColumn("##OS"); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextColumn(); ImGui::TextUnformatted("OS"); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%i", _currentMass->os()); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextColumn(); ImGui::TextUnformatted("Modules"); for(UnsignedInt i = 0; i < _currentMass->modules().size(); i++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%i", _currentMass->modules()[i]); } ImGui::EndTable(); } ImGui::TableSetColumnIndex(2); if(ImGui::BeginTable("##ArchTable", 1, ImGuiTableFlags_Borders)) { ImGui::TableSetupColumn("##Arch"); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextColumn(); ImGui::TextUnformatted("Architecture"); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%i", _currentMass->architecture()); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextColumn(); ImGui::TextUnformatted("Techs"); for(UnsignedInt i = 0; i < _currentMass->techs().size(); i++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%i", _currentMass->techs()[i]); } ImGui::EndTable(); } ImGui::EndTable(); } auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return DCS_Fail; } ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {8.0f, 0.0f}); Containers::ScopeGuard guard{[]{ ImGui::PopStyleVar(); }}; DCSResult return_value = DCS_Fail; if(!ImGui::BeginChild("##CustomStyle", {0.0f, 244.0f}, true, ImGuiWindowFlags_MenuBar)) { ImGui::EndChild(); return DCS_Fail; } if(ImGui::BeginMenuBar()) { ImGui::TextUnformatted(style.name.c_str()); static Containers::StaticArray<33, char> name_buf{ValueInit}; if(ImGui::SmallButton(ICON_FA_EDIT " Rename")) { for(auto& c : name_buf) { c = '\0'; } std::strncpy(name_buf.data(), style.name.c_str(), 32); ImGui::OpenPopup("name_edit"); } if(drawRenamePopup(name_buf)) { style.name = name_buf.data(); } ImGui::EndMenuBar(); } if(ImGui::BeginTable("##StyleTable", 2, ImGuiTableFlags_BordersInnerV)) { ImGui::TableSetupColumn("##Colour", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("##Pattern", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Colour:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Metallic:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Gloss:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Glow:"); ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); ImGui::ColorEdit3("##Picker", &style.colour.r()); ImGui::SameLine(); drawHelpMarker("Right-click for more option, click the coloured square for the full picker."); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SliderFloat("##SliderMetallic", &style.metallic, 0.0f, 1.0f); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SliderFloat("##SliderGloss", &style.gloss,0.0f, 1.0f); ImGui::Checkbox("##Glow", &style.glow); ImGui::EndGroup(); ImGui::TableNextColumn(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Pattern:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Opacity:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("X offset:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Y offset:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Rotation:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Scale:"); ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("%i", style.patternId); ImGui::PushItemWidth(-FLT_MIN); ImGui::SliderFloat("##SliderOpacity", &style.opacity, 0.0f, 1.0f); ImGui::SliderFloat("##SliderOffsetX", &style.offset.x(), 0.0f, 1.0f); ImGui::SliderFloat("##SliderOffsetY", &style.offset.y(), 0.0f, 1.0f); ImGui::SliderFloat("##SliderRotation", &style.rotation, 0.0f, 1.0f); ImGui::SliderFloat("##SliderScale", &style.scale, 0.0f, 1.0f); ImGui::PopItemWidth(); ImGui::EndGroup(); ImGui::EndTable(); } if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { return_value = DCS_Save; } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO " Reset")) { return_value = DCS_ResetStyle; } ImGui::EndChild(); return return_value; } void SaveTool::drawDecalEditor(Decal& decal) { ImGui::Text("ID: %i", decal.id); if(ImGui::BeginTable("##DecalTable", 2, ImGuiTableFlags_BordersInnerV)) { ImGui::TableSetupColumn("##Normal", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("##Advanced", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Colour:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Offset:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Rotation:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Scale:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Flip X:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Surface wrap:"); ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); ImGui::ColorEdit3("##Picker", &decal.colour.r()); ImGui::SameLine(); drawHelpMarker("Right-click for more option, click the coloured square for the full picker."); ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth()); ImGui::SliderFloat("##OffsetX", &decal.offset.x(), 0.0f, 1.0f, "X: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::SliderFloat("##OffsetY", &decal.offset.y(), 0.0f, 1.0f, "Y: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(); drawHelpMarker("0.0 = -100 in-game\n1.0 = 100 in-game"); ImGui::SliderFloat("##Rotation", &decal.rotation, 0.0f, 1.0f); ImGui::SameLine(); drawHelpMarker("0.0 = 0 in-game\n1.0 = 360 in-game"); ImGui::SliderFloat("##Scale", &decal.scale, 0.0f, 1.0f); ImGui::SameLine(); drawHelpMarker("0.0 = 1 in-game\n1.0 = 100 in-game"); ImGui::Checkbox("##Flip", &decal.flip); ImGui::Checkbox("##Wrap", &decal.wrap); ImGui::EndGroup(); ImGui::TableNextColumn(); ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::TextUnformatted("Advanced settings. Touch these at your own risk."); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Position:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("U axis:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("V axis:"); ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); ImGui::DragFloat("##PosX", &decal.position.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::DragFloat("##PosY", &decal.position.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::DragFloat("##PosZ", &decal.position.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); ImGui::PopItemWidth(); ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); ImGui::DragFloat("##UX", &decal.uAxis.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::DragFloat("##UY", &decal.uAxis.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::DragFloat("##UZ", &decal.uAxis.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); ImGui::PopItemWidth(); ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); ImGui::DragFloat("##VX", &decal.vAxis.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::DragFloat("##VY", &decal.vAxis.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::DragFloat("##VZ", &decal.vAxis.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); ImGui::PopItemWidth(); ImGui::EndGroup(); ImGui::EndTable(); } } void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView style_view) { if(accessory.id < 1) { ImGui::TextUnformatted("Accessory: "); } else if(accessories.find(accessory.id) != accessories.cend()) { ImGui::Text("Accessory #%i - %s", accessory.id, accessories.at(accessory.id)); } else { ImGui::Text("Accessory #%i", accessory.id); drawTooltip("WARNING: accessory mapping is a WIP."); } #ifdef SAVETOOL_DEBUG_BUILD ImGui::SameLine(0.0f, ImGui::GetStyle().FramePadding.x * 5.0f); ImGui::Text("Attach index: %i", accessory.attachIndex); #endif ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Styles:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Base position:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Position offset:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Base rotation:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Rotation offset:"); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Scale:"); ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth()); if(ImGui::BeginCombo("##Style1", getStyleName(accessory.styles[0], style_view))) { for(const auto& style : style_names) { if(ImGui::Selectable(getStyleName(style.first, style_view), accessory.styles[0] == style.first)) { accessory.styles[0] = style.first; } } ImGui::EndCombo(); } ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); if(ImGui::BeginCombo("##Style2", getStyleName(accessory.styles[1], style_view))) { for(const auto& style : style_names) { if(ImGui::Selectable(getStyleName(style.first, style_view), accessory.styles[1] == style.first)) { accessory.styles[1] = style.first; } } ImGui::EndCombo(); } ImGui::PopItemWidth(); ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); ImGui::DragFloat("##PosX", &accessory.relativePosition.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::DragFloat("##PosY", &accessory.relativePosition.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::DragFloat("##PosZ", &accessory.relativePosition.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); ImGui::PopItemWidth(); ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); ImGui::SliderFloat("##PosOffsetX", &accessory.relativePositionOffset.x(), -500.0f, +500.0f, "X: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::SliderFloat("##PosOffsetY", &accessory.relativePositionOffset.y(), -500.0f, +500.0f, "Y: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::SliderFloat("##PosOffsetZ", &accessory.relativePositionOffset.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", &accessory.relativeRotation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "Roll: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::DragFloat("##RotY", &accessory.relativeRotation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Yaw: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::DragFloat("##RotZ", &accessory.relativeRotation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Pitch: %.3f"); ImGui::PopItemWidth(); ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); ImGui::SliderFloat("##RotOffsetX", &accessory.relativeRotationOffset.x(), -180.0f, +180.0f, "Roll: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::SliderFloat("##RotOffsetY", &accessory.relativeRotationOffset.y(), -180.0f, +180.0f, "Yaw: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::SliderFloat("##RotOffsetZ", &accessory.relativeRotationOffset.z(), -180.0f, +180.0f, "Pitch: %.3f"); ImGui::PopItemWidth(); ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); ImGui::SliderFloat("##ScaleX", &accessory.localScale.x(), -3.0f, +3.0f, "X: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::SliderFloat("##ScaleY", &accessory.localScale.y(), -3.0f, +3.0f, "Y: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::SliderFloat("##ScaleZ", &accessory.localScale.z(), -3.0f, +3.0f, "Z: %.3f"); ImGui::PopItemWidth(); ImGui::SameLine(); drawHelpMarker("+/-3.0 = +/-150 in-game"); ImGui::EndGroup(); } auto SaveTool::getStyleName(Int id, Containers::ArrayView view) -> const char* { if(id >= 0 && id <= 15) { return view[id].name.c_str(); } else if(id >= 50 && id <= 65) { return _currentMass->globalStyles()[id - 50].name.c_str(); } else { return style_names.at(id); } }