Rewrote Move Reminder screen

This commit is contained in:
Maruno17
2024-11-03 01:15:53 +00:00
parent ae80d9dcd4
commit 098ca562d0
4 changed files with 392 additions and 7 deletions

View File

@@ -363,16 +363,36 @@ module UI
#---------------------------------------------------------------------------
def set_viewport_color(new_color)
@viewport.color = new_color
end
def fade_in
# TODO: pbFadeInAndShow changes the colour of all sprites. Make it instead
# change the opacity of @viewport like def pbFadeOutIn.
pbFadeInAndShow(@sprites)
duration = 0.4 # In seconds
col = Color.new(0, 0, 0, 0)
timer_start = System.uptime
loop do
col.set(0, 0, 0, lerp(255, 0, duration, timer_start, System.uptime))
set_viewport_color(col)
Graphics.update
Input.update
update_visuals
break if col.alpha == 0
end
end
def fade_out
# TODO: pbFadeOutAndHide changes the colour of all sprites. Make it
# instead change the opacity of @viewport like def pbFadeOutIn.
pbFadeOutAndHide(@sprites)
duration = 0.4 # In seconds
col = Color.new(0, 0, 0, 0)
timer_start = System.uptime
loop do
col.set(0, 0, 0, lerp(0, 255, duration, timer_start, System.uptime))
set_viewport_color(col)
Graphics.update
Input.update
update_visuals
break if col.alpha == 255
end
end
def dispose

View File

@@ -27,7 +27,6 @@ class UI::PokemonSummaryMoveCursor < ChangelingSprite
self.visible = (@index >= 0)
end
def refresh_position
return if @index < 0
self.x = UI::PokemonSummaryVisuals::MOVE_LIST_X_DETAILED - CURSOR_THICKNESS

View File

