2022-03-02 14:43:02 +01:00
|
|
|
// 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 <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
#include "../FontAwesome/IconsFontAwesome5.h"
|
|
|
|
|
|
|
|
#include "../Maps/ArmourSets.h"
|
|
|
|
#include "../Maps/StyleNames.h"
|
|
|
|
|
|
|
|
#include "SaveTool.h"
|
|
|
|
|
2022-12-03 16:49:39 +01:00
|
|
|
void
|
|
|
|
SaveTool::drawArmour() {
|
2022-03-02 14:43:02 +01:00
|
|
|
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(ImGui::Button(ICON_FA_UNDO_ALT " Reset all")) {
|
|
|
|
_currentMass->getArmourParts();
|
2022-03-20 10:17:14 +01:00
|
|
|
_currentMass->getBulletLauncherAttachments();
|
2022-03-02 14:43:02 +01:00
|
|
|
}
|
|
|
|
|
2022-03-20 09:14:46 +01:00
|
|
|
if(!ImGui::BeginChild("##ArmourParts", {0.0f, 0.0f}, true)) {
|
2022-03-02 14:43:02 +01:00
|
|
|
ImGui::EndChild();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-04 21:18:55 +01:00
|
|
|
static Containers::StringView slot_labels[] = {
|
|
|
|
#define c(enumerator, strenum, name) name,
|
|
|
|
#include "../Maps/ArmourSlots.hpp"
|
|
|
|
#undef c
|
2022-03-02 14:43:02 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
for(UnsignedInt i = 0; i < _currentMass->armourParts().size(); i++) {
|
2022-11-21 20:47:09 +01:00
|
|
|
ImGui::PushID(int(i));
|
2022-03-02 14:43:02 +01:00
|
|
|
|
|
|
|
auto& part = _currentMass->armourParts()[i];
|
|
|
|
|
|
|
|
static char header[129] = {'\0'};
|
|
|
|
|
|
|
|
std::memset(header, '\0', 129);
|
|
|
|
|
|
|
|
if(armour_sets.find(part.id) != armour_sets.cend()) {
|
2022-03-04 21:18:55 +01:00
|
|
|
std::snprintf(header, 128, "%s: %s###%u", slot_labels[UnsignedInt(part.slot)].data(), armour_sets.at(part.id).name.data(), UnsignedInt(part.slot));
|
2022-03-02 14:43:02 +01:00
|
|
|
}
|
|
|
|
else {
|
2022-03-04 21:18:55 +01:00
|
|
|
std::snprintf(header, 128, "%s: %i###%u", slot_labels[UnsignedInt(part.slot)].data(), part.id, UnsignedInt(part.slot));
|
2022-03-02 14:43:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(ImGui::CollapsingHeader(header)) {
|
|
|
|
ImGui::BeginGroup();
|
|
|
|
|
2022-11-25 10:33:55 +01:00
|
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x * 0.491f);
|
2022-03-02 14:43:02 +01:00
|
|
|
if(ImGui::BeginListBox("##ChangePart")) {
|
2022-03-04 21:18:55 +01:00
|
|
|
if(std::strncmp("Neck", slot_labels[UnsignedInt(part.slot)].data(), 4) != 0) {
|
2022-03-02 14:43:02 +01:00
|
|
|
for(auto& set : armour_sets) {
|
2022-03-04 21:18:55 +01:00
|
|
|
if(ImGui::Selectable(set.second.name.data(), set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) {
|
2022-03-02 14:43:02 +01:00
|
|
|
part.id = set.first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for(auto& set : armour_sets) {
|
|
|
|
if(!set.second.neck_compatible) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-03-04 21:18:55 +01:00
|
|
|
if(ImGui::Selectable(set.second.name.data(), set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) {
|
2022-03-02 14:43:02 +01:00
|
|
|
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++) {
|
|
|
|
drawAlignedText("Slot %d:", j + 1);
|
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
|
|
|
ImGui::PushID(j);
|
|
|
|
|
2022-11-25 10:33:55 +01:00
|
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 2.0f);
|
2022-03-04 21:18:55 +01:00
|
|
|
if(ImGui::BeginCombo("##Style", getStyleName(part.styles[j], _currentMass->armourCustomStyles()).data())) {
|
2022-03-02 14:43:02 +01:00
|
|
|
for(const auto& style : style_names) {
|
2022-03-04 21:18:55 +01:00
|
|
|
if(ImGui::Selectable(getStyleName(style.first, _currentMass->armourCustomStyles()).data(), part.styles[j] == style.first)) {
|
2022-03-02 14:43:02 +01:00
|
|
|
part.styles[j] = style.first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::PopID();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndGroup();
|
|
|
|
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
ImGui::PushID("Decal");
|
|
|
|
|
|
|
|
drawAlignedText("Showing/editing decal");
|
|
|
|
for(UnsignedInt j = 0; j < part.decals.size(); j++) {
|
|
|
|
ImGui::SameLine();
|
2022-11-21 20:47:09 +01:00
|
|
|
ImGui::RadioButton(std::to_string(j + 1).c_str(), &_selectedArmourDecals[i], int(j));
|
2022-03-02 14:43:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
drawDecalEditor(part.decals[_selectedArmourDecals[i]]);
|
|
|
|
|
|
|
|
ImGui::PopID();
|
|
|
|
|
2022-03-13 10:48:32 +01:00
|
|
|
if(part.accessories.size() != 0) {
|
2022-03-02 14:43:02 +01:00
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
ImGui::PushID("Accessory");
|
|
|
|
|
|
|
|
drawAlignedText("Showing/editing accessory");
|
|
|
|
for(UnsignedInt j = 0; j < part.accessories.size(); j++) {
|
|
|
|
ImGui::SameLine();
|
2022-11-21 20:47:09 +01:00
|
|
|
ImGui::RadioButton(std::string{char(65 + j)}.c_str(), &_selectedArmourAccessories[i], int(j));
|
2022-03-02 14:43:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
drawAccessoryEditor(part.accessories[_selectedArmourAccessories[i]], _currentMass->armourCustomStyles());
|
|
|
|
|
|
|
|
ImGui::PopID();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
|
2022-03-22 09:59:30 +01:00
|
|
|
_modifiedBySaveTool = true;
|
2022-03-02 14:43:02 +01:00
|
|
|
if(!_currentMass->writeArmourPart(part.slot)) {
|
2022-03-22 09:59:30 +01:00
|
|
|
_modifiedBySaveTool = false;
|
2022-03-02 14:43:02 +01:00
|
|
|
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::PopID();
|
|
|
|
}
|
|
|
|
|
2022-03-20 10:17:14 +01:00
|
|
|
if(_currentMass->bulletLauncherAttachmentStyle() != BulletLauncherAttachmentStyle::NotFound &&
|
|
|
|
ImGui::CollapsingHeader("Bullet launcher placement"))
|
|
|
|
{
|
|
|
|
drawAlignedText("Attachment style:"_s);
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("Active one",
|
|
|
|
_currentMass->bulletLauncherAttachmentStyle() == BulletLauncherAttachmentStyle::ActiveOne);
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("Active one per slot",
|
|
|
|
_currentMass->bulletLauncherAttachmentStyle() == BulletLauncherAttachmentStyle::ActiveOnePerSlot);
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("All equipped",
|
|
|
|
_currentMass->bulletLauncherAttachmentStyle() == 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[UnsignedInt(placement.socket)].data())) {
|
|
|
|
for(UnsignedInt i = 0; i < (sizeof(socket_labels) / sizeof(socket_labels[0])); i++) {
|
|
|
|
if(ImGui::Selectable(socket_labels[i].data(), i == UnsignedInt(placement.socket), ImGuiSelectableFlags_SpanAvailWidth)) {
|
|
|
|
placement.socket = static_cast<BulletLauncherSocket>(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(placement.socket != BulletLauncherSocket::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();
|
|
|
|
}
|
|
|
|
|
2022-03-22 09:59:30 +01:00
|
|
|
_modifiedBySaveTool = true;
|
2022-03-20 10:17:14 +01:00
|
|
|
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); }) &&
|
|
|
|
!_currentMass->writeBulletLauncherAttachments())
|
|
|
|
{
|
2022-03-22 09:59:30 +01:00
|
|
|
_modifiedBySaveTool = false;
|
2022-03-20 10:17:14 +01:00
|
|
|
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-02 14:43:02 +01:00
|
|
|
ImGui::EndChild();
|
|
|
|
}
|
|
|
|
|
2022-12-03 16:49:39 +01:00
|
|
|
void
|
|
|
|
SaveTool::drawCustomArmourStyles() {
|
2022-03-02 14:43:02 +01:00
|
|
|
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++) {
|
2022-11-21 20:47:09 +01:00
|
|
|
ImGui::PushID(int(i));
|
2022-03-02 14:43:02 +01:00
|
|
|
DCSResult result;
|
|
|
|
result = drawCustomStyle(_currentMass->armourCustomStyles()[i]);
|
|
|
|
switch(result) {
|
|
|
|
case DCS_ResetStyle:
|
|
|
|
_currentMass->getArmourCustomStyles();
|
|
|
|
break;
|
|
|
|
case DCS_Save:
|
2022-03-22 09:59:30 +01:00
|
|
|
_modifiedBySaveTool = true;
|
2022-07-06 17:22:55 +02:00
|
|
|
if(!_currentMass->writeArmourCustomStyle(i)) {
|
2022-03-22 09:59:30 +01:00
|
|
|
_modifiedBySaveTool = false;
|
2022-03-02 14:43:02 +01:00
|
|
|
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ImGui::PopID();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndChild();
|
|
|
|
}
|