implement artifact win condition (REQ-WIN-ARTIFACT-COUNT, REQ-WIN-SCREEN)
- Add [artifacts] config section to world.toml with artifact_chance_formula (x = station level) and artifact_win_count - Roll artifact chance before each schematic drop; on success show 2 schematics + Artifact option instead of 3 - Selecting Artifact increments artifact count; reaching artifact_win_count triggers win screen - Display "Artifacts: x/y" in header bar via ArtifactCountChangedEvent - Win screen mirrors game-over screen with "Won!" caption and same Restart/Quit buttons Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DUsFgd2Ga6pmLz8giS8WUn
This commit is contained in:
@@ -29,6 +29,10 @@ target_score_formula = "1 / (1 + x)" # x = distance / max weapon r
|
|||||||
overclaim_penalty_formula = "max(0.5, 1 - 0.1*x)" # x = competing claim count; multiplies score, clamped to [0,1]
|
overclaim_penalty_formula = "max(0.5, 1 - 0.1*x)" # x = competing claim count; multiplies score, clamped to [0,1]
|
||||||
target_hysteresis = 0.40 # keep current target unless a challenger beats it by >10%
|
target_hysteresis = 0.40 # keep current target unless a challenger beats it by >10%
|
||||||
|
|
||||||
|
[artifacts]
|
||||||
|
artifact_chance_formula = "0.05 * x" # 5% chance per station level
|
||||||
|
artifact_win_count = 3
|
||||||
|
|
||||||
[waves]
|
[waves]
|
||||||
threat_rate_formula = "x"
|
threat_rate_formula = "x"
|
||||||
ship_level_formula = "1"
|
ship_level_formula = "1"
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ target_score_formula = "1 / (1 + x)" # x = distance / max weapon r
|
|||||||
overclaim_penalty_formula = "max(0.5, 1 - 0.1*x)" # x = competing claim count; multiplies score, clamped to [0,1]
|
overclaim_penalty_formula = "max(0.5, 1 - 0.1*x)" # x = competing claim count; multiplies score, clamped to [0,1]
|
||||||
target_hysteresis = 0.10 # keep current target unless a challenger beats it by >10%
|
target_hysteresis = 0.10 # keep current target unless a challenger beats it by >10%
|
||||||
|
|
||||||
|
[artifacts]
|
||||||
|
artifact_chance_formula = "0.05 * x" # 5% chance per station level
|
||||||
|
artifact_win_count = 3
|
||||||
|
|
||||||
[waves]
|
[waves]
|
||||||
threat_rate_formula = "x"
|
threat_rate_formula = "x"
|
||||||
ship_level_formula = "1 + x / 10"
|
ship_level_formula = "1 + x / 10"
|
||||||
|
|||||||
@@ -301,6 +301,9 @@ WorldConfig ConfigLoader::loadWorld(const std::string& path)
|
|||||||
cfg.targeting.overclaimPenaltyFormula = requireFormula(tbl["targeting"]["overclaim_penalty_formula"], file, "targeting.overclaim_penalty_formula");
|
cfg.targeting.overclaimPenaltyFormula = requireFormula(tbl["targeting"]["overclaim_penalty_formula"], file, "targeting.overclaim_penalty_formula");
|
||||||
cfg.targeting.hysteresis = requireDouble(tbl["targeting"]["target_hysteresis"], file, "targeting.target_hysteresis");
|
cfg.targeting.hysteresis = requireDouble(tbl["targeting"]["target_hysteresis"], file, "targeting.target_hysteresis");
|
||||||
|
|
||||||
|
cfg.artifacts.artifactChanceFormula = requireFormula(tbl["artifacts"]["artifact_chance_formula"], file, "artifacts.artifact_chance_formula");
|
||||||
|
cfg.artifacts.artifactWinCount = static_cast<int>(requireInt(tbl["artifacts"]["artifact_win_count"], file, "artifacts.artifact_win_count"));
|
||||||
|
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,13 @@ struct WorldTargeting
|
|||||||
double hysteresis; // fractional margin a challenger must beat the current target by
|
double hysteresis; // fractional margin a challenger must beat the current target by
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Artifact win condition (REQ-WIN-ARTIFACT-COUNT, REQ-WIN-SCREEN).
|
||||||
|
struct WorldArtifacts
|
||||||
|
{
|
||||||
|
Formula artifactChanceFormula; // x = station level, result clamped to [0,1]
|
||||||
|
int artifactWinCount;
|
||||||
|
};
|
||||||
|
|
||||||
struct WorldConfig
|
struct WorldConfig
|
||||||
{
|
{
|
||||||
int heightTiles; // REQ-GW-HEIGHT
|
int heightTiles; // REQ-GW-HEIGHT
|
||||||
@@ -65,4 +72,5 @@ struct WorldConfig
|
|||||||
WorldPush push;
|
WorldPush push;
|
||||||
WorldWaves waves;
|
WorldWaves waves;
|
||||||
WorldTargeting targeting;
|
WorldTargeting targeting;
|
||||||
|
WorldArtifacts artifacts;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ enum class SchematicType
|
|||||||
{
|
{
|
||||||
Ship,
|
Ship,
|
||||||
Module,
|
Module,
|
||||||
Recipe
|
Recipe,
|
||||||
|
Artifact
|
||||||
};
|
};
|
||||||
|
|
||||||
// One option presented to the player in the schematic choice dialog
|
// One option presented to the player in the schematic choice dialog
|
||||||
|
|||||||
11
src/lib/eventsystem/event/ArtifactCountChangedEvent.h
Normal file
11
src/lib/eventsystem/event/ArtifactCountChangedEvent.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Event.h"
|
||||||
|
|
||||||
|
class ArtifactCountChangedEvent : public Event
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ArtifactCountChangedEvent(int count, int winCount) : count(count), winCount(winCount) {}
|
||||||
|
const int count;
|
||||||
|
const int winCount;
|
||||||
|
};
|
||||||
@@ -9,6 +9,8 @@ SET(HDRS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/SchematicChoicesAvailableEvent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/SchematicChoicesAvailableEvent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/SelectionChangedEvent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/SelectionChangedEvent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/GameOverEvent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/GameOverEvent.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/WinEvent.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ArtifactCountChangedEvent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BuilderModeExitedEvent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/BuilderModeExitedEvent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintModeExitedEvent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintModeExitedEvent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/EscapeMenuRequestedEvent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/EscapeMenuRequestedEvent.h
|
||||||
|
|||||||
5
src/lib/eventsystem/event/WinEvent.h
Normal file
5
src/lib/eventsystem/event/WinEvent.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Event.h"
|
||||||
|
|
||||||
|
class WinEvent : public Event {};
|
||||||
@@ -134,6 +134,8 @@ void Simulation::reset(unsigned int seed)
|
|||||||
m_nextBuildingId = 1;
|
m_nextBuildingId = 1;
|
||||||
m_buildingBlocksStock = m_config.world.startingBuildingBlocks;
|
m_buildingBlocksStock = m_config.world.startingBuildingBlocks;
|
||||||
m_gameOver = false;
|
m_gameOver = false;
|
||||||
|
m_isWon = false;
|
||||||
|
m_artifactCount = 0;
|
||||||
m_hqBuildingId = kInvalidBuildingId;
|
m_hqBuildingId = kInvalidBuildingId;
|
||||||
m_hqProxyEntity = entt::null;
|
m_hqProxyEntity = entt::null;
|
||||||
m_playerStation1Entity = entt::null;
|
m_playerStation1Entity = entt::null;
|
||||||
@@ -584,7 +586,14 @@ void Simulation::generateSchematicChoices(int destroyedStationLevel)
|
|||||||
|
|
||||||
if (pool.empty()) { return; }
|
if (pool.empty()) { return; }
|
||||||
|
|
||||||
const int numChoices = std::min(static_cast<int>(pool.size()), 3);
|
const double artifactChance = std::clamp(
|
||||||
|
m_config.world.artifacts.artifactChanceFormula.evaluate(
|
||||||
|
static_cast<double>(destroyedStationLevel)),
|
||||||
|
0.0, 1.0);
|
||||||
|
std::uniform_real_distribution<double> realDist(0.0, 1.0);
|
||||||
|
const bool artifactRolled = realDist(m_rng) < artifactChance;
|
||||||
|
|
||||||
|
const int numChoices = std::min(static_cast<int>(pool.size()), artifactRolled ? 2 : 3);
|
||||||
m_pendingSchematicChoices.clear();
|
m_pendingSchematicChoices.clear();
|
||||||
|
|
||||||
const std::set<std::string> currentShipIds = getUnlockedShipSchematicIds();
|
const std::set<std::string> currentShipIds = getUnlockedShipSchematicIds();
|
||||||
@@ -657,6 +666,17 @@ void Simulation::generateSchematicChoices(int destroyedStationLevel)
|
|||||||
|
|
||||||
m_pendingSchematicChoices.push_back(option);
|
m_pendingSchematicChoices.push_back(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (artifactRolled)
|
||||||
|
{
|
||||||
|
SchematicChoiceOption artifactOption;
|
||||||
|
artifactOption.schematicId = "";
|
||||||
|
artifactOption.type = SchematicType::Artifact;
|
||||||
|
artifactOption.displayName = "Artifact";
|
||||||
|
artifactOption.isNewUnlock = false;
|
||||||
|
artifactOption.targetLevel = 0;
|
||||||
|
m_pendingSchematicChoices.push_back(std::move(artifactOption));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Simulation::applySchematicChoice(int choiceIndex)
|
void Simulation::applySchematicChoice(int choiceIndex)
|
||||||
@@ -664,6 +684,17 @@ void Simulation::applySchematicChoice(int choiceIndex)
|
|||||||
assert(choiceIndex >= 0 && choiceIndex < static_cast<int>(m_pendingSchematicChoices.size()));
|
assert(choiceIndex >= 0 && choiceIndex < static_cast<int>(m_pendingSchematicChoices.size()));
|
||||||
const SchematicChoiceOption& chosen = m_pendingSchematicChoices[static_cast<std::size_t>(choiceIndex)];
|
const SchematicChoiceOption& chosen = m_pendingSchematicChoices[static_cast<std::size_t>(choiceIndex)];
|
||||||
|
|
||||||
|
if (chosen.type == SchematicType::Artifact)
|
||||||
|
{
|
||||||
|
m_artifactCount += 1;
|
||||||
|
if (m_artifactCount >= m_config.world.artifacts.artifactWinCount)
|
||||||
|
{
|
||||||
|
m_isWon = true;
|
||||||
|
}
|
||||||
|
m_pendingSchematicChoices.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (chosen.type == SchematicType::Recipe)
|
if (chosen.type == SchematicType::Recipe)
|
||||||
{
|
{
|
||||||
m_unlockedRecipeSchematicIds.insert(chosen.schematicId);
|
m_unlockedRecipeSchematicIds.insert(chosen.schematicId);
|
||||||
@@ -866,6 +897,16 @@ bool Simulation::isGameOver() const
|
|||||||
return m_gameOver;
|
return m_gameOver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Simulation::isWon() const
|
||||||
|
{
|
||||||
|
return m_isWon;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Simulation::artifactCount() const
|
||||||
|
{
|
||||||
|
return m_artifactCount;
|
||||||
|
}
|
||||||
|
|
||||||
double Simulation::threatLevel() const
|
double Simulation::threatLevel() const
|
||||||
{
|
{
|
||||||
return m_waveSystem->threatLevel();
|
return m_waveSystem->threatLevel();
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ public:
|
|||||||
Tick currentTick() const;
|
Tick currentTick() const;
|
||||||
int buildingBlocksStock() const;
|
int buildingBlocksStock() const;
|
||||||
bool isGameOver() const;
|
bool isGameOver() const;
|
||||||
|
bool isWon() const;
|
||||||
|
int artifactCount() const;
|
||||||
double threatLevel() const;
|
double threatLevel() const;
|
||||||
double threatAccumulationRate() const;
|
double threatAccumulationRate() const;
|
||||||
double maxFactoryProductionThreatRate() const;
|
double maxFactoryProductionThreatRate() const;
|
||||||
@@ -131,7 +133,9 @@ private:
|
|||||||
Tick m_nextDepartureTick;
|
Tick m_nextDepartureTick;
|
||||||
BuildingId m_nextBuildingId;
|
BuildingId m_nextBuildingId;
|
||||||
int m_buildingBlocksStock;
|
int m_buildingBlocksStock;
|
||||||
bool m_gameOver = false;
|
bool m_gameOver = false;
|
||||||
|
bool m_isWon = false;
|
||||||
|
int m_artifactCount = 0;
|
||||||
|
|
||||||
// Pre-placed structure IDs.
|
// Pre-placed structure IDs.
|
||||||
BuildingId m_hqBuildingId; // Building id (for belt integration)
|
BuildingId m_hqBuildingId; // Building id (for belt integration)
|
||||||
|
|||||||
@@ -251,6 +251,24 @@ void GameWorldView::onFrame()
|
|||||||
m_schematicChoiceShown = false;
|
m_schematicChoiceShown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Artifact count update
|
||||||
|
const int currentArtifactCount = m_sim->artifactCount();
|
||||||
|
if (currentArtifactCount != m_lastArtifactCount)
|
||||||
|
{
|
||||||
|
m_lastArtifactCount = currentArtifactCount;
|
||||||
|
EventManager::getInstance()->sendEventImmediately(
|
||||||
|
std::make_shared<ArtifactCountChangedEvent>(
|
||||||
|
currentArtifactCount, m_sim->config().world.artifacts.artifactWinCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Win check
|
||||||
|
if (m_sim->isWon() && !m_winShown)
|
||||||
|
{
|
||||||
|
m_winShown = true;
|
||||||
|
m_gameSpeedMultiplier = 0.0;
|
||||||
|
EventManager::getInstance()->sendEventImmediately(std::make_shared<WinEvent>());
|
||||||
|
}
|
||||||
|
|
||||||
// Game over check
|
// Game over check
|
||||||
if (m_sim->isGameOver() && !m_gameOverShown)
|
if (m_sim->isGameOver() && !m_gameOverShown)
|
||||||
{
|
{
|
||||||
@@ -1667,12 +1685,14 @@ void GameWorldView::resetForNewGame()
|
|||||||
m_scrollXTiles = 0.0f;
|
m_scrollXTiles = 0.0f;
|
||||||
m_scrollLeft = false;
|
m_scrollLeft = false;
|
||||||
m_scrollRight = false;
|
m_scrollRight = false;
|
||||||
m_gameOverShown = false;
|
m_gameOverShown = false;
|
||||||
m_prevNonZeroSpeed = 1.0;
|
m_winShown = false;
|
||||||
|
m_prevNonZeroSpeed = 1.0;
|
||||||
m_lastTick = Tick(-1);
|
m_lastTick = Tick(-1);
|
||||||
m_lastBlocks = -1;
|
m_lastBlocks = -1;
|
||||||
m_lastBossCounter = -1;
|
m_lastBossCounter = -1;
|
||||||
m_lastBossCountdown = Tick(-1);
|
m_lastBossCountdown = Tick(-1);
|
||||||
|
m_lastArtifactCount = -1;
|
||||||
EventManager::getInstance()->sendEventImmediately(
|
EventManager::getInstance()->sendEventImmediately(
|
||||||
std::make_shared<SelectionChangedEvent>(std::vector<BuildingId>{}));
|
std::make_shared<SelectionChangedEvent>(std::vector<BuildingId>{}));
|
||||||
setGameSpeed(1.0);
|
setGameSpeed(1.0);
|
||||||
|
|||||||
@@ -25,8 +25,10 @@
|
|||||||
#include "ExitBlueprintModeRequestedEvent.h"
|
#include "ExitBlueprintModeRequestedEvent.h"
|
||||||
#include "ExitBuilderModeRequestedEvent.h"
|
#include "ExitBuilderModeRequestedEvent.h"
|
||||||
#include "DebugDrawToggledEvent.h"
|
#include "DebugDrawToggledEvent.h"
|
||||||
|
#include "ArtifactCountChangedEvent.h"
|
||||||
#include "BeamFiredEvent.h"
|
#include "BeamFiredEvent.h"
|
||||||
#include "SchematicChoiceOption.h"
|
#include "SchematicChoiceOption.h"
|
||||||
|
#include "WinEvent.h"
|
||||||
#include "SpeedChangeRequestedEvent.h"
|
#include "SpeedChangeRequestedEvent.h"
|
||||||
|
|
||||||
#include "entt/entity/entity.hpp"
|
#include "entt/entity/entity.hpp"
|
||||||
@@ -191,10 +193,12 @@ private:
|
|||||||
bool m_scrollLeft;
|
bool m_scrollLeft;
|
||||||
bool m_scrollRight;
|
bool m_scrollRight;
|
||||||
bool m_gameOverShown;
|
bool m_gameOverShown;
|
||||||
|
bool m_winShown;
|
||||||
bool m_schematicChoiceShown;
|
bool m_schematicChoiceShown;
|
||||||
|
|
||||||
Tick m_lastTick = Tick(-1);
|
Tick m_lastTick = Tick(-1);
|
||||||
int m_lastBlocks = -1;
|
int m_lastBlocks = -1;
|
||||||
int m_lastBossCounter = -1;
|
int m_lastBossCounter = -1;
|
||||||
Tick m_lastBossCountdown = Tick(-1);
|
Tick m_lastBossCountdown = Tick(-1);
|
||||||
|
int m_lastArtifactCount = -1;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ HeaderBar::HeaderBar(QWidget* parent)
|
|||||||
layout->setContentsMargins(8, 4, 8, 4);
|
layout->setContentsMargins(8, 4, 8, 4);
|
||||||
layout->setSpacing(8);
|
layout->setSpacing(8);
|
||||||
|
|
||||||
m_timeLabel = new QLabel("00:00", this);
|
m_timeLabel = new QLabel("00:00", this);
|
||||||
m_blocksLabel = new QLabel(tr("Blocks: 0"), this);
|
m_blocksLabel = new QLabel(tr("Blocks: 0"), this);
|
||||||
m_bossLabel = new QLabel(tr("Boss Wave #1 Next boss: 5:00"), this);
|
m_artifactsLabel = new QLabel(tr("Artifacts: 0/?"), this);
|
||||||
|
m_bossLabel = new QLabel(tr("Boss Wave #1 Next boss: 5:00"), this);
|
||||||
layout->addWidget(m_timeLabel);
|
layout->addWidget(m_timeLabel);
|
||||||
layout->addWidget(m_blocksLabel);
|
layout->addWidget(m_blocksLabel);
|
||||||
|
layout->addWidget(m_artifactsLabel);
|
||||||
layout->addStretch();
|
layout->addStretch();
|
||||||
layout->addWidget(m_bossLabel);
|
layout->addWidget(m_bossLabel);
|
||||||
|
|
||||||
@@ -88,6 +90,11 @@ void HeaderBar::handleEvent(std::shared_ptr<const BossWaveUpdatedEvent> event)
|
|||||||
.arg(bossSeconds % 60, 2, 10, QChar('0')));
|
.arg(bossSeconds % 60, 2, 10, QChar('0')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HeaderBar::handleEvent(std::shared_ptr<const ArtifactCountChangedEvent> event)
|
||||||
|
{
|
||||||
|
m_artifactsLabel->setText(tr("Artifacts: %1/%2").arg(event->count).arg(event->winCount));
|
||||||
|
}
|
||||||
|
|
||||||
void HeaderBar::onSpeedButton(int index)
|
void HeaderBar::onSpeedButton(int index)
|
||||||
{
|
{
|
||||||
if (index >= 0 && index < kSpeedCount)
|
if (index >= 0 && index < kSpeedCount)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "ArtifactCountChangedEvent.h"
|
||||||
#include "BossWaveUpdatedEvent.h"
|
#include "BossWaveUpdatedEvent.h"
|
||||||
#include "BuildingBlocksChangedEvent.h"
|
#include "BuildingBlocksChangedEvent.h"
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
@@ -18,7 +19,8 @@ class HeaderBar : public QWidget,
|
|||||||
public CombinedEventHandler<TickAdvancedEvent,
|
public CombinedEventHandler<TickAdvancedEvent,
|
||||||
BuildingBlocksChangedEvent,
|
BuildingBlocksChangedEvent,
|
||||||
GameSpeedChangedEvent,
|
GameSpeedChangedEvent,
|
||||||
BossWaveUpdatedEvent>
|
BossWaveUpdatedEvent,
|
||||||
|
ArtifactCountChangedEvent>
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -34,9 +36,11 @@ private:
|
|||||||
void handleEvent(std::shared_ptr<const BuildingBlocksChangedEvent> event) override;
|
void handleEvent(std::shared_ptr<const BuildingBlocksChangedEvent> event) override;
|
||||||
void handleEvent(std::shared_ptr<const GameSpeedChangedEvent> event) override;
|
void handleEvent(std::shared_ptr<const GameSpeedChangedEvent> event) override;
|
||||||
void handleEvent(std::shared_ptr<const BossWaveUpdatedEvent> event) override;
|
void handleEvent(std::shared_ptr<const BossWaveUpdatedEvent> event) override;
|
||||||
|
void handleEvent(std::shared_ptr<const ArtifactCountChangedEvent> event) override;
|
||||||
|
|
||||||
QLabel* m_timeLabel;
|
QLabel* m_timeLabel;
|
||||||
QLabel* m_blocksLabel;
|
QLabel* m_blocksLabel;
|
||||||
|
QLabel* m_artifactsLabel;
|
||||||
QLabel* m_bossLabel;
|
QLabel* m_bossLabel;
|
||||||
std::vector<QPushButton*> m_speedButtons;
|
std::vector<QPushButton*> m_speedButtons;
|
||||||
|
|
||||||
|
|||||||
@@ -313,3 +313,42 @@ void MainWindow::handleEvent(std::shared_ptr<const GameOverEvent> /*event*/)
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::handleEvent(std::shared_ptr<const WinEvent> /*event*/)
|
||||||
|
{
|
||||||
|
const Tick tick = m_sim->currentTick();
|
||||||
|
const int totalSeconds = static_cast<int>(ticksToSeconds(tick));
|
||||||
|
const int minutes = totalSeconds / 60;
|
||||||
|
const int seconds = totalSeconds % 60;
|
||||||
|
|
||||||
|
QMessageBox box(this);
|
||||||
|
box.setWindowTitle(tr("Won!"));
|
||||||
|
box.setText(tr("You collected all artifacts!\nSurvival time: %1:%2")
|
||||||
|
.arg(minutes, 2, 10, QChar('0'))
|
||||||
|
.arg(seconds, 2, 10, QChar('0')));
|
||||||
|
QPushButton* restartBtn = box.addButton(tr("Restart"), QMessageBox::AcceptRole);
|
||||||
|
box.addButton(tr("Quit"), QMessageBox::RejectRole);
|
||||||
|
box.exec();
|
||||||
|
|
||||||
|
if (box.clickedButton() == restartBtn)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GameConfig newConfig = ConfigLoader::loadFromDirectory(m_configDir);
|
||||||
|
VisualsConfig newVisuals = VisualsLoader::load(m_configDir + "/visuals.toml");
|
||||||
|
m_visuals = std::move(newVisuals);
|
||||||
|
m_sim->reset(std::move(newConfig));
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Config Error"),
|
||||||
|
tr("Failed to reload config:\n%1").arg(e.what()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_gameWorldView->resetForNewGame();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
#include "GameOverEvent.h"
|
#include "GameOverEvent.h"
|
||||||
#include "LayoutDialogRequestedEvent.h"
|
#include "LayoutDialogRequestedEvent.h"
|
||||||
|
#include "WinEvent.h"
|
||||||
#include "RecipeSelectionRequestedEvent.h"
|
#include "RecipeSelectionRequestedEvent.h"
|
||||||
#include "SchematicChoicesAvailableEvent.h"
|
#include "SchematicChoicesAvailableEvent.h"
|
||||||
#include "ShipLayoutBlueprint.h"
|
#include "ShipLayoutBlueprint.h"
|
||||||
@@ -30,6 +31,7 @@ class MainWindow : public QWidget,
|
|||||||
public CombinedEventHandler<BuildingBlocksChangedEvent,
|
public CombinedEventHandler<BuildingBlocksChangedEvent,
|
||||||
SchematicChoicesAvailableEvent,
|
SchematicChoicesAvailableEvent,
|
||||||
GameOverEvent,
|
GameOverEvent,
|
||||||
|
WinEvent,
|
||||||
EscapeMenuRequestedEvent,
|
EscapeMenuRequestedEvent,
|
||||||
LayoutDialogRequestedEvent,
|
LayoutDialogRequestedEvent,
|
||||||
RecipeSelectionRequestedEvent>
|
RecipeSelectionRequestedEvent>
|
||||||
@@ -48,6 +50,7 @@ private:
|
|||||||
void handleEvent(std::shared_ptr<const BuildingBlocksChangedEvent> event) override;
|
void handleEvent(std::shared_ptr<const BuildingBlocksChangedEvent> event) override;
|
||||||
void handleEvent(std::shared_ptr<const SchematicChoicesAvailableEvent> event) override;
|
void handleEvent(std::shared_ptr<const SchematicChoicesAvailableEvent> event) override;
|
||||||
void handleEvent(std::shared_ptr<const GameOverEvent> event) override;
|
void handleEvent(std::shared_ptr<const GameOverEvent> event) override;
|
||||||
|
void handleEvent(std::shared_ptr<const WinEvent> event) override;
|
||||||
void handleEvent(std::shared_ptr<const EscapeMenuRequestedEvent> event) override;
|
void handleEvent(std::shared_ptr<const EscapeMenuRequestedEvent> event) override;
|
||||||
void handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> event) override;
|
void handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> event) override;
|
||||||
void handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent> event) override;
|
void handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent> event) override;
|
||||||
|
|||||||
@@ -45,60 +45,69 @@ SchematicChoiceDialog::SchematicChoiceDialog(
|
|||||||
nameLabel->setAlignment(Qt::AlignCenter);
|
nameLabel->setAlignment(Qt::AlignCenter);
|
||||||
cardLayout->addWidget(nameLabel);
|
cardLayout->addWidget(nameLabel);
|
||||||
|
|
||||||
QString typeText;
|
if (option.type == SchematicType::Artifact)
|
||||||
if (option.type == SchematicType::Ship)
|
|
||||||
{
|
{
|
||||||
typeText = tr("Ship");
|
QLabel* artifactTypeLabel = new QLabel(tr("Artifact"), card);
|
||||||
}
|
artifactTypeLabel->setAlignment(Qt::AlignCenter);
|
||||||
else if (option.type == SchematicType::Module)
|
cardLayout->addWidget(artifactTypeLabel);
|
||||||
{
|
|
||||||
typeText = tr("Module");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
typeText = tr("Recipe");
|
QString typeText;
|
||||||
}
|
if (option.type == SchematicType::Ship)
|
||||||
QLabel* typeLabel = new QLabel(typeText, card);
|
|
||||||
typeLabel->setAlignment(Qt::AlignCenter);
|
|
||||||
cardLayout->addWidget(typeLabel);
|
|
||||||
|
|
||||||
QString statusText;
|
|
||||||
if (option.isNewUnlock)
|
|
||||||
{
|
|
||||||
statusText = tr("New unlock");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
statusText = tr("Level up -> %1").arg(option.targetLevel);
|
|
||||||
}
|
|
||||||
QLabel* statusLabel = new QLabel(statusText, card);
|
|
||||||
statusLabel->setAlignment(Qt::AlignCenter);
|
|
||||||
cardLayout->addWidget(statusLabel);
|
|
||||||
|
|
||||||
QLabel* unlocksHeaderLabel = new QLabel(tr("Unlocks recipes for:"), card);
|
|
||||||
QFont unlocksHeaderFont = unlocksHeaderLabel->font();
|
|
||||||
unlocksHeaderFont.setBold(true);
|
|
||||||
unlocksHeaderLabel->setFont(unlocksHeaderFont);
|
|
||||||
unlocksHeaderLabel->setAlignment(Qt::AlignCenter);
|
|
||||||
cardLayout->addWidget(unlocksHeaderLabel);
|
|
||||||
|
|
||||||
QString unlocksText;
|
|
||||||
if (option.newlyUnlockedItemNames.empty())
|
|
||||||
{
|
|
||||||
unlocksText = tr("None");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QStringList itemLines;
|
|
||||||
for (const std::string& itemName : option.newlyUnlockedItemNames)
|
|
||||||
{
|
{
|
||||||
itemLines << QString::fromStdString(itemName);
|
typeText = tr("Ship");
|
||||||
}
|
}
|
||||||
unlocksText = itemLines.join("\n");
|
else if (option.type == SchematicType::Module)
|
||||||
|
{
|
||||||
|
typeText = tr("Module");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
typeText = tr("Recipe");
|
||||||
|
}
|
||||||
|
QLabel* typeLabel = new QLabel(typeText, card);
|
||||||
|
typeLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
cardLayout->addWidget(typeLabel);
|
||||||
|
|
||||||
|
QString statusText;
|
||||||
|
if (option.isNewUnlock)
|
||||||
|
{
|
||||||
|
statusText = tr("New unlock");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
statusText = tr("Level up -> %1").arg(option.targetLevel);
|
||||||
|
}
|
||||||
|
QLabel* statusLabel = new QLabel(statusText, card);
|
||||||
|
statusLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
cardLayout->addWidget(statusLabel);
|
||||||
|
|
||||||
|
QLabel* unlocksHeaderLabel = new QLabel(tr("Unlocks recipes for:"), card);
|
||||||
|
QFont unlocksHeaderFont = unlocksHeaderLabel->font();
|
||||||
|
unlocksHeaderFont.setBold(true);
|
||||||
|
unlocksHeaderLabel->setFont(unlocksHeaderFont);
|
||||||
|
unlocksHeaderLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
cardLayout->addWidget(unlocksHeaderLabel);
|
||||||
|
|
||||||
|
QString unlocksText;
|
||||||
|
if (option.newlyUnlockedItemNames.empty())
|
||||||
|
{
|
||||||
|
unlocksText = tr("None");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QStringList itemLines;
|
||||||
|
for (const std::string& itemName : option.newlyUnlockedItemNames)
|
||||||
|
{
|
||||||
|
itemLines << QString::fromStdString(itemName);
|
||||||
|
}
|
||||||
|
unlocksText = itemLines.join("\n");
|
||||||
|
}
|
||||||
|
QLabel* unlocksLabel = new QLabel(unlocksText, card);
|
||||||
|
unlocksLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
cardLayout->addWidget(unlocksLabel);
|
||||||
}
|
}
|
||||||
QLabel* unlocksLabel = new QLabel(unlocksText, card);
|
|
||||||
unlocksLabel->setAlignment(Qt::AlignCenter);
|
|
||||||
cardLayout->addWidget(unlocksLabel);
|
|
||||||
|
|
||||||
QPushButton* selectButton = new QPushButton(tr("Select"), card);
|
QPushButton* selectButton = new QPushButton(tr("Select"), card);
|
||||||
cardLayout->addWidget(selectButton);
|
cardLayout->addWidget(selectButton);
|
||||||
|
|||||||
Reference in New Issue
Block a user