117 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from typing import TYPE_CHECKING, Dict, List, Set
 | |
| 
 | |
| from .data import NUM_REAL_SPECIES, UNEVOLVED_POKEMON, data
 | |
| from .options import RandomizeTrainerParties
 | |
| from .pokemon import filter_species_by_nearby_bst
 | |
| from .util import int_to_bool_array
 | |
| 
 | |
| if TYPE_CHECKING:
 | |
|     from . import PokemonEmeraldWorld
 | |
| 
 | |
| 
 | |
| def randomize_opponent_parties(world: "PokemonEmeraldWorld") -> None:
 | |
|     if world.options.trainer_parties == RandomizeTrainerParties.option_vanilla:
 | |
|         return
 | |
| 
 | |
|     from collections import defaultdict
 | |
| 
 | |
|     should_match_bst = world.options.trainer_parties in {
 | |
|         RandomizeTrainerParties.option_match_base_stats,
 | |
|         RandomizeTrainerParties.option_match_base_stats_and_type,
 | |
|     }
 | |
|     should_match_type = world.options.trainer_parties in {
 | |
|         RandomizeTrainerParties.option_match_type,
 | |
|         RandomizeTrainerParties.option_match_base_stats_and_type,
 | |
|     }
 | |
| 
 | |
|     per_species_tmhm_moves: Dict[int, List[int]] = {}
 | |
| 
 | |
|     for trainer in world.modified_trainers:
 | |
|         new_party = []
 | |
|         for pokemon in trainer.party.pokemon:
 | |
|             original_species = data.species[pokemon.species_id]
 | |
| 
 | |
|             # Construct progressive tiers of blacklists that can be peeled back if they
 | |
|             # collectively cover too much of the pokedex. A lower index in `blacklists`
 | |
|             # indicates a more important set of species to avoid. Entries at `0` will
 | |
|             # always be blacklisted.
 | |
|             blacklists: Dict[int, List[Set[int]]] = defaultdict(list)
 | |
| 
 | |
|             # Blacklist unevolved species
 | |
|             if pokemon.level >= world.options.force_fully_evolved:
 | |
|                 blacklists[0].append(UNEVOLVED_POKEMON)
 | |
| 
 | |
|             # Blacklist from player options
 | |
|             blacklists[2].append(world.blacklisted_opponent_pokemon)
 | |
| 
 | |
|             # Type matching blacklist
 | |
|             if should_match_type:
 | |
|                 blacklists[3].append({
 | |
|                     species.species_id
 | |
|                     for species in world.modified_species.values()
 | |
|                     if not bool(set(species.types) & set(original_species.types))
 | |
|                 })
 | |
| 
 | |
|             merged_blacklist: Set[int] = set()
 | |
|             for max_priority in reversed(sorted(blacklists.keys())):
 | |
|                 merged_blacklist = set()
 | |
|                 for priority in blacklists.keys():
 | |
|                     if priority <= max_priority:
 | |
|                         for blacklist in blacklists[priority]:
 | |
|                             merged_blacklist |= blacklist
 | |
| 
 | |
|                 if len(merged_blacklist) < NUM_REAL_SPECIES:
 | |
|                     break
 | |
|             else:
 | |
|                 raise RuntimeError("This should never happen")
 | |
| 
 | |
|             candidates = [
 | |
|                 species
 | |
|                 for species in world.modified_species.values()
 | |
|                 if species.species_id not in merged_blacklist
 | |
|             ]
 | |
| 
 | |
|             if should_match_bst:
 | |
|                 candidates = filter_species_by_nearby_bst(candidates, sum(original_species.base_stats))
 | |
| 
 | |
|             new_species = world.random.choice(candidates)
 | |
| 
 | |
|             if new_species.species_id not in per_species_tmhm_moves:
 | |
|                 per_species_tmhm_moves[new_species.species_id] = sorted({
 | |
|                     world.modified_tmhm_moves[i]
 | |
|                     for i, is_compatible in enumerate(int_to_bool_array(new_species.tm_hm_compatibility))
 | |
|                     if is_compatible and world.modified_tmhm_moves[i] not in world.blacklisted_moves
 | |
|                 })
 | |
| 
 | |
|             # TMs and HMs compatible with the species
 | |
|             tm_hm_movepool = per_species_tmhm_moves[new_species.species_id]
 | |
| 
 | |
|             # Moves the pokemon could have learned by now
 | |
|             level_up_movepool = sorted({
 | |
|                 move.move_id
 | |
|                 for move in new_species.learnset
 | |
|                 if move.move_id != 0 and move.level <= pokemon.level
 | |
|             })
 | |
| 
 | |
|             if len(level_up_movepool) < 4:
 | |
|                 level_up_moves = [level_up_movepool[i] if i < len(level_up_movepool) else 0 for i in range(4)]
 | |
|             else:
 | |
|                 level_up_moves = world.random.sample(level_up_movepool, 4)
 | |
| 
 | |
|             if len(tm_hm_movepool) < 4:
 | |
|                 hm_moves = list(reversed(list(tm_hm_movepool[i] if i < len(tm_hm_movepool) else 0 for i in range(4))))
 | |
|             else:
 | |
|                 hm_moves = world.random.sample(tm_hm_movepool, 4)
 | |
| 
 | |
|             # 25% chance to pick a move from TMs or HMs
 | |
|             new_moves = (
 | |
|                 hm_moves[0] if world.random.random() < 0.25 else level_up_moves[0],
 | |
|                 hm_moves[1] if world.random.random() < 0.25 else level_up_moves[1],
 | |
|                 hm_moves[2] if world.random.random() < 0.25 else level_up_moves[2],
 | |
|                 hm_moves[3] if world.random.random() < 0.25 else level_up_moves[3]
 | |
|             )
 | |
| 
 | |
|             new_party.append(pokemon._replace(species_id=new_species.species_id, moves=new_moves))
 | |
| 
 | |
|         trainer.party = trainer.party._replace(pokemon=new_party)
 | 
