Release 6.9.0

This commit is contained in:
chardub
2026-11-02 11:34:46 -05:00
parent 060726298d
commit ae6e2ebc64
5697 changed files with 131795 additions and 17423 deletions
@@ -0,0 +1,35 @@
class BstIndex
# entries: [bst, species, legendary?] sorted by bst
def initialize(entries)
@bsts = entries.map { |e| e[0] }
@species = entries.map { |e| e[1] }
end
def empty?
@species.empty?
end
def sample_any
@species.sample
end
def sample_in_range(lo, hi)
first = @bsts.bsearch_index { |b| b >= lo }
return nil if first.nil?
last = @bsts.bsearch_index { |b| b > hi } || @bsts.length
return nil if last <= first
@species[first + rand(last - first)]
end
end
class SpeciesPools
attr_reader :all, :legendary, :normal
def initialize(species_list)
entries = species_list.map { |sp| [calcBaseStatsSum(sp), sp, is_legendary(sp)] }
entries.sort_by! { |e| e[0] }
@all = BstIndex.new(entries)
@legendary = BstIndex.new(entries.select { |e| e[2] })
@normal = BstIndex.new(entries.reject { |e| e[2] })
end
end
@@ -0,0 +1,94 @@
#===============================================================================
# Standalone progress bar, reuses the battle Exp bar graphic
#===============================================================================
class ShuffleProgressBar
MIN_WIDTH = 240
HEIGHT = 56
PADDING = 12
def initialize(text=_INTL("Shuffling..."))
@viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
@viewport.z = 99999
@barBitmap = AnimatedBitmap.new("Graphics/Pictures/Battle/overlay_exp")
@bg = Sprite.new(@viewport)
@bg.bitmap = Bitmap.new(@barBitmap.width + 4, @barBitmap.height + 4)
@bg.bitmap.fill_rect(0, 0, @bg.bitmap.width, @bg.bitmap.height, Color.new(0, 0, 0, 160))
@bg.x = (Graphics.width - @barBitmap.width) / 2 - 40
@bg.y = Graphics.height - 48
@bg.z = 998
@sprite = SpriteWrapper.new(@viewport)
@sprite.bitmap = @barBitmap.bitmap
@sprite.src_rect.width = 0
@sprite.x = ((Graphics.width - @barBitmap.width) / 2) - 100
@sprite.y = Graphics.height - 46
@sprite.z = 999
@text = text
@textSprite = Sprite.new(@viewport)
@textSprite.z = 1000
createTextBitmap(MIN_WIDTH)
drawProgressText(0)
end
def createTextBitmap(width)
@textSprite.bitmap.dispose if @textSprite.bitmap
@textSprite.bitmap = Bitmap.new(width, HEIGHT)
pbSetSystemFont(@textSprite.bitmap)
@textSprite.x = @sprite.x
@textSprite.y = @bg.y - HEIGHT
end
# progress: float from 0.0 to 1.0
def progress=(progress)
progress = 0 if progress < 0
progress = 1 if progress > 1
w = progress * @barBitmap.width
w = ((w / 2).round) * 2
@sprite.src_rect.width = w
drawProgressText(progress)
end
def drawProgressText(progress)
return if !@textSprite || @textSprite.disposed?
pct = sprintf("%.0f%%", progress * 100)
bmp = @textSprite.bitmap
textWidth = bmp.text_size(@text).width
pctWidth = bmp.text_size(pct).width
neededWidth = [textWidth, pctWidth].max + PADDING * 2
neededWidth = MIN_WIDTH if neededWidth < MIN_WIDTH
if bmp.width != neededWidth
createTextBitmap(neededWidth)
bmp = @textSprite.bitmap
end
bmp.clear
textPositions = [
[@text, PADDING, 0, :left, Color.new(255, 255, 255), Color.new(0, 0, 0)],
[pct, PADDING, 24, :left, Color.new(255, 255, 255), Color.new(0, 0, 0)]
]
pbDrawTextPositions(bmp, textPositions)
end
def update
Graphics.update
Input.update
end
def dispose
@textSprite.bitmap.dispose
@textSprite.dispose
@sprite.dispose
@bg.bitmap.dispose
@bg.dispose
@barBitmap.dispose
@viewport.dispose
end
def disposed?
@viewport.disposed?
end
end
+120 -10
View File
@@ -10,6 +10,7 @@ class RandomizerOptionsScene < PokemonOption_Scene
@openWildOptions = false
@openGymOptions = false
@openItemOptions = false
@openReferenceMenu = false
$game_switches[SWITCH_RANDOMIZED_AT_LEAST_ONCE] = true
end
@@ -19,6 +20,9 @@ class RandomizerOptionsScene < PokemonOption_Scene
def pbStartScene(inloadscreen = false)
super
@old_wild_pokemon_options = recordCurrentWildPokemonOptions
@old_trainer_options = recordCurrentTrainerOptions
@changedColor = true
@sprites["title"] = Window_UnformattedTextPokemon.newWithSize(
_INTL("Randomizer settings"), 0, 0, Graphics.width, 64, @viewport)
@@ -26,6 +30,50 @@ class RandomizerOptionsScene < PokemonOption_Scene
pbFadeInAndShow(@sprites) { pbUpdate }
end
def pbEndScene
super
new_wild_pokemon_options = recordCurrentWildPokemonOptions
new_trainer_options = recordCurrentTrainerOptions
wild_pokemon_on = $game_switches[SWITCH_RANDOM_WILD]
trainers_on = $game_switches[SWITCH_RANDOM_TRAINERS]
if wild_pokemon_on && new_wild_pokemon_options != @old_wild_pokemon_options
$PokemonTemp.should_reshuffle_pokemon = true
end
if trainers_on && new_trainer_options != @old_trainer_options
$PokemonTemp.should_reshuffle_pokemon = true
end
end
def recordCurrentWildPokemonOptions
is_on = $game_switches[SWITCH_RANDOM_WILD]
randomness_degree = $game_variables[VAR_RANDOMIZER_WILD_POKE_BST]
type_global = $game_switches[RandomizerWildPokemonOptionsScene::RANDOM_WILD_GLOBAL]
type_area = $game_switches[RandomizerWildPokemonOptionsScene::RANDOM_WILD_AREA]
customs_only = $game_switches[SWITCH_RANDOM_WILD_ONLY_CUSTOMS]
legendaries = $game_switches[SWITCH_RANDOM_WILD_LEGENDARIES]
egg_groups = $game_switches[SWITCH_RANDOM_WILD_EGG_GROUP]
starters = $game_switches[SWITCH_RANDOM_STARTERS]
starters_first_stage = $game_switches[SWITCH_RANDOM_STARTER_FIRST_STAGE]
random_static = $game_switches[RandomizerWildPokemonOptionsScene::RANDOM_STATIC]
gift_pokemon = $game_switches[RandomizerWildPokemonOptionsScene::GIFT_POKEMON]
fuse_everything = $game_switches[RandomizerWildPokemonOptionsScene::REGULAR_TO_FUSIONS]
return [is_on, randomness_degree, type_global, type_area, customs_only, legendaries, egg_groups,
starters, starters_first_stage, random_static, gift_pokemon, fuse_everything]
end
def recordCurrentTrainerOptions
is_on = $game_switches[SWITCH_RANDOM_TRAINERS]
randomness_degree = $game_variables[VAR_RANDOMIZER_TRAINER_BST]
customs_only = $game_switches[RandomizerTrainerOptionsScene::RANDOM_TEAMS_CUSTOM_SPRITES]
egg_groups = $game_switches[SWITCH_RANDOM_TRAINER_EGG_GROUP]
references_filter = $game_switches[SWITCH_RANDOM_REFERENCE_SPRITES]
return [is_on, randomness_degree, customs_only, egg_groups, references_filter]
end
def pbGetOptions(inloadscreen = false)
options = [
EnumOption.new(_INTL("Pokémon"), [_INTL("On"), _INTL("Off")],
@@ -127,6 +175,21 @@ class RandomizerTrainerOptionsScene < PokemonOption_Scene
@changedColor = false
end
def pbEndScene
super
if $game_switches[SWITCH_RANDOM_REFERENCE_SPRITES] && !$game_switches[RANDOM_TEAMS_CUSTOM_SPRITES]
pbMessage(_INTL("\"The References Filter\" option needs the \"Custom Sprites Only\" option to be enabled in order to work."))
if pbConfirmMessage(_INTL("Would you like to turn on the \"Custom Sprites Only\" option?"))
$game_switches[RANDOM_TEAMS_CUSTOM_SPRITES] = true
pbMessage(_INTL("The \"Custom Sprites Only\" option was enabled."))
else
$game_switches[SWITCH_RANDOM_REFERENCE_SPRITES] = false
pbMessage(_INTL("The \"References Filter\" option was disabled."))
end
end
$PokemonTemp.should_reshuffle_trainers = true
end
def pbStartScene(inloadscreen = false)
super
@sprites["option"].nameBaseColor = MessageConfig::BLUE_TEXT_MAIN_COLOR
@@ -156,7 +219,7 @@ class RandomizerTrainerOptionsScene < PokemonOption_Scene
$game_variables[VAR_RANDOMIZER_TRAINER_BST] = value
},
_INTL("The higher the number, the more random it is.")
)
)
end
options << EnumOption.new(_INTL("Custom Sprites only"), [_INTL("On"), _INTL("Off")],
proc { $game_switches[RANDOM_TEAMS_CUSTOM_SPRITES] ? 0 : 1 },
@@ -166,15 +229,39 @@ class RandomizerTrainerOptionsScene < PokemonOption_Scene
_INTL("Use only Pokémon that have custom sprites in trainer teams")
)
# options << EnumOption.new("Allow legendaries", ["On", "Off"],
# proc { $game_switches[SWITCH_RANDOM_TRAINER_LEGENDARIES] ? 0 : 1 },
# proc { |value|
# $game_switches[SWITCH_RANDOM_TRAINER_LEGENDARIES] = value == 0
# }, _INTL("Regular Pokémon can also be randomized into legendaries")
# )
if Settings::HOENN
options << EnumOption.new(_INTL("Egg Groups"), [_INTL("On"), _INTL("Off")],
proc { $game_switches[SWITCH_RANDOM_TRAINER_EGG_GROUP] ? 0 : 1 },
proc { |value|
$game_switches[SWITCH_RANDOM_TRAINER_EGG_GROUP] = value == 0
},
_INTL("Limit to the same egg groups for randomization that feels more natural.")
)
end
options << EnumOption.new(_INTL("References Filter"), [_INTL("On"), _INTL("Off")],
proc { $game_switches[SWITCH_RANDOM_REFERENCE_SPRITES] ? 0 : 1 },
proc { |value|
if !$game_switches[SWITCH_RANDOM_REFERENCE_SPRITES] && value == 0
@openReferenceMenu = true
openReferencesMenu()
end
$game_switches[SWITCH_RANDOM_REFERENCE_SPRITES] = value == 0
},
_INTL("All Trainer teams use reference sprites.")
)
return options
end
def openReferencesMenu()
return unless @openReferenceMenu
pbFadeOutIn {
select_randomizer_references(200)
}
@openReferenceMenu = false
end
end
class RandomizerWildPokemonOptionsScene < PokemonOption_Scene
@@ -207,6 +294,7 @@ class RandomizerWildPokemonOptionsScene < PokemonOption_Scene
super
end
# TODO: Move the randomizer settings to $PokemonGlobal instead of living in game switches
def pbGetOptions(inloadscreen = false)
options = []
if !$game_switches[SWITCH_DURING_INTRO]
@@ -252,13 +340,24 @@ class RandomizerWildPokemonOptionsScene < PokemonOption_Scene
proc { $game_switches[SWITCH_RANDOM_WILD_LEGENDARIES] ? 0 : 1 },
proc { |value|
$game_switches[SWITCH_RANDOM_WILD_LEGENDARIES] = value == 0
},
},
[
_INTL("Regular wild Pokémon can also be randomized into legendaries."),
_INTL("Only legendaries can be randomized into legendaries")
]
)
if Settings::HOENN
options << EnumOption.new(_INTL("Egg Groups"), [_INTL("On"), _INTL("Off")],
proc { $game_switches[SWITCH_RANDOM_WILD_EGG_GROUP] ? 0 : 1 },
proc { |value|
$game_switches[SWITCH_RANDOM_WILD_EGG_GROUP] = value == 0
},
_INTL("Limit to the same egg groups for randomization that feels more natural.")
)
end
options << EnumOption.new(_INTL("Starters"), [_INTL("1st Stage"), _INTL("Any"), _INTL("Off")],
proc {
getStarterRandomizerSelectedOption() },
@@ -274,7 +373,7 @@ class RandomizerWildPokemonOptionsScene < PokemonOption_Scene
$game_switches[SWITCH_RANDOM_STARTERS] = false
$game_switches[SWITCH_RANDOM_STARTER_FIRST_STAGE] = false
end
},
},
[
_INTL("The starters will always be a first evolution Pokémon"),
_INTL("The starters can be any Pokémon"),
@@ -370,7 +469,7 @@ class RandomizerGymOptionsScene < PokemonOption_Scene
proc { $game_switches[SWITCH_RANDOM_GYM_CUSTOMS] ? 0 : 1 },
proc { |value|
$game_switches[SWITCH_RANDOM_GYM_CUSTOMS] = value == 0
},
},
[
_INTL("Use only Pokémon that have custom sprites in gym trainers or gym leader teams"),
_INTL("Pick any possible fusion, including auto-generated sprites.")
@@ -398,6 +497,17 @@ class RandomizerGymOptionsScene < PokemonOption_Scene
}, _INTL("Gym trainers and leaders have a new team each try instead of keeping the same one")
)
# if Settings::HOENN
# options << EnumOption.new(_INTL("Egg Groups"), [_INTL("On"), _INTL("Off")],
# proc { $game_switches[SWITCH_RANDOM_GYM_EGG_GROUP] ? 0 : 1 },
# proc { |value|
# $game_switches[SWITCH_RANDOM_GYM_EGG_GROUP] = value == 0
# },
# _INTL("Limit to the same egg groups for randomization that feels more natural.")
#
# )
# end
return options
end
end
@@ -0,0 +1,80 @@
def select_randomizer_references(minimum_allowed = nil)
references_map = map_sprites_by_reference()
label_to_key = {}
non_ref_list = getNonReferenceSprites(nil, references_map)
references_map["Include non-reference"] = non_ref_list
references_list = references_map.keys.sort_by { |k| [-references_map[k].length, k] }.map do |key|
label = format_reference_for_menu(references_map,key)
label_to_key[label] = key
label
end
current_references = $PokemonSystem.selected_reference_sprite_categories
current_references = references_map.keys if current_references.nil?
formatted_current_references = current_references.map do |reference_key|
format_reference_for_menu(references_map, reference_key)
end.compact
scene = MultiSelectOptionsScene.new(
references_list, formatted_current_references,
_INTL("Select all reference sprites to be included"), nil,
_INTL("Press Shift to search"),
proc { |label| references_map[label_to_key[label]].length }
)
screen = MultiSelectOptionScreen.new(scene)
selected_labels = screen.pbStartScreen
return [] if selected_labels.nil?
selected_keys = selected_labels.map { |label| label_to_key[label] }
total_sprites = 0
selected_keys.each do |key|
sprites = references_map[key]
total_sprites += sprites.length
end
if minimum_allowed && total_sprites < minimum_allowed
pbMessage(_INTL("Warning: The categories you selected only have a total of {1} sprites, and so is likely that you will see repeats during your playthrough.", total_sprites))
pbMessage(_INTL("You can still continue but it is recommended to select at least {1} sprites for a new playthrough.", minimum_allowed))
end
write_selected_references_to_file(selected_keys, references_map)
scene.pbEndScene
$PokemonSystem.selected_reference_sprite_categories = selected_keys
return selected_keys
end
#Util method to export a list - not used by the game itself
def print_reference_map_to_file(references_map)
output_path = "reference_map_ids.txt"
File.open(output_path, "w") do |file|
references_map.keys.sort.each do |reference_id|
file.puts(reference_id)
end
end
rescue => e
echoln("Failed to write reference map to #{output_path}: #{e.message}")
end
def format_reference_for_menu(references_map, key)
return nil unless references_map && references_map.key?(key)
label = "#{titleize(key)} (#{references_map[key].length})"
return label
end
def write_selected_references_to_file(selected_keys, references_map)
output_path = Settings::REFERENCES_FILE_PATH
sprite_names = selected_keys.flat_map { |key| references_map[key] }
File.open(output_path, "w") do |file|
sprite_names.each do |spritename|
file.puts(spritename)
end
end
end# frozen_string_literal: true
@@ -9,6 +9,14 @@ def Kernel.randomizeWildPokemonByRoute()
onlyCustoms = $game_switches[SWITCH_RANDOM_WILD_ONLY_CUSTOMS] && randomizeToFusions
customsList = onlyCustoms ? getCustomSpeciesList() : []
maxSpecies = randomizeToFusions ? PBSpecies.maxValue : NB_POKEMON
# Shared across all routes (only needed for custom sprites)
if onlyCustoms
bst_cache = new_bst_cache
egg_group_cache = new_egg_group_cache
legendary_cache = new_legendary_cache
end
GameData::EncounterRandom::DATA.clear
GameData::Encounter.each do |enc_data|
encounters_hash = {}
@@ -19,8 +27,8 @@ def Kernel.randomizeWildPokemonByRoute()
types_hash = {}
enc_data.types.each do |key, value|
pokemonList = value
newType = randomizePokemonList(pokemonList,bstRange,maxSpecies,onlyCustoms,customsList)
types_hash[key]= newType
newType = randomizePokemonList(pokemonList, bstRange, maxSpecies, onlyCustoms, customsList, bst_cache, egg_group_cache, legendary_cache)
types_hash[key] = newType
end
encounters_hash[:types] = types_hash
GameData::EncounterRandom.register(encounters_hash)
@@ -33,17 +41,27 @@ def Kernel.randomizeWildPokemonByRoute()
end
#input: [[60, :TENTACOOL,5,40, [30, :GOLDEEN, 5, 35], etc.]]
def randomizePokemonList(encountersList,bstRange=50,maxSpecies=NB_POKEMON,customOnly=false,customsList=[])
def randomizePokemonList(encountersList, bstRange = 50, maxSpecies = NB_POKEMON, customOnly = false, customsList = [],
bst_cache = nil, egg_group_cache = nil, legendary_cache = nil)
same_egg_group = $game_switches[SWITCH_RANDOM_WILD_EGG_GROUP]
includeLegendaries = $game_switches[SWITCH_RANDOM_WILD_LEGENDARIES]
newList=[]
if customOnly
bst_cache ||= new_bst_cache
egg_group_cache ||= new_egg_group_cache
legendary_cache ||= new_legendary_cache
end
newList = []
for encounter in encountersList
oldPokemon = encounter[1]
if customOnly
newPokemon = getNewCustomSpecies(oldPokemon,customsList,bstRange,false,includeLegendaries)
newPokemon = getNewCustomSpecies(oldPokemon, customsList, bstRange, false, includeLegendaries, same_egg_group,
bst_cache, egg_group_cache, legendary_cache)
else
newPokemon = getNewSpecies(oldPokemon,bstRange,false,maxSpecies,includeLegendaries)
newPokemon = getNewSpecies(oldPokemon, bstRange, false, maxSpecies, includeLegendaries, same_egg_group)
end
newEntry =[]
newEntry = []
newEntry << encounter[0]
newEntry << getSpecies(newPokemon).species
newEntry << encounter[2]
@@ -54,6 +72,7 @@ def randomizePokemonList(encountersList,bstRange=50,maxSpecies=NB_POKEMON,custom
end
#
# def Kernel.randomizeWildPokemonByRouteOld()
# bstRange = $game_variables[VAR_RANDOMIZER_WILD_POKE_BST]
@@ -140,12 +140,39 @@ def legendaryOk(oldspecies,newspecies,includeLegendaries)
end
def egg_group_ok(oldSpecies_id,newSpecies_id)
oldSpecies = GameData::Species.get(oldSpecies_id)
newSpecies = GameData::Species.get(newSpecies_id)
oldEggGroups = Array(oldSpecies.egg_groups)
newEggGroups = Array(newSpecies.egg_groups)
return oldEggGroups.any? { |g| newEggGroups.include?(g) }
end
def bstNotOk(newspecies, oldPokemonSpecies, bst_range = 50)
newBST = calcBaseStatsSum(newspecies)
originalBST = calcBaseStatsSum(oldPokemonSpecies)
return newBST < originalBST - bst_range || newBST > originalBST + bst_range
end
def legendaryOk_cached(oldspecies, newspecies, includeLegendaries, legendary_cache)
old_legendary = legendary_cache[oldspecies]
return legendary_cache[newspecies] if old_legendary
return true if includeLegendaries
return !legendary_cache[newspecies]
end
def egg_group_ok_cached(oldSpecies_id, newSpecies_id, egg_group_cache)
old_groups = egg_group_cache[oldSpecies_id]
new_groups = egg_group_cache[newSpecies_id]
old_groups.any? { |g| new_groups.include?(g) }
end
def bstNotOk_cached(newspecies, oldPokemonSpecies, bst_range, bst_cache)
newBST = bst_cache[newspecies]
originalBST = bst_cache[oldPokemonSpecies]
newBST < originalBST - bst_range || newBST > originalBST + bst_range
end
def gymLeaderOk(newspecies)
return true if $game_variables[VAR_CURRENT_GYM_TYPE] == -1 #not in a gym
leaderType = getLeaderType()
@@ -281,56 +308,99 @@ def Kernel.pbRandomizeTM()
end
end
def getNewSpecies(oldSpecies, bst_range = 50, ignoreRivalPlaceholder = false, maxDexNumber = PBSpecies.maxValue, includeLegendaries=true)
def getNewSpecies(oldSpecies, bst_range = 50, ignoreRivalPlaceholder = false, maxDexNumber = PBSpecies.maxValue, includeLegendaries=true, same_egg_group=false)
oldSpecies_dex = dexNum(oldSpecies)
return oldSpecies_dex if (oldSpecies_dex == Settings::RIVAL_STARTER_PLACEHOLDER_SPECIES && !ignoreRivalPlaceholder)
return oldSpecies_dex if oldSpecies_dex >= Settings::ZAPMOLCUNO_NB
if $game_switches[SWITCH_LEGENDARY_MODE]
new_species= convert_species_to_legendary(oldSpecies)
newspecies_dex = dexNum(new_species)
return newspecies_dex
new_species = convert_species_to_legendary(oldSpecies)
return dexNum(new_species)
end
newspecies_dex = rand(maxDexNumber - 1) + 1
i = 0
while bstNotOk(newspecies_dex, oldSpecies_dex, bst_range) || !(legendaryOk(oldSpecies_dex,newspecies_dex,includeLegendaries))
newspecies_dex = rand(maxDexNumber - 1) + 1
i += 1
if i % 10 == 0
bst_range += 5
validated = false
until validated
is_valid = true
is_valid = false if bstNotOk(newspecies_dex, oldSpecies_dex, bst_range)
is_valid = false unless legendaryOk(oldSpecies_dex, newspecies_dex, includeLegendaries)
is_valid = false if same_egg_group && !egg_group_ok(oldSpecies_dex, newspecies_dex)
validated = is_valid
unless validated
newspecies_dex = rand(maxDexNumber - 1) + 1
i += 1
bst_range += 5 if i % 10 == 0
end
break if i > 200
end
return newspecies_dex
end
def getNewCustomSpecies(oldSpecies, customSpeciesList, bst_range = 50, ignoreRivalPlaceholder = false,includeLegendaries=true)
#Don't use cache past this point
CUSTOM_LIST_THRESHOLD = 5000
def new_bst_cache
Hash.new { |h, sp| h[sp] = calcBaseStatsSum(sp) }
end
def new_egg_group_cache
Hash.new { |h, sp| h[sp] = Array(GameData::Species.get(sp).egg_groups) }
end
def new_legendary_cache
Hash.new { |h, sp| h[sp] = is_legendary_num(sp) }
end
def getNewCustomSpecies(oldSpecies, customSpeciesList, bst_range, ignoreRivalPlaceholder, includeLegendaries, same_egg_group,
bst_cache = nil, egg_group_cache = nil, legendary_cache = nil, fallback_cache = nil)
if $game_switches[SWITCH_LEGENDARY_MODE]
new_species= convert_species_to_legendary(oldSpecies)
echoln "CHOSEN #{get_readable_fusion_name(oldSpecies)} -> #{get_readable_fusion_name(new_species)}"
newspecies_dex = dexNum(new_species)
return newspecies_dex
return dexNum(convert_species_to_legendary(oldSpecies))
end
oldSpecies_dex = dexNum(oldSpecies)
return oldSpecies_dex if (oldSpecies_dex == Settings::RIVAL_STARTER_PLACEHOLDER_SPECIES && !ignoreRivalPlaceholder)
return oldSpecies_dex if oldSpecies_dex >= Settings::ZAPMOLCUNO_NB
i = rand(customSpeciesList.length - 1) + 1
n = 0
newspecies_dex = customSpeciesList[i]
while bstNotOk(newspecies_dex, oldSpecies_dex, bst_range) || !(legendaryOk(oldSpecies_dex,newspecies_dex,includeLegendaries))
i = rand(customSpeciesList.length - 1) #+1
newspecies_dex = customSpeciesList[i]
n += 1
if n % 10 == 0
bst_range += 5
end
#when cache not passed as argument: initialize
bst_cache ||= new_bst_cache
egg_group_cache ||= new_egg_group_cache
legendary_cache ||= new_legendary_cache
fallback_cache ||= {}
ok = lambda do |sp, range|
!bstNotOk_cached(sp, oldSpecies_dex, range, bst_cache) &&
legendaryOk_cached(oldSpecies_dex, sp, includeLegendaries, legendary_cache) &&
(!same_egg_group || egg_group_ok_cached(oldSpecies_dex, sp, egg_group_cache))
end
return newspecies_dex
# Fast path: random picks (works great when the list is big)
range = bst_range
100.times do |n|
sp = customSpeciesList.sample
return sp if ok.call(sp, range)
range += 5 if (n + 1) % 10 == 0
end
# Slow path (rare with big lists, cheap with small ones): full filter, cached per old species
key = [oldSpecies_dex, bst_range, includeLegendaries, same_egg_group]
candidates = fallback_cache[key]
if candidates.nil?
range = bst_range
candidates = []
10.times do
candidates = customSpeciesList.select { |sp| ok.call(sp, range) }
break unless candidates.empty?
range += 5
end
candidates = customSpeciesList if candidates.empty?
fallback_cache[key] = candidates
end
candidates.sample
end
def playShuffleSE(i)
@@ -351,36 +421,56 @@ end
def Kernel.pbShuffleTrainers(bst_range = 50, customsOnly = false, customsList = nil)
bst_range = pbGet(VAR_RANDOMIZER_TRAINER_BST)
if customsOnly && customsList == nil
customsOnly = false
end
customsOnly = false if customsOnly && customsList == nil
randomTrainersHash = Hash.new
# Create the bar BEFORE any heavy work so the screen updates immediately
progress_bar = ShuffleProgressBar.new(_INTL("Shuffling Trainers..."))
update_progress_bar(progress_bar, 0, 1)
trainers_data = getTrainersDataMode.list_all
trainers_data.each do |key, value|
trainer = trainers_data[key]
#echoln "------"
#echoln "Processing [#{trainer.id}#] {trainer.trainer_type} ##{trainer.real_name}"
begin
trainers_data = getTrainersDataMode.list_all
bst_cache = new_bst_cache
egg_group_cache = new_egg_group_cache
legendary_cache = new_legendary_cache
fallback_cache = {}
total = trainers_data.size
i = 0
new_party = []
for poke in trainer.pokemon
old_poke = GameData::Species.get(poke[:species]).id_number
new_poke = customsOnly ? getNewCustomSpecies(old_poke, customsList, bst_range) : getNewSpecies(old_poke, bst_range)
new_party << new_poke
end
randomTrainersHash[trainer.id] = new_party
playShuffleSE(i)
i += 1
if i % 2 == 0
n = (i.to_f / trainers.length) * 100
Kernel.pbMessageNoSound(_INTL("\\ts[]Shuffling trainers...\\n {1}%\\^", sprintf('%.2f', n), PBSpecies.maxValue))
trainers_data.each do |key, value|
trainer = trainers_data[key]
new_party = []
same_egg_group = $game_switches[SWITCH_RANDOM_TRAINERS]
for poke in trainer.pokemon
old_poke = GameData::Species.get(poke[:species]).id_number
new_poke = customsOnly ?
getNewCustomSpecies(old_poke, customsList, bst_range, false, true, same_egg_group, bst_cache, egg_group_cache, legendary_cache, fallback_cache) :
getNewSpecies(old_poke, bst_range, false, PBSpecies.maxValue, true, same_egg_group)
new_party << new_poke
end
randomTrainersHash[trainer.id] = new_party
i += 1
if i % 20 == 0 || i == total
playShuffleSE(i)
update_progress_bar(progress_bar, i, total)
end
end
ensure
progress_bar.dispose
end
$PokemonGlobal.randomTrainersHash = randomTrainersHash
$PokemonTemp.should_reshuffle_trainers = false
end
def update_progress_bar(progress_bar, nb_processed, nb_to_process)
return if !progress_bar || progress_bar.disposed?
progress_bar.progress = nb_processed.to_f / nb_to_process
progress_bar.update
end
# def Kernel.pbShuffleTrainers(bst_range = 50)
# randomTrainersHash = Hash.new
#
@@ -407,16 +497,24 @@ def Kernel.pbShuffleTrainersCustom(bst_range = 50)
bst_range = pbGet(VAR_RANDOMIZER_TRAINER_BST)
Kernel.pbMessage(_INTL("Parsing custom sprites folder...\\wtnp[20]"))
customsList = getCustomSpeciesList(true, true)
reference_sprites = $game_switches[SWITCH_RANDOM_REFERENCE_SPRITES]
if reference_sprites
reference_sprites_list = getSelectedReferenceSprites
$PokemonTemp.selected_reference_sprites = reference_sprites_list
customsList = getSelectedReferenceSpeciesList(reference_sprites_list)
else
customsList = getCustomSpeciesList(true, true)
end
Kernel.pbMessage(_INTL("{1} sprites found. Shuffling...\\wtnp[20]", customsList.length.to_s))
if customsList.length == 0
Kernel.pbMessage(_INTL("To use custom sprites, please place correctly named sprites in the /CustomBattlers folder. See readMe.txt for more information."))
Kernel.pbMessage(_INTL("Trainer Pokémon will include auto-generated sprites."))
return Kernel.pbShuffleTrainers(bst_range)
elsif customsList.length < 200
elsif customsList.length < 100 && ! $game_switches[SWITCH_RANDOM_REFERENCE_SPRITES]
if Kernel.pbConfirmMessage(_INTL("Too few custom sprites were found. This will result in a very low Pokémon variety for trainers. Would you like to disable the Custom Sprites only option?"))
Kernel.pbMessage(_INTL("Trainer Pokémon will include auto-generated sprites."))
Kernel.pbMessage(_INTL("Trainer Pokémon may also include auto-generated sprites."))
return Kernel.pbShuffleTrainers(bst_range) ##use regular shuffle if not enough sprites
end
if Kernel.pbConfirmMessage(_INTL("This will result in a very low Pokémon variety for trainers. Continue anyway?"))
@@ -426,6 +524,7 @@ def Kernel.pbShuffleTrainersCustom(bst_range = 50)
return Kernel.pbShuffleTrainers(bst_range) ##use regular shuffle if not enough sprites
end
end
Kernel.pbShuffleTrainers(bst_range, true, customsList)
end
@@ -493,7 +592,40 @@ def getCustomSpeciesList(allowOnline = true, redownload_file = false)
return speciesList
end
def getNonReferenceSprites(full_list, references_map = nil)
full_list = getCustomSpeciesList(false, false) unless full_list
references_map = map_sprites_by_reference() unless references_map
referenced = {}
references_map.each_value do |sprites|
sprites.each { |sprite| referenced[sprite] = true }
end
full_list.reject { |sprite| referenced.key?(sprite) }
return full_list
end
def getSelectedReferenceSprites
spritesList = []
path = Settings::REFERENCES_FILE_PATH
return spritesList if !File.exist?(path)
File.foreach(path) do |line|
spritename = line.chomp
spritesList << spritename if !spritename.empty?
end
return spritesList
end
def getSelectedReferenceSpeciesList(spriteslist)
species_id_list = []
for file in spriteslist
dexnum = getDexNumFromFilename(file)
species_id_list << dexnum
end
return species_id_list
end
def is_file_alt(file)
filename = file.split(".")[0]
+123 -47
View File
@@ -6,6 +6,15 @@ LEGENDARIES_LIST = [:ARTICUNO, :ZAPDOS, :MOLTRES, :MEWTWO, :MEW,
:GENESECT, :RESHIRAM, :ZEKROM, :KYUREM, :MELOETTA_A,:MELOETTA_P,
:NECROZMA, :U_NECROZMA, :DIANCIE]
class PokemonTemp
attr_accessor :should_reshuffle_trainers
attr_accessor :should_reshuffle_pokemon
attr_accessor :should_reshuffle_items
attr_accessor :should_reshuffle_gyms
end
class PokemonGlobalMetadata
attr_accessor :psuedoHash
attr_accessor :psuedoBSTHash
@@ -42,79 +51,145 @@ end
def get_randomized_bst_hash(poke_list, bst_range, show_progress = true)
bst_hash = Hash.new
msg = ""
progress_bar = ShuffleProgressBar.new(_INTL("Shuffling wild Pokémon...")) if show_progress
for i in 1..NB_POKEMON
show_shuffle_progress(i) if show_progress
show_shuffle_progress(progress_bar, i, NB_POKEMON) if progress_bar
baseStats = getBaseStatsFormattedForRandomizer(i)
statsTotal = getStatsTotal(baseStats)
targetStats_max = statsTotal + bst_range
targetStats_min = statsTotal - bst_range
max_bst_allowed = targetStats_max
min_bst_allowed = targetStats_min
#if a match, add to hash, remove from array, and cycle to next poke in dex
max_bst_allowed = statsTotal + bst_range
min_bst_allowed = statsTotal - bst_range
#only randomize legendaries to legendaries if Allow Legendaries not enabled
#
#
# if !$game_switches[SWITCH_RANDOM_WILD_LEGENDARIES]
# current_species = GameData::Species.get(i).id
# random_poke_species = GameData::Species.get(random_poke).id
# next if !legendaryOk(current_species,random_poke_species,$game_switches[SWITCH_RANDOM_WILD_LEGENDARIES])
#
# if !is_legendary(current_species)
# next if is_legendary(random_poke_species,true)
# else
# next if !is_legendary(random_poke_species,true)
# end
# end
playShuffleSE(i)
random_poke = poke_list.sample
random_poke_bst = getStatsTotal(getBaseStatsFormattedForRandomizer(random_poke))
j = 0
includeLegendaries = $game_switches[SWITCH_RANDOM_WILD_LEGENDARIES]
current_species = GameData::Species.get(i).id
random_poke_species = GameData::Species.get(random_poke).id
while (random_poke_bst <= min_bst_allowed || random_poke_bst >= max_bst_allowed) || !legendaryOk(current_species,random_poke_species,includeLegendaries)
random_poke = poke_list.sample
random_poke_species = GameData::Species.get(random_poke).id
#todo: right now, the main function uses dex numbers, but the legendaryOK check needs the ids.
# This can be a hit on performance to recalculate the ids from the dex numbers.
# The function should be optimized to just use the ids everywhere
same_egg_group = $game_switches[SWITCH_RANDOM_WILD_EGG_GROUP]
current_species_data = GameData::Species.get(i)
current_species = current_species_data.id
current_egg_groups = Array(current_species_data.egg_groups)
j = 0
loop do
random_poke = poke_list.sample
random_poke_species_data = GameData::Species.get(random_poke)
random_poke_species = random_poke_species_data.id
random_poke_bst = getStatsTotal(getBaseStatsFormattedForRandomizer(random_poke))
if same_egg_group
random_poke_egg_groups = Array(random_poke_species_data.egg_groups)
egg_group_ok = random_poke_egg_groups.any? { |g| current_egg_groups.include?(g) }
else
egg_group_ok = true
end
bst_ok = random_poke_bst > min_bst_allowed && random_poke_bst < max_bst_allowed
legendary_ok = legendaryOk(current_species, random_poke_species, includeLegendaries)
j += 1
if j % 5 == 0 #to avoid infinite loops if can't find anything
if j % 5 == 0
min_bst_allowed -= 1
max_bst_allowed += 1
end
if egg_group_ok && bst_ok && legendary_ok
msg += "#{current_species_data.species} (#{current_species_data.egg_groups})-> #{random_poke_species_data.species} (#{random_poke_species_data.egg_groups})\n"
bst_hash[i] = random_poke
break
end
end
bst_hash[i] = random_poke
end
progress_bar.dispose if progress_bar
Input.clipboard = msg if $DEBUG
return bst_hash
end
def is_legendary(dex_num,printInfo=false)
pokemon_id = getPokemon(dex_num).id
is_legendary = is_fusion_of_any(pokemon_id,LEGENDARIES_LIST)
#echoln "#{pokemon_id} is legendary? : #{is_legendary}"
#echoln "{1} ({2}) {3}",dex_num,pokemon_id,is_legendary) if printInfo
return is_legendary
LEGENDARY_LOOKUP = {}
LEGENDARIES_LIST.each { |sp| LEGENDARY_LOOKUP[sp] = true }
def is_legendary(dex_num)
if dex_num.is_a?(Integer)
return is_legendary_num(dex_num)
end
id = getPokemon(dex_num).id
if species_is_fusion(id)
LEGENDARY_LOOKUP.key?(get_body_species_from_symbol(id)) ||
LEGENDARY_LOOKUP.key?(get_head_species_from_symbol(id))
else
LEGENDARY_LOOKUP.key?(id)
end
end
def show_shuffle_progress(i)
if i % 2 == 0
n = (i.to_f / NB_POKEMON) * 100
Kernel.pbMessageNoSound(_INTL("\\ts[]Shuffling wild Pokémon...\\n {1}%\\^", sprintf('%.2f', n), NB_POKEMON))
def legendary_nums
$legendary_nums ||= begin
h = {}
LEGENDARIES_LIST.each { |sym| h[GameData::Species.get(sym).id_number] = true }
h
end
end
def is_legendary_num(dex)
if dex <= NB_POKEMON
legendary_nums.key?(dex)
else
legendary_nums.key?(dex / NB_POKEMON) || legendary_nums.key?(dex % NB_POKEMON)
end
end
def build_species_cache(species_ids)
bst_cache = {}
legendary_cache = {}
egg_group_cache = {}
species_ids.each do |id|
next if bst_cache.key?(id)
bst_cache[id] = calcBaseStatsSum(id)
legendary_cache[id] = is_legendary(id)
sp = GameData::Species.get(id)
egg_group_cache[id] = Array(sp.egg_groups)
end
[bst_cache, legendary_cache, egg_group_cache]
end
def show_shuffle_progress(progress_bar, nb_processed, nb_to_process)
return if !progress_bar || progress_bar.disposed?
progress_bar.progress = nb_processed.to_f / nb_to_process
progress_bar.update
# if i % 2 == 0
# n = (i.to_f / NB_POKEMON) * 100
# Kernel.pbMessageNoSound(_INTL("\\ts[]Shuffling wild Pokémon...\\n {1}%\\^", sprintf('%.2f', n), NB_POKEMON))
# end
end
#equivalent of common event 28 (APPLY randomizer options)
def reshuffleWithCurrentSettings()
if $PokemonTemp.should_reshuffle_pokemon
bst = pbGet(VAR_RANDOMIZER_WILD_POKE_BST)
if $game_switches[SWITCH_RANDOM_WILD_TO_FUSION]
type = 1
else
type = nil
end
Kernel.initRandomTypeArray
Kernel.pbShuffleDex(bst,type, true)
end
if $PokemonTemp.should_reshuffle_trainers
if $game_switches[SWITCH_RANDOM_TRAINERS_CUSTOMS_ONLY]
Kernel.pbShuffleTrainersCustom
else
Kernel.pbShuffleTrainers
end
end
end
##############
# randomizer shuffle
# ##############
def Kernel.pbShuffleDex(range = 50, type = 0)
def Kernel.pbShuffleDex(range = nil, type = nil, force_reshuffle=true)
range = 50 unless range
type = 0 unless type
return if $PokemonGlobal.psuedoBSTHash && !force_reshuffle
$game_switches[SWITCH_RANDOMIZED_AT_LEAST_ONCE] = true
#type 0: BST
@@ -127,7 +202,8 @@ def Kernel.pbShuffleDex(range = 50, type = 0)
if !pokemon_list #when not enough custom sprites
pokemon_list = get_pokemon_list(should_include_fusions)
end
$PokemonGlobal.psuedoBSTHash = get_randomized_bst_hash(pokemon_list, range, should_include_fusions)
$PokemonGlobal.psuedoBSTHash = get_randomized_bst_hash(pokemon_list, range, true)
$PokemonTemp.should_reshuffle_pokemon = false
end
def itemCanBeRandomized(item)