Files
infinitefusion-e18/Data/Scripts/016b_UI redesign/005_UI_Party.rb
2025-01-23 23:47:20 +00:00

1493 lines
46 KiB
Ruby

# TODO: Rewrite all the ItemHandlers to stop using pbDisplay and whatnot, and
# ensure they do whatever is appropriate when being called with a screen
# of UI::Party.
#===============================================================================
#
#===============================================================================
class UI::PartyVisualsPanel < UI::SpriteContainer
attr_reader :index, :switch_index
attr_reader :pokemon, :text
GRAPHICS_FOLDER = "Party/"
TEXT_COLOR_THEMES = { # Themes not in DEFAULT_TEXT_COLOR_THEMES
:male => [Color.new(0, 112, 248), Color.new(120, 184, 232)],
:female => [Color.new(232, 32, 16), Color.new(248, 168, 184)]
}
def initialize(pokemon, index, viewport)
@pokemon = pokemon
@index = index
@x = (@index % 2) * Graphics.width / 2
@y = (16 * (@index % 2)) + (96 * (@index / 2))
@selected = false
@switch_index = -1 # -1 = not switching, 0+ = index of first panel for switching
@text = nil
super(viewport)
end
def initialize_bitmaps
@bitmaps[:numbers] = AnimatedBitmap.new(graphics_folder + "numbers")
end
def initialize_sprites
initialize_panel_bg
initialize_overlay
initialize_other_sprites
end
def initialize_panel_bg
@sprites[:panel_bg] = ChangelingSprite.new(0, 0, @viewport)
@sprites[:panel_bg].add_bitmap(:blank, graphics_folder + "panel_blank")
@sprites[:panel_bg].add_bitmap(:able, graphics_folder + "panel_rect")
@sprites[:panel_bg].add_bitmap(:able_sel, graphics_folder + "panel_rect_sel")
@sprites[:panel_bg].add_bitmap(:fainted, graphics_folder + "panel_rect_faint")
@sprites[:panel_bg].add_bitmap(:fainted_sel, graphics_folder + "panel_rect_faint_sel")
@sprites[:panel_bg].add_bitmap(:switch, graphics_folder + "panel_rect_switch")
@sprites[:panel_bg].add_bitmap(:switch_sel, graphics_folder + "panel_rect_switch_sel")
@sprites[:panel_bg].add_bitmap(:switch_sel2, graphics_folder + "panel_rect_switch_sel2")
record_values(:panel_bg)
end
def initialize_overlay
add_overlay(:overlay, 256, 98)
record_values(:overlay)
end
def initialize_other_sprites
# HP bar sprite
@sprites[:hp_bar] = ChangelingSprite.new(104, 50, @viewport)
@sprites[:hp_bar].z = 1
@sprites[:hp_bar].add_bitmap(:able, graphics_folder + _INTL("overlay_hp_back"))
@sprites[:hp_bar].add_bitmap(:fainted, graphics_folder + _INTL("overlay_hp_back_faint"))
@sprites[:hp_bar].add_bitmap(:switch, graphics_folder + _INTL("overlay_hp_back_switch"))
record_values(:hp_bar)
# Ball sprite
@sprites[:ball] = ChangelingSprite.new(10, 0, @viewport)
@sprites[:ball].z = 1
@sprites[:ball].add_bitmap(:desel, graphics_folder + "icon_ball")
@sprites[:ball].add_bitmap(:sel, graphics_folder + "icon_ball_sel")
record_values(:ball)
# Pokémon icon
@sprites[:pokemon] = PokemonIconSprite.new(@pokemon, @viewport)
@sprites[:pokemon].x = 60
@sprites[:pokemon].y = 40
@sprites[:pokemon].z = 2
@sprites[:pokemon].setOffset(PictureOrigin::CENTER)
@sprites[:pokemon].active = @active
record_values(:pokemon)
# Held item icon
@sprites[:held_item] = HeldItemIconSprite.new(70, 48, @pokemon, @viewport)
@sprites[:held_item].z = 3
record_values(:held_item)
end
#-----------------------------------------------------------------------------
def text=(value)
return if @text == value
@text = value
refresh
end
def pokemon=(value)
@pokemon = value
@sprites[:pokemon].pokemon = @pokemon if @sprites[:pokemon] && !@sprites[:pokemon].disposed?
@sprites[:held_item].pokemon = @pokemon if @sprites[:held_item] && !@sprites[:held_item].disposed?
refresh
end
def blank?
return @pokemon.nil?
end
def selected=(value)
return if @selected == value
@selected = value
@sprites[:pokemon].selected = @selected
refresh
end
def set_switch_index(value)
@switch_index = value
refresh
end
#-----------------------------------------------------------------------------
def refresh
super
refresh_panel_bg
refresh_hp_bar_graphic
refresh_ball_graphic
refresh_pokemon_icon
refresh_held_item_icon
end
def refresh_panel_bg
return if !@sprites[:panel_bg] || @sprites[:panel_bg].disposed?
if @pokemon.nil?
@sprites[:panel_bg].change_bitmap(:blank)
elsif @selected
if @switch_index == @index
@sprites[:panel_bg].change_bitmap(:switch_sel2)
elsif @switch_index >= 0
@sprites[:panel_bg].change_bitmap(:switch_sel)
elsif @pokemon.fainted?
@sprites[:panel_bg].change_bitmap(:fainted_sel)
else
@sprites[:panel_bg].change_bitmap(:able_sel)
end
else
if @switch_index == @index
@sprites[:panel_bg].change_bitmap(:switch)
elsif @pokemon.fainted?
@sprites[:panel_bg].change_bitmap(:fainted)
else
@sprites[:panel_bg].change_bitmap(:able)
end
end
end
def refresh_hp_bar_graphic
return if !@sprites[:hp_bar] || @sprites[:hp_bar].disposed?
@sprites[:hp_bar].visible = (@pokemon && !@pokemon.egg? && !(@text && @text.length > 0))
return if !@sprites[:hp_bar].visible
if @switch_index == @index || (@switch_index >= 0 && @selected)
@sprites[:hp_bar].change_bitmap(:switch)
elsif @pokemon.fainted?
@sprites[:hp_bar].change_bitmap(:fainted)
else
@sprites[:hp_bar].change_bitmap(:able)
end
end
def refresh_ball_graphic
return if !@sprites[:ball] || @sprites[:ball].disposed?
@sprites[:ball].visible = !@pokemon.nil?
@sprites[:ball].change_bitmap((@selected) ? :sel : :desel)
end
def refresh_pokemon_icon
return if !@sprites[:pokemon] || @sprites[:pokemon].disposed?
@sprites[:pokemon].visible = !@pokemon.nil?
@sprites[:pokemon].selected = @selected
end
def refresh_held_item_icon
return if !@sprites[:held_item] || @sprites[:held_item].disposed?
@sprites[:held_item].visible = !@pokemon.nil?
end
def refresh_overlay
super
return if @pokemon.nil?
draw_name
draw_level
draw_gender
draw_hp_bar
draw_hp_numbers
draw_status_icon
draw_shiny_icon
draw_annotation
end
#-----------------------------------------------------------------------------
def draw_name
pokemon_name = @pokemon.name
pokemon_name = crop_text(pokemon_name, 144)
name_width = @sprites[:overlay].bitmap.text_size(pokemon_name).width
draw_text(pokemon_name, 94 - [name_width - 130, 0].max, 22, theme: :white)
end
def draw_level
return if @pokemon.egg?
draw_image(graphics_folder + _INTL("overlay_lv"), 16, 70)
draw_number_from_image(@bitmaps[:numbers], @pokemon.level, 38, 70)
end
def draw_gender
return if @pokemon.egg?
if @pokemon.male?
draw_text(_INTL(""), 230, 22, theme: :male)
elsif @pokemon.female?
draw_text(_INTL(""), 230, 22, theme: :female)
end
end
def draw_hp_bar
return if @pokemon.egg? || @pokemon.fainted? || (@text && @text.length > 0)
bar_x = 136
bar_y = 52
bar_total_width = 96
bar_height = 8
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
bar_width -= 2 if bar_width == bar_total_width && @pokemon.hp < @pokemon.totalhp
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 * bar_height, bar_width, bar_height)
end
def draw_hp_numbers
return if @pokemon.egg? || (@text && @text.length > 0)
draw_number_from_image(@bitmaps[:numbers], @pokemon.hp, 178, 70, align: :right)
draw_number_from_image(@bitmaps[:numbers], "/" + @pokemon.totalhp.to_s, 178, 70, align: :left)
end
def draw_status_icon
return if @pokemon.egg? || (@text && @text.length > 0)
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"), 86, 68,
0, status * GameData::Status::ICON_SIZE[1], *GameData::Status::ICON_SIZE)
end
end
def draw_shiny_icon
return if @pokemon.egg? || (@text && @text.length > 0)
draw_image(UI_FOLDER + "shiny", 88, 48) if @pokemon.shiny?
end
def draw_annotation
draw_text(@text, 94, 62, theme: :white) if @text && @text.length > 0
end
end
#===============================================================================
#
#===============================================================================
class UI::PartyVisualsButton < UI::SpriteContainer
GRAPHICS_FOLDER = "Party/"
def initialize(text, x, y, narrow, viewport)
@text = text
@x = x
@y = y
@narrow = narrow
@selected = false
super(viewport)
refresh
end
def initialize_sprites
@sprites[:button] = ChangelingSprite.new(0, 0, @viewport)
if @narrow
@sprites[:button].add_bitmap(:desel, graphics_folder + "icon_cancel_narrow")
@sprites[:button].add_bitmap(:sel, graphics_folder + "icon_cancel_narrow_sel")
else
@sprites[:button].add_bitmap(:desel, graphics_folder + "icon_cancel")
@sprites[:button].add_bitmap(:sel, graphics_folder + "icon_cancel_sel")
end
@sprites[:button].change_bitmap(:desel)
record_values(:button)
initialize_overlay
end
def initialize_overlay
add_overlay(:overlay, 112, 48)
@sprites[:overlay].z = 1
record_values(:overlay)
end
#-----------------------------------------------------------------------------
def selected=(value)
return if @selected == value
@selected = value
refresh
end
#-----------------------------------------------------------------------------
def refresh
super
@sprites[:button].change_bitmap((@selected) ? :sel : :desel)
end
def refresh_overlay
super
draw_text(@text, @sprites[:overlay].width / 2, (@narrow) ? 8 : 14, align: :center, theme: :white)
end
end
#===============================================================================
#
#===============================================================================
class UI::PartyVisuals < UI::BaseVisuals
attr_reader :index
GRAPHICS_FOLDER = "Party/" # Subfolder in Graphics/UI
def initialize(party, mode = :normal)
@party = party
@mode = mode
@index = (@party.length == 0) ? Settings::MAX_PARTY_SIZE : 0
@multi_select = (@mode == :choose_entry_order)
super()
set_index(@index)
end
def initialize_message_box
super
@sprites[:help_window] = Window_AdvancedTextPokemon.new("")
@sprites[:help_window].viewport = @viewport
@sprites[:help_window].z = 1500
@sprites[:help_window].setSkin(MessageConfig.pbGetSpeechFrame)
pbBottomLeftLines(@sprites[:help_window], 1, 396)
end
def initialize_sprites
initialize_panels
initialize_cancel_button
@sprites[:storage_text] = Window_UnformattedTextPokemon.new(
(can_access_screen_menu?) ? _INTL("[Action]: Menu") : ""
)
@sprites[:storage_text].x = 32
@sprites[:storage_text].y = Graphics.height - @sprites[:message_box].height - 16
@sprites[:storage_text].z = 10
@sprites[:storage_text].viewport = @viewport
@sprites[:storage_text].baseColor = Color.new(248, 248, 248)
@sprites[:storage_text].shadowColor = Color.black
@sprites[:storage_text].windowskin = nil
end
def initialize_panels
Settings::MAX_PARTY_SIZE.times do |i|
@sprites["pokemon#{i}"] = UI::PartyVisualsPanel.new(@party[i], i, @viewport)
end
end
def initialize_cancel_button
party_max = Settings::MAX_PARTY_SIZE
if @multi_select
@sprites["pokemon#{party_max}"] = UI::PartyVisualsButton.new(_INTL("CONFIRM"), 396, 308, true, @viewport)
@sprites["pokemon#{party_max + 1}"] = UI::PartyVisualsButton.new(_INTL("CANCEL"), 396, 346, true, @viewport)
else
@sprites["pokemon#{party_max}"] = UI::PartyVisualsButton.new(_INTL("CANCEL"), 396, 328, false, @viewport)
end
end
#-----------------------------------------------------------------------------
def can_access_screen_menu?
return false if @mode != :normal
return !switching? && (can_access_storage? || @party.length > 1)
end
def can_access_storage?
return false if @mode != :normal
return ($player.has_box_link || $bag.has?(:POKEMONBOXLINK)) &&
!$game_switches[Settings::DISABLE_BOX_LINK_SWITCH] &&
!$game_map.metadata&.has_flag?("DisableBoxLink")
end
def set_help_text(text)
@sprites[:help_window].text = text
pbBottomLeftLines(@sprites[:help_window], 1, 396)
@sprites[:help_window].resizeHeightToFit(text, @sprites[:help_window].width)
pbBottomLeft(@sprites[:help_window])
@sprites[:help_window].visible = true
end
def panels_have_annotations?
return !@sprites["pokemon0"].text.nil?
end
def set_annotations(annot)
Settings::MAX_PARTY_SIZE.times do |i|
@sprites["pokemon#{i}"].text = (annot) ? annot[i] : nil
end
end
def set_able_annotation_proc(able_proc)
@able_proc = able_proc
refresh_able_annotations
end
# Used for extra restrictions on Pokémon when choosing one to trade.
def set_able_annotation_proc2(able_proc)
@able_proc2 = able_proc
refresh_able_annotations
end
def set_index(new_index)
@index = new_index
num_sprites = Settings::MAX_PARTY_SIZE + ((@multi_select) ? 2 : 1)
num_sprites.times do |i|
@sprites["pokemon#{i}"].selected = (i == @index)
end
end
#-----------------------------------------------------------------------------
def switch_index
return @sprites["pokemon0"].switch_index
end
def switching?
return switch_index >= 0
end
def start_switching(index)
Settings::MAX_PARTY_SIZE.times do |i|
@sprites["pokemon#{i}"].set_switch_index(index)
end
end
def end_switching
Settings::MAX_PARTY_SIZE.times do |i|
@sprites["pokemon#{i}"].set_switch_index(-1)
end
end
def animate_switch_panels_out(index1, index2)
pbSEPlay("GUI party switch")
# Setup values
sprite1 = @sprites["pokemon#{index1}"]
sprite2 = @sprites["pokemon#{index2}"]
sprite1_start_x = sprite1.x
sprite2_start_x = sprite2.x
sprite1_dir = (index1.even?) ? -1 : 1
sprite2_dir = (index2.even?) ? -1 : 1
# Animate the panels moving off-screen
duration = 0.4
timer_start = System.uptime
loop do
sprite1.x = sprite1_start_x + lerp(0, sprite1_dir * Graphics.width / 2, duration, timer_start, System.uptime)
sprite2.x = sprite2_start_x + lerp(0, sprite2_dir * Graphics.width / 2, duration, timer_start, System.uptime)
Graphics.update
Input.update
update_visuals
break if sprite1.x == sprite1_start_x + (sprite1_dir * Graphics.width / 2)
end
end
def animate_switch_panels_in(index1, index2)
pbSEPlay("GUI party switch")
# Setup values
sprite1 = @sprites["pokemon#{index1}"]
sprite2 = @sprites["pokemon#{index2}"]
sprite1.pokemon = @party[index1]
sprite2.pokemon = @party[index2]
sprite1_start_x = sprite1.x
sprite2_start_x = sprite2.x
sprite1_dir = (index1.even?) ? 1 : -1
sprite2_dir = (index2.even?) ? 1 : -1
# Animate the panels moving back into position
duration = 0.4
timer_start = System.uptime
loop do
sprite1.x = sprite1_start_x + lerp(0, sprite1_dir * Graphics.width / 2, duration, timer_start, System.uptime)
sprite2.x = sprite2_start_x + lerp(0, sprite2_dir * Graphics.width / 2, duration, timer_start, System.uptime)
Graphics.update
Input.update
update_visuals
break if sprite1.x == sprite1_start_x + (sprite1_dir * Graphics.width / 2)
end
end
#-----------------------------------------------------------------------------
def show_message(text)
@sprites[:help_window].visible = false
super
@sprites[:help_window].visible = true
end
def show_confirm_message(text)
@sprites[:help_window].visible = false
ret = super
@sprites[:help_window].visible = true
return ret
end
def show_menu(text, options, index = 0, cmd_side: :right)
@sprites[:help_window].visible = false
# cmd_side = (@index.even?) ? :right : :left
ret = super(text, options, index, cmd_side: cmd_side)
@sprites[:help_window].visible = true
return ret
end
def show_choice(options, index = 0)
@sprites[:help_window].visible = false
ret = super
@sprites[:help_window].visible = true
return ret
end
def choose_number(help_text, maximum, init_num = 1)
@sprites[:help_window].visible = false
ret = super
@sprites[:help_window].visible = true
return ret
end
#-----------------------------------------------------------------------------
def refresh
ensure_valid_index
refresh_panels
end
def ensure_valid_index
old_index = @index
@index = @party.length - 1 if @index < Settings::MAX_PARTY_SIZE && @index >= @party.length
set_index(@index) if @index != old_index
end
def refresh_panels
Settings::MAX_PARTY_SIZE.times { |i| refresh_panel(i) }
end
def refresh_panel(panel_index)
sprite = @sprites["pokemon#{panel_index}"]
return if !sprite
if sprite.is_a?(UI::PartyVisualsPanel)
sprite.pokemon = sprite.pokemon
else
sprite.refresh
end
end
def refresh_party
Settings::MAX_PARTY_SIZE.times do |i|
@sprites["pokemon#{i}"].pokemon = @party[i]
end
end
def refresh_able_annotations
Settings::MAX_PARTY_SIZE.times do |i|
next if @sprites["pokemon#{i}"].blank?
if !@able_proc && !@able_proc2
@sprites["pokemon#{i}"].text = nil
next
end
is_able = true
is_able = false if @able_proc && !@able_proc.call(@party[i])
is_able = false if @able_proc2 && !@able_proc2.call(@party[i])
@sprites["pokemon#{i}"].text = (is_able) ? _INTL("ABLE") : _INTL("NOT ABLE")
end
end
#-----------------------------------------------------------------------------
def update_input
# Check for movement to a new Pokémon/button
old_index = @index
update_cursor_movement
if @index != old_index
pbPlayCursorSE
set_index(@index)
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)
elsif Input.trigger?(Input::SPECIAL)
return update_interaction(Input::SPECIAL)
end
return nil
end
def update_cursor_movement
num_sprites = Settings::MAX_PARTY_SIZE + ((@multi_select) ? 2 : 1)
if Input.repeat?(Input::UP)
if @index >= Settings::MAX_PARTY_SIZE
@index -= 1
@index = @party.length - 1 if @index < Settings::MAX_PARTY_SIZE && !@party[@index]
@index = num_sprites - 1 if !@party[@index] # In case party is empty
else
loop do
@index -= 2
break if @index < 0 || @party[@index]
end
@index = num_sprites - 1 if @index < 0 # Wrap around to the cancel button
end
elsif Input.repeat?(Input::DOWN)
if @index >= Settings::MAX_PARTY_SIZE - 1
@index += 1
@index = 0 if @index >= num_sprites # Wrap around to the first Pokémon
else
@index += 2
end
@index = Settings::MAX_PARTY_SIZE if @index < Settings::MAX_PARTY_SIZE && !@party[@index]
elsif Input.repeat?(Input::LEFT)
loop do
@index -= 1
break if @index < 0 || @index >= Settings::MAX_PARTY_SIZE || @party[@index]
end
@index = num_sprites - 1 if @index < 0 # Wrap around to the cancel button
elsif Input.repeat?(Input::RIGHT)
loop do
@index += 1
break if @index >= Settings::MAX_PARTY_SIZE || @party[@index]
end
@index = 0 if @index >= num_sprites # Wrap around to the first Pokémon
@index = Settings::MAX_PARTY_SIZE if @index < Settings::MAX_PARTY_SIZE && !@party[@index]
end
end
def update_interaction(input)
case input
when Input::USE
return :switch_pokemon_end if switching?
if @index == Settings::MAX_PARTY_SIZE
pbPlayCloseMenuSE
return :quit
elsif @sub_mode == :switch_pokemon
return :switch_pokemon_start
elsif @sub_mode == :switch_items
return :item_move if @party[@index].hasItem?
return nil
end
pbPlayDecisionSE
return :interact_menu
when Input::ACTION
if can_access_screen_menu?
pbPlayDecisionSE
return :screen_menu
end
when Input::BACK
return :switch_pokemon_cancel if switching?
return :clear_sub_mode if (@sub_mode || :none) != :none
pbPlayCloseMenuSE
return :quit
end
return nil
end
def navigate
refresh
super
end
#-----------------------------------------------------------------------------
def update_input_choose_pokemon
# Check for movement to a new Pokémon/button
old_index = @index
update_cursor_movement
if @index != old_index
pbPlayCursorSE
set_index(@index)
end
# Check for interaction
if Input.trigger?(Input::USE)
return update_interaction_choose_pokemon(Input::USE)
elsif Input.trigger?(Input::BACK)
return update_interaction_choose_pokemon(Input::BACK)
end
return nil
end
def update_interaction_choose_pokemon(input)
case input
when Input::USE
if @index == Settings::MAX_PARTY_SIZE
return :confirm if @multi_select # Confirm
(switching?) ? pbPlayCancelSE : pbPlayCloseMenuSE
return :quit
elsif @index == Settings::MAX_PARTY_SIZE + 1 # Cancel
(switching?) ? pbPlayCancelSE : pbPlayCloseMenuSE
return :quit
else
return :chosen
end
when Input::BACK
(switching?) ? pbPlayCancelSE : pbPlayCloseMenuSE
return :quit
end
return nil
end
def navigate_choose_pokemon
ret = nil
loop do
Graphics.update
Input.update
update_visuals
ret = update_input_choose_pokemon
break if ret
end
return ret
end
end
#===============================================================================
#
#===============================================================================
class UI::Party < UI::BaseScreen
attr_reader :party
SCREEN_ID = :party_screen
# mode is one of:
# :normal Can choose Fly/Dig/Teleport to use
# :choose_pokemon Result is index of chosen Pokémon; def pbMoveTutorChoose
# wants this to yield when chosen
# :battle_choose_pokemon For battle.
# :battle_choose_to_box For battle. Like :choose_pokemon but with a different help text.
# :battle_use_item For battle.
# :use_item Like :choose_pokemon but with a different help text
# :teach_pokemon Like :choose_pokemon but with a different help text
# :choose_entry_order Battle Frontier thing
def initialize(party, mode: :normal)
@party = (party.is_a?(Array)) ? party : [party]
@mode = mode
super()
reset_help_text
end
def initialize_visuals
@visuals = UI::PartyVisuals.new(@party, @mode)
end
#-----------------------------------------------------------------------------
def pokemon
return (index < @party.length) ? @party[index] : nil
end
def can_access_storage?
return @visuals.can_access_storage?
end
def set_index(new_index)
@visuals.set_index(new_index)
end
def set_help_text(text)
@visuals.set_help_text(text)
end
def set_able_annotation_proc(able_proc)
@able_proc = able_proc
@visuals.set_able_annotation_proc(able_proc)
end
# Used for extra restrictions on Pokémon when choosing one to trade.
def set_able_annotation_proc2(able_proc)
@able_proc2 = able_proc
@visuals.set_able_annotation_proc2(able_proc)
end
def set_annotations(annot)
@visuals.set_annotations(annot)
end
def clear_annotations
@visuals.set_annotations(nil)
end
#-----------------------------------------------------------------------------
def switch_index
return @visuals.switch_index
end
def switching?
return @visuals.switching?
end
def start_switching(index = nil)
@visuals.start_switching(index || @visuals.index)
end
def end_switching
@visuals.end_switching
end
def switch_pokemon(index1, index2)
if index1 >= 0 && index1 < @party.length &&
index2 >= 0 && index2 < @party.length &&
index1 != index2
@visuals.animate_switch_panels_out(index1, index2)
@party[index1], @party[index2] = @party[index2], @party[index1]
@visuals.animate_switch_panels_in(index1, index2)
end
end_switching
end
#-----------------------------------------------------------------------------
alias pbShowCommands show_menu
#-----------------------------------------------------------------------------
def refresh
super
reset_help_text
end
def reset_help_text
case @mode
when :normal
if switching?
set_help_text(_INTL("Move to where?"))
else
case @visuals.sub_mode
when :switch_pokemon
set_help_text(_INTL("Choose Pokémon to switch."))
when :switch_items
set_help_text(_INTL("Choose to switch items."))
else
set_help_text((@party.length > 1) ? _INTL("Choose a Pokémon.") : _INTL("Choose Pokémon or cancel."))
end
end
when :choose_pokemon, :battle_choose_pokemon
if switching?
set_help_text(_INTL("Move to where?"))
else
set_help_text(_INTL("Choose a Pokémon."))
end
when :use_item, :battle_use_item
set_help_text(_INTL("Use on which Pokémon?"))
when :teach_pokemon
set_help_text(_INTL("Teach which Pokémon?"))
when :battle_choose_to_box
set_help_text(_INTL("Send which Pokémon to Boxes?"))
when :choose_entry_order
set_help_text(_INTL("Choose Pokémon and confirm."))
end
end
def refresh_party
@visuals.refresh_party
end
# TODO: Get rid of this method once ItemHandlers have been rewritten.
def pbHardRefresh
refresh_party
refresh
end
#-----------------------------------------------------------------------------
# For Soft-Boiled and Milk Drink.
def use_field_move(move_id)
pkmn = pokemon
move = pkmn.moves.select { |mov| mov.id == move_id }.first
move_name = move.name
case move_id
when :SOFTBOILED, :MILKDRINK
heal_amt = [(pkmn.totalhp / 5).floor, 1].max
if pkmn.hp <= heal_amt
show_message(_INTL("Not enough HP..."))
return
end
old_party_idx = index
start_switching(old_party_idx)
loop do
set_help_text(_INTL("Use on which Pokémon?"))
new_party_idx = choose_pokemon_core
if new_party_idx < 0 || new_party_idx == old_party_idx
end_switching
break
end
new_pkmn = pokemon
if new_party_idx == old_party_idx
show_message(_INTL("{1} can't use {2} on itself!", pkmn.name, move_name))
elsif new_pkmn.egg?
show_message(_INTL("{1} can't be used on an Egg!", move_name))
elsif new_pkmn.fainted? || new_pkmn.hp == new_pkmn.totalhp
show_message(_INTL("{1} can't be used on that Pokémon.", move_name))
else
pkmn.hp -= heal_amt
hp_gain = pbItemRestoreHP(new_pkmn, heal_amt)
show_message(_INTL("{1}'s HP was restored by {2} points.", new_pkmn.name, hp_gain))
refresh
end
break if pkmn.hp <= heal_amt
end
end_switching
set_index(old_party_idx)
else
if pbCanUseHiddenMove?(pkmn, move_id) && pbConfirmUseHiddenMove(pkmn, move_id)
if move_id == :FLY
pbFadeOutInWithUpdate(sprites) do
town_map_screen = UI::TownMap.new(mode: :fly)
town_map_screen.main
if town_map_screen.result
$game_temp.field_move_to_use = move_id
$game_temp.field_move_user = pkmn
$game_temp.fly_destination = town_map_screen.result
silent_end_screen
end
end
else
$game_temp.field_move_to_use = move_id
$game_temp.field_move_user = pkmn
end_screen
end
end
end
end
#-----------------------------------------------------------------------------
def on_start_main_loop
reset_help_text
end
def choose_pokemon
start_screen
loop do
on_start_main_loop
chosen_index = choose_pokemon_core
if chosen_index >= 0 && block_given?
next if !yield @party[chosen_index], chosen_index
end
@result = chosen_index
break
end
end_screen
return @result
end
def choose_pokemon_core
ret = -1
loop do
command = @visuals.navigate_choose_pokemon
if command != :chosen || index < 0 || index >= @party.length
ret = -1
break
end
if (@able_proc && !@able_proc.call(pokemon)) ||
(@able_proc2 && !@able_proc2.call(pokemon))
if pokemon.egg?
show_message(_INTL("This egg can't be chosen."))
else
show_message(_INTL("This Pokémon can't be chosen."))
end
next
end
ret = index
break
end
return ret
end
# Used by the Battle Frontier.
def choose_pokemon_entry_order(ruleset)
return nil if !ruleset.hasValidTeam?(@party)
# Setup party panel annotations
annot = []
statuses = []
ordinals = [_INTL("INELIGIBLE"), _INTL("NOT ENTERED"), _INTL("BANNED")]
positions = [_INTL("FIRST"), _INTL("SECOND"), _INTL("THIRD"), _INTL("FOURTH"),
_INTL("FIFTH"), _INTL("SIXTH"), _INTL("SEVENTH"), _INTL("EIGHTH"),
_INTL("NINTH"), _INTL("TENTH"), _INTL("ELEVENTH"), _INTL("TWELFTH")]
Settings::MAX_PARTY_SIZE.times do |i|
ordinals.push(positions[i] || "#{i + 1}th")
end
@party.length.times do |i|
statuses[i] = (ruleset.isPokemonValid?(@party[i])) ? 1 : 2
annot[i] = ordinals[statuses[i]]
end
set_annotations(annot)
# Main loop
start_screen
ret = nil
added_entry = false # Whether an entry was added in the previous loop
loop do
on_start_main_loop
# Get an array of the chosen Pokémon in order
real_order = []
@party.length.times do |i|
@party.length.times do |j|
next if statuses[j] != i + 3
real_order.push(j)
break
end
end
real_order.length.times { |i| statuses[real_order[i]] = i + 3 }
# Refresh annotations
@party.length.times { |i| annot[i] = ordinals[statuses[i]] }
set_annotations(annot)
# Move index to the "Confirm" button if the required number of Pokémon are
# now chosen
if real_order.length == ruleset.number && added_entry
@visuals.set_index(Settings::MAX_PARTY_SIZE)
end
added_entry = false
# Choose a Pokémon or button
command = @visuals.navigate_choose_pokemon
case command
when :chosen
commands = {}
commands[:enter] = _INTL("Entry") if (statuses[index] || 0) == 1 # Not entered yet
commands[:not_enter] = _INTL("No Entry") if (statuses[index] || 0) > 2 # Already entered
commands[:summary] = _INTL("Summary")
commands[:cancel] = _INTL("Cancel")
chosen_command = show_menu(_INTL("Do what with {1}?", pokemon.name), commands)
case chosen_command
when :enter
if real_order.length >= ruleset.number && ruleset.number > 0
show_message(_INTL("No more than {1} Pokémon may enter.", ruleset.number))
else
statuses[index] = real_order.length + 3
added_entry = true
refresh
end
when :not_enter
statuses[index] = 1
refresh
when :summary
perform_action(:summary)
end
when :confirm
ret = []
real_order.each { |i| ret.push(@party[i]) }
error = []
break if ruleset.isValid?(ret, error)
show_message(error[0])
ret = nil
when :quit # Cancelled
break
end
end
end_screen
@result = ret
return @result
end
def choose_move(pkmn, message)
# TODO: The move names can get rather wide, making the message box rather
# thin. It's just about acceptable, but maybe the choice window needs
# to be displayed above the message box instead of to the right of it.
move_names = []
pkmn.moves.each do |move|
next if !move || !move.id
if move.total_pp <= 0
move_names.push(_INTL("{1} (PP: ---)", move.name))
else
move_names.push(_INTL("{1} (PP: {2}/{3})", move.name, move.pp, move.total_pp))
end
end
return show_menu(message, move_names)
end
alias pbChooseMove choose_move
end
#===============================================================================
# Actions that can be triggered in the party screen.
#===============================================================================
# Shows a choice menu using the MenuHandlers options below.
UIActionHandlers.add(UI::Party::SCREEN_ID, :screen_menu, {
:menu => :party_screen_menu,
:menu_message => proc { |screen| _INTL("Choose an option.") }
})
# Shows a choice menu using the MenuHandlers options below.
UIActionHandlers.add(UI::Party::SCREEN_ID, :interact_menu, {
:menu => :party_screen_interact,
:menu_message => proc { |screen| _INTL("Do what with {1}?", screen.pokemon.name) }
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :summary, {
:effect => proc { |screen|
summary_mode = [:battle_choose_pokemon, :battle_choose_to_box, :battle_use_item].include?(screen.mode) ? :in_battle : :normal
pbFadeOutInWithUpdate(screen.sprites) do
new_index = UI::PokemonSummary.new(screen.party, screen.index, mode: summary_mode).main
screen.set_index(new_index)
end
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :debug, {
:effect => proc { |screen|
screen.pokemon_debug_menu(screen.pokemon, screen.index)
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :switch_pokemon_start, {
:effect => proc { |screen|
pbPlayDecisionSE
screen.start_switching
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :switch_pokemon_end, {
:effect => proc { |screen|
screen.switch_pokemon(screen.switch_index, screen.index)
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :switch_pokemon_cancel, {
:effect => proc { |screen|
pbPlayCancelSE
screen.end_switching
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :switch_pokemon_mode, {
:effect => proc { |screen|
screen.set_sub_mode(:switch_pokemon)
}
})
# Shows a choice menu using the MenuHandlers options below.
UIActionHandlers.add(UI::Party::SCREEN_ID, :item_menu, {
:menu => :party_screen_interact_item,
:menu_message => proc { |screen| _INTL("Do what with an item?") }
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :item_use, {
:effect => proc { |screen|
pkmn = screen.pokemon
used_item = nil
pbFadeOutInWithUpdate(screen.sprites) do
bag_screen = UI::Bag.new($bag, mode: :choose_item)
bag_screen.set_filter_proc(proc { |itm|
item_data = GameData::Item.get(itm)
next false if !pbCanUseItemOnPokemon?(itm)
next false if pkmn.hyper_mode && !item_data&.is_scent?
if item_data.is_machine?
move = item_data.move
next false if pkmn.hasMove?(move) || !pkmn.compatible_with_move?(move)
end
next true
})
used_item = bag_screen.choose_item
end
if used_item
pbUseItemOnPokemon(used_item, pkmn, screen)
screen.refresh
end
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :item_give, {
:effect => proc { |screen|
pkmn = screen.pokemon
given_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? })
given_item = bag_screen.choose_item
end
if given_item
pbGiveItemToPokemon(given_item, pkmn, screen, screen.index)
screen.refresh
end
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :item_take, {
:effect => proc { |screen|
pkmn = screen.pokemon
screen.refresh if pbTakeItemFromPokemon(pkmn, screen)
}
})
# TODO: Switching Pokémon goes through the regular navigate, but switching items
# (here) has the whole switching process in this handler. Be consistent.
UIActionHandlers.add(UI::Party::SCREEN_ID, :item_move, {
:effect => proc { |screen|
pbPlayDecisionSE
old_pkmn = screen.pokemon
old_item = old_pkmn.item
old_item_name = old_item.name
old_item_portion_name = old_item.portion_name
screen.set_help_text(_INTL("Move held item to where?"))
old_party_idx = screen.index
moved = false
loop do
screen.start_switching(old_party_idx)
new_party_idx = screen.choose_pokemon_core
if new_party_idx < 0 || new_party_idx == old_party_idx
screen.end_switching
break
end
new_pkmn = screen.party[new_party_idx]
if new_pkmn.egg?
screen.show_message(_INTL("Eggs can't hold items."))
next
elsif !new_pkmn.hasItem?
new_pkmn.item = old_item
old_pkmn.item = nil
screen.end_switching
screen.show_message(_INTL("{1} was given the {2} to hold.", new_pkmn.name, old_item_portion_name))
moved = true
break
elsif new_pkmn.item.is_mail?
screen.show_message(_INTL("{1}'s mail must be removed before giving it an item.", new_pkmn.name))
next
end
# New Pokémon is also holding an item; ask what to do with it
new_item = new_pkmn.item
new_item_portion_name = new_item.portion_name
if new_item_portion_name.starts_with_vowel?
screen.show_message(_INTL("{1} is already holding an {2}.", new_pkmn.name, new_item_portion_name) + "\1")
else
screen.show_message(_INTL("{1} is already holding a {2}.", new_pkmn.name, new_item_portion_name) + "\1")
end
next if !screen.show_confirm_message(_INTL("Would you like to switch the two items?"))
new_pkmn.item = old_item
old_pkmn.item = new_item
screen.end_switching
screen.show_message(_INTL("{1} was given the {2} to hold.", new_pkmn.name, old_item_portion_name) + "\1")
screen.show_message(_INTL("{1} was given the {2} to hold.", old_pkmn.name, new_item_portion_name))
moved = true
break
end
screen.set_index(old_party_idx) if !moved
}
})
# Shows a choice menu using the MenuHandlers options below.
UIActionHandlers.add(UI::Party::SCREEN_ID, :mail_menu, {
:menu => :party_screen_interact_mail,
:menu_message => proc { |screen| _INTL("Do what with the Mail?") }
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :item_move_mode, {
:effect => proc { |screen|
screen.set_sub_mode(:switch_items)
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :mail_read, {
:effect => proc { |screen|
pbFadeOutInWithUpdate(screen.sprites) do
pbDisplayMail(screen.pokemon.mail, screen.pokemon)
end
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :mail_take, {
:effect => proc { |screen|
screen.refresh if pbTakeItemFromPokemon(screen.pokemon, screen)
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :clear_sub_mode, {
:effect => proc { |screen|
pbPlayCancelSE
screen.set_sub_mode
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :open_storage, {
:effect => proc { |screen|
pbFadeOutInWithUpdate(screen.sprites) do
UI::PokemonStorage.new($PokemonStorage, mode: :organize).main
screen.refresh_party
screen.refresh
end
}
})
# NOTE: This code adds a number of UIActionHandlers, one per move usable in the
# overworld.
HiddenMoveHandlers.eachHandler do |move_id|
eval <<-__END__
UIActionHandlers.add(UI::Party::SCREEN_ID, :use_#{move_id}, {
:effect => proc { |screen|
screen.use_field_move(:#{move_id})
}
})
__END__
end
UIActionHandlers.add(UI::Party::SCREEN_ID, :use_MILKDRINK, {
:effect => proc { |screen|
screen.use_field_move(:MILKDRINK)
}
})
UIActionHandlers.add(UI::Party::SCREEN_ID, :use_SOFTBOILED, {
:effect => proc { |screen|
screen.use_field_move(:SOFTBOILED)
}
})
#===============================================================================
# Menu options for choice menus that exist in the party screen.
#===============================================================================
MenuHandlers.add(:party_screen_menu, :open_storage, {
"name" => _INTL("Access Pokémon Boxes"),
"order" => 10,
"condition" => proc { |screen| next screen.can_access_storage? }
})
MenuHandlers.add(:party_screen_menu, :switch_pokemon_mode, {
"name" => _INTL("Mode: Switch Pokémon"),
"order" => 20,
"condition" => proc { |screen| next screen.party.length > 1 }
})
MenuHandlers.add(:party_screen_menu, :item_move_mode, {
"name" => _INTL("Mode: Switch items"),
"order" => 30,
"condition" => proc { |screen| next screen.party.length > 1 }
})
MenuHandlers.add(:party_screen_menu, :cancel, {
"name" => _INTL("Cancel"),
"order" => 9999
})
#-------------------------------------------------------------------------------
MenuHandlers.add(:party_screen_interact, :summary, {
"name" => _INTL("Summary"),
"order" => 10
})
MenuHandlers.add(:party_screen_interact, :debug, {
"name" => _INTL("Debug"),
"order" => 20,
"condition" => proc { |screen| next $DEBUG }
})
MenuHandlers.add(:party_screen_interact, :field_moves, {
"order" => 30,
"multi_options" => proc { |screen|
ret = []
next ret if screen.pokemon.egg?
screen.pokemon.moves.each do |move|
next if !HiddenMoveHandlers.hasHandler(move.id) &&
![:MILKDRINK, :SOFTBOILED].include?(move.id)
ret.push(["use_#{move.id}".to_sym, nil, "<c3=0050A0,80C0F0>" + move.name + "</c3>"])
end
next ret
}
})
MenuHandlers.add(:party_screen_interact, :switch_pokemon_start, {
"name" => _INTL("Switch"),
"order" => 40,
"condition" => proc { |screen| next screen.party.length > 1 }
})
MenuHandlers.add(:party_screen_interact, :item_menu, {
"name" => _INTL("Item"),
"order" => 50,
"condition" => proc { |screen| next !screen.pokemon.egg? && !screen.pokemon.mail }
})
MenuHandlers.add(:party_screen_interact_item, :item_use, {
"name" => _INTL("Use"),
"order" => 10
})
MenuHandlers.add(:party_screen_interact_item, :item_give, {
"name" => _INTL("Give"),
"order" => 20
})
MenuHandlers.add(:party_screen_interact_item, :item_take, {
"name" => _INTL("Take"),
"order" => 30,
"condition" => proc { |screen| next screen.pokemon.hasItem? }
})
MenuHandlers.add(:party_screen_interact_item, :item_move, {
"name" => _INTL("Move"),
"order" => 40,
"condition" => proc { |screen| next screen.pokemon.hasItem? && !screen.pokemon.item.is_mail? }
})
MenuHandlers.add(:party_screen_interact_item, :item_cancel, {
"name" => _INTL("Cancel"),
"order" => 9999
})
MenuHandlers.add(:party_screen_interact, :mail_menu, {
"name" => _INTL("Mail"),
"order" => 50,
"condition" => proc { |screen| next !screen.pokemon.egg? && screen.pokemon.mail }
})
MenuHandlers.add(:party_screen_interact_mail, :mail_read, {
"name" => _INTL("Read"),
"order" => 10
})
MenuHandlers.add(:party_screen_interact_mail, :mail_take, {
"name" => _INTL("Take"),
"order" => 20
})
MenuHandlers.add(:party_screen_interact_mail, :mail_cancel, {
"name" => _INTL("Cancel"),
"order" => 30
})
MenuHandlers.add(:party_screen_interact, :cancel, {
"name" => _INTL("Cancel"),
"order" => 9999
})
#===============================================================================
# Open the party screen.
#===============================================================================
def pbPokemonScreen
pbFadeOutIn do
UI::Party.new($player.party).main
end
end
#===============================================================================
# Choose a Pokémon in the party.
#===============================================================================
# Choose a Pokémon/egg from the party.
# Stores result in variable _index_game_var_ and the chosen Pokémon's name in
# variable _name_game_var_; result is -1 if no Pokémon was chosen
# _allowIneligible is unused.
def pbChoosePokemon(index_game_var, name_game_var, able_proc = nil, _allow_ineligible = false)
chosen = -1
pbFadeOutIn do
screen = UI::Party.new($player.party, mode: :choose_pokemon)
screen.set_able_annotation_proc(able_proc) if able_proc
chosen = screen.choose_pokemon
end
pbSet(index_game_var, chosen)
pbSet(name_game_var, (chosen >= 0) ? $player.party[chosen].name : "")
end
def pbChooseNonEggPokemon(index_game_var, name_game_var)
pbChoosePokemon(index_game_var, name_game_var, proc { |pkmn| !pkmn.egg? })
end
def pbChooseAblePokemon(index_game_var, name_game_var)
pbChoosePokemon(index_game_var, name_game_var, proc { |pkmn| !pkmn.egg? && pkmn.hp > 0 })
end
# Same as pbChoosePokemon, but prevents choosing an egg or a Shadow Pokémon or a
# Pokémon that is marked and untradable.
def pbChooseTradablePokemon(index_game_var, name_game_var, able_proc = nil, _allow_ineligible = false)
chosen = 0
pbFadeOutIn do
screen = UI::Party.new($player.party, mode: :choose_pokemon)
screen.set_able_annotation_proc(able_proc) if able_proc
screen.set_able_annotation_proc2(proc { |pkmn| next !pkmn.egg? && !pkmn.shadowPokemon? && !pkmn.cannot_trade })
chosen = screen.choose_pokemon
end
pbSet(index_game_var, chosen)
pbSet(name_game_var, (chosen >= 0) ? $player.party[chosen].name : "")
end
def pbChoosePokemonForTrade(index_game_var, name_game_var, wanted)
wanted = GameData::Species.get(wanted).species
pbChooseTradablePokemon(index_game_var, name_game_var, proc { |pkmn|
next pkmn.isSpecies?(wanted)
})
end