DNA Splicers WIP

This commit is contained in:
chardub
2025-05-08 22:59:30 -04:00
parent 6536fcda77
commit 4ecf97b777
287 changed files with 5291 additions and 31635 deletions

View File

@@ -0,0 +1,52 @@
class SpritesBitmapCache
@@cache = {} # Cache storage for individual sprites
@@usage_order = [] # Tracks usage order for LRU eviction
def getCache()
return @@cache
end
def get_bitmap(pif_sprite)
sprite_key = get_cache_key(pif_sprite)
if @@cache.key?(sprite_key)
mark_key_as_recently_used(sprite_key)
return @@cache[sprite_key].clone
end
return nil
end
def mark_key_as_recently_used(sprite_key)
@@usage_order.delete(sprite_key)
@@usage_order << sprite_key
end
#Keys format: [type]_B[body]H[head]_letter
# ex:
# AUTOGEN_B12H12_
# CUSTOM_B12H12_a
# BASE_BH12_a
# etc.
def get_cache_key(pif_sprite)
return "#{pif_sprite.type.to_s}_B#{pif_sprite.body_id}H#{pif_sprite.head_id}_#{pif_sprite.alt_letter}".to_sym
end
#Keys format: AUTOGEN_B12H12_a
def add(pif_sprite,bitmap)
sprite_key = get_cache_key(pif_sprite)
echoln "adding key #{sprite_key} to cache"
@@cache[sprite_key] = bitmap.clone
if @@cache.size >= Settings::SPRITE_CACHE_MAX_NB
# Evict least recently used (first in order)
oldest_key = @@usage_order.shift
@@cache.delete(oldest_key)
echoln "Evicted: #{oldest_key} from sprite cache"
end
@@usage_order << sprite_key
end
def clear
@@cache = {}
@@usage_order = []
end
end

View File

@@ -0,0 +1,98 @@
class PIFSpriteExtracter
COLUMNS = 20 # Number of columns in the spritesheet
@@spritesheet_cache = SpritesBitmapCache.new
#factor by which the sprite needs to be resized to get it to base game resolution (288x288)
def get_resize_scale
return 1
end
def load_sprite(pif_sprite,download_allowed=true)
begin
start_time = Time.now
bitmap = @@spritesheet_cache.get_bitmap(pif_sprite)
loaded_from_spritesheet=false
if !bitmap
download_new_spritesheet(pif_sprite) if should_update_spritesheet?(pif_sprite) && download_allowed
if pbResolveBitmap(getSpritesheetPath(pif_sprite))
bitmap = load_bitmap_from_spritesheet(pif_sprite)
loaded_from_spritesheet=true
@@spritesheet_cache.add(pif_sprite, bitmap)
else
return nil
end
end
sprite_bitmap = AnimatedBitmap.from_bitmap(bitmap)
sprite_bitmap.scale_bitmap(get_resize_scale())
end_time = Time.now
source = loaded_from_spritesheet ? :"spritesheet" : "cache"
echoln "Loaded sprite for <head:#{pif_sprite.head_id}, body: #{pif_sprite.body_id}, variant: #{pif_sprite.alt_letter}> from #{source} in #{end_time - start_time} seconds"
return sprite_bitmap
rescue Exception
e = $!
echoln "Error loading sprite: #{e}" if bitmap
end
end
def download_new_spritesheet(pif_sprite)
spritesheet_file = getSpritesheetPath(pif_sprite)
if download_spritesheet(pif_sprite,spritesheet_file)
$updated_spritesheets << spritesheet_file
update_downloaded_spritesheets_list()
return true
end
return false
end
def update_downloaded_spritesheets_list()
File.open(Settings::UPDATED_SPRITESHEETS_CACHE, "w") do |file|
$updated_spritesheets.each { |line| file.puts(line) }
end
end
def get_sprite_position_on_spritesheet(body_id,sprite_size,nb_column)
row = body_id / nb_column
col = body_id % nb_column
# echoln "(#{col},#{row})"
# Define the area of the sprite on the spritesheet
sprite_x_position = col * sprite_size
sprite_y_position = row * sprite_size
return sprite_x_position, sprite_y_position
end
def extract_bitmap_to_file(pif_sprite, dest_folder)
# Create the directory if it doesn't exist
Dir.mkdir(dest_folder) unless Dir.exist?(dest_folder)
single_sprite_bitmap=load_sprite(pif_sprite)
# Save the single sprite bitmap to a file
file_path = "#{dest_folder}/#{head_id}.#{body_id}.png"
single_sprite_bitmap.save_to_png(file_path)
# Dispose of the single sprite bitmap
single_sprite_bitmap.dispose
# Return the path to the saved PNG file
return file_path
end
#Implemented for base and custom, not autogen
def should_update_spritesheet?(spritesheet_file)
return false
end
def getSpritesheetPath(pif_sprite)
return nil #implement in subclasses
end
def clear_cache()
@@spritesheet_cache.clear
end
end
class PokemonGlobalMetadata
attr_accessor :current_spritepack_date
end

View File

