// 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 "../FontAwesome/IconsFontAwesome5.h" #include "../Maps/StyleNames.h" #include "../Maps/WeaponParts.h" #include "SaveTool.h" void SaveTool::drawWeapons() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { _currentWeapon = 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::GetContentRegionAvail().x * 0.2f, -footer_height_to_reserve})) { ImGui::EndGroup(); return; } ImGui::TableSetupColumn("Weapon"); drawWeaponCategory("Melee weapons", _currentMass->meleeWeapons(), _meleeDirty, "MeleeWeapon", "Melee weapon"); drawWeaponCategory("Shield", _currentMass->shields(), _shieldsDirty, "Shield", "Shield"); drawWeaponCategory("Bullet shooters", _currentMass->bulletShooters(), _bShootersDirty, "BShooter", "Bullet shooter"); drawWeaponCategory("Energy shooters", _currentMass->energyShooters(), _eShootersDirty, "EShooter", "Energy shooter"); drawWeaponCategory("Bullet launchers", _currentMass->bulletLaunchers(), _bLaunchersDirty, "BLauncher", "Bullet launcher"); drawWeaponCategory("Energy launchers", _currentMass->energyLaunchers(), _eLaunchersDirty, "ELauncher", "Energy launcher"); 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) { _modifiedBySaveTool = true; if(!_currentMass->writeMeleeWeapons()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } else { _meleeDirty = false; } } if(_shieldsDirty) { _modifiedBySaveTool = true; if(!_currentMass->writeShields()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } else { _shieldsDirty = false; } } if(_bShootersDirty) { _modifiedBySaveTool = true; if(!_currentMass->writeBulletShooters()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } else { _bShootersDirty = false; } } if(_eShootersDirty) { _modifiedBySaveTool = true; if(_currentMass->writeEnergyShooters()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } else { _eShootersDirty = false; } } if(_bLaunchersDirty) { _modifiedBySaveTool = true; if(_currentMass->writeBulletLaunchers()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } else { _bLaunchersDirty = false; } } if(_eLaunchersDirty) { _modifiedBySaveTool = true; if(_currentMass->writeEnergyLaunchers()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } else { _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(!_currentWeapon) { ImGui::TextUnformatted("No weapon selected."); return; } ImGui::BeginGroup(); if(!ImGui::BeginChild("##WeaponChild", {0.0f, -footer_height_to_reserve})) { ImGui::EndChild(); return; } drawWeaponEditor(*_currentWeapon); ImGui::EndChild(); ImGui::Separator(); if(drawUnsafeWidget([](){ return ImGui::Button(ICON_FA_SAVE " Save changes to weapon category"); })) { _modifiedBySaveTool = true; switch(_currentWeapon->type) { case WeaponType::Melee: if(!_currentMass->writeMeleeWeapons()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; case WeaponType::Shield: if(!_currentMass->writeShields()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; case WeaponType::BulletShooter: if(!_currentMass->writeBulletShooters()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; case WeaponType::EnergyShooter: if(!_currentMass->writeEnergyShooters()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; case WeaponType::BulletLauncher: if(!_currentMass->writeBulletLaunchers()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; case WeaponType::EnergyLauncher: if(!_currentMass->writeEnergyLaunchers()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; default: _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, "Unknown weapon type"); } } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO_ALT " Reset weapon category")) { switch(_currentWeapon->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::drawWeaponCategory(Containers::StringView name, Containers::ArrayView weapons_view, bool& dirty, Containers::StringView payload_type, Containers::StringView payload_tooltip) { ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextColumn(); ImGui::TextUnformatted(name.data()); ImGui::PushID(payload_type.data()); for(UnsignedInt i = 0; i < weapons_view.size(); i++) { auto& weapon = weapons_view[i]; ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::PushID(int(i)); if(ImGui::Selectable(weapon.name.data(), _currentWeapon == &weapon)) { _currentWeapon = &weapon; } if(ImGui::BeginDragDropSource()) { ImGui::SetDragDropPayload(payload_type.data(), &i, sizeof(UnsignedInt)); if(ImGui::GetIO().KeyCtrl) { ImGui::Text("%s %i - %s (copy)", payload_tooltip.data(), i + 1, weapon.name.data()); } else { ImGui::Text("%s %i - %s", payload_tooltip.data(), i + 1, weapon.name.data()); } ImGui::EndDragDropSource(); } if(ImGui::BeginDragDropTarget()) { if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(payload_type.data())) { int index = *static_cast(payload->Data); if(!ImGui::GetIO().KeyCtrl) { if(_currentWeapon == &weapons_view[index]) { _currentWeapon = &weapons_view[i]; } else if (_currentWeapon == &weapons_view[i]) { _currentWeapon = &weapons_view[index]; } std::swap(weapons_view[index], weapons_view[i]); } else { weapons_view[i] = weapons_view[index]; } dirty = true; } ImGui::EndDragDropTarget(); } ImGui::PopID(); if(weapon.attached) { ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0x1F008CFFu); } } ImGui::PopID(); } void SaveTool::drawWeaponEditor(Weapon& weapon) { if(!_currentMass || _currentMass->state() != Mass::State::Valid || !_currentWeapon) { return; } static Containers::StringView labels[] { #define c(enumerator, strenum, name) name, #include "../Maps/WeaponTypes.hpp" #undef c }; drawAlignedText("%s: %s", labels[UnsignedInt(weapon.type)].data(), weapon.name.data()); 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.data(), 32); ImGui::OpenPopup("name_edit"); } if(drawRenamePopup(name_buf)) { weapon.name = name_buf.data(); } ImGui::BeginGroup(); drawAlignedText("Equipped:"); if(weapon.type != WeaponType::Shield) { drawAlignedText("Damage type:"); } if(weapon.type == WeaponType::Melee) { drawAlignedText("Dual-wield:"); drawAlignedText("Custom effect mode:"); drawAlignedText("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::Shock)) { weapon.damageType = DamageType::Shock; } } 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); ImGui::SameLine(); drawHelpMarker("Click the coloured square for the full picker."); if(!custom_effect) { ImGui::EndDisabled(); } } ImGui::EndGroup(); ImGui::Separator(); if(ImGui::CollapsingHeader("Weapon parts")) { drawAlignedText("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).c_str(), &_selectedWeaponPart, i); } auto& part = weapon.parts[_selectedWeaponPart]; const auto* map = [this, &weapon]()-> const std::map* { switch(weapon.type) { case WeaponType::Melee: return _selectedWeaponPart == 0 ? &melee_grips : &melee_assaulters; case WeaponType::Shield: return _selectedWeaponPart == 0 ? &shield_handles : &shield_shells; case WeaponType::BulletShooter: return _selectedWeaponPart == 0 ? &bshooter_triggers : &bshooter_barrels; case WeaponType::EnergyShooter: return _selectedWeaponPart == 0 ? &eshooter_triggers : &eshooter_busters; case WeaponType::BulletLauncher: return _selectedWeaponPart == 0 ? &blauncher_pods : &blauncher_projectiles; case WeaponType::EnergyLauncher: return _selectedWeaponPart == 0 ? &elauncher_generators : &elauncher_pods; } return nullptr; }(); if(!map) { return; } if(map->find(part.id) != map->cend()) { ImGui::TextUnformatted(map->at(part.id).data()); } else if(part.id == -1) { ImGui::TextUnformatted(""); } else{ ImGui::Text("ID: %i", part.id); } if(!map->empty()) { ImGui::SameLine(); if(ImGui::SmallButton("Change")) { ImGui::OpenPopup("##WeaponPartPopup"); } if(ImGui::BeginPopup("##WeaponPartPopup")) { if(ImGui::BeginListBox("##WeaponParts")) { for(const auto& mapped_part : *map) { if(ImGui::Selectable(mapped_part.second.data(), mapped_part.first == part.id)) { part.id = mapped_part.first; } if(mapped_part.first == part.id) { ImGui::SetItemDefaultFocus(); } } ImGui::EndListBox(); } ImGui::EndPopup(); } } if(weapon.type == WeaponType::Shield || (weapon.type == WeaponType::BulletLauncher && _selectedWeaponPart != 0)) { ImGui::SameLine(); if(ImGui::SmallButton("Unequip")) { part.id = -1; } if(weapon.type == WeaponType::Shield && _selectedWeaponPart == 0) { drawTooltip("This will make the whole shield and its accessories invisible."); } else { drawTooltip("This will make accessories invisible as well."); } } if(ImGui::BeginChild("##PartDetails", {0.0f, 0.0f}, true)) { ImGui::TextUnformatted("Styles:"); for(Int i = 0; i < 4; i++) { drawAlignedText("Slot %d:", i + 1); ImGui::SameLine(); ImGui::PushID(i); if(ImGui::BeginCombo("##Style", getStyleName(part.styles[i], weapon.customStyles).data())) { for(const auto& style: style_names) { if(ImGui::Selectable(getStyleName(style.first, weapon.customStyles).data(), part.styles[i] == style.first)) { part.styles[i] = style.first; } } ImGui::EndCombo(); } ImGui::PopID(); } ImGui::Separator(); ImGui::PushID("Decal"); drawAlignedText("Showing/editing decal"); for(UnsignedLong i = 0; i < part.decals.size(); i++) { ImGui::SameLine(); ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponDecal, int(i)); } drawDecalEditor(part.decals[_selectedWeaponDecal]); ImGui::PopID(); if(part.accessories.size() != 0) { ImGui::Separator(); ImGui::PushID("Accessory"); drawAlignedText("Showing/editing accessory"); for(UnsignedLong i = 0; i < part.accessories.size(); i++) { ImGui::SameLine(); ImGui::RadioButton(std::string{char(65 + i)}.c_str(), &_selectedWeaponAccessory, int(i)); } drawAccessoryEditor(part.accessories[_selectedWeaponAccessory], weapon.customStyles); ImGui::PopID(); } } ImGui::EndChild(); } }