Files
BGG2026/src/round.cpp
2026-08-25 21:36:17 -05:00

140 lines
3.6 KiB
C++

#include "Round.h"
#include "Creature.h"
#include <algorithm>
#include <cmath>
#include <raylib.h>
namespace {
AppearanceTier RollAppearanceTier(const RoundConfig &cfg) {
float roll = (float)GetRandomValue(0, 10000) / 10000.0f;
float t1 = cfg.w_standard;
float t2 = t1 + cfg.w_subtle1;
float t3 = t2 + cfg.w_subtle2;
if (roll < t1)
return AppearanceTier::Standard;
if (roll < t2)
return AppearanceTier::Subtle1;
if (roll < t3)
return AppearanceTier::Subtle2;
return AppearanceTier::Obvious;
}
float IncorrectBehaviourChance(AppearanceTier tier) {
switch (tier) {
case AppearanceTier::Standard:
return 1.0f;
case AppearanceTier::Subtle1:
return 0.5f;
case AppearanceTier::Subtle2:
return 0.5f;
case AppearanceTier::Obvious:
return 0.3f;
}
return 1.0f;
}
Species RandomSpecies() { return static_cast<Species>(GetRandomValue(0, 3)); }
} // namespace
RoundConfig MakeRoundConfig(int roundIndex) {
RoundConfig cfg;
cfg.creature_count = std::min(5 + roundIndex * 2, 20);
float rampedStandard = std::min(0.35f + roundIndex * 0.03f, 0.70f);
float remaining = 1.0f - rampedStandard;
float baseRemaining = 0.30f + 0.20f + 0.15f;
cfg.w_standard = rampedStandard;
cfg.w_subtle1 = remaining * (0.30f / baseRemaining);
cfg.w_subtle2 = remaining * (0.20f / baseRemaining);
cfg.w_obvious = remaining * (0.15f / baseRemaining);
cfg.cryptid_ratio = 0.35f;
float ambiguousFraction =
(1.0f - cfg.cryptid_ratio) + cfg.cryptid_ratio * cfg.w_standard;
float ambiguousCount = cfg.creature_count * ambiguousFraction;
cfg.marshmallow_supply = std::max(2, (int)std::round(ambiguousCount * 0.85f));
return cfg;
}
Round GenerateRound(const RoundConfig &config) {
Round round;
round.conf = config;
round.marshmallowsRemaining = config.marshmallow_supply;
int cryptidCount =
std::clamp((int)std::round(config.creature_count * config.cryptid_ratio),
1, config.creature_count - 1);
for (int i = 0; i < config.creature_count; ++i) {
Creature c;
c.species = RandomSpecies();
c.is_cryptid = i < cryptidCount;
if (!c.is_cryptid) {
c.appearance = AppearanceTier::Standard;
c.behaves_normally = true;
} else {
c.appearance = RollAppearanceTier(config);
float lieChance = IncorrectBehaviourChance(c.appearance);
float roll = (float)GetRandomValue(0, 10000) / 10000.0f;
c.behaves_normally = !(roll < lieChance);
}
round.creatures.push_back(c);
}
for (int i = (int)round.creatures.size() - 1; i > 0; --i) {
int j = GetRandomValue(0, i);
std::swap(round.creatures[i], round.creatures[j]);
}
return round;
}
bool FeedCurrentCreature(Round &round) {
Creature *c = round.CurrentCreature();
if (!c || c->has_been_fed || round.marshmallowsRemaining <= 0)
return false;
c->has_been_fed = true;
c->observed_reaction = true;
round.marshmallowsRemaining--;
return true;
}
bool ResolveCurrentCreature(Round &round, bool captured) {
Creature *c = round.CurrentCreature();
if (!c)
return false;
c->captured = captured;
c->resolved = true;
bool died = false;
if (!captured && c->is_cryptid) {
died = true;
} else if (captured && !c->is_cryptid) {
round.miscaptures++;
}
round.currentIndex++;
return died;
}
bool Round::IsComplete() const { return currentIndex >= (int)creatures.size(); }
Creature *Round::CurrentCreature() {
if (IsComplete())
return nullptr;
return &creatures[currentIndex];
}
float Round::SuccessRate() const {
if (conf.creature_count == 0)
return 1.0f;
return 1.0f - (float)miscaptures / (float)conf.creature_count;
}
bool Round::Passed() const { return SuccessRate() >= 0.8f; }