@@ -0,0 +1,160 @@
class AutogenExtracter < PIFSpriteExtracter
SPRITESHEET_FOLDER_PATH = "Graphics\\Battlers\\spritesheets_autogen\\"
SPRITE_SIZE = 96 # Size of each sprite in the spritesheet
COLUMNS = 10 # Number of columns in the spritesheet
SHEET_WIDTH = SPRITE_SIZE * COLUMNS # 2880 pixels wide spritesheet
@instance = new
def self.instance
@@instance ||= new # If @@instance is nil, create a new instance
@@instance # Return the existing or new instance
end
def load_bitmap_from_spritesheet(pif_sprite)
body_id = pif_sprite.body_id
spritesheet_file = getSpritesheetPath(pif_sprite)
spritesheet_bitmap = AnimatedBitmap.new(spritesheet_file).bitmap
# Extract individual sprite
sprite_x_position, sprite_y_position = get_sprite_position_on_spritesheet(body_id, SPRITE_SIZE, COLUMNS)
src_rect = Rect.new(sprite_x_position, sprite_y_position, SPRITE_SIZE, SPRITE_SIZE)
bitmap = Bitmap.new(SPRITE_SIZE, SPRITE_SIZE)
bitmap.blt(0, 0, spritesheet_bitmap, src_rect)
# Dispose of spritesheet if it's no longer needed
spritesheet_bitmap.dispose
return bitmap
end
def getSpritesheetPath(pif_sprite)
head_id = pif_sprite.head_id
return "#{SPRITESHEET_FOLDER_PATH}#{head_id}.png"
end
def get_resize_scale
return 3
end
#
# # Check cache before loading from disk
# sprite_bitmap = @@spritesheet_cache.fetch(pif_sprite) do
# # Load spritesheet from disk if necessary
# echoln "Loading spritesheet from disk: #{spritesheet_file}"
# spritesheet_bitmap = AnimatedBitmap.new(spritesheet_file).bitmap
#
# # Extract individual sprite
# sprite_x_position, sprite_y_position = get_sprite_position_on_spritesheet(body_id, SPRITE_SIZE, COLUMNS)
# src_rect = Rect.new(sprite_x_position, sprite_y_position, SPRITE_SIZE, SPRITE_SIZE)
#
# sprite = Bitmap.new(SPRITE_SIZE, SPRITE_SIZE)
# sprite.blt(0, 0, spritesheet_bitmap, src_rect)
#
# # Dispose of spritesheet if it's no longer needed
# spritesheet_bitmap.dispose
#
# sprite
# end
# animatedBitmap = AnimatedBitmap.from_bitmap(sprite_bitmap)
#
# end_time = Time.now
# echoln "finished load sprite in #{end_time - start_time} seconds"
# echoln animatedBitmap
# return animatedBitmap
# end
def load_sprite_with_spritesheet_cache(pif_sprite)
start_time = Time.now
head_id = pif_sprite.head_id
body_id = pif_sprite.body_id
spritesheet_file = "#{SPRITESHEET_FOLDER_PATH}#{head_id}.png"
# Check cache before loading from disk
spritesheet_bitmap = @@spritesheet_cache.fetch(spritesheet_file) do
echoln "Loading spritesheet from disk: #{spritesheet_file}"
AnimatedBitmap.new(spritesheet_file).bitmap
end
sprite_x_position, sprite_y_position = get_sprite_position_on_spritesheet(body_id, SPRITE_SIZE, COLUMNS)
src_rect = Rect.new(sprite_x_position, sprite_y_position, SPRITE_SIZE, SPRITE_SIZE)
sprite_bitmap = Bitmap.new(SPRITE_SIZE, SPRITE_SIZE)
sprite_bitmap.blt(0, 0, spritesheet_bitmap, src_rect)
#spritesheet_bitmap.dispose # Dispose since not needed
animatedBitmap = AnimatedBitmap.from_bitmap(sprite_bitmap)
end_time = Time.now
echoln "finished load sprite in #{end_time - start_time} seconds"
return animatedBitmap
end
end
# def extract_bitmap_to_file(head_id, body_id, folder)
# # Create the directory if it doesn't exist
# Dir.mkdir(folder) unless Dir.exist?(folder)
#
# # Load the entire spritesheet
# spritesheet_file = "#{SPRITESHEET_FOLDER_PATH}#{head_id}.png"
# spritesheet_bitmap = AnimatedBitmap.new(spritesheet_file).bitmap
#
# # Calculate the 0-based row and column from the sprite index
# zero_index = body_id - 1
# row = zero_index / COLUMNS
# col = zero_index % COLUMNS
#
# # Define the area of the sprite on the spritesheet
# sprite_x_position = col * SPRITE_SIZE
# sprite_y_position = row * SPRITE_SIZE
#
# # Create a new bitmap for the single sprite
# single_sprite_bitmap = Bitmap.new(SPRITE_SIZE, SPRITE_SIZE)
# single_sprite_bitmap.blt(0, 0, spritesheet_bitmap, Rect.new(sprite_x_position, sprite_y_position, SPRITE_SIZE, SPRITE_SIZE))
#
# # Dispose of the spritesheet bitmap if its no longer needed
# spritesheet_bitmap.dispose
#
# # Save the single sprite bitmap to a file
# file_path = "#{folder}/#{head_id}.#{body_id}.png"
# single_sprite_bitmap.save_to_png(file_path)
#
# # Dispose of the single sprite bitmap
# single_sprite_bitmap.dispose
#
# # Return the path to the saved PNG file
# return file_path
# end
#end
#
#
# class SpritesBitmapCache
# @@cache = {} # Cache storage for individual sprites
# @@usage_order = [] # Tracks usage order for LRU eviction
#
# def self.fetch(pif_sprite)
# sprite_key = "B#{pif_sprite.body_id}H#{pif_sprite.head_id}".to_sym
# if @@cache.key?(sprite_key)
# # Move key to the end to mark it as recently used
# @@usage_order.delete(sprite_key)
# @@usage_order << sprite_key
# return @@cache[sprite_key]
# end
#
# # Load sprite via block if not found in cache
# sprite_bitmap = yield
#
# if @@cache.size >= Settings::SPRITE_CACHE_MAX_NB
# # Evict least recently used (first in order)
# oldest_key = @@usage_order.shift
# @@cache.delete(oldest_key)
# echoln "Evicted: #{oldest_key} from sprite cache"
# end
#
# # Add new sprite to cache and track its usage
# @@cache[sprite_key] = sprite_bitmap
# @@usage_order << sprite_key
# sprite_bitmap
# echoln @@cache
# end
# end

View File

@@ -0,0 +1,61 @@
class BaseSpriteExtracter < PIFSpriteExtracter
@instance = new
def self.instance
@@instance ||= new # If @@instance is nil, create a new instance
@@instance # Return the existing or new instance
end
SPRITESHEET_FOLDER_PATH = "Graphics/CustomBattlers/spritesheets/spritesheets_base/"
SPRITE_SIZE = 96 # Original sprite size
NB_COLUMNS_BASESPRITES = 10
SHEET_WIDTH = SPRITE_SIZE * NB_COLUMNS_BASESPRITES # 2880 pixels wide spritesheet
def load_bitmap_from_spritesheet(pif_sprite)
alt_letter = pif_sprite.alt_letter
spritesheet_file = getSpritesheetPath(pif_sprite)
spritesheet_bitmap = AnimatedBitmap.new(spritesheet_file).bitmap
letter_index = letters_to_index(alt_letter)
sprite_x_position, sprite_y_position = get_sprite_position_on_spritesheet(letter_index, SPRITE_SIZE, NB_COLUMNS_BASESPRITES)
src_rect = Rect.new(sprite_x_position, sprite_y_position, SPRITE_SIZE, SPRITE_SIZE)
sprite_bitmap = Bitmap.new(SPRITE_SIZE, SPRITE_SIZE)
sprite_bitmap.blt(0, 0, spritesheet_bitmap, src_rect)
spritesheet_bitmap.dispose # Dispose since not needed
return sprite_bitmap
end
def letters_to_index(letters)
letters = letters.downcase # Ensure input is case-insensitive
index = 0
letters.each_char do |char|
index = index * 26 + (char.ord - 'a'.ord + 1)
end
#echoln "index: #{index}"
return index
end
def load_sprite_directly(head_id, body_id, alt_letter = "")
load_sprite(PIFSprite.new(:CUSTOM, head_id, body_id, alt_letter))
end
def getSpritesheetPath(pif_sprite)
dex_number = getDexNumberForSpecies(pif_sprite.head_id)
return "#{SPRITESHEET_FOLDER_PATH}#{dex_number}.png"
end
def should_update_spritesheet?(pif_sprite)
return false if !$updated_spritesheets
return false if !downloadAllowed?()
return false if requestRateExceeded?(Settings::CUSTOMSPRITES_RATE_LOG_FILE,Settings::CUSTOMSPRITES_ENTRIES_RATE_TIME_WINDOW,Settings::CUSTOMSPRITES_RATE_MAX_NB_REQUESTS,false)
spritesheet_file = getSpritesheetPath(pif_sprite)
return true if !pbResolveBitmap(spritesheet_file)
return !$updated_spritesheets.include?(spritesheet_file)
end
def get_resize_scale
return 3
end
end

