#include "BalancingWindow.h" #include #include #include #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(m_arenas.size()); ArenaEntry entry; entry.config = arenaConfig; entry.simulation = std::make_unique( 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(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(m_arenas.size()); ++i) { startArena(i); } } void BalancingWindow::handleEvent(std::shared_ptr event) { startArena(event->arenaIndex); } void BalancingWindow::handleEvent(std::shared_ptr event) { inspectArena(event->arenaIndex); } void BalancingWindow::handleEvent(std::shared_ptr /*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( 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(m_inspectedArenaIndex)].widget->resetToGrey(); } m_inspectedSim.reset(); m_inspectedArenaIndex = -1; } ArenaEntry& entry = m_arenas[static_cast(index)]; if (entry.worker.joinable()) { entry.simulation->requestStop(); entry.worker.join(); } m_inspectedSim = std::make_unique( 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(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()) { 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); }