mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-06 06:01:46 +00:00
1567 lines
56 KiB
Ruby
1567 lines
56 KiB
Ruby
#===============================================================================
|
|
#
|
|
#===============================================================================
|
|
class UI::PokemonSummaryMoveCursor < ChangelingSprite
|
|
BITMAPS = {
|
|
:normal => ["Graphics/UI/Summary/cursor_move", 0, 0, 252, 74],
|
|
:selected => ["Graphics/UI/Summary/cursor_move", 0, 74, 252, 74]
|
|
}
|
|
CURSOR_THICKNESS = 6
|
|
|
|
def initialize(viewport = nil, selected = false, extra_move = false)
|
|
super(0, 0, viewport)
|
|
change_bitmap((selected) ? :selected : :normal)
|
|
@extra_move = extra_move
|
|
self.z = 1600
|
|
self.visible = false
|
|
@index = 0
|
|
end
|
|
|
|
def index=(value)
|
|
@index = value
|
|
refresh_visibility
|
|
refresh_position
|
|
end
|
|
|
|
def refresh_visibility
|
|
self.visible = (@index >= 0)
|
|
end
|
|
|
|
|
|
def refresh_position
|
|
return if @index < 0
|
|
self.x = UI::PokemonSummaryVisuals::MOVE_LIST_X_DETAILED - CURSOR_THICKNESS
|
|
self.y = UI::PokemonSummaryVisuals::MOVE_LIST_Y - CURSOR_THICKNESS + (@index * UI::PokemonSummaryVisuals::MOVE_LIST_SPACING)
|
|
self.y += UI::PokemonSummaryVisuals::MOVE_LIST_OFFSET_WHEN_NEW_MOVE if @extra_move
|
|
self.y += UI::PokemonSummaryVisuals::MOVE_LIST_NEW_MOVE_SPACING if @extra_move && @index == Pokemon::MAX_MOVES
|
|
end
|
|
end
|
|
|
|
#===============================================================================
|
|
#
|
|
#===============================================================================
|
|
class UI::PokemonSummaryRibbonCursor < ChangelingSprite
|
|
attr_reader :index
|
|
|
|
BITMAPS = {
|
|
:normal => ["Graphics/UI/Summary/cursor_ribbon", 0, 0, 68, 68],
|
|
:selected => ["Graphics/UI/Summary/cursor_ribbon", 0, 68, 68, 68]
|
|
}
|
|
CURSOR_THICKNESS = 2
|
|
|
|
def initialize(viewport = nil, selected = false)
|
|
super(0, 0, viewport)
|
|
change_bitmap((selected) ? :selected : :normal)
|
|
self.z = 1600
|
|
self.visible = false
|
|
@index = 0
|
|
end
|
|
|
|
def index=(value)
|
|
@index = value
|
|
refresh_visibility
|
|
refresh_position
|
|
end
|
|
|
|
def refresh_visibility
|
|
self.visible = (@index >= 0 && @index < UI::PokemonSummaryVisuals::RIBBON_COLUMNS * UI::PokemonSummaryVisuals::RIBBON_ROWS)
|
|
end
|
|
|
|
def refresh_position
|
|
return if @index < 0
|
|
cols = UI::PokemonSummaryVisuals::RIBBON_COLUMNS
|
|
offset_x = UI::PokemonSummaryVisuals::RIBBON_SIZE[0] + UI::PokemonSummaryVisuals::RIBBON_SPACING_X
|
|
offset_y = UI::PokemonSummaryVisuals::RIBBON_SIZE[1] + UI::PokemonSummaryVisuals::RIBBON_SPACING_Y
|
|
self.x = UI::PokemonSummaryVisuals::RIBBON_X - CURSOR_THICKNESS + ((self.index % cols) * offset_x)
|
|
self.y = UI::PokemonSummaryVisuals::RIBBON_Y - CURSOR_THICKNESS + ((self.index / cols) * offset_y)
|
|
end
|
|
end
|
|
|
|
#===============================================================================
|
|
#
|
|
#===============================================================================
|
|
class UI::PokemonSummaryVisuals < UI::BaseVisuals
|
|
GRAPHICS_FOLDER = "Summary/" # Subfolder in Graphics/UI
|
|
TEXT_COLOR_THEMES = { # These color themes are added to @sprites[:overlay]
|
|
:default => [Color.new(248, 248, 248), Color.new(104, 104, 104)], # Base and shadow colour
|
|
:white => [Color.new(248, 248, 248), Color.new(104, 104, 104)],
|
|
:raised_stat => [Color.new(248, 248, 248), Color.new(136, 96, 72)],
|
|
:lowered_stat => [Color.new(248, 248, 248), Color.new(64, 120, 152)],
|
|
:black => [Color.new(64, 64, 64), Color.new(176, 176, 176)],
|
|
:faded => [Color.new(192, 200, 208), Color.new(208, 216, 224)],
|
|
:male => [Color.new(24, 112, 216), Color.new(136, 168, 208)],
|
|
:female => [Color.new(248, 56, 32), Color.new(224, 152, 144)],
|
|
:shiny => [Color.new(248, 56, 32), Color.new(224, 152, 144)],
|
|
:pp_half => [Color.new(248, 192, 0), Color.new(144, 104, 0)],
|
|
:pp_quarter => [Color.new(248, 136, 32), Color.new(144, 72, 24)],
|
|
:pp_zero => [Color.new(248, 72, 72), Color.new(136, 48, 48)]
|
|
}
|
|
PARTY_ICONS_COUNT = 6
|
|
PARTY_ICON_SIZE = [18, 18] # In the party_icons.png graphic
|
|
MARKING_WIDTH = 16
|
|
MARKING_HEIGHT = 16
|
|
MOVE_LIST_X = 230
|
|
MOVE_LIST_X_DETAILED = MOVE_LIST_X + 32
|
|
MOVE_LIST_Y = 98
|
|
MOVE_LIST_SPACING = 64 # Y distance between top of two adjacent move areas
|
|
MOVE_LIST_OFFSET_WHEN_NEW_MOVE = -76 # Offset for y coordinate
|
|
MOVE_LIST_NEW_MOVE_SPACING = 22 # New move is separated from known moves
|
|
RIBBON_SIZE = [64, 64] # [width, height]
|
|
RIBBON_SPACING_X = 4
|
|
RIBBON_SPACING_Y = 4
|
|
RIBBON_COLUMNS = 4
|
|
RIBBON_ROWS = 3
|
|
RIBBON_X = 230 # Left edge of top left ribbon
|
|
RIBBON_Y = 78 # Top edge of top left ribbon
|
|
RIBBON_COLUMNS_GRAPHIC = 8 # In the ribbons.png graphic
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
PAGE_HANDLERS = HandlerHash.new
|
|
PAGE_HANDLERS.add(:info, {
|
|
:name => proc { next _INTL("INFO") },
|
|
:order => 10,
|
|
:icon_index => 0,
|
|
:draw => [
|
|
:draw_common_page_contents,
|
|
:draw_pokedex_number,
|
|
:draw_species,
|
|
:draw_original_trainer_details,
|
|
:draw_held_item,
|
|
:draw_exp,
|
|
:draw_shadow_pokemon_info
|
|
]
|
|
})
|
|
PAGE_HANDLERS.add(:skills, {
|
|
:name => proc { next _INTL("SKILLS") },
|
|
:order => 20,
|
|
:icon_index => 1,
|
|
:draw => [
|
|
:draw_common_page_contents,
|
|
:draw_stats,
|
|
:draw_ability
|
|
]
|
|
})
|
|
PAGE_HANDLERS.add(:moves, {
|
|
:name => proc { next _INTL("MOVES") },
|
|
:order => 30,
|
|
:icon_index => 2,
|
|
:draw => [
|
|
:draw_common_page_contents,
|
|
:draw_moves_list
|
|
]
|
|
})
|
|
PAGE_HANDLERS.add(:ribbons, {
|
|
:name => proc { next _INTL("RIBBONS") },
|
|
:order => 40,
|
|
:icon_index => 3,
|
|
:should_show => proc { |pokemon| next pokemon.numRibbons > 0 },
|
|
:draw => [
|
|
:draw_common_page_contents,
|
|
:draw_ribbon_count,
|
|
:draw_ribbons_in_grid,
|
|
:draw_ribbon_properties
|
|
]
|
|
})
|
|
PAGE_HANDLERS.add(:memo, {
|
|
:name => proc { next _INTL("TRAINER MEMO") },
|
|
:order => 50,
|
|
:icon_index => 4,
|
|
:draw => [
|
|
:draw_common_page_contents,
|
|
:draw_memo
|
|
]
|
|
})
|
|
PAGE_HANDLERS.add(:egg_memo, {
|
|
:name => proc { next _INTL("TRAINER MEMO") },
|
|
:egg_page => true,
|
|
:icon_index => 5,
|
|
:draw => [
|
|
:draw_page_name,
|
|
:draw_page_icons,
|
|
:draw_poke_ball,
|
|
:draw_pokemon_name,
|
|
:draw_markings,
|
|
:draw_egg_memo
|
|
]
|
|
})
|
|
# Pseudo-page, only used to draw different contents for the detailed moves
|
|
# page.
|
|
PAGE_HANDLERS.add(:detailed_moves, {
|
|
:icon_index => 2,
|
|
:should_show => proc { |pokemon| next false },
|
|
:draw => [
|
|
:draw_page_name,
|
|
:draw_page_icons,
|
|
:draw_pokemon_types_for_detailed_moves_page,
|
|
:draw_moves_list,
|
|
:draw_move_properties
|
|
]
|
|
})
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
def initialize(party, party_index = 0, mode = :normal, new_move = nil)
|
|
@party = party
|
|
@party_index = party_index
|
|
@pokemon = @party[@party_index]
|
|
@visible_party_length = 0
|
|
@visible_index = 0
|
|
@party.each_with_index do |pkmn, i|
|
|
next if !pkmn
|
|
@visible_party_length += 1
|
|
@visible_index = @visible_party_length - 1 if i == @party_index
|
|
end
|
|
@visible_top_index = 0
|
|
@mode = mode
|
|
@new_move = new_move # Only used if @mode is :choose_move
|
|
@page = (@mode == :choose_move) ? :moves : all_pages[0]
|
|
@move_index = (@mode == :choose_move) ? 0 : nil
|
|
super()
|
|
refresh_move_cursor if @move_index
|
|
end
|
|
|
|
def initialize_bitmaps
|
|
@bitmaps[:types] = AnimatedBitmap.new(UI_FOLDER + _INTL("types"))
|
|
@bitmaps[:markings] = AnimatedBitmap.new(graphics_folder + "markings")
|
|
@bitmaps[:numbers] = AnimatedBitmap.new(graphics_folder + "numbers")
|
|
@bitmaps[:page_icons] = AnimatedBitmap.new(graphics_folder + "page_icons")
|
|
@bitmaps[:party_icons] = AnimatedBitmap.new(graphics_folder + "party_icons")
|
|
end
|
|
|
|
def initialize_background
|
|
super
|
|
addBackgroundPlane(@sprites, :page_background, self.class::GRAPHICS_FOLDER + page_background_filename, @viewport)
|
|
@sprites[:page_background].z = 100
|
|
end
|
|
|
|
def initialize_sprites
|
|
# Pokémon sprite
|
|
@sprites[:pokemon] = PokemonSprite.new(@viewport)
|
|
@sprites[:pokemon].setOffset(PictureOrigin::CENTER)
|
|
@sprites[:pokemon].x = 112
|
|
@sprites[:pokemon].y = 208
|
|
@sprites[:pokemon].visible = (@mode != :choose_move)
|
|
@sprites[:pokemon].setPokemonBitmap(@pokemon)
|
|
# Page-specific sprites
|
|
initialize_info_page_sprites
|
|
initialize_move_page_sprites
|
|
initialize_ribbon_page_sprites
|
|
initialize_marking_sprites
|
|
end
|
|
|
|
def initialize_info_page_sprites
|
|
# Held item icon
|
|
@sprites[:held_item_icon] = ItemIconSprite.new(260, 256, @pokemon.item_id, @viewport)
|
|
@sprites[:held_item_icon].z = 200
|
|
@sprites[:held_item_icon].blankzero = true
|
|
end
|
|
|
|
def initialize_move_page_sprites
|
|
# Pokémon icon
|
|
@sprites[:pokemon_icon] = PokemonIconSprite.new(@pokemon, @viewport)
|
|
@sprites[:pokemon_icon].setOffset(PictureOrigin::CENTER)
|
|
@sprites[:pokemon_icon].x = 50
|
|
@sprites[:pokemon_icon].y = 92
|
|
@sprites[:pokemon_icon].z = 200
|
|
@sprites[:pokemon_icon].visible = (@mode == :choose_move)
|
|
# Cursor to highlight a move selected to be swapped
|
|
@sprites[:selected_move_cursor] = UI::PokemonSummaryMoveCursor.new(@viewport, true)
|
|
# Cursor to highlight the currently selected move
|
|
@sprites[:move_cursor] = UI::PokemonSummaryMoveCursor.new(@viewport, false, !@new_move.nil?)
|
|
@sprites[:move_cursor].visible = (@mode == :choose_move)
|
|
end
|
|
|
|
def initialize_ribbon_page_sprites
|
|
# Cursor to highlight a ribbon selected to be swapped
|
|
@sprites[:selected_ribbon_cursor] = UI::PokemonSummaryRibbonCursor.new(@viewport, true)
|
|
# Cursor to highlight the currently selected ribbon
|
|
@sprites[:ribbon_cursor] = UI::PokemonSummaryRibbonCursor.new(@viewport)
|
|
# Arrow to indicate more ribbons are above the ones visible when navigating ribbons
|
|
add_animated_arrow(:up_arrow, 350, 56, :up)
|
|
@sprites[:up_arrow].z = 1700
|
|
# Arrow to indicate more ribbons are below the ones visible when navigating ribbons
|
|
add_animated_arrow(:down_arrow, 350, 260, :down)
|
|
@sprites[:down_arrow].z = 1700
|
|
end
|
|
|
|
def initialize_marking_sprites
|
|
# Background image of marking panel
|
|
add_icon_sprite(:marking_bg, 254, 88, graphics_folder + "overlay_marking")
|
|
@sprites[:marking_bg].z = 1400
|
|
@sprites[:marking_bg].visible = false
|
|
# Overlay for marking panel
|
|
add_overlay(:marking_overlay)
|
|
@sprites[:marking_overlay].z = 1500
|
|
@sprites[:marking_overlay].visible = false
|
|
# Cursor to highlight the currently selected marking option
|
|
add_icon_sprite(:marking_cursor, 0, 0, graphics_folder + "cursor_marking")
|
|
@sprites[:marking_cursor].z = 1600
|
|
@sprites[:marking_cursor].visible = false
|
|
@sprites[:marking_cursor].src_rect.height = @sprites[:marking_cursor].bitmap.height / 2
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
def page_background_filename
|
|
return "bg_moves_learning" if @new_move # Intentionally first
|
|
return "bg_moves_detailed" if @move_index # Intentionally second
|
|
ret = BACKGROUND_FILENAME + "_" + @page.to_s
|
|
return ret
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
def all_pages
|
|
ret = []
|
|
PAGE_HANDLERS.each do |key, hash|
|
|
next if @pokemon.egg? != !!hash[:egg_page]
|
|
next if hash[:should_show] && !hash[:should_show].call(@pokemon)
|
|
ret.push([key, hash[:order] || 0])
|
|
end
|
|
ret.sort_by! { |val| val[1] }
|
|
ret.map! { |val| val[0] }
|
|
return ret
|
|
end
|
|
|
|
def go_to_next_page
|
|
pages = all_pages
|
|
return if pages.length == 1
|
|
page_index = pages.index(@page)
|
|
return if page_index.nil? || page_index >= pages.length - 1
|
|
@page = pages[page_index + 1]
|
|
@ribbon_offset = 0
|
|
pbSEPlay("GUI summary change page")
|
|
refresh
|
|
end
|
|
|
|
def go_to_previous_page
|
|
pages = all_pages
|
|
return if pages.length == 1
|
|
page_index = pages.index(@page)
|
|
return if page_index.nil? || page_index == 0
|
|
@page = pages[page_index - 1]
|
|
@ribbon_offset = 0
|
|
pbSEPlay("GUI summary change page")
|
|
refresh
|
|
end
|
|
|
|
def set_party_index(new_index)
|
|
return if @party_index == new_index
|
|
old_page_index = all_pages.index(@page)
|
|
# Set the new Pokémon
|
|
@party_index = new_index
|
|
@pokemon = @party[@party_index]
|
|
@visible_index = 0
|
|
@party.each_with_index do |pkmn, i|
|
|
next if !pkmn
|
|
break if i == @party_index
|
|
@visible_index += 1
|
|
end
|
|
# Want to stay on the nth page, or the closest available one
|
|
pages = all_pages
|
|
new_page_index = old_page_index.clamp(0, pages.length - 1)
|
|
@page = pages[new_page_index]
|
|
# Set the Pokémon's sprite
|
|
@sprites[:pokemon].setPokemonBitmap(@pokemon)
|
|
@ribbon_offset = 0
|
|
# Play sound effect
|
|
play_pokemon_cry
|
|
refresh
|
|
end
|
|
|
|
def play_pokemon_cry
|
|
pbSEStop
|
|
(@pokemon.egg?) ? pbSEPlay("GUI summary change page") : @pokemon.play_cry
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
def refresh
|
|
super
|
|
@sprites[:held_item_icon].visible = false
|
|
refresh_background
|
|
draw_page_contents
|
|
end
|
|
|
|
def refresh_background
|
|
@sprites[:background].setBitmap(graphics_folder + background_filename)
|
|
@sprites[:page_background].setBitmap(graphics_folder + page_background_filename)
|
|
end
|
|
|
|
def draw_page_contents
|
|
PAGE_HANDLERS[page_to_draw][:draw].each { |method| self.send(method) }
|
|
end
|
|
|
|
def page_to_draw
|
|
ret = @page
|
|
ret = :detailed_moves if showing_detailed_move_page?
|
|
return ret
|
|
end
|
|
|
|
def showing_detailed_move_page?
|
|
return !(@new_move || @move_index).nil?
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
def draw_common_page_contents
|
|
draw_page_name
|
|
draw_page_icons
|
|
draw_poke_ball
|
|
draw_pokemon_name
|
|
draw_shiny_icon
|
|
draw_pokemon_level
|
|
draw_pokerus_icon
|
|
draw_gender
|
|
draw_party_icons
|
|
draw_markings
|
|
draw_pokemon_types_common
|
|
draw_hp_numbers
|
|
draw_hp_bar
|
|
draw_status_icon
|
|
end
|
|
|
|
def draw_page_name
|
|
draw_text(PAGE_HANDLERS[@page][:name].call, 30, 22)
|
|
end
|
|
|
|
def draw_page_icons
|
|
return if @new_move
|
|
icon_width = 56
|
|
icon_height = 64
|
|
start_x = 226
|
|
start_y = 2
|
|
spacing_x = 0
|
|
# Draw all inactive page icons
|
|
pages = all_pages
|
|
pages.each_with_index do |this_page, i|
|
|
src_index = PAGE_HANDLERS[this_page][:icon_index]
|
|
next if !src_index
|
|
draw_image(@bitmaps[:page_icons], start_x + (i * (icon_width + spacing_x)), start_y,
|
|
src_index * icon_width, 0, icon_width, icon_height)
|
|
end
|
|
# Draw current page's active icon
|
|
index = pages.index(@page)
|
|
src_index = PAGE_HANDLERS[@page][:icon_index]
|
|
if src_index
|
|
draw_image(@bitmaps[:page_icons], start_x + (index * (icon_width + spacing_x)), start_y,
|
|
src_index * icon_width, icon_height, icon_width, icon_height)
|
|
end
|
|
end
|
|
|
|
# Show the Poké Ball containing the Pokémon
|
|
def draw_poke_ball
|
|
ball_filename = graphics_folder + sprintf("icon_ball_%s", @pokemon.poke_ball)
|
|
draw_image(ball_filename, 6, 60)
|
|
end
|
|
|
|
def draw_pokemon_name
|
|
pokemon_name = @pokemon.name
|
|
pokemon_name = crop_text(pokemon_name, 152)
|
|
draw_text(pokemon_name, 42, 68)
|
|
end
|
|
|
|
def draw_shiny_icon
|
|
draw_image(UI_FOLDER + "shiny", 14, 100) if @pokemon.shiny?
|
|
end
|
|
|
|
def draw_pokemon_level
|
|
draw_image(graphics_folder + _INTL("level"), 42, 102)
|
|
draw_number_from_image(@bitmaps[:numbers], @pokemon.level, 64, 102)
|
|
end
|
|
|
|
def draw_pokerus_icon
|
|
return if @pokemon.pokerusStage != 2
|
|
draw_image(graphics_folder + "icon_pokerus", 134, 100)
|
|
end
|
|
|
|
def draw_gender
|
|
if @pokemon.male?
|
|
draw_text(_INTL("♂"), 174, 98, theme: :male)
|
|
elsif @pokemon.female?
|
|
draw_text(_INTL("♀"), 174, 98, theme: :female)
|
|
end
|
|
end
|
|
|
|
def draw_party_icons
|
|
# Don't show any party icons if there is 0 or 1 Pokémon in the party
|
|
return if @visible_party_length <= 1
|
|
# Setup numbers
|
|
@visible_top_index = [@visible_top_index, @visible_index - ((PARTY_ICONS_COUNT - 1) / 2)].min
|
|
@visible_top_index = [@visible_top_index, @visible_index - (PARTY_ICONS_COUNT / 2), 0].max
|
|
@visible_top_index = @visible_top_index.clamp(0, [@visible_party_length - PARTY_ICONS_COUNT, 0].max)
|
|
x_pos = 0
|
|
y_pos = 162
|
|
# Draw up arrow
|
|
arrow_y = y_pos - PARTY_ICON_SIZE[1]
|
|
arrow_y -= PARTY_ICON_SIZE[1] if @visible_top_index > 0 # Showing up dots
|
|
if @visible_index > 0 # Enabled arrow
|
|
draw_image(@bitmaps[:party_icons], x_pos, arrow_y,
|
|
PARTY_ICON_SIZE[0] * 2, 0, *PARTY_ICON_SIZE)
|
|
else # Disabled arrow
|
|
draw_image(@bitmaps[:party_icons], x_pos, arrow_y,
|
|
PARTY_ICON_SIZE[0] * 3, 0, *PARTY_ICON_SIZE)
|
|
end
|
|
# Draw up dots
|
|
if @visible_top_index > 0
|
|
draw_image(@bitmaps[:party_icons], x_pos, y_pos - PARTY_ICON_SIZE[1],
|
|
PARTY_ICON_SIZE[0] * 4, 0, *PARTY_ICON_SIZE)
|
|
end
|
|
# Draw party icons
|
|
num_balls = [@visible_party_length, PARTY_ICONS_COUNT].min
|
|
num_balls.times do |i|
|
|
src_x_index = (i + @visible_top_index == @visible_index) ? 1 : 0
|
|
draw_image(@bitmaps[:party_icons], x_pos, y_pos + (i * PARTY_ICON_SIZE[1]),
|
|
PARTY_ICON_SIZE[0] * src_x_index, 0, *PARTY_ICON_SIZE)
|
|
end
|
|
# Draw down dots
|
|
if @visible_party_length - @visible_top_index > PARTY_ICONS_COUNT
|
|
draw_image(@bitmaps[:party_icons], x_pos, y_pos + (num_balls * PARTY_ICON_SIZE[1]),
|
|
PARTY_ICON_SIZE[0] * 7, 0, *PARTY_ICON_SIZE)
|
|
end
|
|
# Draw down arrow
|
|
arrow_y = y_pos + (num_balls * PARTY_ICON_SIZE[1])
|
|
arrow_y += PARTY_ICON_SIZE[1] if @visible_party_length - @visible_top_index > PARTY_ICONS_COUNT # Showing down dots
|
|
if @visible_index < @visible_party_length - 1 # Enabled arrow
|
|
draw_image(@bitmaps[:party_icons], x_pos, arrow_y,
|
|
PARTY_ICON_SIZE[0] * 5, 0, *PARTY_ICON_SIZE)
|
|
else # Disabled arrow
|
|
draw_image(@bitmaps[:party_icons], x_pos, arrow_y,
|
|
PARTY_ICON_SIZE[0] * 6, 0, *PARTY_ICON_SIZE)
|
|
end
|
|
end
|
|
|
|
def draw_markings
|
|
x = 106
|
|
y = 288
|
|
mark_variants = @bitmaps[:markings].bitmap.height / MARKING_HEIGHT
|
|
markings = @pokemon.markings
|
|
(@bitmaps[:markings].bitmap.width / MARKING_WIDTH).times do |i|
|
|
src_x = i * MARKING_WIDTH
|
|
src_y = [(markings[i] || 0), mark_variants - 1].min * MARKING_HEIGHT
|
|
draw_image(@bitmaps[:markings], x + (i * MARKING_WIDTH), y,
|
|
src_x, src_y, MARKING_WIDTH, MARKING_HEIGHT)
|
|
end
|
|
end
|
|
|
|
def draw_pokemon_types_common
|
|
draw_pokemon_type_icons(66, 314, 2)
|
|
end
|
|
|
|
# x and y are the top left corner of the type icon if there is only one type.
|
|
def draw_pokemon_type_icons(x, y, spacing)
|
|
@pokemon.types.each_with_index do |type, i|
|
|
type_number = GameData::Type.get(type).icon_position
|
|
offset = ((@pokemon.types.length - 1) * (GameData::Type::ICON_SIZE[0] + spacing) / 2)
|
|
offset = (offset / 2) * 2
|
|
type_x = x - offset + ((GameData::Type::ICON_SIZE[0] + spacing) * i)
|
|
draw_image(@bitmaps[:types], type_x, y,
|
|
0, type_number * GameData::Type::ICON_SIZE[1], *GameData::Type::ICON_SIZE)
|
|
end
|
|
end
|
|
|
|
def draw_hp_bar
|
|
return if @pokemon.fainted?
|
|
bar_x = 48
|
|
bar_y = 352
|
|
bar_total_width = 128
|
|
bar_width = [@pokemon.hp * bar_total_width / @pokemon.totalhp.to_f, 1.0].max
|
|
bar_width = ((bar_width / 2).round) * 2 # Make the bar's length a multiple of 2 pixels
|
|
hp_zone = 0 # Green
|
|
hp_zone = 1 if @pokemon.hp <= (@pokemon.totalhp / 2).floor # Yellow
|
|
hp_zone = 2 if @pokemon.hp <= (@pokemon.totalhp / 4).floor # Red
|
|
draw_image(graphics_folder + "hp_bar_fill", bar_x, bar_y,
|
|
0, hp_zone * 6, bar_width, 6)
|
|
end
|
|
|
|
def draw_hp_numbers
|
|
hp_text = sprintf("%d/%d", @pokemon.hp, @pokemon.totalhp)
|
|
draw_number_from_image(@bitmaps[:numbers], hp_text, 182, 366, align: :right)
|
|
end
|
|
|
|
# Show status/fainted/Pokérus infected icon
|
|
def draw_status_icon
|
|
status = -1
|
|
if @pokemon.fainted?
|
|
status = GameData::Status.count - 1
|
|
elsif @pokemon.status != :NONE
|
|
status = GameData::Status.get(@pokemon.status).icon_position
|
|
elsif @pokemon.pokerusStage == 1
|
|
status = GameData::Status.count
|
|
end
|
|
if status >= 0
|
|
draw_image(UI_FOLDER + _INTL("statuses"), 12, 364,
|
|
0, status * GameData::Status::ICON_SIZE[1], *GameData::Status::ICON_SIZE)
|
|
end
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
def draw_pokedex_number
|
|
draw_text(_INTL("Dex No."), 238, 86)
|
|
# Figure out what the Dex number is
|
|
dex_num = 0
|
|
dex_num_shift = false
|
|
if $player.pokedex.unlocked?(-1) # National Dex is unlocked
|
|
dex_num = (GameData::Species.keys.index(@pokemon.species_data.species) || 0) + 1
|
|
dex_num_shift = true if Settings::DEXES_WITH_OFFSETS.include?(-1)
|
|
else # Only Regional Dexes are unlocked
|
|
($player.pokedex.dexes_count - 1).times do |i|
|
|
next if !$player.pokedex.unlocked?(i)
|
|
num = pbGetRegionalNumber(i, @pokemon.species)
|
|
break if num <= 0
|
|
dex_num = num
|
|
dex_num_shift = true if Settings::DEXES_WITH_OFFSETS.include?(i)
|
|
break
|
|
end
|
|
end
|
|
# Draw text
|
|
number_x = 428
|
|
number_y = 86
|
|
dex_num_theme = (@pokemon.shiny?) ? :shiny : :black
|
|
if dex_num <= 0
|
|
draw_text("???", number_x, number_y, align: :center, theme: dex_num_theme)
|
|
else
|
|
dex_num -= 1 if dex_num_shift
|
|
draw_text(sprintf("%03d", dex_num), number_x, number_y, align: :center, theme: dex_num_theme)
|
|
end
|
|
end
|
|
|
|
def draw_species
|
|
draw_text(_INTL("Species"), 238, 118)
|
|
species_name = @pokemon.speciesName
|
|
species_name = crop_text(species_name, 144)
|
|
draw_text(species_name, 428, 118, align: :center, theme: :black)
|
|
end
|
|
|
|
def draw_original_trainer_details
|
|
draw_text(_INTL("OT"), 238, 150)
|
|
draw_text(_INTL("ID No."), 238, 182)
|
|
owner_name = (@pokemon.owner.name.empty?) ? _INTL("RENTAL") : @pokemon.owner.name
|
|
owner_name = crop_text(owner_name, 144)
|
|
owner_theme = [:male, :female][@pokemon.owner.gender || 99] || :black
|
|
draw_text(owner_name, 428, 150, align: :center, theme: owner_theme)
|
|
owner_number = (@pokemon.owner.name.empty?) ? "?????" : sprintf("%05d", @pokemon.owner.public_id)
|
|
draw_text(owner_number, 428, 182, align: :center, theme: :black)
|
|
end
|
|
|
|
def draw_held_item
|
|
@sprites[:held_item_icon].visible = true
|
|
@sprites[:held_item_icon].item = @pokemon.item_id
|
|
draw_text(_INTL("Held Item"), 302, 230)
|
|
# Write the held item's name
|
|
item_name = (@pokemon.hasItem?) ? @pokemon.item.name : _INTL("None")
|
|
item_name = crop_text(item_name, 192)
|
|
draw_text(item_name, 302, 262, theme: :black)
|
|
end
|
|
|
|
def draw_exp
|
|
return if @pokemon.shadowPokemon?
|
|
# Draw text
|
|
draw_text(_INTL("Exp. Points"), 238, 310)
|
|
draw_text(@pokemon.exp.to_s_formatted, 490, 310, align: :right, theme: :black)
|
|
draw_text(_INTL("To Next Lv."), 238, 342)
|
|
end_exp = @pokemon.growth_rate.minimum_exp_for_level(@pokemon.level + 1)
|
|
draw_text((end_exp - @pokemon.exp).to_s_formatted, 490, 342, align: :right, theme: :black)
|
|
# Draw Exp bar
|
|
if @pokemon.level < GameData::GrowthRate.max_level
|
|
exp_width = @pokemon.exp_fraction * 128
|
|
exp_width = ((exp_width / 2).round) * 2
|
|
draw_image(graphics_folder + "exp_bar_fill", 362, 372,
|
|
0, 0, exp_width, -1)
|
|
end
|
|
end
|
|
|
|
def draw_shadow_pokemon_info
|
|
return if !@pokemon.shadowPokemon?
|
|
# Draw background to cover the Exp area
|
|
draw_image(graphics_folder + "overlay_shadow", 224, 298)
|
|
# Draw heart gauge bar
|
|
shadow_fract = @pokemon.heart_gauge.to_f / @pokemon.max_gauge_size
|
|
draw_image(graphics_folder + "shadow_bar_fill", 242, 372,
|
|
0, 0, (shadow_fract * 248).floor, -1)
|
|
# Draw heart gauge text
|
|
heart_message = [_INTL("The door to its heart is open! Undo the final lock!"),
|
|
_INTL("The door to its heart is almost fully open."),
|
|
_INTL("The door to its heart is nearly open."),
|
|
_INTL("The door to its heart is opening wider."),
|
|
_INTL("The door to its heart is opening up."),
|
|
_INTL("The door to its heart is tightly shut.")][@pokemon.heartStage]
|
|
draw_formatted_text(heart_message, 232, 310, 268, theme: :black)
|
|
end
|
|
|
|
def draw_stats
|
|
# Determine which stats are boosted and lowered by the Pokémon's nature
|
|
stat_themes = {}
|
|
GameData::Stat.each_main { |s| stat_themes[s.id] = :default }
|
|
if !@pokemon.shadowPokemon? || @pokemon.heartStage <= 3
|
|
@pokemon.nature_for_stats.stat_changes.each do |change|
|
|
stat_themes[change[0]] = :raised_stat if change[1] > 0
|
|
stat_themes[change[0]] = :lowered_stat if change[1] < 0
|
|
end
|
|
end
|
|
# Write stats text (except for HP)
|
|
text_y = 86
|
|
GameData::Stat.each_main do |stat|
|
|
next if stat.id == :HP
|
|
draw_text(stat.name_semi_brief, 238, text_y, theme: stat_themes[stat.id])
|
|
draw_text(@pokemon.stat(stat.id), 458, text_y, align: :right, theme: :black)
|
|
text_y += 32
|
|
end
|
|
end
|
|
|
|
def draw_ability
|
|
draw_text(_INTL("Ability"), 224, 262)
|
|
ability = @pokemon.ability
|
|
return if !ability
|
|
ability_name = ability.name
|
|
ability_name = crop_text(ability_name, 182)
|
|
draw_text(ability_name, 328, 262, theme: :black)
|
|
draw_paragraph_text(ability.description, 224, 294, 284, 3, theme: :black)
|
|
end
|
|
|
|
def draw_moves_list
|
|
limit = (@new_move) ? Pokemon::MAX_MOVES + 1 : Pokemon::MAX_MOVES
|
|
limit.times do |i|
|
|
move = (i == Pokemon::MAX_MOVES) ? @new_move : @pokemon.moves[i]
|
|
text_y = MOVE_LIST_Y + (i * MOVE_LIST_SPACING)
|
|
text_y += MOVE_LIST_OFFSET_WHEN_NEW_MOVE if @new_move # Showing an extra move being learned
|
|
text_y += MOVE_LIST_NEW_MOVE_SPACING if i == Pokemon::MAX_MOVES # New move is separated from known moves
|
|
area_x = MOVE_LIST_X
|
|
area_x = MOVE_LIST_X_DETAILED if showing_detailed_move_page?
|
|
draw_move_in_list(move, area_x, text_y)
|
|
end
|
|
end
|
|
|
|
def draw_move_in_list(move, x, y)
|
|
pp_numbers_x = (showing_detailed_move_page?) ? x + 234 : x + 256
|
|
if !move
|
|
draw_text("---", x + 8, y + 6, theme: :black)
|
|
return
|
|
end
|
|
# Draw move name
|
|
move_name = move.name
|
|
move_name = crop_text(move_name, (showing_detailed_move_page?) ? 230 : 262)
|
|
draw_text(move_name, x + 8, y + 6, theme: :black)
|
|
# Draw move type icon
|
|
type_number = GameData::Type.get(move.display_type(@pokemon)).icon_position
|
|
draw_image(@bitmaps[:types], x + 8, y + 32,
|
|
0, type_number * GameData::Type::ICON_SIZE[1], *GameData::Type::ICON_SIZE)
|
|
# Draw move category
|
|
draw_image(UI_FOLDER + "category", x + 74, y + 32,
|
|
0, move.display_category(@pokemon) * GameData::Move::CATEGORY_ICON_SIZE[1], *GameData::Move::CATEGORY_ICON_SIZE)
|
|
# Draw PP text
|
|
if move.total_pp > 0
|
|
pp_text_x = (showing_detailed_move_page?) ? x + 144 : x + 154
|
|
draw_text(_INTL("PP"), pp_text_x, y + 38, theme: :black)
|
|
pp_text_theme = :black
|
|
if move.pp == 0
|
|
pp_text_theme = :pp_zero
|
|
elsif move.pp * 4 <= move.total_pp
|
|
pp_text_theme = :pp_quarter
|
|
elsif move.pp * 2 <= move.total_pp
|
|
pp_text_theme = :pp_half
|
|
end
|
|
draw_text(sprintf("%d/%d", move.pp, move.total_pp), pp_numbers_x, y + 38, align: :right, theme: pp_text_theme)
|
|
end
|
|
end
|
|
|
|
def draw_pokemon_types_for_detailed_moves_page
|
|
draw_pokemon_type_icons(138, 78, 6)
|
|
end
|
|
|
|
def draw_move_properties
|
|
selected_move = ((@move_index || 0) == Pokemon::MAX_MOVES) ? @new_move : @pokemon.moves[@move_index || 0]
|
|
# Power
|
|
draw_text(_INTL("POWER"), 20, 128)
|
|
power_text = selected_move.display_damage(@pokemon)
|
|
power_text = "---" if power_text == 0 # Status move
|
|
power_text = "???" if power_text == 1 # Variable power move
|
|
draw_text(power_text, 222, 128, align: :right, theme: :black)
|
|
# Accuracy
|
|
draw_text(_INTL("ACCURACY"), 20, 160)
|
|
accuracy = selected_move.display_accuracy(@pokemon)
|
|
if accuracy == 0
|
|
draw_text("---", 222, 160, align: :right, theme: :black)
|
|
else
|
|
draw_text(accuracy, 222, 160, align: :right, theme: :black)
|
|
draw_text("%", 222, 160, theme: :black)
|
|
end
|
|
# Description
|
|
draw_paragraph_text(selected_move.description, 4, 192, 246, 6, theme: :black)
|
|
end
|
|
|
|
def draw_ribbon_count
|
|
draw_text(_INTL("No. of Ribbons:"), 234, 338, theme: :black)
|
|
draw_text(@pokemon.numRibbons, 450, 338, align: :right, theme: :black)
|
|
end
|
|
|
|
def draw_ribbons_in_grid
|
|
(RIBBON_COLUMNS * RIBBON_ROWS).times do |index| # Number of visible ribbons
|
|
r_index = (@ribbon_offset * RIBBON_COLUMNS) + index
|
|
break if !@pokemon.ribbons[r_index]
|
|
ribbon_data = GameData::Ribbon.get(@pokemon.ribbons[r_index])
|
|
image_offset = ribbon_data.icon_position
|
|
draw_image(graphics_folder + "ribbons",
|
|
RIBBON_X + ((RIBBON_SIZE[0] + RIBBON_SPACING_X) * (index % RIBBON_COLUMNS)),
|
|
RIBBON_Y + ((RIBBON_SIZE[1] + RIBBON_SPACING_Y) * (index / RIBBON_COLUMNS).floor),
|
|
RIBBON_SIZE[0] * (image_offset % RIBBON_COLUMNS_GRAPHIC),
|
|
RIBBON_SIZE[1] * (image_offset / RIBBON_COLUMNS_GRAPHIC).floor,
|
|
*RIBBON_SIZE)
|
|
end
|
|
end
|
|
|
|
def draw_ribbon_properties
|
|
return if !@ribbon_index
|
|
ribbon_id = @pokemon.ribbons[@ribbon_index]
|
|
# Draw the description box
|
|
draw_image(graphics_folder + "overlay_ribbon", 8, 280)
|
|
if ribbon_id
|
|
ribbon_data = GameData::Ribbon.get(ribbon_id)
|
|
# Draw name of selected ribbon
|
|
draw_text(ribbon_data.name, 18, 292)
|
|
# Draw selected ribbon's description
|
|
draw_paragraph_text(ribbon_data.description, 18, 324, 480, 2, theme: :black)
|
|
end
|
|
end
|
|
|
|
def draw_memo
|
|
# Set up memo
|
|
red_text_tag = shadowc3tag(*TEXT_COLOR_THEMES[:shiny])
|
|
black_text_tag = shadowc3tag(*TEXT_COLOR_THEMES[:black])
|
|
memo = ""
|
|
show_nature = (!@pokemon.shadowPokemon? || @pokemon.heartStage <= 3)
|
|
# Add nature to memo
|
|
if show_nature
|
|
nature_name = red_text_tag + @pokemon.nature.name + black_text_tag
|
|
memo += _INTL("{1} nature.", nature_name) + "\n"
|
|
end
|
|
# Add characteristic to memo
|
|
if show_nature
|
|
best_stat = nil
|
|
best_iv = 0
|
|
stats_order = [:HP, :ATTACK, :DEFENSE, :SPEED, :SPECIAL_ATTACK, :SPECIAL_DEFENSE]
|
|
start_point = @pokemon.personalID % stats_order.length # Tiebreaker
|
|
stats_order.length.times do |i|
|
|
stat = stats_order[(i + start_point) % stats_order.length]
|
|
if !best_stat || @pokemon.iv[stat] > @pokemon.iv[best_stat]
|
|
best_stat = stat
|
|
best_iv = @pokemon.iv[best_stat]
|
|
end
|
|
end
|
|
characteristics = {
|
|
:HP => [_INTL("Loves to eat."),
|
|
_INTL("Takes plenty of siestas."),
|
|
_INTL("Nods off a lot."),
|
|
_INTL("Scatters things often."),
|
|
_INTL("Likes to relax.")],
|
|
:ATTACK => [_INTL("Proud of its power."),
|
|
_INTL("Likes to thrash about."),
|
|
_INTL("A little quick tempered."),
|
|
_INTL("Likes to fight."),
|
|
_INTL("Quick tempered.")],
|
|
:DEFENSE => [_INTL("Sturdy body."),
|
|
_INTL("Capable of taking hits."),
|
|
_INTL("Highly persistent."),
|
|
_INTL("Good endurance."),
|
|
_INTL("Good perseverance.")],
|
|
:SPECIAL_ATTACK => [_INTL("Highly curious."),
|
|
_INTL("Mischievous."),
|
|
_INTL("Thoroughly cunning."),
|
|
_INTL("Often lost in thought."),
|
|
_INTL("Very finicky.")],
|
|
:SPECIAL_DEFENSE => [_INTL("Strong willed."),
|
|
_INTL("Somewhat vain."),
|
|
_INTL("Strongly defiant."),
|
|
_INTL("Hates to lose."),
|
|
_INTL("Somewhat stubborn.")],
|
|
:SPEED => [_INTL("Likes to run."),
|
|
_INTL("Alert to sounds."),
|
|
_INTL("Impetuous and silly."),
|
|
_INTL("Somewhat of a clown."),
|
|
_INTL("Quick to flee.")]
|
|
}
|
|
memo += black_text_tag + characteristics[best_stat][best_iv % 5] + "\n"
|
|
end
|
|
# Add blank line
|
|
memo += "\n" if show_nature
|
|
# Write how Pokémon was obtained
|
|
met_text = [
|
|
_INTL("Met at Lv. {1}.", @pokemon.obtain_level),
|
|
_INTL("Egg received."),
|
|
_INTL("Traded at Lv. {1}.", @pokemon.obtain_level),
|
|
"",
|
|
_INTL("Had a fateful encounter at Lv. {1}.", @pokemon.obtain_level)
|
|
][@pokemon.obtain_method]
|
|
memo += black_text_tag + met_text + "\n" if met_text && met_text != ""
|
|
# Add date received to memo
|
|
if @pokemon.timeReceived
|
|
date = @pokemon.timeReceived.day
|
|
month = pbGetMonthName(@pokemon.timeReceived.mon)
|
|
year = @pokemon.timeReceived.year
|
|
if System.user_language[3..4] == "US"
|
|
memo += black_text_tag + _INTL("{1} {2}, {3}", month, date, year) + "\n"
|
|
else
|
|
memo += black_text_tag + _INTL("{1} {2}, {3}", date, month, year) + "\n"
|
|
end
|
|
end
|
|
# Add map name Pokémon was received on to memo
|
|
map_name = pbGetMapNameFromId(@pokemon.obtain_map)
|
|
map_name = @pokemon.obtain_text if @pokemon.obtain_text && !@pokemon.obtain_text.empty?
|
|
map_name = _INTL("Faraway place") if nil_or_empty?(map_name)
|
|
memo += red_text_tag + map_name + "\n"
|
|
# If Pokémon was hatched, add when and where it hatched to memo
|
|
if @pokemon.obtain_method == 1
|
|
memo += black_text_tag + _INTL("Egg hatched.") + "\n"
|
|
if @pokemon.timeEggHatched
|
|
date = @pokemon.timeEggHatched.day
|
|
month = pbGetMonthName(@pokemon.timeEggHatched.mon)
|
|
year = @pokemon.timeEggHatched.year
|
|
if System.user_language[3..4] == "US"
|
|
memo += black_text_tag + _INTL("{1} {2}, {3}", month, date, year) + "\n"
|
|
else
|
|
memo += black_text_tag + _INTL("{1} {2}, {3}", date, month, year) + "\n"
|
|
end
|
|
end
|
|
map_name = pbGetMapNameFromId(@pokemon.hatched_map)
|
|
map_name = _INTL("Faraway place") if nil_or_empty?(map_name)
|
|
memo += red_text_tag + map_name + "\n"
|
|
end
|
|
# Write memo
|
|
draw_formatted_text(memo, 232, 86, 268)
|
|
end
|
|
|
|
def draw_egg_memo
|
|
# Set up memo
|
|
red_text_tag = shadowc3tag(*TEXT_COLOR_THEMES[:shiny])
|
|
black_text_tag = shadowc3tag(*TEXT_COLOR_THEMES[:black])
|
|
memo = ""
|
|
# Add date received to memo
|
|
if @pokemon.timeReceived
|
|
date = @pokemon.timeReceived.day
|
|
month = pbGetMonthName(@pokemon.timeReceived.mon)
|
|
year = @pokemon.timeReceived.year
|
|
if System.user_language[3..4] == "US"
|
|
memo += black_text_tag + _INTL("{1} {2}, {3}", month, date, year) + "\n"
|
|
else
|
|
memo += black_text_tag + _INTL("{1} {2}, {3}", date, month, year) + "\n"
|
|
end
|
|
end
|
|
# Add map name egg was received on to memo
|
|
map_name = pbGetMapNameFromId(@pokemon.obtain_map)
|
|
map_name = @pokemon.obtain_text if @pokemon.obtain_text && !@pokemon.obtain_text.empty?
|
|
if map_name && map_name != ""
|
|
map_name = red_text_tag + map_name + black_text_tag
|
|
memo += black_text_tag + _INTL("A mysterious Pokémon Egg received from {1}.", map_name) + "\n"
|
|
else
|
|
memo += black_text_tag + _INTL("A mysterious Pokémon Egg.") + "\n"
|
|
end
|
|
# Draw obtain text
|
|
draw_formatted_text(memo, 232, 86, 268)
|
|
# Add Egg Watch blurb to memo
|
|
draw_text(_INTL("The Egg Watch"), 238, 246)
|
|
egg_state = _INTL("It looks like this Egg will take a long time to hatch.")
|
|
egg_state = _INTL("What will hatch from this? It doesn't seem close to hatching.") if @pokemon.steps_to_hatch < 10_200
|
|
egg_state = _INTL("It appears to move occasionally. It may be close to hatching.") if @pokemon.steps_to_hatch < 2550
|
|
egg_state = _INTL("Sounds can be heard coming from inside! It will hatch soon!") if @pokemon.steps_to_hatch < 1275
|
|
memo = black_text_tag + egg_state
|
|
# Draw all text
|
|
draw_formatted_text(memo, 232, 278, 268)
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
def refresh_move_cursor
|
|
# Update cursor positions
|
|
@sprites[:move_cursor].index = @move_index
|
|
@sprites[:selected_move_cursor].index = @swap_move_index || -1
|
|
# Update cursor z values
|
|
if @swap_move_index && @swap_move_index >= 0
|
|
@sprites[:selected_move_cursor].z = @sprites[:move_cursor].z + 1
|
|
@sprites[:selected_move_cursor].z -= 2 if @move_index != @swap_move_index
|
|
end
|
|
end
|
|
|
|
def refresh_ribbon_cursor
|
|
# Scroll ribbons grid to keep cursor on-screen
|
|
sel_ribbon_row = @ribbon_index / RIBBON_COLUMNS
|
|
@ribbon_offset = [@ribbon_offset, sel_ribbon_row].min # Scroll up
|
|
@ribbon_offset = [@ribbon_offset, sel_ribbon_row - RIBBON_ROWS + 1].max # Scroll down
|
|
# Update cursor positions
|
|
@sprites[:ribbon_cursor].index = @ribbon_index - (@ribbon_offset * RIBBON_COLUMNS)
|
|
@sprites[:selected_ribbon_cursor].index = @swap_ribbon_index - (@ribbon_offset * RIBBON_COLUMNS)
|
|
# Update cursor z values
|
|
if @swap_ribbon_index >= 0
|
|
@sprites[:selected_ribbon_cursor].z = @sprites[:ribbon_cursor].z + 1
|
|
@sprites[:selected_ribbon_cursor].z -= 2 if @ribbon_index != @swap_ribbon_index
|
|
end
|
|
end
|
|
|
|
def refresh_markings_cursor
|
|
case @marking_index
|
|
when 6 # OK
|
|
@sprites[:marking_cursor].x = 282
|
|
@sprites[:marking_cursor].y = 244
|
|
@sprites[:marking_cursor].src_rect.y = @sprites[:marking_cursor].bitmap.height / 2
|
|
when 7 # Cancel
|
|
@sprites[:marking_cursor].x = 282
|
|
@sprites[:marking_cursor].y = 294
|
|
@sprites[:marking_cursor].src_rect.y = @sprites[:marking_cursor].bitmap.height / 2
|
|
else
|
|
@sprites[:marking_cursor].x = 282 + (62 * (@marking_index % 3))
|
|
@sprites[:marking_cursor].y = 144 + (50 * (@marking_index / 3))
|
|
@sprites[:marking_cursor].src_rect.y = 0
|
|
end
|
|
end
|
|
|
|
def refresh_markings_panel
|
|
# Set values to use when drawing the markings panel
|
|
marking_x = 298
|
|
marking_y = 154
|
|
marking_spacing_x = 46 + MARKING_WIDTH
|
|
marking_spacing_y = 34 + MARKING_HEIGHT
|
|
markings_per_row = 3
|
|
mark_variants = @bitmaps[:markings].bitmap.height / MARKING_HEIGHT
|
|
# Clear the bitmap
|
|
@sprites[:marking_overlay].bitmap.clear
|
|
# Draw marking icons
|
|
(@bitmaps[:markings].bitmap.width / MARKING_WIDTH).times do |i|
|
|
src_x = i * MARKING_WIDTH
|
|
src_y = [(@markings[i] || 0), mark_variants - 1].min * MARKING_HEIGHT
|
|
draw_image(@bitmaps[:markings],
|
|
marking_x + (marking_spacing_x * (i % markings_per_row)),
|
|
marking_y + (marking_spacing_y * (i / markings_per_row)),
|
|
src_x, src_y, MARKING_WIDTH, MARKING_HEIGHT,
|
|
overlay: :marking_overlay)
|
|
end
|
|
# Draw text
|
|
draw_text(_INTL("Mark {1}", @pokemon.name), 368, 102, align: :center, overlay: :marking_overlay)
|
|
draw_text(_INTL("OK"), 368, 254, align: :center, overlay: :marking_overlay)
|
|
draw_text(_INTL("Cancel"), 368, 304, align: :center, overlay: :marking_overlay)
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
def update_input
|
|
# Check for movement to a new Pokémon
|
|
if Input.trigger?(Input::UP)
|
|
return :go_to_previous_pokemon
|
|
elsif Input.trigger?(Input::DOWN)
|
|
return :go_to_next_pokemon
|
|
end
|
|
# Check for movement to a new page
|
|
if Input.trigger?(Input::LEFT)
|
|
go_to_previous_page
|
|
elsif Input.trigger?(Input::RIGHT)
|
|
go_to_next_page
|
|
end
|
|
# Check for interaction
|
|
if Input.trigger?(Input::USE)
|
|
return update_interaction(Input::USE)
|
|
elsif Input.trigger?(Input::BACK)
|
|
return update_interaction(Input::BACK)
|
|
elsif Input.trigger?(Input::ACTION)
|
|
return update_interaction(Input::ACTION)
|
|
end
|
|
return nil
|
|
end
|
|
|
|
def update_interaction(input)
|
|
case input
|
|
when Input::USE
|
|
case @page
|
|
when :moves
|
|
pbPlayDecisionSE
|
|
return :navigate_moves
|
|
when :ribbons
|
|
pbPlayDecisionSE
|
|
return :navigate_ribbons
|
|
else
|
|
if @mode != :in_battle
|
|
pbPlayDecisionSE
|
|
return :interact_menu
|
|
end
|
|
end
|
|
when Input::ACTION
|
|
@pokemon.play_cry if !@pokemon.egg?
|
|
when Input::BACK
|
|
pbPlayCloseMenuSE
|
|
return :quit
|
|
end
|
|
return nil
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
# Returns true to finish choosing a move.
|
|
def update_input_move
|
|
# Check for movement to a new move
|
|
if Input.repeat?(Input::UP)
|
|
max_move_index = (@new_move) ? Pokemon::MAX_MOVES : Pokemon::MAX_MOVES - 1
|
|
@move_index -= 1
|
|
@move_index = max_move_index if @move_index < 0 # Wrap around
|
|
if @move_index < Pokemon::MAX_MOVES && @move_index >= @pokemon.numMoves
|
|
@move_index = @pokemon.numMoves - 1 # Wrap around
|
|
end
|
|
elsif Input.repeat?(Input::DOWN)
|
|
max_move_index = (@new_move) ? Pokemon::MAX_MOVES : Pokemon::MAX_MOVES - 1
|
|
@move_index += 1
|
|
@move_index = 0 if @move_index > max_move_index # Wrap around
|
|
if @move_index < Pokemon::MAX_MOVES && @move_index >= @pokemon.numMoves
|
|
@move_index = (@new_move) ? max_move_index : 0 # Wrap around or go to new move
|
|
end
|
|
end
|
|
# Check for interaction
|
|
if Input.trigger?(Input::USE)
|
|
pbPlayDecisionSE
|
|
return true if @mode == :choose_move
|
|
if !@pokemon.shadowPokemon?
|
|
if @swap_move_index >= 0
|
|
# End swapping moves
|
|
if @move_index != @swap_move_index
|
|
@pokemon.moves[@move_index], @pokemon.moves[@swap_move_index] = @pokemon.moves[@swap_move_index], @pokemon.moves[@move_index]
|
|
end
|
|
@swap_move_index = -1
|
|
refresh_move_cursor
|
|
refresh
|
|
else
|
|
# Start swapping moves
|
|
@swap_move_index = @move_index
|
|
refresh_move_cursor
|
|
end
|
|
end
|
|
elsif Input.trigger?(Input::BACK)
|
|
if @mode == :choose_move
|
|
pbPlayCloseMenuSE
|
|
@move_index = -1
|
|
return true
|
|
end
|
|
# Cancel swapping moves, or return true to close
|
|
if @swap_move_index >= 0
|
|
pbPlayCancelSE
|
|
@swap_move_index = -1
|
|
refresh_move_cursor
|
|
else
|
|
pbPlayCloseMenuSE
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
# This is used for both general navigating through the list of moves (allowing
|
|
# swapping of moves) and for choosing a move to forget when trying to learn a
|
|
# new one.
|
|
def navigate_moves
|
|
# Setup
|
|
@move_index ||= 0
|
|
@swap_move_index = -1
|
|
@sprites[:pokemon].visible = false if @sprites[:pokemon]
|
|
@sprites[:pokemon_icon].pokemon = @pokemon
|
|
@sprites[:pokemon_icon].visible = true
|
|
@sprites[:move_cursor].visible = true
|
|
refresh_move_cursor
|
|
refresh
|
|
# Navigate loop
|
|
loop do
|
|
Graphics.update
|
|
Input.update
|
|
update_visuals
|
|
old_move_index = @move_index
|
|
break if update_input_move
|
|
if @move_index != old_move_index
|
|
pbPlayCursorSE
|
|
refresh_move_cursor
|
|
refresh
|
|
end
|
|
end
|
|
# Clean up
|
|
if @mode != :choose_move
|
|
@sprites[:move_cursor].visible = false
|
|
@sprites[:pokemon].visible = true
|
|
@sprites[:pokemon_icon].visible = false
|
|
end
|
|
ret = @move_index
|
|
@move_index = nil
|
|
return ret
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
def update_input_ribbon
|
|
# Check for movement to a new ribbon
|
|
if Input.repeat?(Input::UP)
|
|
total_rows = [((@pokemon.ribbons.length + RIBBON_COLUMNS - 1) / RIBBON_COLUMNS).floor, RIBBON_ROWS].max
|
|
@ribbon_index -= RIBBON_COLUMNS
|
|
@ribbon_index += total_rows * RIBBON_COLUMNS if @ribbon_index < 0 # Wrap around
|
|
elsif Input.repeat?(Input::DOWN)
|
|
total_rows = [((@pokemon.ribbons.length + RIBBON_COLUMNS - 1) / RIBBON_COLUMNS).floor, RIBBON_ROWS].max
|
|
@ribbon_index += RIBBON_COLUMNS
|
|
@ribbon_index -= total_rows * RIBBON_COLUMNS if @ribbon_index >= total_rows * RIBBON_COLUMNS # Wrap around
|
|
elsif Input.repeat?(Input::LEFT)
|
|
@ribbon_index -= 1
|
|
@ribbon_index += RIBBON_COLUMNS if (@ribbon_index % RIBBON_COLUMNS) == RIBBON_COLUMNS - 1
|
|
elsif Input.repeat?(Input::RIGHT)
|
|
@ribbon_index += 1
|
|
@ribbon_index -= RIBBON_COLUMNS if (@ribbon_index % RIBBON_COLUMNS) == 0
|
|
end
|
|
# Check for interaction
|
|
if Input.trigger?(Input::USE)
|
|
if @swap_ribbon_index >= 0
|
|
# End swapping ribbons
|
|
pbPlayDecisionSE
|
|
if @ribbon_index != @swap_ribbon_index
|
|
@pokemon.ribbons[@ribbon_index], @pokemon.ribbons[@swap_ribbon_index] = @pokemon.ribbons[@swap_ribbon_index], @pokemon.ribbons[@ribbon_index]
|
|
@pokemon.ribbons.compact! # Don't leave gaps
|
|
if @ribbon_index >= @pokemon.ribbons.length
|
|
@ribbon_index = @pokemon.ribbons.length - 1
|
|
end
|
|
end
|
|
@swap_ribbon_index = -1
|
|
refresh_ribbon_cursor
|
|
refresh
|
|
elsif @pokemon.ribbons[@ribbon_index]
|
|
# Start swapping ribbons
|
|
pbPlayDecisionSE
|
|
@swap_ribbon_index = @ribbon_index
|
|
refresh_ribbon_cursor
|
|
end
|
|
elsif Input.trigger?(Input::BACK)
|
|
# Cancel swapping ribbons, or return true to close
|
|
if @swap_ribbon_index >= 0
|
|
pbPlayCancelSE
|
|
@swap_ribbon_index = -1
|
|
refresh_ribbon_cursor
|
|
else
|
|
pbPlayCloseMenuSE
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
def navigate_ribbons
|
|
# Setup
|
|
@ribbon_index = @ribbon_offset * RIBBON_COLUMNS
|
|
@swap_ribbon_index = -1
|
|
total_rows = [(@pokemon.ribbons.length + RIBBON_COLUMNS - 1) / RIBBON_COLUMNS, RIBBON_ROWS].max
|
|
@sprites[:ribbon_cursor].visible = true
|
|
refresh_ribbon_cursor
|
|
refresh
|
|
# Navigate loop
|
|
loop do
|
|
@sprites[:up_arrow].visible = (@ribbon_offset > 0)
|
|
@sprites[:down_arrow].visible = (@ribbon_offset < total_rows - RIBBON_ROWS)
|
|
Graphics.update
|
|
Input.update
|
|
update_visuals
|
|
old_ribbon_index = @ribbon_index
|
|
old_swap_ribbon_index = @swap_ribbon_index
|
|
break if update_input_ribbon
|
|
if @ribbon_index != old_ribbon_index || @swap_ribbon_index != old_swap_ribbon_index
|
|
pbPlayCursorSE if @swap_ribbon_index == old_swap_ribbon_index
|
|
refresh_ribbon_cursor
|
|
refresh
|
|
end
|
|
end
|
|
# Clean up
|
|
@sprites[:ribbon_cursor].visible = false
|
|
@sprites[:up_arrow].visible = false
|
|
@sprites[:down_arrow].visible = false
|
|
@ribbon_index = nil
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
# NOTE: This is hardcoded to assume there are 6 marks, arranged in a 3x2 grid,
|
|
# with an OK and Cancel button below.
|
|
def update_input_marking
|
|
# Check for movement to a new option
|
|
if Input.repeat?(Input::UP)
|
|
if @marking_index == 7 # Cancel
|
|
@marking_index = 6
|
|
elsif @marking_index == 6 # OK
|
|
@marking_index = 4
|
|
elsif @marking_index < 3
|
|
@marking_index = 7
|
|
else
|
|
@marking_index -= 3
|
|
end
|
|
elsif Input.repeat?(Input::DOWN)
|
|
if @marking_index == 7 # Cancel
|
|
@marking_index = 1
|
|
elsif @marking_index == 6 # OK
|
|
@marking_index = 7
|
|
elsif @marking_index >= 3
|
|
@marking_index = 6
|
|
else
|
|
@marking_index += 3
|
|
end
|
|
elsif Input.repeat?(Input::LEFT)
|
|
if @marking_index < 6
|
|
@marking_index -= 1
|
|
@marking_index += 3 if (@marking_index % 3) == 2
|
|
end
|
|
elsif Input.repeat?(Input::RIGHT)
|
|
if @marking_index < 6
|
|
@marking_index += 1
|
|
@marking_index -= 3 if (@marking_index % 3) == 0
|
|
end
|
|
end
|
|
# Check for interaction
|
|
if Input.trigger?(Input::USE)
|
|
pbPlayDecisionSE
|
|
case @marking_index
|
|
when 6 # OK
|
|
return true
|
|
when 7 # Cancel
|
|
@marking_index = -1
|
|
return true
|
|
else # Change marking
|
|
mark_variants = @bitmaps[:markings].bitmap.height / MARKING_HEIGHT
|
|
@markings[@marking_index] = ((@markings[@marking_index] || 0) + 1) % mark_variants
|
|
refresh_markings_panel
|
|
end
|
|
elsif Input.trigger?(Input::BACK)
|
|
pbPlayCloseMenuSE
|
|
@marking_index = -1
|
|
return true
|
|
elsif Input.trigger?(Input::ACTION)
|
|
if @marking_index < 6 && @markings[@marking_index] > 0
|
|
pbPlayDecisionSE
|
|
@markings[@marking_index] = 0
|
|
refresh_markings_panel
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
def navigate_markings
|
|
# Setup
|
|
@sprites[:marking_bg].visible = true
|
|
@sprites[:marking_overlay].visible = true
|
|
@sprites[:marking_cursor].visible = true
|
|
@markings = @pokemon.markings.clone
|
|
@marking_index = 0
|
|
refresh_markings_panel
|
|
refresh_markings_cursor
|
|
# Navigate loop
|
|
loop do
|
|
Graphics.update
|
|
Input.update
|
|
update_visuals
|
|
old_marking_index = @marking_index
|
|
break if update_input_marking
|
|
if @marking_index != old_marking_index
|
|
pbPlayCursorSE
|
|
refresh_markings_panel
|
|
refresh_markings_cursor
|
|
end
|
|
end
|
|
# Clean up
|
|
@sprites[:marking_bg].visible = false
|
|
@sprites[:marking_overlay].visible = false
|
|
@sprites[:marking_cursor].visible = false
|
|
@pokemon.markings = @markings if @marking_index >= 0
|
|
@marking_index = nil
|
|
end
|
|
end
|
|
|
|
#===============================================================================
|
|
#
|
|
#===============================================================================
|
|
class UI::PokemonSummary < UI::BaseScreen
|
|
attr_reader :party, :mode
|
|
attr_accessor :party_index, :pokemon
|
|
|
|
SCREEN_ID = :summary_screen
|
|
|
|
# party is an array of Pokemon objects or a single Pokemon object.
|
|
# mode is :normal or :choose_move or :in_battle.
|
|
# If mode is :choose_move, new_move is either nil or a move ID.
|
|
def initialize(party, party_index = 0, mode: :normal, new_move: nil)
|
|
@party = (party.is_a?(Array)) ? party : [party]
|
|
@party_index = party_index
|
|
@pokemon = @party[@party_index]
|
|
@mode = mode
|
|
@new_move = (new_move) ? Pokemon::Move.new(new_move) : nil
|
|
super()
|
|
end
|
|
|
|
def initialize_visuals
|
|
@visuals = UI::PokemonSummaryVisuals.new(@party, @party_index, @mode, @new_move)
|
|
end
|
|
|
|
def start_screen
|
|
super # Fade in
|
|
@visuals.play_pokemon_cry if @mode != :choose_move
|
|
end
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
def main
|
|
return choose_move if @mode == :choose_move
|
|
super
|
|
@result = @party_index
|
|
end
|
|
|
|
def choose_move
|
|
pbSEPlay("GUI menu open")
|
|
start_screen
|
|
@result = perform_action(:navigate_moves)
|
|
end_screen
|
|
return @result
|
|
end
|
|
end
|
|
|
|
#===============================================================================
|
|
# Actions that can be triggered in the Pokémon summary screen.
|
|
#===============================================================================
|
|
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :go_to_previous_pokemon, {
|
|
:effect => proc { |screen|
|
|
if screen.party_index > 0
|
|
new_index = screen.party_index
|
|
loop do
|
|
new_index -= 1
|
|
break if screen.party[new_index]
|
|
break if new_index <= 0
|
|
end
|
|
if new_index != screen.party_index && screen.party[new_index]
|
|
# NOTE: @visuals.set_party_index plays an SE.
|
|
screen.party_index = new_index
|
|
screen.pokemon = screen.party[screen.party_index]
|
|
screen.visuals.set_party_index(screen.party_index)
|
|
end
|
|
end
|
|
}
|
|
})
|
|
|
|
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :go_to_next_pokemon, {
|
|
:effect => proc { |screen|
|
|
if screen.party_index < screen.party.length - 1
|
|
new_index = screen.party_index
|
|
loop do
|
|
new_index += 1
|
|
break if screen.party[new_index]
|
|
break if new_index >= screen.party.length - 1
|
|
end
|
|
if new_index != screen.party_index && screen.party[new_index]
|
|
# NOTE: @visuals.set_party_index plays an SE.
|
|
screen.party_index = new_index
|
|
screen.pokemon = screen.party[screen.party_index]
|
|
screen.visuals.set_party_index(screen.party_index)
|
|
end
|
|
end
|
|
}
|
|
})
|
|
|
|
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :navigate_moves, {
|
|
:returns_value => true,
|
|
:effect => proc { |screen|
|
|
move_index = screen.visuals.navigate_moves
|
|
next move_index if screen.mode == :choose_move
|
|
screen.refresh
|
|
next nil
|
|
}
|
|
})
|
|
|
|
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :navigate_ribbons, {
|
|
:effect => proc { |screen|
|
|
screen.visuals.navigate_ribbons
|
|
screen.refresh
|
|
}
|
|
})
|
|
|
|
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :marking, {
|
|
:effect => proc { |screen|
|
|
screen.visuals.navigate_markings
|
|
screen.refresh
|
|
}
|
|
})
|
|
|
|
# Shows a choice menu using the MenuHandlers options below.
|
|
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :interact_menu, {
|
|
:menu => :summary_screen_interact,
|
|
:condition => proc { |screen| next screen.mode != :in_battle }
|
|
})
|
|
|
|
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :give_item, {
|
|
:effect => proc { |screen|
|
|
item = nil
|
|
pbFadeOutInWithUpdate(screen.sprites) do
|
|
bag_screen = UI::Bag.new($bag, mode: :choose_item)
|
|
bag_screen.set_filter_proc(proc { |itm| GameData::Item.get(itm).can_hold? })
|
|
item = bag_screen.choose_item
|
|
end
|
|
screen.refresh if pbGiveItemToPokemon(item, screen.pokemon, screen, screen.party_index)
|
|
}
|
|
})
|
|
|
|
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :take_item, {
|
|
:effect => proc { |screen|
|
|
screen.refresh if pbTakeItemFromPokemon(screen.pokemon, screen)
|
|
}
|
|
})
|
|
|
|
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :pokedex, {
|
|
:effect => proc { |screen|
|
|
$player.pokedex.register_last_seen(screen.pokemon)
|
|
pbFadeOutInWithUpdate(screen.sprites) do
|
|
dex_scene = PokemonPokedexInfo_Scene.new
|
|
dex_screen = PokemonPokedexInfoScreen.new(dex_scene)
|
|
dex_screen.pbStartSceneSingle(screen.pokemon.species)
|
|
end
|
|
}
|
|
})
|
|
|
|
#===============================================================================
|
|
# Menu options for choice menus that exist in the Pokémon summary screen.
|
|
#===============================================================================
|
|
MenuHandlers.add(:summary_screen_interact, :give_item, {
|
|
"name" => _INTL("Give item"),
|
|
"order" => 10,
|
|
"condition" => proc { |screen| next !screen.pokemon.egg? }
|
|
})
|
|
|
|
MenuHandlers.add(:summary_screen_interact, :take_item, {
|
|
"name" => _INTL("Take item"),
|
|
"order" => 20,
|
|
"condition" => proc { |screen| next !screen.pokemon.egg? && screen.pokemon.hasItem? }
|
|
})
|
|
|
|
MenuHandlers.add(:summary_screen_interact, :pokedex, {
|
|
"name" => _INTL("View Pokédex"),
|
|
"order" => 30,
|
|
"condition" => proc { |screen| next !screen.pokemon.egg? && $player.has_pokedex }
|
|
})
|
|
|
|
MenuHandlers.add(:summary_screen_interact, :marking, {
|
|
"name" => _INTL("Mark"),
|
|
"order" => 40
|
|
})
|
|
|
|
MenuHandlers.add(:summary_screen_interact, :cancel, {
|
|
"name" => _INTL("Cancel"),
|
|
"order" => 9999
|
|
})
|
|
|
|
#===============================================================================
|
|
#
|
|
#===============================================================================
|
|
def pbChooseMove(pokemon, variableNumber, nameVarNumber)
|
|
return if !pokemon
|
|
ret = -1
|
|
pbFadeOutIn do
|
|
screen = UI::PokemonSummary.new(pokemon, mode: :choose_move)
|
|
ret = screen.choose_move
|
|
end
|
|
$game_variables[variableNumber] = ret
|
|
if ret >= 0
|
|
$game_variables[nameVarNumber] = pokemon.moves[ret].name
|
|
else
|
|
$game_variables[nameVarNumber] = ""
|
|
end
|
|
$game_map.need_refresh = true if $game_map
|
|
end
|