View File

@@ -0,0 +1,275 @@
class BattleSpriteLoader
def initialize
@download_allowed = true
end
def load_pif_sprite_directly(pif_sprite)
if pif_sprite.local_path && pbResolveBitmap(pif_sprite.local_path)
return AnimatedBitmap.new(pif_sprite.local_path)
end
extractor = get_sprite_extractor_instance(pif_sprite.type)
return extractor.load_sprite(pif_sprite)
end
#random alt
def load_pif_sprite(pif_sprite)
case pif_sprite.type
when :CUSTOM, :AUTOGEN
load_fusion_sprite(pif_sprite.head_id, pif_sprite.body_id)
when :BASE
load_base_sprite(pif_sprite.head_id)
end
end
# Only preloads if the pokemon's sprite has been assigned an alt letter
def preload_sprite_from_pokemon(pokemon)
return if !pokemon
substitution_id = get_sprite_substitution_id_from_dex_number(pokemon.species)
# echoln substitution_id
# echoln $PokemonGlobal.alt_sprite_substitutions
pif_sprite = $PokemonGlobal.alt_sprite_substitutions[substitution_id] if $PokemonGlobal
if !pif_sprite
pif_sprite = get_pif_sprite_from_species(pokemon.species)
end
preload(pif_sprite)
end
#loads a sprite into cache without actually returning it
# Does not download spritesheet
def preload(pif_sprite)
echoln "preloading"
previous_download_allowed = @download_allowed
@download_allowed = false
load_pif_sprite(pif_sprite)
@download_allowed = previous_download_allowed
end
def clear_sprites_cache(type)
extractor = get_sprite_extractor_instance(type)
extractor.clear_cache
end
def load_from_dex_number(dex_number)
if dex_number > NB_POKEMON
if dex_number > ZAPMOLCUNO_NB #Triple Fusion
return load_triple_fusion_sprite(dex_number)
else
#Regular fusion
body_id = getBodyID(dex_number)
head_id = getHeadID(dex_number, body_id)
return load_fusion_sprite(head_id, body_id)
end
else
#base pokemon
return load_base_sprite(dex_number)
end
end
def obtain_fusion_pif_sprite(head_id,body_id)
$PokemonGlobal.alt_sprite_substitutions = {} unless $PokemonGlobal.alt_sprite_substitutions
substitution_id = get_sprite_substitution_id_for_fusion(head_id, body_id)
pif_sprite = $PokemonGlobal.alt_sprite_substitutions[substitution_id] if $PokemonGlobal
#pif_sprite.dump_info if pif_sprite
if !pif_sprite
pif_sprite = select_new_pif_fusion_sprite(head_id, body_id)
local_path = check_for_local_sprite(pif_sprite)
if local_path
pif_sprite.local_path = local_path
pif_sprite.type = :CUSTOM
end
substitution_id = get_sprite_substitution_id_for_fusion(head_id, body_id)
$PokemonGlobal.alt_sprite_substitutions[substitution_id] = pif_sprite if $PokemonGlobal
end
return pif_sprite
end
def load_fusion_sprite(head_id, body_id)
pif_sprite = obtain_fusion_pif_sprite(head_id,body_id)
if pif_sprite.local_path
return AnimatedBitmap.new(pif_sprite.local_path)
end
extractor = get_sprite_extractor_instance(pif_sprite.type)
loaded_sprite = extractor.load_sprite(pif_sprite, @download_allowed)
if !loaded_sprite
loaded_sprite = handle_unloaded_sprites(extractor,pif_sprite)
end
return loaded_sprite
end
def load_base_sprite(dex_number)
substitution_id = get_sprite_substitution_id_from_dex_number(dex_number)
pif_sprite = $PokemonGlobal.alt_sprite_substitutions[substitution_id] if $PokemonGlobal
if !pif_sprite
pif_sprite = select_new_pif_base_sprite(dex_number)
$PokemonGlobal.alt_sprite_substitutions[substitution_id] = pif_sprite if $PokemonGlobal
end
if pif_sprite.local_path
return AnimatedBitmap.new(pif_sprite.local_path)
end
extractor = get_sprite_extractor_instance(pif_sprite.type)
loaded_sprite = extractor.load_sprite(pif_sprite)
if !loaded_sprite
loaded_sprite = handle_unloaded_sprites(extractor,pif_sprite)
end
return loaded_sprite
end
def handle_unloaded_sprites(extractor,pif_sprite)
if(extractor.is_a?(CustomSpriteExtracter)) #Custom failed to load, load an autogen (which should always be there)
new_extractor = get_sprite_extractor_instance(:AUTOGEN)
return new_extractor.load_sprite(pif_sprite)
else
$Trainer.seen_qmarks_sprite=true if $Trainer
#If autogen or base sprite aren't able to load a sprite then we have nothing else to load -> show a ? instead.
return AnimatedBitmap.new(Settings::DEFAULT_SPRITE_PATH)
end
end
#Always loaded from local individual sprites
def load_triple_fusion_sprite(dex_number)
sprite_path = getSpecialSpriteName(dex_number)
return AnimatedBitmap.new(sprite_path)
end
def get_sprite_extractor_instance(type)
case type
when :AUTOGEN
return AutogenExtracter.instance
when :CUSTOM
return CustomSpriteExtracter.instance
when :BASE
return BaseSpriteExtracter.instance
else
raise ArgumentError, "Unknown sprite type: #{type}"
end
end
def check_for_local_sprite(pif_sprite)
return pif_sprite.local_path if pif_sprite.local_path
if pif_sprite.type == :BASE
sprite_path = "#{Settings::CUSTOM_BASE_SPRITES_FOLDER}#{pif_sprite.head_id}#{pif_sprite.alt_letter}.png"
else
sprite_path = "#{Settings::CUSTOM_BATTLERS_FOLDER_INDEXED}#{pif_sprite.head_id}/#{pif_sprite.head_id}.#{pif_sprite.body_id}#{pif_sprite.alt_letter}.png"
end
return pbResolveBitmap(sprite_path)
end
def get_pif_sprite_from_species(species)
substitution_id = get_sprite_substitution_id_from_dex_number(species)
pif_sprite = $PokemonGlobal.alt_sprite_substitutions[substitution_id] if $PokemonGlobal
return pif_sprite if pif_sprite
species_data = GameData::Species.get(species)
if species_data.id_number <= NB_POKEMON #base pokemon
return select_new_pif_base_sprite(species_data.id_number)
else #isFusion
return select_new_pif_fusion_sprite(species_data.get_head_species, species_data.get_body_species)
end
end
#
# Flow:
# # if none found, look for custom sprite in custom spritesheet (download if can't find spritesheet or new spritepack released)
# if none found, load from autogen spritesheet
def select_new_pif_fusion_sprite(head_id, body_id)
species_symbol = "B#{body_id}H#{head_id}".to_sym
spritename = get_fusion_spritename(head_id,body_id)
customSpritesList = $game_temp.custom_sprites_list[species_symbol]
alt_letter = ""
if customSpritesList
alt_letter = get_random_alt_letter_for_custom(head_id,body_id,true)
type = :CUSTOM
type = :AUTOGEN if !alt_letter
else
type = :AUTOGEN
end
if $game_temp.forced_alt_sprites && $game_temp.forced_alt_sprites.include?(spritename)
alt_letter = $game_temp.forced_alt_sprites[spritename]
end
return PIFSprite.new(type, head_id, body_id, alt_letter)
end
def select_new_pif_base_sprite(dex_number)
random_alt = get_random_alt_letter_for_unfused(dex_number, true) #nil if no main
random_alt = "" if !random_alt
return PIFSprite.new(:BASE, dex_number, nil, random_alt)
end
#todo refactor by using get_triple_fusion_components()
def getSpecialSpriteName(dexNum)
base_path = "Graphics/Battlers/special/"
case dexNum
when Settings::ZAPMOLCUNO_NB
return sprintf(base_path + "144.145.146")
when Settings::ZAPMOLCUNO_NB + 1
return sprintf(base_path + "144.145.146")
when Settings::ZAPMOLCUNO_NB + 2
return sprintf(base_path + "243.244.245")
when Settings::ZAPMOLCUNO_NB + 3
return sprintf(base_path +"340.341.342")
when Settings::ZAPMOLCUNO_NB + 4
return sprintf(base_path +"343.344.345")
when Settings::ZAPMOLCUNO_NB + 5
return sprintf(base_path +"349.350.351")
when Settings::ZAPMOLCUNO_NB + 6
return sprintf(base_path +"151.251.381")
when Settings::ZAPMOLCUNO_NB + 11
return sprintf(base_path +"150.348.380")
#starters
when Settings::ZAPMOLCUNO_NB + 7
return sprintf(base_path +"3.6.9")
when Settings::ZAPMOLCUNO_NB + 8
return sprintf(base_path +"154.157.160")
when Settings::ZAPMOLCUNO_NB + 9
return sprintf(base_path +"278.281.284")
when Settings::ZAPMOLCUNO_NB + 10
return sprintf(base_path +"318.321.324")
#starters prevos
when Settings::ZAPMOLCUNO_NB + 12
return sprintf(base_path +"1.4.7")
when Settings::ZAPMOLCUNO_NB + 13
return sprintf(base_path +"2.5.8")
when Settings::ZAPMOLCUNO_NB + 14
return sprintf(base_path +"152.155.158")
when Settings::ZAPMOLCUNO_NB + 15
return sprintf(base_path +"153.156.159")
when Settings::ZAPMOLCUNO_NB + 16
return sprintf(base_path +"276.279.282")
when Settings::ZAPMOLCUNO_NB + 17
return sprintf(base_path +"277.280.283")
when Settings::ZAPMOLCUNO_NB + 18
return sprintf(base_path +"316.319.322")
when Settings::ZAPMOLCUNO_NB + 19
return sprintf(base_path +"317.320.323")
when Settings::ZAPMOLCUNO_NB + 20 #birdBoss Left
return sprintf(base_path +"invisible")
when Settings::ZAPMOLCUNO_NB + 21 #birdBoss middle
return sprintf(base_path + "144.145.146")
when Settings::ZAPMOLCUNO_NB + 22 #birdBoss right
return sprintf(base_path +"invisible")
when Settings::ZAPMOLCUNO_NB + 23 #sinnohboss left
return sprintf(base_path +"invisible")
when Settings::ZAPMOLCUNO_NB + 24 #sinnohboss middle
return sprintf(base_path +"343.344.345")
when Settings::ZAPMOLCUNO_NB + 25 #sinnohboss right
return sprintf(base_path +"invisible")
when Settings::ZAPMOLCUNO_NB + 25 #cardboard
return sprintf(base_path +"invisible")
when Settings::ZAPMOLCUNO_NB + 26 #cardboard
return sprintf(base_path + "cardboard")
when Settings::ZAPMOLCUNO_NB + 27 #Triple regi
return sprintf(base_path + "447.448.449")
#Triple Kalos 1
when Settings::ZAPMOLCUNO_NB + 28
return sprintf(base_path + "479.482.485")
when Settings::ZAPMOLCUNO_NB + 29
return sprintf(base_path + "480.483.486")
when Settings::ZAPMOLCUNO_NB + 30
return sprintf(base_path + "481.484.487")
else
return sprintf(base_path + "000")
end
end
end

