#include "Spawns.h" #include #include namespace bs { namespace { float rarityWeight(Rarity r) { switch (r) { case Rarity::Uncommon: return 0.45f; case Rarity::Rare: return 0.12f; case Rarity::Legendary: return 0.025f; default: return 1.0f; } } float landTypeWeight(const std::string& zoneId, const std::string& type) { // Only "meadow" exists today; other zones fall back to the same table. (void)zoneId; static const std::map meadow = { {"grass", 3.0f}, {"bug", 3.0f}, {"normal", 3.0f}, {"ground", 2.0f}, {"flying", 2.0f}, {"rock", 1.0f}, {"poison", 1.0f}, {"fighting", 1.0f}, {"electric", 1.0f}, {"psychic", 0.6f}, {"fire", 0.6f}, {"ice", 0.3f}, {"ghost", 0.4f}, {"dragon", 0.2f}, {"water", 0.5f}, }; auto it = meadow.find(type); return it == meadow.end() ? 0.5f : it->second; } float waterTypeWeight(const std::string& type) { if (type == "water") return 5.0f; if (type == "ice") return 1.0f; return 0.2f; } float conditionWeight(const std::string& type, bool night, Weather w) { float m = 1.0f; if (night) { if (type == "ghost") m *= 3.0f; else if (type == "psychic") m *= 2.0f; else if (type == "bug") m *= 1.5f; else if (type == "fire") m *= 0.6f; } else { if (type == "fire") m *= 1.4f; else if (type == "grass") m *= 1.2f; } if (w == Weather::Rain || w == Weather::Storm) { if (type == "water") m *= 2.0f; if (type == "electric") m *= w == Weather::Storm ? 3.0f : 1.4f; } if (w == Weather::Fog && type == "ghost") m *= 2.0f; return m; } } // namespace void SpawnTables::update(const Roster& roster, const std::string& zoneId, float hour, Weather weather) { bool night = isNight(hour); int key = int(night) * int(Weather::Count) + int(weather); if (key == key_ && roster_ == &roster) return; key_ = key; roster_ = &roster; for (int h = 0; h < 2; ++h) { Table& t = tables_[h]; t.cumulative.clear(); t.ids.clear(); float total = 0.0f; for (const Species& s : roster.all()) { bool ok = h == int(SpawnHabitat::Land) ? s.habitat != Habitat::Water : s.habitat != Habitat::Grass; if (!ok || s.retired) continue; // retired companies keep their id but no longer spawn auto typeW = [&](const std::string& type) { float z = h == int(SpawnHabitat::Land) ? landTypeWeight(zoneId, type) : waterTypeWeight(type); return z * conditionWeight(type, night, weather); }; float tw = typeW(s.type); if (!s.type2.empty()) tw = std::max(tw, 0.6f * typeW(s.type2)); float w = rarityWeight(s.rarity) * tw; if (w <= 0.0f) continue; total += w; t.cumulative.push_back(total); t.ids.push_back(s.id); } } } bool SpawnTables::pick(Rng& rng, SpawnHabitat h, uint16_t& species) const { const Table& t = tables_[int(h)]; if (t.ids.empty()) return false; float r = rng.uniform() * t.cumulative.back(); size_t i = size_t(std::upper_bound(t.cumulative.begin(), t.cumulative.end(), r) - t.cumulative.begin()); species = t.ids[std::min(i, t.ids.size() - 1)]; return true; } Weather nextWeather(Rng& rng, Weather cur, float hour) { bool morning = hour >= 4.0f && hour <= 8.0f; // fog season float w[int(Weather::Count)] = {}; auto& clear = w[int(Weather::Clear)]; auto& cloudy = w[int(Weather::Cloudy)]; auto& rain = w[int(Weather::Rain)]; auto& storm = w[int(Weather::Storm)]; auto& fog = w[int(Weather::Fog)]; switch (cur) { case Weather::Clear: clear = 5, cloudy = 3, rain = 0.2f, storm = 0.03f, fog = morning ? 3.5f : 0.2f; break; case Weather::Cloudy: clear = 3, cloudy = 2, rain = 3, storm = 0.3f, fog = morning ? 2.0f : 0.3f; break; case Weather::Rain: clear = 1, cloudy = 3, rain = 2, storm = 0.5f, fog = morning ? 1.0f : 0.1f; break; case Weather::Storm: clear = 0.3f, cloudy = 1.5f, rain = 3, storm = 0.4f; break; case Weather::Fog: clear = 3, cloudy = 2, rain = 0.3f, fog = morning ? 3.0f : 0.4f; break; default: clear = 1; break; } float total = 0; for (float v : w) total += v; float r = rng.uniform() * total; for (int i = 0; i < int(Weather::Count); ++i) { if (r < w[i]) return Weather(i); r -= w[i]; } return Weather::Clear; } } // namespace bs