@@ -0,0 +1,364 @@
#===============================================================================
#
#===============================================================================
class UI::MoveReminderCursor < IconSprite
attr_accessor :top_index
CURSOR_WIDTH = 258
CURSOR_HEIGHT = 72
CURSOR_THICKNESS = 6
def initialize(viewport = nil)
super(0, 0, viewport)
setBitmap("Graphics/UI/Move Reminder/cursor")
self.src_rect = Rect.new(0, 0, CURSOR_WIDTH, CURSOR_HEIGHT)
self.z = 1600
@bg_sprite = IconSprite.new(x, y, viewport)
@bg_sprite.setBitmap("Graphics/UI/Move Reminder/cursor")
@bg_sprite.src_rect = Rect.new(0, CURSOR_HEIGHT, CURSOR_WIDTH, CURSOR_HEIGHT)
@top_index = 0
self.index = 0
end
def dispose
@bg_sprite.dispose
@bg_sprite = nil
super
end
def index=(value)
@index = value
refresh_position
end
def visible=(value)
super
@bg_sprite.visible = value
end
def refresh_position
return if @index < 0
self.x = UI::MoveReminderVisuals::MOVE_LIST_X
self.y = UI::MoveReminderVisuals::MOVE_LIST_Y - CURSOR_THICKNESS
self.y += (@index - @top_index) * UI::MoveReminderVisuals::MOVE_LIST_SPACING
@bg_sprite.x = self.x
@bg_sprite.y = self.y
end
end
#===============================================================================
#
#===============================================================================
class UI::MoveReminderVisuals < UI::BaseVisuals
attr_reader :index
GRAPHICS_FOLDER = "Move Reminder/" # Subfolder in Graphics/UI
TEXT_COLOR_THEMES = { # These color themes are added to @sprites[:overlay]
:default => [Color.new(248, 248, 248), Color.new(0, 0, 0)], # Base and shadow colour
:white => [Color.new(248, 248, 248), Color.new(0, 0, 0)],
:black => [Color.new(64, 64, 64), Color.new(176, 176, 176)],
:header => [Color.new(88, 88, 80), Color.new(168, 184, 184)]
}
MOVE_LIST_X = 0
MOVE_LIST_Y = 84
MOVE_LIST_SPACING = 64 # Y distance between top of two adjacent move areas
VISIBLE_MOVES = 4
def initialize(pokemon, moves)
@pokemon = pokemon
@moves = moves
@top_index = 0
@index = 0
super()
refresh_cursor
end
def initialize_bitmaps
@bitmaps[:types] = AnimatedBitmap.new(UI_FOLDER + _INTL("types"))
@bitmaps[:buttons] = AnimatedBitmap.new(graphics_folder + "buttons")
end
def initialize_sprites
# Pokémon icon
@sprites[:pokemon_icon] = PokemonIconSprite.new(@pokemon, @viewport)
@sprites[:pokemon_icon].setOffset(PictureOrigin::CENTER)
@sprites[:pokemon_icon].x = 314
@sprites[:pokemon_icon].y = 84
@sprites[:pokemon_icon].z = 200
# Cursor
@sprites[:cursor] = UI::MoveReminderCursor.new(@viewport)
end
#-----------------------------------------------------------------------------
def moves=(move_list)
@moves = move_list
@index = @moves.length - 1 if @index >= @moves.length
refresh_on_index_changed(@index)
@cursor.visible = false if @moves.empty?
refresh
end
#-----------------------------------------------------------------------------
def refresh_overlay
super
draw_header
draw_pokemon_type_icons(396, 70, 6)
draw_moves_list
draw_move_properties
draw_buttons
end
def draw_header
draw_text(_INTL("Teach which move?"), 16, 14, theme: :header)
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_moves_list
VISIBLE_MOVES.times do |i|
move = @moves[@top_index + i]
next if move.nil?
move_data = GameData::Move.get(move)
draw_move_in_list(move_data, MOVE_LIST_X, MOVE_LIST_Y + (i * MOVE_LIST_SPACING))
end
end
def draw_move_in_list(move_data, x, y)
# Draw move name
move_name = move_data.name
move_name = crop_text(move_name, 230)
draw_text(move_name, x + 10, y + 6)
# Draw move type icon
type_number = GameData::Type.get(move_data.display_type(@pokemon)).icon_position
draw_image(@bitmaps[:types], x + 10, y + 32,
0, type_number * GameData::Type::ICON_SIZE[1], *GameData::Type::ICON_SIZE)
# Draw move category
draw_image(UI_FOLDER + "category", x + 76, y + 32,
0, move_data.display_category(@pokemon) * GameData::Move::CATEGORY_ICON_SIZE[1], *GameData::Move::CATEGORY_ICON_SIZE)
# Draw PP text
if move_data.total_pp > 0
draw_text(_INTL("PP"), x + 150, y + 38, theme: :black)
draw_text(sprintf("%d/%d", move_data.total_pp, move_data.total_pp), x + 240, y + 38, align: :right, theme: :black)
end
end
def draw_move_properties
move = @moves[@index]
move_data = GameData::Move.get(move)
# Power
draw_text(_INTL("POWER"), 278, 120)
power_text = move_data.display_power(@pokemon)
power_text = "---" if power_text == 0 # Status move
power_text = "???" if power_text == 1 # Variable power move
draw_text(power_text, 480, 120, align: :right, theme: :black)
# Accuracy
draw_text(_INTL("ACCURACY"), 278, 152)
accuracy = move_data.display_accuracy(@pokemon)
if accuracy == 0
draw_text("---", 480, 152, align: :right, theme: :black)
else
draw_text(accuracy, 480, 152, align: :right, theme: :black)
draw_text("%", 480, 152, theme: :black)
end
# Description
draw_paragraph_text(move_data.description, 262, 184, 246, 6, theme: :black)
end
def draw_buttons
draw_image(@bitmaps[:buttons], 44, 350, 0, 0, 76, 32) if @top_index < @moves.length - VISIBLE_MOVES
draw_image(@bitmaps[:buttons], 132, 350, 76, 0, 76, 32) if @top_index > 0
end
#-----------------------------------------------------------------------------
def refresh_cursor
@sprites[:cursor].top_index = @top_index
@sprites[:cursor].index = @index
end
def refresh_on_index_changed(old_index)
pbPlayCursorSE
# Change @top_index to keep @index in the middle of the visible list (or as
# close to it as possible)
middle_range_top = (VISIBLE_MOVES / 2) - ((VISIBLE_MOVES + 1) % 2)
middle_range_bottom = VISIBLE_MOVES / 2
if @index < @top_index + middle_range_top
@top_index = @index - middle_range_top
elsif @index > @top_index + middle_range_bottom
@top_index = @index - middle_range_bottom
end
@top_index = @top_index.clamp(0, [@moves.length - VISIBLE_MOVES, 0].max)
refresh_cursor
refresh
end
#-----------------------------------------------------------------------------
def update_input
# Check for movement to a new move
update_cursor_movement
# 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_cursor_movement
# Check for movement to a new move
if Input.repeat?(Input::UP)
@index -= 1
if Input.trigger?(Input::UP)
@index = @moves.length - 1 if @index < 0 # Wrap around
else
@index = 0 if @index < 0
end
elsif Input.repeat?(Input::DOWN)
@index += 1
if Input.trigger?(Input::DOWN)
@index = 0 if @index >= @moves.length # Wrap around
else
@index = @moves.length - 1 if @index >= @moves.length
end
elsif Input.repeat?(Input::JUMPUP)
@index -= VISIBLE_MOVES
@index = 0 if @index < 0
elsif Input.repeat?(Input::JUMPDOWN)
@index += VISIBLE_MOVES
@index = @moves.length - 1 if @index >= @moves.length
end
return false
end
def update_interaction(input)
case input
when Input::USE
pbPlayDecisionSE
return :learn
when Input::BACK
pbPlayCloseMenuSE
return :quit
end
return nil
end
end
#===============================================================================
#
#===============================================================================
class UI::MoveReminder < UI::BaseScreen
attr_reader :pokemon, :mode
SCREEN_ID = :move_reminder_screen
# mode is either :normal or :single.
def initialize(pokemon, mode: :normal)
@pokemon = pokemon
@mode = mode
generate_move_list
super()
end
def initialize_visuals
@visuals = UI::MoveReminderVisuals.new(@pokemon, @moves)
end
#-----------------------------------------------------------------------------
def generate_move_list
@moves = []
return if !@pokemon || @pokemon.egg? || @pokemon.shadowPokemon?
@pokemon.getMoveList.each do |move|
next if move[0] > @pokemon.level || @pokemon.hasMove?(move[1])
@moves.push(move[1]) if !@moves.include?(move[1])
end
if Settings::MOVE_RELEARNER_CAN_TEACH_MORE_MOVES && @pokemon.first_moves
first_moves = []
@pokemon.first_moves.each do |move|
first_moves.push(move) if !@moves.include?(move) && !@pokemon.hasMove?(move)
end
@moves = first_moves + @moves # List first moves before level-up moves
end
@moves = @moves | [] # remove duplicates
end
def refresh_move_list
generate_move_list
@visuals.moves = @moves
end
#-----------------------------------------------------------------------------
def move
return @moves[self.index]
end
#-----------------------------------------------------------------------------
def main
return if @disposed
start_screen
loop do
on_start_main_loop
command = @visuals.navigate
break if command == :quit && (@mode == @normal ||
show_confirm_message(_INTL("Give up trying to teach a new move to {1}?", @pokemon.name)))
perform_action(command)
if @moves.empty?
show_message(_INTL("There are no more moves for {1} to learn.", @pokemon.name))
break
end
if @disposed
@result = true
break
end
end
end_screen
return @result
end
end
#===============================================================================
# Actions that can be triggered in the Move Reminder screen.
#===============================================================================
UIActionHandlers.add(UI::MoveReminder::SCREEN_ID, :learn, {
:effect => proc { |screen|
if screen.show_confirm_message(_INTL("Teach {1}?", GameData::Move.get(screen.move).name))
if pbLearnMove(screen.pokemon, screen.move, false, false, screen)
$stats.moves_taught_by_reminder += 1
if screen.mode == :normal
screen.refresh_move_list
else
screen.end_screen
end
end
end
}
})
#===============================================================================
#
#===============================================================================
def pbRelearnMoveScreen(pkmn)
ret = true
pbFadeOutIn do
ret = UI::MoveReminder.new(pkmn, mode: :single).main
end
return ret
end

View File

@@ -1,3 +1,4 @@
=begin
#===============================================================================
# Scene class for handling appearance of the screen.
#===============================================================================
@@ -197,3 +198,4 @@ def pbRelearnMoveScreen(pkmn)
end
return retval
end
=end