View File

@@ -0,0 +1,106 @@
class CustomSpriteExtracter < PIFSpriteExtracter
@instance = new
def self.instance
@@instance ||= new # If @@instance is nil, create a new instance
@@instance # Return the existing or new instance
end
SPRITESHEET_FOLDER_PATH = "Graphics/CustomBattlers/spritesheets/spritesheets_custom/"
SPRITE_SIZE = 96 # Original sprite size
SHEET_WIDTH = SPRITE_SIZE * COLUMNS # 2880 pixels wide spritesheet
def load_bitmap_from_spritesheet(pif_sprite)
body_id = pif_sprite.body_id
spritesheet_file = getSpritesheetPath(pif_sprite)
spritesheet_bitmap = AnimatedBitmap.new(spritesheet_file).bitmap
sprite_x_position,sprite_y_position =get_sprite_position_on_spritesheet(body_id,SPRITE_SIZE,COLUMNS)
src_rect = Rect.new(sprite_x_position, sprite_y_position, SPRITE_SIZE, SPRITE_SIZE)
sprite_bitmap = Bitmap.new(SPRITE_SIZE, SPRITE_SIZE)
sprite_bitmap.blt(0, 0, spritesheet_bitmap, src_rect)
spritesheet_bitmap.dispose # Dispose since not needed
return sprite_bitmap
end
def load_sprite_to_file(pif_sprite)
head_id = pif_sprite.head_id
body_id = pif_sprite.body_id
alt_letter = pif_sprite.alt_letter
base_folder = "#{Settings::CUSTOM_BATTLERS_FOLDER_INDEXED}#{head_id}/"
individualSpriteFile = "#{base_folder}#{head_id}.#{body_id}#{alt_letter}.png"
if !pbResolveBitmap(individualSpriteFile)
animatedBitmap = load_sprite_from_spritesheet(pif_sprite)
Dir.mkdir(base_folder) unless Dir.exist?(base_folder)
animatedBitmap.bitmap.save_to_png(individualSpriteFile)
end
return AnimatedBitmap.new(individualSpriteFile)
end
def getSpritesheetPath(pif_sprite)
alt_letter = pif_sprite.alt_letter
head_id = pif_sprite.head_id
return "#{SPRITESHEET_FOLDER_PATH}#{head_id}/#{head_id}#{alt_letter}.png"
end
def should_update_spritesheet?(pif_sprite)
return false if !$updated_spritesheets
return false if !downloadAllowed?()
return false if requestRateExceeded?(Settings::CUSTOMSPRITES_RATE_LOG_FILE,Settings::CUSTOMSPRITES_ENTRIES_RATE_TIME_WINDOW,Settings::CUSTOMSPRITES_RATE_MAX_NB_REQUESTS,false)
spritesheet_file = getSpritesheetPath(pif_sprite)
return true if !pbResolveBitmap(spritesheet_file)
return !$updated_spritesheets.include?(spritesheet_file)
end
def load_sprite_directly(head_id,body_id,alt_letter="")
load_sprite(PIFSprite.new(:CUSTOM,head_id,body_id,alt_letter))
end
def get_resize_scale
return 3
end
#
# def extract_bitmap_to_file(head_id, body_id, alt_letter, folder)
# # Create the directory if it doesn't exist
# Dir.mkdir(folder) unless Dir.exist?(folder)
#
# # Load the entire spritesheet
# spritesheet_file = "#{SPRITESHEET_FOLDER_PATH}#{head_id}\\#{head_id}#{alt_letter}.png"
# spritesheet_bitmap = AnimatedBitmap.new(spritesheet_file).bitmap
#
# # Calculate the 0-based row and column from the sprite index
# index = body_id
# row = index / COLUMNS
# col = index % COLUMNS
#
# # Define the area of the sprite on the spritesheet
# sprite_x_position = col * SPRITE_SIZE
# sprite_y_position = row * SPRITE_SIZE
#
# # Create a new bitmap for the sprite at its original size
# sprite_bitmap = Bitmap.new(SPRITE_SIZE, SPRITE_SIZE)
#
# # Copy the sprite from the spritesheet to the new bitmap
# src_rect = Rect.new(sprite_x_position, sprite_y_position, SPRITE_SIZE, SPRITE_SIZE)
# sprite_bitmap.blt(0, 0, spritesheet_bitmap, src_rect)
#
# # Dispose of the spritesheet bitmap if its no longer needed
# spritesheet_bitmap.dispose
#
# # Save the sprite bitmap to a file
# file_path = "#{folder}/#{head_id}.#{body_id}.png"
# sprite_bitmap.save_to_png(file_path)
#
# # Dispose of the sprite bitmap
# sprite_bitmap.dispose
#
# # Return the path to the saved PNG file
# return file_path
# end
end

