Add artifact win condition (#3)
Reviewed-on: #3 Co-authored-by: Malte Langkabel <malte.langkabel@gmail.com> Co-committed-by: Malte Langkabel <malte.langkabel@gmail.com>
This commit was merged in pull request #3.
This commit is contained in:
@@ -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.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;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,13 @@ struct WorldTargeting
|
||||
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
|
||||
{
|
||||
int heightTiles; // REQ-GW-HEIGHT
|
||||
@@ -65,4 +72,5 @@ struct WorldConfig
|
||||
WorldPush push;
|
||||
WorldWaves waves;
|
||||
WorldTargeting targeting;
|
||||
WorldArtifacts artifacts;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,8 @@ enum class SchematicType
|
||||
{
|
||||
Ship,
|
||||
Module,
|
||||
Recipe
|
||||
Recipe,
|
||||
Artifact
|
||||
};
|
||||
|
||||
// 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}/SelectionChangedEvent.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}/BlueprintModeExitedEvent.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 {};
|
||||
@@ -141,6 +141,8 @@ void Simulation::reset(unsigned int seed)
|
||||
m_nextBuildingId = 1;
|
||||
m_buildingBlocksStock = m_config.world.startingBuildingBlocks;
|
||||
m_gameOver = false;
|
||||
m_isWon = false;
|
||||
m_artifactCount = 0;
|
||||
m_hqBuildingId = kInvalidBuildingId;
|
||||
m_hqProxyEntity = entt::null;
|
||||
m_playerStation1Entity = entt::null;
|
||||
@@ -677,7 +679,14 @@ void Simulation::generateSchematicChoices(int destroyedStationLevel)
|
||||
|
||||
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();
|
||||
|
||||
const std::set<std::string> currentShipIds = getUnlockedShipSchematicIds();
|
||||
@@ -750,6 +759,17 @@ void Simulation::generateSchematicChoices(int destroyedStationLevel)
|
||||
|
||||
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)
|
||||
@@ -757,6 +777,17 @@ void Simulation::applySchematicChoice(int choiceIndex)
|
||||
assert(choiceIndex >= 0 && choiceIndex < static_cast<int>(m_pendingSchematicChoices.size()));
|
||||
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)
|
||||
{
|
||||
m_unlockedRecipeSchematicIds.insert(chosen.schematicId);
|
||||
@@ -961,6 +992,8 @@ unsigned long long Simulation::computeStateChecksum() const
|
||||
hasher.append(m_nextBuildingId);
|
||||
hasher.append(m_buildingBlocksStock);
|
||||
hasher.append(m_gameOver);
|
||||
hasher.append(m_isWon);
|
||||
hasher.append(m_artifactCount);
|
||||
|
||||
// WaveSystem scalar state, reached through existing accessors.
|
||||
hasher.append(threatLevel());
|
||||
@@ -1074,6 +1107,16 @@ bool Simulation::isGameOver() const
|
||||
return m_gameOver;
|
||||
}
|
||||
|
||||
bool Simulation::isWon() const
|
||||
{
|
||||
return m_isWon;
|
||||
}
|
||||
|
||||
int Simulation::artifactCount() const
|
||||
{
|
||||
return m_artifactCount;
|
||||
}
|
||||
|
||||
double Simulation::threatLevel() const
|
||||
{
|
||||
return m_waveSystem->threatLevel();
|
||||
|
||||
@@ -73,6 +73,8 @@ public:
|
||||
unsigned int getSeed() const;
|
||||
int buildingBlocksStock() const;
|
||||
bool isGameOver() const;
|
||||
bool isWon() const;
|
||||
int artifactCount() const;
|
||||
double threatLevel() const;
|
||||
double threatAccumulationRate() const;
|
||||
double maxFactoryProductionThreatRate() const;
|
||||
@@ -165,7 +167,9 @@ private:
|
||||
Tick m_nextDepartureTick;
|
||||
BuildingId m_nextBuildingId;
|
||||
int m_buildingBlocksStock;
|
||||
bool m_gameOver = false;
|
||||
bool m_gameOver = false;
|
||||
bool m_isWon = false;
|
||||
int m_artifactCount = 0;
|
||||
|
||||
// Pre-placed structure IDs.
|
||||
BuildingId m_hqBuildingId; // Building id (for belt integration)
|
||||
|
||||
Reference in New Issue
Block a user