#include "Round.h" #include "Creature.h" #include #include #include 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(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; }