View File

@@ -0,0 +1,403 @@
module GameData
class Species
def self.sprite_bitmap_from_pokemon(pkmn, back = false, species = nil)
species = pkmn.species if !species
species = GameData::Species.get(species).id_number # Just to be sure it's a number
return self.egg_sprite_bitmap(species, pkmn.form) if pkmn.egg?
if back
ret = self.back_sprite_bitmap(species, pkmn.shiny?, pkmn.bodyShiny?, pkmn.headShiny?)
else
ret = self.front_sprite_bitmap(species, pkmn.shiny?, pkmn.bodyShiny?, pkmn.headShiny?)
end
ret.scale_bitmap(pkmn.sprite_scale) if ret #for pokemon with size differences
return ret
end
def self.sprite_bitmap_from_pokemon_id(id, back = false, shiny = false, bodyShiny = false, headShiny = false)
if back
ret = self.back_sprite_bitmap(id, shiny, bodyShiny, headShiny)
else
ret = self.front_sprite_bitmap(id, shiny, bodyShiny, headShiny)
end
return ret
end
MAX_SHIFT_VALUE = 360
MINIMUM_OFFSET = 40
ADDITIONAL_OFFSET_WHEN_TOO_CLOSE = 40
MINIMUM_DEX_DIF = 20
def self.calculateShinyHueOffset(dex_number, isBodyShiny = false, isHeadShiny = false)
if dex_number <= NB_POKEMON
if SHINY_COLOR_OFFSETS[dex_number]
return SHINY_COLOR_OFFSETS[dex_number]
end
body_number = dex_number
head_number = dex_number
else
body_number = getBodyID(dex_number)
head_number = getHeadID(dex_number, body_number)
end
if isBodyShiny && isHeadShiny && SHINY_COLOR_OFFSETS[body_number] && SHINY_COLOR_OFFSETS[head_number]
offset = SHINY_COLOR_OFFSETS[body_number] + SHINY_COLOR_OFFSETS[head_number]
elsif isHeadShiny && SHINY_COLOR_OFFSETS[head_number]
offset = SHINY_COLOR_OFFSETS[head_number]
elsif isBodyShiny && SHINY_COLOR_OFFSETS[body_number]
offset = SHINY_COLOR_OFFSETS[body_number]
else
offset = calculateShinyHueOffsetDefaultMethod(body_number, head_number, dex_number, isBodyShiny, isHeadShiny)
end
return offset
end
def self.calculateShinyHueOffsetDefaultMethod(body_number, head_number, dex_number, isBodyShiny = false, isHeadShiny = false)
dex_offset = dex_number
#body_number = getBodyID(dex_number)
#head_number=getHeadID(dex_number,body_number)
dex_diff = (body_number - head_number).abs
if isBodyShiny && isHeadShiny
dex_offset = dex_number
elsif isHeadShiny
dex_offset = head_number
elsif isBodyShiny
dex_offset = dex_diff > MINIMUM_DEX_DIF ? body_number : body_number + ADDITIONAL_OFFSET_WHEN_TOO_CLOSE
end
offset = dex_offset + Settings::SHINY_HUE_OFFSET
offset /= MAX_SHIFT_VALUE if offset > NB_POKEMON
offset = MINIMUM_OFFSET if offset < MINIMUM_OFFSET
offset = MINIMUM_OFFSET if (MAX_SHIFT_VALUE - offset).abs < MINIMUM_OFFSET
offset += pbGet(VAR_SHINY_HUE_OFFSET) #for testing - always 0 during normal gameplay
return offset
end
def self.getAutogenSprite(head_id, body_id)
end
# species can be either a number, a Species objet of a symbol
def self.front_sprite_bitmap(species, isShiny = false, bodyShiny = false, headShiny = false)
dex_number = getDexNumberForSpecies(species)
if species.is_a?(Species)
dex_number = species.id_number
end
spriteLoader = BattleSpriteLoader.new
if isFusion(dex_number)
body_id = getBodyID(dex_number)
head_id = getHeadID(dex_number, body_id)
sprite = spriteLoader.load_fusion_sprite(head_id,body_id)
else
if isTripleFusion?(dex_number)
sprite = spriteLoader.load_triple_fusion_sprite(dex_number)
else
sprite = spriteLoader.load_base_sprite(dex_number)
end
end
if isShiny
sprite.shiftColors(self.calculateShinyHueOffset(dex_number, bodyShiny, headShiny))
end
return sprite
end
# def self.front_sprite_bitmap(dex_number, isShiny = false, bodyShiny = false, headShiny = false)
# # body_id = getBodyID(dex_number)
# # head_id = getHeadID(dex_number, body_id)
# # return getAutogenSprite(head_id,body_id)
#
# #la méthode est utilisé ailleurs avec d'autres arguments (gender, form, etc.) mais on les veut pas
# if dex_number.is_a?(Symbol)
# dex_number = GameData::Species.get(dex_number).id_number
# end
# filename = self.sprite_filename(dex_number)
# sprite = (filename) ? AnimatedBitmap.new(filename) : nil
# if isShiny
# sprite.shiftColors(self.calculateShinyHueOffset(dex_number, bodyShiny, headShiny))
# end
# return sprite
# end
def self.back_sprite_bitmap(dex_number, isShiny = false, bodyShiny = false, headShiny = false)
# filename = self.sprite_filename(dex_number)
# sprite = (filename) ? AnimatedBitmap.new(filename) : nil
# if isShiny
# sprite.shiftColors(self.calculateShinyHueOffset(dex_number, bodyShiny, headShiny))
# end
# return sprite
sprite = self.front_sprite_bitmap(dex_number,isShiny,bodyShiny,headShiny)
return sprite#.mirror
end
def self.egg_sprite_bitmap(dex_number, form = nil)
filename = self.egg_sprite_filename(dex_number, form)
return (filename) ? AnimatedBitmap.new(filename) : nil
end
end
end
# def self.sprite_filename(dex_number)
# #dex_number = GameData::NAT_DEX_MAPPING[dex_number] ? GameData::NAT_DEX_MAPPING[dex_number] : dex_number
# if dex_number.is_a?(GameData::Species)
# dex_number = dex_number.id_number
# end
# if dex_number.is_a?(Symbol)
# dex_number = getDexNumberForSpecies(dex_number)
# end
# return nil if dex_number == nil
# if dex_number <= Settings::NB_POKEMON
# return get_unfused_sprite_path(dex_number)
# else
# if dex_number >= Settings::ZAPMOLCUNO_NB
# specialPath = getSpecialSpriteName(dex_number)
# return pbResolveBitmap(specialPath)
# head_id = nil
# else
# body_id = getBodyID(dex_number)
# head_id = getHeadID(dex_number, body_id)
# return get_fusion_sprite_path(head_id, body_id)
# # folder = head_id.to_s
# # filename = sprintf("%s.%s.png", head_id, body_id)
# end
# end
# # customPath = pbResolveBitmap(Settings::CUSTOM_BATTLERS_FOLDER_INDEXED + "/" + head_id.to_s + "/" +filename)
# # customPath = download_custom_sprite(head_id,body_id)
# #
# # species = getSpecies(dex_number)
# # use_custom = customPath && !species.always_use_generated
# # if use_custom
# # return customPath
# # end
# # #return Settings::BATTLERS_FOLDER + folder + "/" + filename
# # return download_autogen_sprite(head_id,body_id)
# end
# def get_unfused_sprite_path(dex_number_id, localOnly = false)
# dex_number = dex_number_id.to_s
# folder = dex_number.to_s
# substitution_id = _INTL("{1}", dex_number)
#
# if alt_sprites_substitutions_available && $PokemonGlobal.alt_sprite_substitutions.keys.include?(substitution_id)
# substitutionPath = $PokemonGlobal.alt_sprite_substitutions[substitution_id]
# return substitutionPath if pbResolveBitmap(substitutionPath)
# end
# random_alt = get_random_alt_letter_for_unfused(dex_number, true) #nil if no main
# random_alt = "" if !random_alt || localOnly
#
#
# filename = _INTL("{1}{2}.png", dex_number,random_alt)
#
# path = Settings::CUSTOM_BASE_SPRITES_FOLDER + filename
# if pbResolveBitmap(path)
# record_sprite_substitution(substitution_id,path)
# return path
# end
# echoln "downloading main sprite #{filename}"
# downloaded_path = download_unfused_main_sprite(dex_number, random_alt) if !localOnly
# if pbResolveBitmap(downloaded_path)
# record_sprite_substitution(substitution_id,downloaded_path)
# return downloaded_path
# end
# return path
# end
def alt_sprites_substitutions_available
return $PokemonGlobal && $PokemonGlobal.alt_sprite_substitutions
end
def print_stack_trace
stack_trace = caller
stack_trace.each_with_index do |call, index|
echo("#{index + 1}: #{call}")
end
end
# def record_sprite_substitution(substitution_id, sprite_name)
# return if !$PokemonGlobal
# return if !$PokemonGlobal.alt_sprite_substitutions
# $PokemonGlobal.alt_sprite_substitutions[substitution_id] = sprite_name
# end
def add_to_autogen_cache(pokemon_id, sprite_name)
return if !$PokemonGlobal
return if !$PokemonGlobal.autogen_sprites_cache
$PokemonGlobal.autogen_sprites_cache[pokemon_id]=sprite_name
end
class PokemonGlobalMetadata
attr_accessor :autogen_sprites_cache
end
#To force a specific sprites before a battle
#
# ex:
# $PokemonTemp.forced_alt_sprites={"20.25" => "20.25a"}
#
class PokemonTemp
attr_accessor :forced_alt_sprites
end
#todo:
# DO NOT USE ANYMORE
# Replace by BattleSpriteLoader
# def get_fusion_sprite_path(head_id, body_id, localOnly=false)
# $PokemonGlobal.autogen_sprites_cache = {} if $PokemonGlobal && !$PokemonGlobal.autogen_sprites_cache
# #Todo: ça va chier si on fusionne une forme d'un pokemon avec une autre forme, mais pas un problème pour tout de suite
# form_suffix = ""
#
# #Swap path if alt is selected for this pokemon
# dex_num = getSpeciesIdForFusion(head_id, body_id)
# substitution_id = dex_num.to_s + form_suffix
#
# if alt_sprites_substitutions_available && $PokemonGlobal.alt_sprite_substitutions.keys.include?(substitution_id)
# substitutionPath= $PokemonGlobal.alt_sprite_substitutions[substitution_id]
# return substitutionPath if pbResolveBitmap(substitutionPath)
# end
#
#
# pokemon_name = _INTL("{1}.{2}",head_id, body_id)
#
# #get altSprite letter
# random_alt = get_random_alt_letter_for_custom(head_id, body_id) #nil if no main
# random_alt = "" if !random_alt
# forcingSprite=false
# if $PokemonTemp.forced_alt_sprites && $PokemonTemp.forced_alt_sprites.key?(pokemon_name)
# random_alt = $PokemonTemp.forced_alt_sprites[pokemon_name]
# forcingSprite=true
# end
#
#
# filename = _INTL("{1}{2}.png", pokemon_name, random_alt)
# #Try local custom sprite
# local_custom_path = Settings::CUSTOM_BATTLERS_FOLDER_INDEXED + head_id.to_s + "/" + filename
# if pbResolveBitmap(local_custom_path)
# record_sprite_substitution(substitution_id, local_custom_path) if !forcingSprite
# return local_custom_path
# end
# #if the game has loaded an autogen earlier, no point in trying to redownload, so load that instead
# return $PokemonGlobal.autogen_sprites_cache[substitution_id] if $PokemonGlobal && $PokemonGlobal.autogen_sprites_cache[substitution_id]
#
# #Try to download custom sprite if none found locally
# downloaded_custom = download_custom_sprite(head_id, body_id, random_alt) if !localOnly
# if downloaded_custom
# record_sprite_substitution(substitution_id, downloaded_custom) if !forcingSprite
# return downloaded_custom
# end
#
# #Try local generated sprite
# local_generated_path = Settings::BATTLERS_FOLDER + head_id.to_s + "/" + filename
# if pbResolveBitmap(local_generated_path)
# add_to_autogen_cache(substitution_id,local_generated_path)
# return local_generated_path
# end
#
# #Download generated sprite if nothing else found
# autogen_path = download_autogen_sprite(head_id, body_id) if !localOnly
# if pbResolveBitmap(autogen_path)
# add_to_autogen_cache(substitution_id,autogen_path)
# return autogen_path
# end
#
# return Settings::DEFAULT_SPRITE_PATH
# end
def get_random_alt_letter_for_custom(head_id, body_id, onlyMain = true)
spriteName = _INTL("{1}.{2}", head_id, body_id)
echoln spriteName
if onlyMain
alts_list = list_main_sprites_letters(spriteName)
return nil if alts_list.empty?
return alts_list.sample
else
alts_list = list_all_sprites_letters(spriteName)
return nil if alts_list.empty?
return alts_list.sample
end
end
def get_random_alt_letter_for_unfused(dex_num, onlyMain = true)
spriteName = _INTL("{1}", dex_num)
if onlyMain
letters_list= list_main_sprites_letters(spriteName)
else
letters_list= list_all_sprites_letters(spriteName)
end
letters_list << "" #add main sprite
return letters_list.sample
end
def list_main_sprites_letters(spriteName)
all_sprites = map_alt_sprite_letters_for_pokemon(spriteName)
main_sprites = []
all_sprites.each do |key, value|
main_sprites << key if value == "main"
end
#add temp sprites if no main sprites found
if main_sprites.empty?
all_sprites.each do |key, value|
main_sprites << key if value == "temp"
end
end
return main_sprites
end
def list_all_sprites_letters_head_body(head_id,body_id)
spriteName = _INTL("{1}.{2}", head_id, body_id)
all_sprites_map = map_alt_sprite_letters_for_pokemon(spriteName)
letters = []
all_sprites_map.each do |key, value|
letters << key
end
return letters
end
def list_all_sprites_letters(spriteName)
all_sprites_map = map_alt_sprite_letters_for_pokemon(spriteName)
letters = []
all_sprites_map.each do |key, value|
letters << key
end
return letters
end
def list_alt_sprite_letters(spriteName)
all_sprites = map_alt_sprite_letters_for_pokemon(spriteName)
alt_sprites = []
all_sprites.each do |key, value|
alt_sprites << key if value == "alt"
end
end
#ex: "1" -> "main"
# "1a" -> "alt"
def map_alt_sprite_letters_for_pokemon(spriteName)
alt_sprites = {}
File.foreach(Settings::CREDITS_FILE_PATH) do |line|
row = line.split(',')
sprite_name = row[0]
if sprite_name.start_with?(spriteName)
if sprite_name.length > spriteName.length #alt letter
letter = sprite_name[spriteName.length]
if letter.match?(/[a-zA-Z]/)
main_or_alt = row[2] ? row[2] : nil
alt_sprites[letter] = main_or_alt
end
else #letterless
main_or_alt = row[2] ? row[2].gsub("\n","") : nil
alt_sprites[""] = main_or_alt
end
end
end
return alt_sprites
end

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
class PokemonSprite
def setPokemonBitmapFromId(id, back = false, shiny = false, bodyShiny = false, headShiny = false,spriteform_body=nil,spriteform_head=nil)
@_iconbitmap.dispose if @_iconbitmap
@_iconbitmap = GameData::Species.sprite_bitmap_from_pokemon_id(id, back, shiny, bodyShiny, headShiny)
self.bitmap = (@_iconbitmap) ? @_iconbitmap.bitmap : nil
self.color = Color.new(0, 0, 0, 0)
changeOrigin
end
end

