From ba48adaec88ecd24c0d0b34d7f4482c47e0c1cb2 Mon Sep 17 00:00:00 2001 From: JT Traub Date: Sat, 4 May 2024 19:11:06 -0700 Subject: [PATCH] Remove the freezing code & add city counting for winner A common New Origins win condition is percent of cities voting. Add a global define to enable this, and put the counting and automatic victory declaration, as well as a times message once someone has made good progress towards it. While this won't be used for NO7, I suspect it will be good to have for the future. Also, the freezing code was highly NO6 specific, so it shouldn't be on the master branch. --- neworigins/extra.cpp | 102 +++++++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 22 deletions(-) diff --git a/neworigins/extra.cpp b/neworigins/extra.cpp index 8643707e..0436399c 100644 --- a/neworigins/extra.cpp +++ b/neworigins/extra.cpp @@ -31,6 +31,8 @@ #include #include +using namespace std; + #define MINIMUM_ACTIVE_QUESTS 5 #define MAXIMUM_ACTIVE_QUESTS 20 #define QUEST_EXPLORATION_PERCENT 30 @@ -39,6 +41,10 @@ #define QUEST_SPAWN_CHANCE 70 #define MAX_DESTINATIONS 5 +// If this is set to true, then the game will end if a faction has > 50% of all cities in the game with their id +// in the name. +#define CITY_VOTE_WIN false + int Game::SetupFaction( Faction *pFac ) { pFac->unclaimed = Globals->START_MONEY + TurnNumber() * 300; @@ -417,17 +423,17 @@ Faction *Game::CheckVictory() string stlstr; set intersection, un; set::iterator it2; + Faction *winner = nullptr; forlist(&quests) { q = (Quest *) elem; if (q->type != Quest::VISIT) continue; - for (it2 = q->destinations.begin(); - it2 != q->destinations.end(); - it2++) { + for (it2 = q->destinations.begin(); it2 != q->destinations.end(); it2++) { un.insert(*it2); } } + visited = 0; unvisited = 0; forlist_reuse(®ions) { @@ -465,14 +471,14 @@ Faction *Game::CheckVictory() if (visited >= (unvisited + visited) * QUEST_EXPLORATION_PERCENT / 100) { // Exploration phase complete: start creating relic quests for (i = 0; i < QUEST_SPAWN_RATE; i++) { - if (quests.Num() < MAXIMUM_ACTIVE_QUESTS && - getrandom(100) < QUEST_SPAWN_CHANCE) + if (quests.Num() < MAXIMUM_ACTIVE_QUESTS && getrandom(100) < QUEST_SPAWN_CHANCE) CreateQuest(®ions, monfaction); } while (quests.Num() < MINIMUM_ACTIVE_QUESTS) { CreateQuest(®ions, monfaction); } } + if (unvisited) { // Tell the players to get exploring :-) if (visited > 9 * unvisited) { @@ -728,30 +734,82 @@ Faction *Game::CheckVictory() } } - if (TurnNumber() >= 29) { // 29 is just a number to avoid breaking snapshots - // Freezing effect - forlist_reuse(®ions) { - ARegion *r = (ARegion *) elem; - - // No effect for regions with clear skies - if (r->clearskies) { - continue; + // Check for victory conditions based on the current game + if (CITY_VOTE_WIN) { + std::map votes; // track votes per faction id + int total_cities = 0; // total cities possible for vote count + + forlist(®ions) { + ARegion *r = (ARegion *)elem; + // Ignore anything but the surface + if (r->level->levelType != ARegionArray::LEVEL_SURFACE) continue; + if (!r->town || (r->town->TownType() != TOWN_CITY)) continue; + + total_cities++; + + string name = r->town->name->const_str(); + string possible_faction = name.substr(0, s.find_first_of(" \t\n")); + // The first word of the name was not all numeric, don't count for anyone + if (!all_of( + possible_faction.begin(), + possible_faction.end(), + [](unsigned char ch){ return std::isdigit(ch); } + )) continue; + // Now that we know it's all numeric, convert it to an int + int faction_id = stoi(possible_faction); + + // Make sure it's a valid faction + Faction *f = GetFaction(&factions, faction_id); + if (!f || f->is_npc) continue; + + auto vote = votes.find(faction_id); + if (vote == votes.end()) { + votes[faction_id] = 1; + } else { + vote->second++; } + } - // Check if regions in freezing zome in surface - if (r->zloc == ARegionArray::LEVEL_SURFACE && (r->yloc <= 0 || r->yloc >= 71)) { - r->Pillage(); - r->SetWeather(W_BLIZZARD); - printf("Freeze (%d,%d) region in %s of %s\n", - r->xloc, r->yloc, r->name->Str(), TerrainDefs[TerrainDefs[r->type].similar_type].name - ); + // Set up the voting result to be reported if we are far enough in + string message = "Voting results: \n"; + + int max_vote = -1; + bool tie = false; + Faction *maxFaction = nullptr; + for (const auto& vote : votes) { + Faction *f = GetFaction(&factions, vote.first); + if (vote.second > max_vote) { + max_vote = vote.second; + maxFaction = f; + tie = false; + } else if (vote.second == max_vote) { + tie = true; + maxFaction = nullptr; } + message += "Faction " + string(f->name->const_str()) + " has " + to_string(vote.second) + " votes.\n"; + } - // TODO: Check if regions in freezing zome in UW + // See if we have enough votes to even report the info. Since a win requires 50% + 1, we can start reporting + // once someone has more than 25% of the cities. + if (max_vote > (total_cities / 4)) { + // Now see if we have a winner at all + if (max_vote > ((total_cities / 2) + 1)) { + winner = maxFaction; + message += "\n" + string(winner->name->const_str()) + " has enough votes and has won the game!"; + } else { + percent = (max_vote * 100) / total_cities; + if (tie) { + message += "\nThere is a tie for the most votes with multiple factions having "; + } else { + message += "\n" + "The current leader is " + string(maxFaction->name->const_str()) + " with "; + } + message += to_string(max_vote) + "/" + to_string(total_cities) + " votes (" + to_string(percent) + "%)."; + } + WriteTimesArticle(message); } } - return NULL; + return winner; } void Game::ModifyTablesPerRuleset(void)