//Quantity is adjusted directly as a side effect of this function MonsterGroupResult MonsterGroupManager::GetResultFromGroup( const mongroup_id &group_name, int *quantity ) { auto &group = GetUpgradedMonsterGroup( group_name ); int spawn_chance = rng( 1, group.freq_total ); //Default 1000 unless specified //Our spawn details specify, by default, a single instance of the default monster MonsterGroupResult spawn_details = MonsterGroupResult( group.defaultMonster, 1 ); bool monster_found = false; // Loop invariant values const time_point sunset = calendar::turn.sunset(); const time_point sunrise = calendar::turn.sunrise(); const season_type season = season_of_year( calendar::turn ); // Step through spawn definitions from the monster group until one is found or for( auto it = group.monsters.begin(); it != group.monsters.end() && !monster_found; ++it ) { // There's a lot of conditions to work through to see if this spawn definition is valid bool valid_entry = true; //Insure that the time is not before the spawn first appears or after it stops appearing valid_entry = valid_entry && ( calendar::time_of_cataclysm + it->starts < calendar::turn ); valid_entry = valid_entry && ( it->lasts_forever() || calendar::time_of_cataclysm + it->ends > calendar::turn ); std::vector<std::pair<time_point, time_point> > valid_times_of_day; bool season_limited = false; bool season_matched = false; //Collect the various spawn conditions, and then insure they are met appropriately for( auto &elem : it->conditions ) { //Collect valid time of day ranges if( elem == "DAY" || elem == "NIGHT" || elem == "DUSK" || elem == "DAWN" ) { if( elem == "DAY" ) { valid_times_of_day.push_back( std::make_pair( sunrise, sunset ) ); } else if( elem == "NIGHT" ) { valid_times_of_day.push_back( std::make_pair( sunset, sunrise ) ); } else if( elem == "DUSK" ) { valid_times_of_day.push_back( std::make_pair( sunset - 1_hours, sunset + 1_hours ) ); } else if( elem == "DAWN" ) { valid_times_of_day.push_back( std::make_pair( sunrise - 1_hours, sunrise + 1_hours ) ); } } //If we have any seasons listed, we know to limit by season, and if any season matches this season, we are good to spawn if( elem == "SUMMER" || elem == "WINTER" || elem == "SPRING" || elem == "AUTUMN" ) { season_limited = true; if( ( season == SUMMER && elem == "SUMMER" ) || ( season == WINTER && elem == "WINTER" ) || ( season == SPRING && elem == "SPRING" ) || ( season == AUTUMN && elem == "AUTUMN" ) ) { season_matched = true; } } } //Make sure the current time of day is within one of the valid time ranges for this spawn bool is_valid_time_of_day = false; if( valid_times_of_day.empty() ) { //Then it can spawn whenever, since no times were defined is_valid_time_of_day = true; } else { //Otherwise, it's valid if it matches any of the times of day for( auto &elem : valid_times_of_day ) { if( calendar::turn > elem.first && calendar::turn < elem.second ) { is_valid_time_of_day = true; } } } if( !is_valid_time_of_day ) { valid_entry = false; } //If we are limited by season, make sure we matched a season if( season_limited && !season_matched ) { valid_entry = false; } //If the entry was valid, check to see if we actually spawn it if( valid_entry ) { //If the monsters frequency is greater than the spawn_chance, select this spawn rule if( it->frequency >= spawn_chance ) { if( it->pack_maximum > 1 ) { spawn_details = MonsterGroupResult( it->name, rng( it->pack_minimum, it->pack_maximum ) ); } else { spawn_details = MonsterGroupResult( it->name, 1 ); } //And if a quantity pointer with remaining value was passed, will modify the external value as a side effect //We will reduce it by the spawn rule's cost multiplier if( quantity ) { *quantity -= std::max( 1, it->cost_multiplier * spawn_details.pack_size ); } monster_found = true; //Otherwise, subtract the frequency from spawn result for the next loop around } else { spawn_chance -= it->frequency; } } } // Force quantity to decrement regardless of whether we found a monster. if( quantity && !monster_found ) { ( *quantity )--; } return spawn_details; }
//Quantity is adjusted directly as a side effect of this function MonsterGroupResult MonsterGroupManager::GetResultFromGroup( const mongroup_id& group_name, int *quantity, int turn ){ int spawn_chance = rng(1, 1000); auto &group = GetUpgradedMonsterGroup( group_name ); //Our spawn details specify, by default, a single instance of the default monster MonsterGroupResult spawn_details = MonsterGroupResult(group.defaultMonster, 1); //If the default monster is too difficult, replace this with NULL_ID if(turn != -1 && (turn + 900 < MINUTES(STARTING_MINUTES) + HOURS( group.defaultMonster.obj().difficulty))) { spawn_details = MonsterGroupResult(NULL_ID, 0); } bool monster_found = false; // Step through spawn definitions from the monster group until one is found or for( auto it = group.monsters.begin(); it != group.monsters.end() && !monster_found; ++it) { const mtype& mt = it->name.obj(); // There's a lot of conditions to work through to see if this spawn definition is valid bool valid_entry = true; // I don't know what turn == -1 is checking for, but it makes monsters always valid for difficulty purposes valid_entry = valid_entry && (turn == -1 || (turn + 900) >= (MINUTES(STARTING_MINUTES) + HOURS(mt.difficulty))); // If we are in classic mode, require the monster type to be either CLASSIC or WILDLIFE if(ACTIVE_WORLD_OPTIONS["CLASSIC_ZOMBIES"]) { valid_entry = valid_entry && (mt.in_category("CLASSIC") || mt.in_category("WILDLIFE")); } //Insure that the time is not before the spawn first appears or after it stops appearing valid_entry = valid_entry && (HOURS(it->starts) < calendar::turn.get_turn()); valid_entry = valid_entry && (it->lasts_forever() || HOURS(it->ends) > calendar::turn.get_turn()); std::vector<std::pair<int, int> > valid_times_of_day; bool season_limited = false; bool season_matched = false; //Collect the various spawn conditions, and then insure they are met appropriately for( auto &elem : it->conditions ) { //Collect valid time of day ranges if( ( elem ) == "DAY" || ( elem ) == "NIGHT" || ( elem ) == "DUSK" || ( elem ) == "DAWN" ) { int sunset = calendar::turn.sunset().get_turn(); int sunrise = calendar::turn.sunrise().get_turn(); if( ( elem ) == "DAY" ) { valid_times_of_day.push_back( std::make_pair(sunrise, sunset) ); } else if( ( elem ) == "NIGHT" ) { valid_times_of_day.push_back( std::make_pair(sunset, sunrise) ); } else if( ( elem ) == "DUSK" ) { valid_times_of_day.push_back( std::make_pair(sunset - HOURS(1), sunset + HOURS(1)) ); } else if( ( elem ) == "DAWN" ) { valid_times_of_day.push_back( std::make_pair(sunrise - HOURS(1), sunrise + HOURS(1)) ); } } //If we have any seasons listed, we know to limit by season, and if any season matches this season, we are good to spawn if( ( elem ) == "SUMMER" || ( elem ) == "WINTER" || ( elem ) == "SPRING" || ( elem ) == "AUTUMN" ) { season_limited = true; if( ( calendar::turn.get_season() == SUMMER && ( elem ) == "SUMMER" ) || ( calendar::turn.get_season() == WINTER && ( elem ) == "WINTER" ) || ( calendar::turn.get_season() == SPRING && ( elem ) == "SPRING" ) || ( calendar::turn.get_season() == AUTUMN && ( elem ) == "AUTUMN" ) ) { season_matched = true; } } } //Make sure the current time of day is within one of the valid time ranges for this spawn bool is_valid_time_of_day = false; if(valid_times_of_day.size() < 1) { //Then it can spawn whenever, since no times were defined is_valid_time_of_day = true; } else { //Otherwise, it's valid if it matches any of the times of day for( auto &elem : valid_times_of_day ) { int time_now = calendar::turn.get_turn(); if( time_now > elem.first && time_now < elem.second ) { is_valid_time_of_day = true; } } } if(!is_valid_time_of_day) { valid_entry = false; } //If we are limited by season, make sure we matched a season if(season_limited && !season_matched) { valid_entry = false; } //If the entry was valid, check to see if we actually spawn it if(valid_entry) { //If the monsters frequency is greater than the spawn_chance, select this spawn rule if(it->frequency >= spawn_chance) { if(it->pack_maximum > 1) { spawn_details = MonsterGroupResult(it->name, rng(it->pack_minimum, it->pack_maximum)); } else { spawn_details = MonsterGroupResult(it->name, 1); } //And if a quantity pointer with remaining value was passed, will modify the external value as a side effect //We will reduce it by the spawn rule's cost multiplier if(quantity) { *quantity -= it->cost_multiplier * spawn_details.pack_size; } monster_found = true; //Otherwise, subtract the frequency from spawn result for the next loop around } else { spawn_chance -= it->frequency; } } } return spawn_details; }