View File

@@ -0,0 +1,114 @@
# object representing a sprite which saves its position in the tileset
class PIFSprite
attr_accessor :type
attr_accessor :head_id
attr_accessor :body_id
attr_accessor :alt_letter
attr_accessor :local_path
# types:
# :AUTOGEN, :CUSTOM, :BASE
def initialize(type, head_id, body_id, alt_letter = "")
@type = type
@head_id = head_id
@body_id = body_id
@alt_letter = alt_letter
@local_path = nil
end
def dump_info()
echoln "Type: #{@type}"
echoln "Head: #{@head_id}"
echoln "Body: #{@body_id}"
echoln "Alt letter: #{@alt_letter}"
echoln "Local path: #{@local_path}"
end
def exists()
filename = get_spritesheet_path()
echoln filename
return File.file?(filename)
end
def get_spritesheet_path()
case @type
when :BASE
path = "#{BaseSpriteExtracter::SPRITESHEET_FOLDER_PATH}#{@head_id}.png"
when :CUSTOM
path = "#{CustomSpriteExtracter::SPRITESHEET_FOLDER_PATH}#{@head_id}/#{@head_id}#{@alt_letter}.png"
when :AUTOGEN
path = "#{AutogenExtracter::SPRITESHEET_FOLDER_PATH}#{@head_id}.png"
else
return nil
end
echoln path
return path
end
end
def equals(other_pif_sprite)
return @type == other_pif_sprite.type &&
@head_id == other_pif_sprite.head_id &&
@body_id == other_pif_sprite.body_id &&
@alt_letter == other_pif_sprite.alt_letter &&
@local_path == other_pif_sprite.local_path
end
# little hack for old methods that expect a filename for a sprite
def to_filename()
case @type
when :CUSTOM
return "#{@head_id}.#{@body_id}#{@alt_letter}.png"
when :AUTOGEN
return "#{@head_id}.#{@body_id}.png"
when :BASE
return "#{@head_id}#{@alt_letter}.png"
end
end
def setup_from_spritename(spritename, type)
@type = type
cleaned_name = spritename.gsub(".png", "")
if cleaned_name =~ /(\d+)\.(\d+)([a-zA-Z]*)/
head_id = $1
body_id = $2
alt_letter = $3
end
@head_id = head_id
@body_id = body_id
@alt_letter = alt_letter
end
def self.from_spritename(spritename, type)
obj = allocate
obj.send(:setup_from_spritename, spritename, type)
obj
end
def new_pif_sprite_from_dex_num(type, dexNum, alt_letter)
body_id = getBodyID(dexNum)
head_id = getHeadID(dexNum, body_id)
return PIFSprite.new(type, head_id, body_id, alt_letter)
end
def pif_sprite_from_spritename(spritename, autogen = false)
spritename = spritename.split(".png")[0] # remove the extension
if spritename =~ /^(\d+)\.(\d+)([a-zA-Z]*)$/ # Two numbers with optional letters
type = :CUSTOM
head_id = $1.to_i # Head (e.g., "1" in "1.2.png")
body_id = $2.to_i # Body (e.g., "2" in "1.2.png")
alt_letter = $3 # Optional trailing letter (e.g., "a" in "1.2a.png")
elsif spritename =~ /^(\d+)([a-zA-Z]*)$/ # One number with optional letters
type = :BASE
head_id = $1.to_i # Head (e.g., "1" in "1.png")
alt_letter = $2 # Optional trailing letter (e.g., "a" in "1a.png")
else
echoln "Invalid sprite format: #{spritename}"
return nil
end
type = :AUTOGEN if autogen
return PIFSprite.new(type, head_id, body_id, alt_letter)
end

