Compare commits
6 commits
0e2546391b
...
b569e27d6d
Author | SHA1 | Date | |
---|---|---|---|
b569e27d6d | |||
2a617b3359 | |||
ec8a6b0c46 | |||
a16383183e | |||
fa78ca2a8a | |||
3f3166691d |
14 changed files with 807 additions and 209 deletions
|
@ -36,8 +36,6 @@ add_executable(MassBuilderSaveTool WIN32
|
||||||
SaveTool/SaveTool_drawMainMenu.cpp
|
SaveTool/SaveTool_drawMainMenu.cpp
|
||||||
SaveTool/SaveTool_MainManager.cpp
|
SaveTool/SaveTool_MainManager.cpp
|
||||||
SaveTool/SaveTool_ProfileManager.cpp
|
SaveTool/SaveTool_ProfileManager.cpp
|
||||||
MassBuilderManager/MassBuilderManager.h
|
|
||||||
MassBuilderManager/MassBuilderManager.cpp
|
|
||||||
ProfileManager/ProfileManager.h
|
ProfileManager/ProfileManager.h
|
||||||
ProfileManager/ProfileManager.cpp
|
ProfileManager/ProfileManager.cpp
|
||||||
Profile/Profile.h
|
Profile/Profile.h
|
||||||
|
@ -48,6 +46,9 @@ add_executable(MassBuilderSaveTool WIN32
|
||||||
Mass/Mass.cpp
|
Mass/Mass.cpp
|
||||||
Maps/LastMissionId.h
|
Maps/LastMissionId.h
|
||||||
Maps/StoryProgress.h
|
Maps/StoryProgress.h
|
||||||
|
ResearchTree/NodeIDs.h
|
||||||
|
ResearchTree/ResearchTree.h
|
||||||
|
ResearchTree/ResearchTree.cpp
|
||||||
FontAwesome/IconsFontAwesome5.h
|
FontAwesome/IconsFontAwesome5.h
|
||||||
FontAwesome/IconsFontAwesome5Brands.h
|
FontAwesome/IconsFontAwesome5Brands.h
|
||||||
resource.rc
|
resource.rc
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
// MassBuilderSaveTool
|
|
||||||
// Copyright (C) 2021 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 "MassBuilderManager.h"
|
|
||||||
|
|
||||||
#include <Corrade/Containers/ScopeGuard.h>
|
|
||||||
#include <Corrade/Utility/Directory.h>
|
|
||||||
#include <Corrade/Utility/Unicode.h>
|
|
||||||
|
|
||||||
#include <shlobj.h>
|
|
||||||
#include <wtsapi32.h>
|
|
||||||
|
|
||||||
using namespace Corrade;
|
|
||||||
|
|
||||||
MassBuilderManager::MassBuilderManager() {
|
|
||||||
_ready = findSaveDirectory();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto MassBuilderManager::ready() const -> bool {
|
|
||||||
return _ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto MassBuilderManager::lastError() -> std::string const& {
|
|
||||||
return _lastError;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto MassBuilderManager::saveDirectory() -> std::string const& {
|
|
||||||
return _saveDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MassBuilderManager::checkGameState() {
|
|
||||||
WTS_PROCESS_INFOW* process_infos = nullptr;
|
|
||||||
unsigned long process_count = 0;
|
|
||||||
|
|
||||||
if(WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &process_infos, &process_count)) {
|
|
||||||
Containers::ScopeGuard guard{process_infos, WTSFreeMemory};
|
|
||||||
|
|
||||||
for(unsigned long i = 0; i < process_count; ++i) {
|
|
||||||
if(std::wcscmp(process_infos[i].pProcessName, L"MASS_Builder-Win64-Shipping.exe") == 0) {
|
|
||||||
_gameState = GameState::Running;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_gameState = GameState::NotRunning;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_gameState = GameState::Unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto MassBuilderManager::gameState() -> GameState {
|
|
||||||
return _gameState;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto MassBuilderManager::findSaveDirectory() -> bool {
|
|
||||||
wchar_t* localappdata_path;
|
|
||||||
Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree};
|
|
||||||
if(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_NO_APPCONTAINER_REDIRECTION, nullptr, &localappdata_path) != S_OK)
|
|
||||||
{
|
|
||||||
_lastError = "SHGetKnownFolderPath() failed in MassBuilderManager::findSaveDirectory()";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_saveDirectory = Utility::Directory::join(Utility::Directory::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)), "MASS_Builder");
|
|
||||||
|
|
||||||
if(!Utility::Directory::exists(_saveDirectory)) {
|
|
||||||
_lastError = _saveDirectory + " wasn't found.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// MassBuilderSaveTool
|
|
||||||
// Copyright (C) 2021 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 <string>
|
|
||||||
|
|
||||||
#include <Magnum/Magnum.h>
|
|
||||||
|
|
||||||
using namespace Magnum;
|
|
||||||
|
|
||||||
enum class GameState : UnsignedByte {
|
|
||||||
Unknown, NotRunning, Running
|
|
||||||
};
|
|
||||||
|
|
||||||
class MassBuilderManager {
|
|
||||||
public:
|
|
||||||
MassBuilderManager();
|
|
||||||
|
|
||||||
auto ready() const -> bool;
|
|
||||||
auto lastError() -> std::string const&;
|
|
||||||
|
|
||||||
auto saveDirectory() -> std::string const&;
|
|
||||||
|
|
||||||
void checkGameState();
|
|
||||||
auto gameState() -> GameState;
|
|
||||||
|
|
||||||
private:
|
|
||||||
auto findSaveDirectory() -> bool;
|
|
||||||
|
|
||||||
bool _ready = false;
|
|
||||||
|
|
||||||
std::string _lastError = "";
|
|
||||||
|
|
||||||
std::string _saveDirectory = "";
|
|
||||||
|
|
||||||
GameState _gameState = GameState::Unknown;
|
|
||||||
};
|
|
|
@ -25,12 +25,11 @@
|
||||||
static const std::string empty_string = "";
|
static const std::string empty_string = "";
|
||||||
|
|
||||||
MassManager::MassManager(const std::string& save_path, const std::string& steam_id, bool demo):
|
MassManager::MassManager(const std::string& save_path, const std::string& steam_id, bool demo):
|
||||||
|
_saveDirectory{save_path},
|
||||||
|
_steamId{steam_id},
|
||||||
|
_demo{demo},
|
||||||
_stagingAreaDirectory{Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "staging")}
|
_stagingAreaDirectory{Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "staging")}
|
||||||
{
|
{
|
||||||
_saveDirectory = save_path;
|
|
||||||
_steamId = steam_id;
|
|
||||||
_demo = demo;
|
|
||||||
|
|
||||||
Containers::arrayReserve(_hangars, 32);
|
Containers::arrayReserve(_hangars, 32);
|
||||||
|
|
||||||
std::string mass_filename = "";
|
std::string mass_filename = "";
|
||||||
|
|
|
@ -54,7 +54,7 @@ class MassManager {
|
||||||
std::string _steamId;
|
std::string _steamId;
|
||||||
bool _demo;
|
bool _demo;
|
||||||
|
|
||||||
std::string _lastError = "";
|
std::string _lastError;
|
||||||
|
|
||||||
Containers::Array<Mass> _hangars;
|
Containers::Array<Mass> _hangars;
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,8 @@ void Profile::refreshValues() {
|
||||||
getMuscularConstruction();
|
getMuscularConstruction();
|
||||||
getMineralExoskeletology();
|
getMineralExoskeletology();
|
||||||
getCarbonizedSkin();
|
getCarbonizedSkin();
|
||||||
|
|
||||||
|
getEngineUnlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Profile::companyName() const -> std::string const& {
|
auto Profile::companyName() const -> std::string const& {
|
||||||
|
@ -1001,3 +1003,54 @@ auto Profile::setCarbonizedSkin(Int amount) -> bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Profile::engineInventory() -> Containers::ArrayView<Int> {
|
||||||
|
return _engineInventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Profile::gearInventory() -> Containers::ArrayView<Int> {
|
||||||
|
return _gearInventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Profile::getEngineUnlocks() {
|
||||||
|
auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename));
|
||||||
|
|
||||||
|
auto iter = std::search(mmap.begin(), mmap.end(), &engine_inventory_locator[0], &engine_inventory_locator[33]);
|
||||||
|
|
||||||
|
if(iter != mmap.end()) {
|
||||||
|
Int* int_iter = reinterpret_cast<Int*>(iter + 0x3B);
|
||||||
|
Int size = *(int_iter++);
|
||||||
|
|
||||||
|
if(size > 0) {
|
||||||
|
_engineInventory = Containers::Array<Int>{DefaultInit, std::size_t(size)};
|
||||||
|
std::copy_n(int_iter, size, _engineInventory.data());
|
||||||
|
|
||||||
|
Utility::Debug{} << "_engineInventory:" << _engineInventory;
|
||||||
|
|
||||||
|
iter = std::search(mmap.begin(), mmap.end(), &gear_inventory_locator[0], &gear_inventory_locator[31]);
|
||||||
|
|
||||||
|
if(iter == mmap.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int_iter = reinterpret_cast<Int*>(iter + 0x39);
|
||||||
|
size = *(int_iter++);
|
||||||
|
|
||||||
|
if(size <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_gearInventory = Containers::Array<Int>{DefaultInit, std::size_t(size)};
|
||||||
|
std::copy_n(int_iter, size, _gearInventory.data());
|
||||||
|
|
||||||
|
Utility::Debug{} << "_gearInventory:" << _gearInventory;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_lastError = "An array can't have a null or negative size.";
|
||||||
|
Utility::Fatal{EXIT_FAILURE} << _lastError.c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <Corrade/Containers/Array.h>
|
||||||
|
|
||||||
#include <Magnum/Magnum.h>
|
#include <Magnum/Magnum.h>
|
||||||
|
|
||||||
using namespace Magnum;
|
using namespace Magnum;
|
||||||
|
@ -141,6 +143,10 @@ class Profile {
|
||||||
auto getCarbonizedSkin() -> Int;
|
auto getCarbonizedSkin() -> Int;
|
||||||
auto setCarbonizedSkin(Int amount) -> bool;
|
auto setCarbonizedSkin(Int amount) -> bool;
|
||||||
|
|
||||||
|
auto engineInventory() -> Containers::ArrayView<Int>;
|
||||||
|
auto gearInventory() -> Containers::ArrayView<Int>;
|
||||||
|
void getEngineUnlocks();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _profileDirectory;
|
std::string _profileDirectory;
|
||||||
std::string _filename;
|
std::string _filename;
|
||||||
|
@ -183,4 +189,13 @@ class Profile {
|
||||||
Int _muscularConstruction;
|
Int _muscularConstruction;
|
||||||
Int _mineralExoskeletology;
|
Int _mineralExoskeletology;
|
||||||
Int _carbonizedSkin;
|
Int _carbonizedSkin;
|
||||||
|
|
||||||
|
Containers::Array<Int> _engineInventory;
|
||||||
|
Containers::Array<Int> _gearInventory;
|
||||||
|
|
||||||
|
Containers::Array<Int> _osInventory;
|
||||||
|
Containers::Array<Int> _moduleInventory;
|
||||||
|
|
||||||
|
Containers::Array<Int> _archInventory;
|
||||||
|
Containers::Array<Int> _techInventory;
|
||||||
};
|
};
|
||||||
|
|
94
src/ResearchTree/NodeIDs.h
Normal file
94
src/ResearchTree/NodeIDs.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// MassBuilderSaveTool
|
||||||
|
// Copyright (C) 2021 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/>.
|
||||||
|
|
||||||
|
enum EngineIDs: Int {
|
||||||
|
// Tier 1
|
||||||
|
VerseEngine = 100000,
|
||||||
|
LoadedEngine = 100020,
|
||||||
|
MetalPlatings1 = 200000,
|
||||||
|
HeatTurbines1 = 200040,
|
||||||
|
Microcontroller1 = 200060,
|
||||||
|
CombustionController1 = 200020,
|
||||||
|
|
||||||
|
// Tier 2
|
||||||
|
ModAlloyEngine = 110000,
|
||||||
|
ChargedEngine = 110040,
|
||||||
|
ReinforcedLoadedEngine = 110020,
|
||||||
|
HeavyImpactsEnabler = 110100,
|
||||||
|
MetalPlatings2 = 210000,
|
||||||
|
HeatTurbines2 = 210040,
|
||||||
|
Microcontroller2 = 210060,
|
||||||
|
WeaponsCargo1 = 210061,
|
||||||
|
CombustionController2 = 210020,
|
||||||
|
PoweredRewiring1 = 210021,
|
||||||
|
|
||||||
|
// Tier 3
|
||||||
|
ModSteelEngine,
|
||||||
|
SuperchargedEngine,
|
||||||
|
NecriumAlloyEngine,
|
||||||
|
MetalPlatings3,
|
||||||
|
HeatTurbines3,
|
||||||
|
Microcontroller3,
|
||||||
|
WeaponsCargo2,
|
||||||
|
CombustionController3,
|
||||||
|
PoweredRewiring2,
|
||||||
|
|
||||||
|
// Tier 4
|
||||||
|
ModLunariteEngine,
|
||||||
|
ChargedLunariteEngine,
|
||||||
|
LunariteEngine,
|
||||||
|
InfusedEngine,
|
||||||
|
MetalPlatings4,
|
||||||
|
HeatTurbines4,
|
||||||
|
Microcontroller4,
|
||||||
|
WeaponsCargo3,
|
||||||
|
CombustionController4,
|
||||||
|
PoweredRewiring3,
|
||||||
|
ArmouredCargo1,
|
||||||
|
ArmouredFuelTank1,
|
||||||
|
ExtraCapacity1,
|
||||||
|
HighmetalEngine,
|
||||||
|
PowerRedirector1,
|
||||||
|
|
||||||
|
// Tier 5
|
||||||
|
AsteriteCarbonEngine,
|
||||||
|
ChargedAsteriteEngine,
|
||||||
|
AsteriteSteelEngine,
|
||||||
|
MeldedEngine,
|
||||||
|
MetalPlatings5,
|
||||||
|
HeatTurbines5,
|
||||||
|
Microcontroller5,
|
||||||
|
WeaponsCargo4,
|
||||||
|
CombustionController5,
|
||||||
|
PoweredRewiring4,
|
||||||
|
ArmouredCargo2,
|
||||||
|
ArmouredFuelTank2,
|
||||||
|
ExtraCapacity2,
|
||||||
|
CastHighmetalEngine,
|
||||||
|
PowerRedirector2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OSIDs: Int {
|
||||||
|
// Tier 1
|
||||||
|
NeuralOS = 300010,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ArchIDs: Int {
|
||||||
|
// Tier 1
|
||||||
|
StandardFrame = 500099,
|
||||||
|
};
|
277
src/ResearchTree/ResearchTree.cpp
Normal file
277
src/ResearchTree/ResearchTree.cpp
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
// MassBuilderSaveTool
|
||||||
|
// Copyright (C) 2021 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 "ResearchTree.h"
|
||||||
|
|
||||||
|
#include "NodeIDs.h"
|
||||||
|
|
||||||
|
//region Node
|
||||||
|
Node::Node(Node::Type type, const char* name, UnsignedByte tier, UnsignedByte slots,
|
||||||
|
const char* description, const char* active_effect, const char* passive_effect):
|
||||||
|
_type{type}, _state{State::Unavailable}, _name{name}, _tier{tier}, _slots{slots},
|
||||||
|
_description{description}, _activeEffect{active_effect}, _passiveEffect{passive_effect}
|
||||||
|
{
|
||||||
|
//ctor
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::type() const -> Node::Type {
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::state() const -> Node::State {
|
||||||
|
return _state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::setState(Node::State state) {
|
||||||
|
if(_state == state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_state = state;
|
||||||
|
|
||||||
|
if(_state == State::Available) {
|
||||||
|
for(Node* n : _children) {
|
||||||
|
n->setState(State::Unavailable, NotClicked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(_state == State::Unlocked) {
|
||||||
|
for(Node* n : _children) {
|
||||||
|
n->setState(State::Available, NotClicked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::name() const -> const char* {
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::tier() const -> UnsignedByte {
|
||||||
|
return _tier;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::slots() const -> UnsignedByte {
|
||||||
|
return _slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::description() const -> const char* {
|
||||||
|
return _description;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::activeEffect() const -> const char* {
|
||||||
|
return _activeEffect;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::passiveEffect() const -> const char* {
|
||||||
|
return _passiveEffect;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Node::children() -> Containers::ArrayView<Node*> {
|
||||||
|
return _children;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::addChild(Node& child) {
|
||||||
|
arrayAppend(_children, &child);
|
||||||
|
child.addParent(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::addParent(Node& parent) {
|
||||||
|
arrayAppend(_parents, &parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::setState(State state, NotClickedT) {
|
||||||
|
if(_state == state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(state == State::Unavailable) {
|
||||||
|
for(Node* node : _parents) {
|
||||||
|
if(node->state() == State::Unlocked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(state == State::Available && _state == State::Unlocked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_state = state;
|
||||||
|
|
||||||
|
if(_state != State::Unlocked) {
|
||||||
|
for(Node* node : _children) {
|
||||||
|
node->setState(State::Unavailable, NotClicked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for(Node* node : _children) {
|
||||||
|
node->setState(State::Available, NotClicked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
void ResearchTree::generateEngineTree() {
|
||||||
|
if(!_engineNodes.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_engineNodes.emplace(VerseEngine, Node{Node::Type::Engine, "Verse Engine", 1, 3,
|
||||||
|
"A basic low-speed engine with great durability.",
|
||||||
|
"Durability +346, Power +60, Armour +22, Acceleration +75, Magazine load +35, Energy capacity +35",
|
||||||
|
"Durability +3%"});
|
||||||
|
|
||||||
|
_engineNodes.emplace(LoadedEngine, Node{Node::Type::Engine, "Loaded Engine", 1, 3,
|
||||||
|
"An alternate heavier model of engine used to operate a M.A.S.S.",
|
||||||
|
"Durability +288, Power +84, Armour +14, Acceleration +75, Magazine load +35, Energy capacity +35",
|
||||||
|
"Power +3%"});
|
||||||
|
|
||||||
|
_engineNodes.emplace(MetalPlatings1, Node{Node::Type::Gear, "Metal Platings 1", 1, 0,
|
||||||
|
"Level 1 metal plating that adds durability and armour to your engine.",
|
||||||
|
"Durability +60, Armour +5, Acceleration -15", ""});
|
||||||
|
|
||||||
|
_engineNodes.emplace(HeatTurbines1, Node{Node::Type::Gear, "Heat Turbines 1", 1, 0,
|
||||||
|
"Modified heat turbines to increase speed for a M.A.S.S.",
|
||||||
|
"Acceleration +75, Fuel capacity +5", ""});
|
||||||
|
|
||||||
|
_engineNodes.emplace(Microcontroller1, Node{Node::Type::Gear, "Microcontroller 1", 1, 0,
|
||||||
|
"A microchip that enhances various aspects of a M.A.S.S.",
|
||||||
|
"Durability +36, Power +1, Armour +11, Magazine load +5, Energy capacity +5, Fuel capacity +3", ""});
|
||||||
|
|
||||||
|
_engineNodes.emplace(CombustionController1, Node{Node::Type::Gear, "Combustion Controller 1", 1, 0,
|
||||||
|
"Controlled combustion allows increased power generation through specific ignition.",
|
||||||
|
"Power +2, Magazine load +10, Energy capacity +10, Acceleration -25", ""});
|
||||||
|
|
||||||
|
_engineNodes.emplace(ModAlloyEngine, Node{Node::Type::Engine, "Mod. Alloy Engine", 2, 3,
|
||||||
|
"Built with a modified alloy and able to sustain greater attacks from any enemy.",
|
||||||
|
"Durability +1152, Power +75, Armour +194, Acceleration +75, Magazine load +40, Energy capacity +40",
|
||||||
|
"Durability +3%"});
|
||||||
|
|
||||||
|
_engineNodes.emplace(ChargedEngine, Node{Node::Type::Engine, "Charged Engine", 2, 3,
|
||||||
|
"Remove most armours to attain more speed and power, and fuel.",
|
||||||
|
"Durability +960, Power +75, Acceleration +300, Magazine load +40, Energy capacity +40, Fuel capacity +52",
|
||||||
|
"Acceleration +5"});
|
||||||
|
|
||||||
|
_engineNodes.emplace(ReinforcedLoadedEngine, Node{Node::Type::Engine, "Reinforced Loaded Engine", 2, 3,
|
||||||
|
"An upgraded build of the basic model, with reinforced Verse Steel for more strength.",
|
||||||
|
"Durability +960, Power +105, Armour +130, Acceleration +75, Magazine load +40, Energy capacity +40",
|
||||||
|
"Power +3%"});
|
||||||
|
|
||||||
|
_engineNodes.emplace(HeavyImpactsEnabler, Node{Node::Type::Engine, "Heavy Impacts Enabler", 2, 0,
|
||||||
|
"Enable the usage of Heavy Combos in close combat attacks.", "", ""});
|
||||||
|
|
||||||
|
_engineNodes.emplace(MetalPlatings2, Node{Node::Type::Gear, "Metal Platings 2", 2, 0,
|
||||||
|
"Level 2 Metal plating that adds durability and armour to your engine.",
|
||||||
|
"Durability +180, Armour +41, Acceleration -15", ""});
|
||||||
|
|
||||||
|
_engineNodes.emplace(HeatTurbines2, Node{Node::Type::Gear, "Heat Turbines 2", 2, 0,
|
||||||
|
"Level 2 Modified heat turbines to increase speed for a M.A.S.S.",
|
||||||
|
"Acceleration +75, Fuel capacity +16", ""});
|
||||||
|
|
||||||
|
_engineNodes.emplace(Microcontroller2, Node{Node::Type::Gear, "Microcontroller 2", 2, 0,
|
||||||
|
"Level 2 Microchip that enhances various aspects of a M.A.S.S.",
|
||||||
|
"Durability +108, Power +3, Armour +17, Magazine load +6, Energy capacity +6, Fuel capacity +8", ""});
|
||||||
|
|
||||||
|
_engineNodes.emplace(WeaponsCargo1, Node{Node::Type::Gear, "Weapons Cargo 1", 2, 0,
|
||||||
|
"Added another cargo hold for ammo and energy recharger",
|
||||||
|
"Magazine load +24, Energy capacity +24, Acceleration -40", ""});
|
||||||
|
|
||||||
|
_engineNodes.emplace(CombustionController2, Node{Node::Type::Gear, "Combustion Controller 2", 2, 0,
|
||||||
|
"Level 2 Controlled combustion allows increased power generation through specific ignition.",
|
||||||
|
"Power +5, Magazine load +12, Energy capacity +12, Acceleration -25", ""});
|
||||||
|
|
||||||
|
_engineNodes.emplace(PoweredRewiring1, Node{Node::Type::Gear, "Powered Rewiring 1", 2, 0,
|
||||||
|
"Rewiring that efficiently improves power and engine durability.",
|
||||||
|
"Durability +180, Power +5", ""});
|
||||||
|
|
||||||
|
_engineNodes.at(VerseEngine).addChild(_engineNodes.at(MetalPlatings1));
|
||||||
|
_engineNodes.at(VerseEngine).addChild(_engineNodes.at(HeatTurbines1));
|
||||||
|
_engineNodes.at(VerseEngine).addChild(_engineNodes.at(LoadedEngine));
|
||||||
|
_engineNodes.at(LoadedEngine).addChild(_engineNodes.at(Microcontroller1));
|
||||||
|
_engineNodes.at(LoadedEngine).addChild(_engineNodes.at(CombustionController1));
|
||||||
|
_engineNodes.at(MetalPlatings1).addChild(_engineNodes.at(ModAlloyEngine));
|
||||||
|
_engineNodes.at(HeatTurbines1).addChild(_engineNodes.at(ChargedEngine));
|
||||||
|
_engineNodes.at(Microcontroller1).addChild(_engineNodes.at(ChargedEngine));
|
||||||
|
_engineNodes.at(CombustionController1).addChild(_engineNodes.at(ReinforcedLoadedEngine));
|
||||||
|
_engineNodes.at(ModAlloyEngine).addChild(_engineNodes.at(MetalPlatings2));
|
||||||
|
_engineNodes.at(ModAlloyEngine).addChild(_engineNodes.at(HeatTurbines2));
|
||||||
|
_engineNodes.at(ChargedEngine).addChild(_engineNodes.at(HeatTurbines2));
|
||||||
|
_engineNodes.at(ChargedEngine).addChild(_engineNodes.at(Microcontroller2));
|
||||||
|
_engineNodes.at(ChargedEngine).addChild(_engineNodes.at(WeaponsCargo1));
|
||||||
|
_engineNodes.at(ReinforcedLoadedEngine).addChild(_engineNodes.at(CombustionController2));
|
||||||
|
_engineNodes.at(ReinforcedLoadedEngine).addChild(_engineNodes.at(PoweredRewiring1));
|
||||||
|
_engineNodes.at(ReinforcedLoadedEngine).addChild(_engineNodes.at(HeavyImpactsEnabler));
|
||||||
|
|
||||||
|
_engineNodes.at(VerseEngine).setState(Node::State::Unlocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResearchTree::generateOSTree() {
|
||||||
|
if(!_osNodes.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_osNodes.emplace(NeuralOS, Node{Node::Type::OS, "Neural OS", 1, 3,
|
||||||
|
"Synchronise the links between your nervous system and the M.A.S.S. perfectly.",
|
||||||
|
"Accuracy +24, Shield +624, Fuel burn rate +200, Magazine reload +24, Energy recharge +66, Shield recover +1620",
|
||||||
|
"Shield +3%"});
|
||||||
|
|
||||||
|
_osNodes.at(NeuralOS).setState(Node::State::Unlocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResearchTree::generateArchTree() {
|
||||||
|
if(!_archNodes.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_archNodes.emplace(StandardFrame, Node{Node::Type::Architect, "Standard Frame", 1, 2,
|
||||||
|
"The standard frame architecture for mass-produced humanoid robots. It uses the most common technologies and materials.",
|
||||||
|
"Durability +300, Shield +200, Physical damage +600, Piercing damage +300, Plasma damage +300",
|
||||||
|
""});
|
||||||
|
|
||||||
|
_archNodes.at(StandardFrame).setState(Node::State::Unlocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResearchTree::readEngineUnlocks(Containers::ArrayView<Int> engines, Containers::ArrayView<Int> gears) {
|
||||||
|
if(engines == nullptr || engines.size() == 0) {
|
||||||
|
Utility::Error{} << "Engines can't be empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Int& engine : engines) {
|
||||||
|
if(_engineNodes.find(engine) != _engineNodes.end()) {
|
||||||
|
_engineNodes.at(engine).setState(Node::State::Unlocked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(gears == nullptr || engines.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Int& gear : gears) {
|
||||||
|
if(_engineNodes.find(gear) != _engineNodes.end()) {
|
||||||
|
_engineNodes.at(gear).setState(Node::State::Unlocked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ResearchTree::getEngineRootNode() -> Node& {
|
||||||
|
return _engineNodes.at(VerseEngine);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ResearchTree::getOSRootNode() -> Node& {
|
||||||
|
return _osNodes.at(NeuralOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ResearchTree::getArchRootNode() -> Node& {
|
||||||
|
return _archNodes.at(StandardFrame);
|
||||||
|
}
|
112
src/ResearchTree/ResearchTree.h
Normal file
112
src/ResearchTree/ResearchTree.h
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// MassBuilderSaveTool
|
||||||
|
// Copyright (C) 2021 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 <unordered_map>
|
||||||
|
|
||||||
|
#include <Corrade/Containers/ArrayView.h>
|
||||||
|
#include <Corrade/Containers/GrowableArray.h>
|
||||||
|
|
||||||
|
#include <Magnum/Magnum.h>
|
||||||
|
|
||||||
|
using namespace Corrade;
|
||||||
|
using namespace Magnum;
|
||||||
|
|
||||||
|
class Node {
|
||||||
|
public:
|
||||||
|
enum class Type {
|
||||||
|
Engine,
|
||||||
|
Gear,
|
||||||
|
OS,
|
||||||
|
Module,
|
||||||
|
Architect,
|
||||||
|
Tech
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
Unavailable,
|
||||||
|
Available,
|
||||||
|
Unlocked
|
||||||
|
};
|
||||||
|
|
||||||
|
Node(Type type, const char* name, UnsignedByte tier, UnsignedByte slots,
|
||||||
|
const char* description, const char* active_effect, const char* passive_effect);
|
||||||
|
|
||||||
|
Node(const Node& other) = delete;
|
||||||
|
Node& operator=(const Node& other) = delete;
|
||||||
|
|
||||||
|
Node(Node&& other) = default;
|
||||||
|
Node& operator=(Node&& other) = default;
|
||||||
|
|
||||||
|
auto type() const -> Type;
|
||||||
|
|
||||||
|
auto state() const -> State;
|
||||||
|
void setState(State state);
|
||||||
|
|
||||||
|
auto name() const -> const char*;
|
||||||
|
|
||||||
|
auto tier() const -> UnsignedByte;
|
||||||
|
|
||||||
|
auto slots() const -> UnsignedByte;
|
||||||
|
|
||||||
|
auto description() const -> const char*;
|
||||||
|
|
||||||
|
auto activeEffect() const -> const char*;
|
||||||
|
|
||||||
|
auto passiveEffect() const -> const char*;
|
||||||
|
|
||||||
|
auto children() -> Containers::ArrayView<Node*>;
|
||||||
|
void addChild(Node& child);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addParent(Node& parent);
|
||||||
|
|
||||||
|
struct NotClickedT {} NotClicked;
|
||||||
|
void setState(State state, NotClickedT);
|
||||||
|
|
||||||
|
Type _type;
|
||||||
|
State _state;
|
||||||
|
|
||||||
|
const char* _name;
|
||||||
|
UnsignedByte _tier;
|
||||||
|
UnsignedByte _slots;
|
||||||
|
const char* _description;
|
||||||
|
const char* _activeEffect;
|
||||||
|
const char* _passiveEffect;
|
||||||
|
|
||||||
|
Containers::Array<Node*> _parents;
|
||||||
|
Containers::Array<Node*> _children;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResearchTree {
|
||||||
|
public:
|
||||||
|
void generateEngineTree();
|
||||||
|
void generateOSTree();
|
||||||
|
void generateArchTree();
|
||||||
|
|
||||||
|
void readEngineUnlocks(Containers::ArrayView<Int> engines, Containers::ArrayView<Int> gears = nullptr);
|
||||||
|
|
||||||
|
auto getEngineRootNode() -> Node&;
|
||||||
|
auto getOSRootNode() -> Node&;
|
||||||
|
auto getArchRootNode() -> Node&;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Tree = std::unordered_map<Int, Node>;
|
||||||
|
Tree _engineNodes;
|
||||||
|
Tree _osNodes;
|
||||||
|
Tree _archNodes;
|
||||||
|
};
|
|
@ -17,8 +17,9 @@
|
||||||
#include "SaveTool.h"
|
#include "SaveTool.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdarg>
|
|
||||||
|
|
||||||
|
#include <Corrade/Containers/ScopeGuard.h>
|
||||||
|
#include <Corrade/Utility/Directory.h>
|
||||||
#include <Corrade/Utility/FormatStl.h>
|
#include <Corrade/Utility/FormatStl.h>
|
||||||
#include <Corrade/Utility/String.h>
|
#include <Corrade/Utility/String.h>
|
||||||
#include <Corrade/Utility/Unicode.h>
|
#include <Corrade/Utility/Unicode.h>
|
||||||
|
@ -33,6 +34,8 @@
|
||||||
#include <winuser.h>
|
#include <winuser.h>
|
||||||
#include <processthreadsapi.h>
|
#include <processthreadsapi.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#include <wtsapi32.h>
|
||||||
|
|
||||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||||
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
||||||
|
@ -60,11 +63,11 @@ SaveTool::SaveTool(const Arguments& arguments):
|
||||||
Utility::Debug{} << "Clickthrough is available.";
|
Utility::Debug{} << "Clickthrough is available.";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Utility::Debug{} << "Clickthrough is not available (hint couldn't be set).";
|
Utility::Warning{} << "Clickthrough is not available (hint couldn't be set).";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Utility::Debug{} << "Clickthrough is not available (SDL2 is too old).";
|
Utility::Warning{} << "Clickthrough is not available (SDL2 is too old).";
|
||||||
}
|
}
|
||||||
|
|
||||||
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||||||
|
@ -80,7 +83,33 @@ SaveTool::SaveTool(const Arguments& arguments):
|
||||||
|
|
||||||
if((_initEventId = SDL_RegisterEvents(1)) == UnsignedInt(-1)) {
|
if((_initEventId = SDL_RegisterEvents(1)) == UnsignedInt(-1)) {
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||||
"SDL_RegisterEvents failed in SaveTool::SaveTool(). Exiting...", nullptr);
|
"SDL_RegisterEvents failed in SaveTool::SaveTool(). Exiting...", window());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!findGameDataDirectory()) {
|
||||||
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", _lastError.c_str(), window());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
_configDir = Utility::Directory::join(_gameDataDir, "Saved/Config/WindowsNoEditor");
|
||||||
|
_saveDir = Utility::Directory::join(_gameDataDir, "Saved/SaveGames");
|
||||||
|
_screenshotsDir = Utility::Directory::join(_gameDataDir, "Saved/Screenshots/WindowsNoEditor");
|
||||||
|
|
||||||
|
if(SDL_InitSubSystem(SDL_INIT_TIMER) != 0) {
|
||||||
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", SDL_GetError(), window());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkGameState();
|
||||||
|
_gameCheckTimerId = SDL_AddTimer(2000,
|
||||||
|
[](UnsignedInt interval, void* param)->UnsignedInt{
|
||||||
|
static_cast<SaveTool*>(param)->checkGameState();
|
||||||
|
return interval;
|
||||||
|
},
|
||||||
|
this);
|
||||||
|
if(_gameCheckTimerId == 0) {
|
||||||
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_GetError(), window());
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +155,9 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id,
|
||||||
case efsw::Actions::Modified:
|
case efsw::Actions::Modified:
|
||||||
if(filename == _currentProfile->filename()) {
|
if(filename == _currentProfile->filename()) {
|
||||||
_currentProfile->refreshValues();
|
_currentProfile->refreshValues();
|
||||||
|
if(_tree) {
|
||||||
|
_tree->readEngineUnlocks(_currentProfile->engineInventory(), _currentProfile->gearInventory());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
|
else if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
|
||||||
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
|
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
|
||||||
|
@ -216,22 +248,6 @@ void SaveTool::initEvent(SDL_Event& event) {
|
||||||
case InitSuccess:
|
case InitSuccess:
|
||||||
_uiState = UiState::ProfileManager;
|
_uiState = UiState::ProfileManager;
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
SDL_InitSubSystem(SDL_INIT_TIMER);
|
|
||||||
_mbManager->checkGameState();
|
|
||||||
_gameCheckTimerId = SDL_AddTimer(2000,
|
|
||||||
[](UnsignedInt interval, void* param)->UnsignedInt{
|
|
||||||
static_cast<MassBuilderManager*>(param)->checkGameState();
|
|
||||||
return interval;
|
|
||||||
},
|
|
||||||
_mbManager.get());
|
|
||||||
if(_gameCheckTimerId == 0) {
|
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_GetError(), window());
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MbManagerFailure:
|
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising MassBuilderManager", _mbManager->lastError().c_str(), window());
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
break;
|
break;
|
||||||
case ProfileManagerFailure:
|
case ProfileManagerFailure:
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising ProfileManager", _profileManager->lastError().c_str(), window());
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising ProfileManager", _profileManager->lastError().c_str(), window());
|
||||||
|
@ -291,14 +307,7 @@ void SaveTool::initialiseManager() {
|
||||||
SDL_zero(event);
|
SDL_zero(event);
|
||||||
event.type = _initEventId;
|
event.type = _initEventId;
|
||||||
|
|
||||||
_mbManager.emplace();
|
_profileManager.emplace(_gameDataDir);
|
||||||
if(!_mbManager->ready()) {
|
|
||||||
event.user.code = MbManagerFailure;
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_profileManager.emplace(_mbManager->saveDirectory());
|
|
||||||
if(!_profileManager->ready()) {
|
if(!_profileManager->ready()) {
|
||||||
event.user.code = ProfileManagerFailure;
|
event.user.code = ProfileManagerFailure;
|
||||||
SDL_PushEvent(&event);
|
SDL_PushEvent(&event);
|
||||||
|
@ -309,6 +318,25 @@ void SaveTool::initialiseManager() {
|
||||||
SDL_PushEvent(&event);
|
SDL_PushEvent(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto SaveTool::findGameDataDirectory() -> bool {
|
||||||
|
wchar_t* localappdata_path = nullptr;
|
||||||
|
Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree};
|
||||||
|
if(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_NO_APPCONTAINER_REDIRECTION, nullptr, &localappdata_path) != S_OK)
|
||||||
|
{
|
||||||
|
_lastError = "SHGetKnownFolderPath() failed in SaveTool::findGameDataDirectory()";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_gameDataDir = Utility::Directory::join(Utility::Directory::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)), "MASS_Builder");
|
||||||
|
|
||||||
|
if(!Utility::Directory::exists(_gameDataDir)) {
|
||||||
|
_lastError = _gameDataDir + " wasn't found. Make sure to play the game at least once.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void SaveTool::initialiseMassManager() {
|
void SaveTool::initialiseMassManager() {
|
||||||
_currentProfile->refreshValues();
|
_currentProfile->refreshValues();
|
||||||
|
|
||||||
|
@ -451,7 +479,7 @@ void SaveTool::drawGameState() {
|
||||||
ImGui::TextUnformatted("Game state:");
|
ImGui::TextUnformatted("Game state:");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
{
|
{
|
||||||
switch(_mbManager->gameState()) {
|
switch(_gameState) {
|
||||||
case GameState::Unknown:
|
case GameState::Unknown:
|
||||||
ImGui::TextColored(ImColor{0xff00a5ff}, ICON_FA_CIRCLE);
|
ImGui::TextColored(ImColor{0xff00a5ff}, ICON_FA_CIRCLE);
|
||||||
drawTooltip("unknown");
|
drawTooltip("unknown");
|
||||||
|
@ -487,18 +515,28 @@ void SaveTool::drawTooltip(const char* text, Float wrap_pos) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveTool::drawUnsafeText(const char* text, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, text);
|
|
||||||
if(!_unsafeMode && _mbManager->gameState() != GameState::NotRunning) {
|
|
||||||
ImGui::TextDisabledV(text, args);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ImGui::TextV(text, args);
|
|
||||||
}
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::openUri(const std::string& uri) {
|
void SaveTool::openUri(const std::string& uri) {
|
||||||
ShellExecuteW(nullptr, nullptr, Utility::Unicode::widen(uri).c_str(), nullptr, nullptr, SW_SHOW);
|
ShellExecuteW(nullptr, nullptr, Utility::Unicode::widen(uri).c_str(), nullptr, nullptr, SW_SHOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SaveTool::checkGameState() {
|
||||||
|
WTS_PROCESS_INFOW* process_infos = nullptr;
|
||||||
|
unsigned long process_count = 0;
|
||||||
|
|
||||||
|
if(WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &process_infos, &process_count)) {
|
||||||
|
Containers::ScopeGuard guard{process_infos, WTSFreeMemory};
|
||||||
|
|
||||||
|
for(unsigned long i = 0; i < process_count; ++i) {
|
||||||
|
if(std::wcscmp(process_infos[i].pProcessName, L"MASS_Builder-Win64-Shipping.exe") == 0) {
|
||||||
|
_gameState = GameState::Running;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_gameState = GameState::NotRunning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_gameState = GameState::Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,9 +31,11 @@
|
||||||
|
|
||||||
#include <efsw/efsw.hpp>
|
#include <efsw/efsw.hpp>
|
||||||
|
|
||||||
#include "../MassBuilderManager/MassBuilderManager.h"
|
|
||||||
#include "../ProfileManager/ProfileManager.h"
|
#include "../ProfileManager/ProfileManager.h"
|
||||||
#include "../MassManager/MassManager.h"
|
#include "../MassManager/MassManager.h"
|
||||||
|
#include "../ResearchTree/ResearchTree.h"
|
||||||
|
|
||||||
|
class Node;
|
||||||
|
|
||||||
using namespace Corrade;
|
using namespace Corrade;
|
||||||
using namespace Magnum;
|
using namespace Magnum;
|
||||||
|
@ -68,7 +70,6 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
||||||
|
|
||||||
enum InitStatus: Int {
|
enum InitStatus: Int {
|
||||||
InitSuccess,
|
InitSuccess,
|
||||||
MbManagerFailure,
|
|
||||||
ProfileManagerFailure
|
ProfileManagerFailure
|
||||||
};
|
};
|
||||||
void initEvent(SDL_Event& event);
|
void initEvent(SDL_Event& event);
|
||||||
|
@ -76,6 +77,7 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
||||||
// Initialisation methods
|
// Initialisation methods
|
||||||
void initialiseGui();
|
void initialiseGui();
|
||||||
void initialiseManager();
|
void initialiseManager();
|
||||||
|
auto findGameDataDirectory() -> bool;
|
||||||
void initialiseMassManager();
|
void initialiseMassManager();
|
||||||
void initialiseFileWatcher();
|
void initialiseFileWatcher();
|
||||||
|
|
||||||
|
@ -94,6 +96,8 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
||||||
auto drawRenamePopup(Containers::ArrayView<char> name_view) -> bool;
|
auto drawRenamePopup(Containers::ArrayView<char> name_view) -> bool;
|
||||||
void drawGeneralInfo();
|
void drawGeneralInfo();
|
||||||
void drawResearchInventory();
|
void drawResearchInventory();
|
||||||
|
void drawResearchTree();
|
||||||
|
void drawNode(Node& node);
|
||||||
void drawMassManager();
|
void drawMassManager();
|
||||||
auto drawDeleteMassPopup(int mass_index) -> ImGuiID;
|
auto drawDeleteMassPopup(int mass_index) -> ImGuiID;
|
||||||
auto drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID;
|
auto drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID;
|
||||||
|
@ -106,7 +110,7 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
||||||
|
|
||||||
template<typename Functor, typename... Args>
|
template<typename Functor, typename... Args>
|
||||||
auto drawUnsafeWidget(Functor func, Args... args) -> bool {
|
auto drawUnsafeWidget(Functor func, Args... args) -> bool {
|
||||||
GameState game_state = _mbManager->gameState();
|
GameState game_state = _gameState; // Copying the value to reduce the risk of a data race.
|
||||||
if(!_unsafeMode && game_state != GameState::NotRunning) {
|
if(!_unsafeMode && game_state != GameState::NotRunning) {
|
||||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f);
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f);
|
||||||
|
@ -123,10 +127,20 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
||||||
} // Obviously, should only be used with ImGui widgets that return a bool.
|
} // Obviously, should only be used with ImGui widgets that return a bool.
|
||||||
// Also, func should be a lambda if there are any default arguments, like ImGui::Button(), etc...
|
// Also, func should be a lambda if there are any default arguments, like ImGui::Button(), etc...
|
||||||
|
|
||||||
void drawUnsafeText(const char* text, ...); // Alternative to the above, for ImGui::Text*() variants.
|
template<typename... Args>
|
||||||
|
void drawUnsafeText(const char* text, Args... args) { // Alternative to the above, for ImGui::Text*() variants.
|
||||||
|
if(!_unsafeMode && _gameState != GameState::NotRunning) {
|
||||||
|
ImGui::TextDisabled(text, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::Text(text, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void openUri(const std::string& uri);
|
void openUri(const std::string& uri);
|
||||||
|
|
||||||
|
void checkGameState();
|
||||||
|
|
||||||
Utility::Resource _rs{"assets"};
|
Utility::Resource _rs{"assets"};
|
||||||
|
|
||||||
// GUI-related members
|
// GUI-related members
|
||||||
|
@ -137,8 +151,7 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
||||||
Initialising,
|
Initialising,
|
||||||
ProfileManager,
|
ProfileManager,
|
||||||
MainManager
|
MainManager
|
||||||
};
|
} _uiState{UiState::Disclaimer};
|
||||||
UiState _uiState{UiState::Disclaimer};
|
|
||||||
|
|
||||||
bool _aboutPopup{false};
|
bool _aboutPopup{false};
|
||||||
#ifdef SAVETOOL_DEBUG_BUILD
|
#ifdef SAVETOOL_DEBUG_BUILD
|
||||||
|
@ -150,8 +163,18 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
||||||
std::thread _thread;
|
std::thread _thread;
|
||||||
UnsignedInt _initEventId;
|
UnsignedInt _initEventId;
|
||||||
|
|
||||||
Containers::Pointer<MassBuilderManager> _mbManager;
|
std::string _lastError;
|
||||||
SDL_TimerID _gameCheckTimerId;
|
|
||||||
|
std::string _gameDataDir;
|
||||||
|
std::string _configDir;
|
||||||
|
std::string _saveDir;
|
||||||
|
std::string _screenshotsDir;
|
||||||
|
|
||||||
|
enum class GameState : UnsignedByte {
|
||||||
|
Unknown, NotRunning, Running
|
||||||
|
} _gameState{GameState::Unknown};
|
||||||
|
|
||||||
|
SDL_TimerID _gameCheckTimerId = 0;
|
||||||
|
|
||||||
Containers::Pointer<ProfileManager> _profileManager;
|
Containers::Pointer<ProfileManager> _profileManager;
|
||||||
Profile* _currentProfile{nullptr};
|
Profile* _currentProfile{nullptr};
|
||||||
|
@ -165,5 +188,7 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
||||||
};
|
};
|
||||||
Containers::StaticArray<2, efsw::WatchID> _watchIDs;
|
Containers::StaticArray<2, efsw::WatchID> _watchIDs;
|
||||||
|
|
||||||
|
Containers::Pointer<ResearchTree> _tree;
|
||||||
|
|
||||||
bool _unsafeMode{false};
|
bool _unsafeMode{false};
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,6 +87,11 @@ void SaveTool::drawManager() {
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ImGui::BeginTabItem("Research tree")) {
|
||||||
|
drawResearchTree();
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::EndTabBar();
|
ImGui::EndTabBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +166,7 @@ auto SaveTool::drawRenamePopup(Containers::ArrayView<char> name_view) -> bool {
|
||||||
ImGuiInputTextFlags_CallbackCharFilter,
|
ImGuiInputTextFlags_CallbackCharFilter,
|
||||||
callback, nullptr);
|
callback, nullptr);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
GameState game_state = _mbManager->gameState();
|
GameState game_state = _gameState;
|
||||||
if((!_unsafeMode && game_state != GameState::NotRunning) ||
|
if((!_unsafeMode && game_state != GameState::NotRunning) ||
|
||||||
!(len >= 6 && len <= 32) ||
|
!(len >= 6 && len <= 32) ||
|
||||||
!(name_view[0] != ' ' && name_view[len - 1] != ' '))
|
!(name_view[0] != ' ' && name_view[len - 1] != ' '))
|
||||||
|
@ -265,7 +270,7 @@ void SaveTool::drawGeneralInfo() {
|
||||||
}
|
}
|
||||||
drawTooltip("Story progress directly affects unlocked levels.");
|
drawTooltip("Story progress directly affects unlocked levels.");
|
||||||
if(ImGui::BeginPopup("StoryProgressMenu")) {
|
if(ImGui::BeginPopup("StoryProgressMenu")) {
|
||||||
if(!_unsafeMode && _mbManager->gameState() != GameState::NotRunning) {
|
if(!_unsafeMode && _gameState != GameState::NotRunning) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
for(const auto& sp : story_progress) {
|
for(const auto& sp : story_progress) {
|
||||||
|
@ -396,6 +401,124 @@ void SaveTool::drawResearchInventory() {
|
||||||
#undef matRow
|
#undef matRow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SaveTool::drawResearchTree() {
|
||||||
|
if(!_tree) {
|
||||||
|
_tree.emplace();
|
||||||
|
|
||||||
|
_tree->generateEngineTree();
|
||||||
|
_tree->readEngineUnlocks(_currentProfile->engineInventory(), _currentProfile->gearInventory());
|
||||||
|
|
||||||
|
_tree->generateOSTree();
|
||||||
|
|
||||||
|
_tree->generateArchTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ImGui::BeginTabBar("##TreeTabBar")) {
|
||||||
|
if(ImGui::BeginTabItem("Engine")) {
|
||||||
|
if(ImGui::BeginTable("##EngineTable", 1, ImGuiTableFlags_RowBg)) {
|
||||||
|
ImGui::TableSetupColumn("##Column", ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
|
||||||
|
drawNode(_tree->getEngineRootNode());
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ImGui::BeginTabItem("OS")) {
|
||||||
|
if(ImGui::BeginTable("##OSTable", 1, ImGuiTableFlags_RowBg)) {
|
||||||
|
ImGui::TableSetupColumn("##Column", ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
|
||||||
|
drawNode(_tree->getOSRootNode());
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ImGui::BeginTabItem("Architect")) {
|
||||||
|
if(ImGui::BeginTable("##ArchTable", 1, ImGuiTableFlags_RowBg)) {
|
||||||
|
ImGui::TableSetupColumn("##Column", ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
|
||||||
|
drawNode(_tree->getArchRootNode());
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveTool::drawNode(Node& node) {
|
||||||
|
auto nodeTooltip = [this, &node]{
|
||||||
|
if(ImGui::IsItemHovered()) {
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
ImGui::Text("Tier %u", node.tier());
|
||||||
|
|
||||||
|
if(node.type() == Node::Type::Engine ||
|
||||||
|
node.type() == Node::Type::OS ||
|
||||||
|
node.type() == Node::Type::Architect)
|
||||||
|
{
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||||
|
ImGui::SameLine();
|
||||||
|
switch(node.type()) {
|
||||||
|
case Node::Type::Engine:
|
||||||
|
ImGui::Text("%u gear slots", node.slots());
|
||||||
|
break;
|
||||||
|
case Node::Type::OS:
|
||||||
|
ImGui::Text("%u module slots", node.slots());
|
||||||
|
break;
|
||||||
|
case Node::Type::Architect:
|
||||||
|
ImGui::Text("%u tech slots", node.slots());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
|
||||||
|
ImGui::TextUnformatted(node.description());
|
||||||
|
if(std::strncmp("", node.activeEffect(), 3) != 0) {
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Active effect: %s", node.activeEffect());
|
||||||
|
}
|
||||||
|
if(std::strncmp("", node.passiveEffect(), 3) != 0) {
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Passive effect: %s", node.passiveEffect());
|
||||||
|
}
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
|
bool has_children = (node.children().size() > 0);
|
||||||
|
if(has_children) {
|
||||||
|
bool open = ImGui::TreeNodeEx(node.name(), ImGuiTreeNodeFlags_SpanAvailWidth|(node.state() == Node::State::Unlocked ? ImGuiTreeNodeFlags_Selected : 0));
|
||||||
|
nodeTooltip();
|
||||||
|
if(open) {
|
||||||
|
for(Node* child : node.children()) {
|
||||||
|
drawNode(*child);
|
||||||
|
}
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::TreeNodeEx(node.name(), ImGuiTreeNodeFlags_SpanAvailWidth|ImGuiTreeNodeFlags_Leaf|ImGuiTreeNodeFlags_NoTreePushOnOpen|
|
||||||
|
ImGuiTreeNodeFlags_Bullet|(node.state() == Node::State::Unlocked ? ImGuiTreeNodeFlags_Selected : 0));
|
||||||
|
nodeTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SaveTool::drawMassManager() {
|
void SaveTool::drawMassManager() {
|
||||||
if(!_massManager) {
|
if(!_massManager) {
|
||||||
return;
|
return;
|
||||||
|
@ -441,7 +564,7 @@ void SaveTool::drawMassManager() {
|
||||||
|
|
||||||
ImGui::EndDragDropSource();
|
ImGui::EndDragDropSource();
|
||||||
}
|
}
|
||||||
if((!_unsafeMode && _mbManager->gameState() == GameState::NotRunning) && ImGui::BeginDragDropTarget()) {
|
if((!_unsafeMode && _gameState == GameState::NotRunning) && ImGui::BeginDragDropTarget()) {
|
||||||
if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("StagedMass")) {
|
if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("StagedMass")) {
|
||||||
if(payload->DataSize != sizeof(std::string)) {
|
if(payload->DataSize != sizeof(std::string)) {
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error",
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error",
|
||||||
|
@ -595,7 +718,7 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_mbManager->gameState() != GameState::NotRunning) {
|
if(_gameState != GameState::NotRunning) {
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -24,18 +24,17 @@
|
||||||
void SaveTool::drawMainMenu() {
|
void SaveTool::drawMainMenu() {
|
||||||
if(ImGui::BeginMainMenuBar()) {
|
if(ImGui::BeginMainMenuBar()) {
|
||||||
if(ImGui::BeginMenu("Save Tool##SaveToolMenu")) {
|
if(ImGui::BeginMenu("Save Tool##SaveToolMenu")) {
|
||||||
if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open data directory", _mbManager != nullptr)) {
|
if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open game data directory", Utility::Directory::exists(_gameDataDir))) {
|
||||||
if(ImGui::MenuItem(ICON_FA_COG " Configuration", nullptr, false, _mbManager != nullptr)) {
|
if(ImGui::MenuItem(ICON_FA_COG " Configuration", nullptr, false, Utility::Directory::exists(_configDir))) {
|
||||||
openUri(Utility::Directory::toNativeSeparators(_mbManager->saveDirectory() + "/Saved/Config/WindowsNoEditor"));
|
openUri(Utility::Directory::toNativeSeparators(_configDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ImGui::MenuItem(ICON_FA_SAVE " Saves", nullptr, false, _profileManager != nullptr)) {
|
if(ImGui::MenuItem(ICON_FA_SAVE " Saves", nullptr, false, Utility::Directory::exists(_saveDir))) {
|
||||||
openUri(Utility::Directory::toNativeSeparators(_profileManager->saveDirectory()));
|
openUri(Utility::Directory::toNativeSeparators(_saveDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _screenshotsAvailable = Utility::Directory::exists(_mbManager->saveDirectory() + "/Saved/Screenshots/WindowsNoEditor");
|
if(ImGui::MenuItem(ICON_FA_IMAGE " Screenshots", nullptr, false, Utility::Directory::exists(_screenshotsDir))) {
|
||||||
if(ImGui::MenuItem(ICON_FA_IMAGE " Screenshots", nullptr, false, _screenshotsAvailable)) {
|
openUri(Utility::Directory::toNativeSeparators(_screenshotsDir));
|
||||||
openUri(Utility::Directory::toNativeSeparators(_mbManager->saveDirectory() + "/Screenshots/WindowsNoEditor"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
|
@ -105,7 +104,7 @@ void SaveTool::drawMainMenu() {
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_mbManager != nullptr) {
|
if(_gameCheckTimerId != 0) {
|
||||||
if(ImGui::BeginTable("##MainMenuLayout", 2)) {
|
if(ImGui::BeginTable("##MainMenuLayout", 2)) {
|
||||||
ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch);
|
ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch);
|
||||||
ImGui::TableSetupColumn("##GameState", ImGuiTableColumnFlags_WidthFixed);
|
ImGui::TableSetupColumn("##GameState", ImGuiTableColumnFlags_WidthFixed);
|
||||||
|
|
Loading…
Reference in a new issue