295 lines
7.7 KiB
C++
295 lines
7.7 KiB
C++
#include "BalancingWindow.h"
|
|
|
|
#include <QHBoxLayout>
|
|
#include <QMessageBox>
|
|
#include <QVBoxLayout>
|
|
|
|
#include "ConfigLoader.h"
|
|
#include "InspectWindow.h"
|
|
#include "VisualsLoader.h"
|
|
|
|
BalancingWindow::BalancingWindow(const BalancingConfig& balancingConfig,
|
|
GameConfig gameConfig,
|
|
const std::string& configDir,
|
|
const std::string& balancingConfigPath,
|
|
QWidget* parent)
|
|
: QWidget(parent)
|
|
, m_gameConfig(std::move(gameConfig))
|
|
, m_configDir(configDir)
|
|
, m_balancingConfigPath(balancingConfigPath)
|
|
, m_nextSeed(0)
|
|
, m_inspectWindow(nullptr)
|
|
, m_inspectedArenaIndex(-1)
|
|
{
|
|
m_visuals = VisualsLoader::load(m_configDir + "/visuals.toml");
|
|
setWindowTitle(tr("DotaFactory — Balancing Tool"));
|
|
resize(800, 600);
|
|
|
|
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
QHBoxLayout* buttonRow = new QHBoxLayout();
|
|
m_reloadButton = new QPushButton(tr("Reload Config"), this);
|
|
m_startAllButton = new QPushButton(tr("Start All"), this);
|
|
buttonRow->addWidget(m_reloadButton);
|
|
buttonRow->addWidget(m_startAllButton);
|
|
buttonRow->addStretch();
|
|
mainLayout->addLayout(buttonRow);
|
|
|
|
connect(m_reloadButton, &QPushButton::clicked, this, &BalancingWindow::reloadConfig);
|
|
connect(m_startAllButton, &QPushButton::clicked, this, &BalancingWindow::startAll);
|
|
|
|
m_scrollArea = new QScrollArea(this);
|
|
m_scrollArea->setWidgetResizable(true);
|
|
mainLayout->addWidget(m_scrollArea);
|
|
|
|
populateArenas(balancingConfig);
|
|
|
|
m_pollTimer = new QTimer(this);
|
|
connect(m_pollTimer, &QTimer::timeout, this, &BalancingWindow::pollStatuses);
|
|
m_pollTimer->start(100);
|
|
|
|
registerForEvents();
|
|
}
|
|
|
|
BalancingWindow::~BalancingWindow()
|
|
{
|
|
unregisterForEvents();
|
|
|
|
m_pollTimer->stop();
|
|
if (m_inspectWindow)
|
|
{
|
|
delete m_inspectWindow;
|
|
m_inspectWindow = nullptr;
|
|
}
|
|
m_inspectedSim.reset();
|
|
stopAllArenas();
|
|
}
|
|
|
|
void BalancingWindow::populateArenas(const BalancingConfig& balancingConfig)
|
|
{
|
|
stopAllArenas();
|
|
m_arenas.clear();
|
|
|
|
QWidget* scrollContent = new QWidget(m_scrollArea);
|
|
QVBoxLayout* contentLayout = new QVBoxLayout(scrollContent);
|
|
contentLayout->setSpacing(8);
|
|
contentLayout->setContentsMargins(8, 8, 8, 8);
|
|
|
|
for (const ArenaConfig& arenaConfig : balancingConfig.arenas)
|
|
{
|
|
int index = static_cast<int>(m_arenas.size());
|
|
|
|
ArenaEntry entry;
|
|
entry.config = arenaConfig;
|
|
entry.simulation = std::make_unique<ArenaSimulation>(
|
|
m_gameConfig, arenaConfig, m_nextSeed++);
|
|
entry.widget = new ArenaWidget(index, arenaConfig.name, scrollContent);
|
|
contentLayout->addWidget(entry.widget);
|
|
|
|
entry.widget->updateStatus(entry.simulation->status());
|
|
|
|
m_arenas.push_back(std::move(entry));
|
|
}
|
|
|
|
contentLayout->addStretch();
|
|
m_scrollArea->setWidget(scrollContent);
|
|
|
|
updateButtons();
|
|
}
|
|
|
|
void BalancingWindow::stopAllArenas()
|
|
{
|
|
for (ArenaEntry& entry : m_arenas)
|
|
{
|
|
entry.simulation->requestStop();
|
|
}
|
|
for (ArenaEntry& entry : m_arenas)
|
|
{
|
|
if (entry.worker.joinable())
|
|
{
|
|
entry.worker.join();
|
|
}
|
|
}
|
|
}
|
|
|
|
void BalancingWindow::pollStatuses()
|
|
{
|
|
for (ArenaEntry& entry : m_arenas)
|
|
{
|
|
if (entry.worker.joinable())
|
|
{
|
|
const ArenaStatus status = entry.simulation->status();
|
|
entry.widget->updateStatus(status);
|
|
}
|
|
}
|
|
|
|
if (m_inspectedSim && m_inspectedArenaIndex >= 0)
|
|
{
|
|
const ArenaStatus status = m_inspectedSim->status();
|
|
m_arenas[static_cast<std::size_t>(m_inspectedArenaIndex)].widget->updateStatus(status);
|
|
}
|
|
|
|
updateButtons();
|
|
}
|
|
|
|
void BalancingWindow::reloadConfig()
|
|
{
|
|
try
|
|
{
|
|
GameConfig newGameConfig = ConfigLoader::loadFromDirectory(m_configDir);
|
|
BalancingConfig newBalancingConfig = loadBalancingConfig(m_balancingConfigPath);
|
|
m_gameConfig = std::move(newGameConfig);
|
|
populateArenas(newBalancingConfig);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
QMessageBox::critical(this, tr("Reload Failed"), QString::fromStdString(e.what()));
|
|
}
|
|
}
|
|
|
|
void BalancingWindow::startAll()
|
|
{
|
|
for (int i = 0; i < static_cast<int>(m_arenas.size()); ++i)
|
|
{
|
|
startArena(i);
|
|
}
|
|
}
|
|
|
|
void BalancingWindow::handleEvent(std::shared_ptr<const ArenaStartRequestedEvent> event)
|
|
{
|
|
startArena(event->arenaIndex);
|
|
}
|
|
|
|
void BalancingWindow::handleEvent(std::shared_ptr<const ArenaInspectRequestedEvent> event)
|
|
{
|
|
inspectArena(event->arenaIndex);
|
|
}
|
|
|
|
void BalancingWindow::handleEvent(std::shared_ptr<const InspectWindowClosedEvent> /*event*/)
|
|
{
|
|
closeInspectWindow();
|
|
}
|
|
|
|
void BalancingWindow::startArena(int index)
|
|
{
|
|
ArenaEntry& entry = m_arenas[index];
|
|
if (entry.worker.joinable())
|
|
{
|
|
entry.simulation->requestStop();
|
|
entry.worker.join();
|
|
}
|
|
entry.simulation = std::make_unique<ArenaSimulation>(
|
|
m_gameConfig, entry.config, m_nextSeed++);
|
|
entry.widget->startSimulation();
|
|
entry.widget->updateStatus(entry.simulation->status());
|
|
ArenaSimulation* sim = entry.simulation.get();
|
|
entry.worker = std::thread([sim]() { sim->run(); });
|
|
updateButtons();
|
|
}
|
|
|
|
void BalancingWindow::inspectArena(int index)
|
|
{
|
|
if (m_inspectWindow)
|
|
{
|
|
delete m_inspectWindow;
|
|
m_inspectWindow = nullptr;
|
|
|
|
if (m_inspectedSim && m_inspectedArenaIndex >= 0
|
|
&& !m_inspectedSim->isFinished())
|
|
{
|
|
m_arenas[static_cast<std::size_t>(m_inspectedArenaIndex)].widget->resetToGrey();
|
|
}
|
|
m_inspectedSim.reset();
|
|
m_inspectedArenaIndex = -1;
|
|
}
|
|
|
|
ArenaEntry& entry = m_arenas[static_cast<std::size_t>(index)];
|
|
|
|
if (entry.worker.joinable())
|
|
{
|
|
entry.simulation->requestStop();
|
|
entry.worker.join();
|
|
}
|
|
|
|
m_inspectedSim = std::make_unique<ArenaSimulation>(
|
|
m_gameConfig, entry.config, m_nextSeed++);
|
|
m_inspectedArenaIndex = index;
|
|
|
|
entry.widget->resetToGrey();
|
|
entry.widget->startSimulation();
|
|
entry.widget->updateStatus(m_inspectedSim->status());
|
|
|
|
m_inspectWindow = new InspectWindow(
|
|
m_inspectedSim.get(), &m_gameConfig, &m_visuals, entry.config.name, nullptr);
|
|
|
|
setMainControlsEnabled(false);
|
|
m_inspectWindow->show();
|
|
}
|
|
|
|
void BalancingWindow::closeInspectWindow()
|
|
{
|
|
if (!m_inspectWindow)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_inspectWindow->deleteLater();
|
|
m_inspectWindow = nullptr;
|
|
|
|
if (m_inspectedArenaIndex >= 0 && m_inspectedSim)
|
|
{
|
|
if (!m_inspectedSim->isFinished())
|
|
{
|
|
m_arenas[static_cast<std::size_t>(m_inspectedArenaIndex)].widget->resetToGrey();
|
|
}
|
|
}
|
|
|
|
m_inspectedSim.reset();
|
|
m_inspectedArenaIndex = -1;
|
|
setMainControlsEnabled(true);
|
|
updateButtons();
|
|
}
|
|
|
|
void BalancingWindow::setMainControlsEnabled(bool enabled)
|
|
{
|
|
m_reloadButton->setEnabled(enabled);
|
|
m_startAllButton->setEnabled(enabled);
|
|
for (ArenaEntry& entry : m_arenas)
|
|
{
|
|
for (QPushButton* btn : entry.widget->findChildren<QPushButton*>())
|
|
{
|
|
btn->setEnabled(enabled);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BalancingWindow::updateButtons()
|
|
{
|
|
if (m_inspectWindow)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool anyRunning = false;
|
|
bool allRunning = true;
|
|
for (ArenaEntry& entry : m_arenas)
|
|
{
|
|
if (entry.worker.joinable() && !entry.simulation->status().finished)
|
|
{
|
|
anyRunning = true;
|
|
}
|
|
else
|
|
{
|
|
allRunning = false;
|
|
}
|
|
}
|
|
if (m_arenas.empty())
|
|
{
|
|
allRunning = false;
|
|
}
|
|
|
|
m_reloadButton->setEnabled(!anyRunning);
|
|
m_startAllButton->setEnabled(!allRunning);
|
|
}
|