View File

@@ -0,0 +1,82 @@
def setSpriteSubstitution(pif_sprite); end
def getSpriteSubstitutionForDex(dex_num); end
def setSpriteSubstitution(head,body); end
def set_updated_spritesheets; end
class PokemonGlobalMetadata
attr_accessor :alt_sprite_substitutions
end
def initialize_alt_sprite_substitutions()
$PokemonGlobal.alt_sprite_substitutions = {} if !$PokemonGlobal.alt_sprite_substitutions
migrate_sprites_substitutions()
end
def get_sprite_substitution_id_for_fusion(head_id, body_id)
species_symbol = "B#{body_id}H#{head_id}".to_sym
return get_sprite_substitution_id_from_dex_number(species_symbol)
end
def get_sprite_substitution_id_from_dex_number(species_symbol)
species = GameData::Species.get(species_symbol)
if species.is_fusion
substitution_id = [species.get_head_species,species.get_body_species]
else
substitution_id= species.id_number
end
return substitution_id
end
def migrate_sprites_substitutions
return if $game_switches[SWITCH_UPDATED_TO_SPRITESHEETS_SPRITES]
new_substitutions = {}
old_number_pokemon = 470
for dex_number_key in $PokemonGlobal.alt_sprite_substitutions.keys
if $PokemonGlobal.alt_sprite_substitutions[dex_number_key].is_a?(String) && can_convert_to_int?(dex_number_key)
old_dex_number = dex_number_key.to_i
if old_dex_number > old_number_pokemon #fusion
body_id = getBodyID(old_dex_number,old_number_pokemon)
head_id = getHeadID(old_dex_number,body_id,old_number_pokemon)
new_id = [head_id,body_id]
type = :CUSTOM
else
new_id = old_dex_number
head_id = old_dex_number
body_id= nil
type = :BASE
end
file_path = $PokemonGlobal.alt_sprite_substitutions[dex_number_key]
alt_letter =get_alt_letter_from_path(file_path)
pif_sprite = PIFSprite.new(type,head_id,body_id,alt_letter)
new_substitutions[new_id] = pif_sprite
end
end
$PokemonGlobal.alt_sprite_substitutions = new_substitutions
$game_switches[SWITCH_UPDATED_TO_SPRITESHEETS_SPRITES] = true
end
def can_convert_to_int?(str)
Integer(str)
true
rescue ArgumentError
false
end
def get_alt_letter_from_path(filename)
# Remove the extension
base_name = filename.sub(/\.png$/, '')
# Check the last character
last_char = base_name[-1]
if last_char.match?(/\d/) # Check if the last character is a number
alt_letter = ""
else
# Reverse the base name and capture all letters until the first number
alt_letter = base_name.reverse[/[a-zA-Z]+/].reverse
end
return alt_letter
end