Rewrote Bag screen

This commit is contained in:
Maruno17
2024-09-13 23:01:40 +01:00
parent 9c95db2324
commit d8263da05e
20 changed files with 1271 additions and 243 deletions

View File

@@ -126,7 +126,7 @@ module MenuHandlers
next
end
if hash["name"].is_a?(Proc)
name = hash["name"].call
name = hash["name"].call(*args)
else
name = _INTL(hash["name"])
end

View File

@@ -835,12 +835,11 @@ class SpriteWindow_Selectable < SpriteWindow_Base
if item < 0 || item >= @item_max || item < self.top_item ||
item > self.top_item + self.page_item_max
return Rect.new(0, 0, 0, 0)
else
cursor_width = (self.width - self.borderX - ((@column_max - 1) * @column_spacing)) / @column_max
x = item % @column_max * (cursor_width + @column_spacing)
y = (item / @column_max * @row_height) - @virtualOy
return Rect.new(x, y, cursor_width, @row_height)
end
cursor_width = (self.width - self.borderX - ((@column_max - 1) * @column_spacing)) / @column_max
x = item % @column_max * (cursor_width + @column_spacing)
y = (item / @column_max * @row_height) - @virtualOy
return Rect.new(x, y, cursor_width, @row_height)
end
def refresh; end

View File

@@ -146,6 +146,15 @@ module GameData
return pbGetMessageFromHash(MessageTypes::ITEM_NAMES, @real_name)
end
def display_name
ret = name
if is_machine?
machine = @move
ret = sprintf("%s %s", ret, GameData::Move.get(@move).name)
end
return ret
end
# @return [String] the translated plural version of the name of this item
def name_plural
return pbGetMessageFromHash(MessageTypes::ITEM_NAME_PLURALS, @real_name_plural)

View File

@@ -199,123 +199,127 @@ class Battle::Scene
else
$bag.reset_last_selections
end
# Start Bag screen
itemScene = PokemonBag_Scene.new
itemScene.pbStartScene($bag, true,
proc { |item|
useType = GameData::Item.get(item).battle_use
next useType && useType > 0
}, false)
# Loop while in Bag screen
wasTargeting = false
loop do
# Select an item
item = itemScene.pbChooseItem
break if !item
# Choose a command for the selected item
item = GameData::Item.get(item)
itemName = item.name
useType = item.battle_use
cmdUse = -1
commands = []
commands[cmdUse = commands.length] = _INTL("Use") if useType && useType != 0
commands[commands.length] = _INTL("Cancel")
command = itemScene.pbShowCommands(_INTL("{1} is selected.", itemName), commands)
next unless cmdUse >= 0 && command == cmdUse # Use
# Use types:
# 0 = not usable in battle
# 1 = use on Pokémon (lots of items, Blue Flute)
# 2 = use on Pokémon's move (Ethers)
# 3 = use on battler (X items, Persim Berry, Red/Yellow Flutes)
# 4 = use on opposing battler (Poké Balls)
# 5 = use no target (Poké Doll, Guard Spec., Poké Flute, Launcher items)
case useType
when 1, 2, 3 # Use on Pokémon/Pokémon's move/battler
# Auto-choose the Pokémon/battler whose action is being decided if they
# are the only available Pokémon/battler to use the item on
# Start Bag screen
bag_screen = UI::Bag.new($bag, mode: :choose_item_in_battle)
bag_screen.set_filter_proc(proc { |itm|
use_type = GameData::Item.get(itm).battle_use
next use_type && use_type > 0
})
bag_screen.show_and_hide do
# Loop while in Bag screen
loop do
# Select an item
item = bag_screen.choose_item_core
break if !item
# Choose a command for the selected item
item = GameData::Item.get(item)
itemName = item.name
useType = item.battle_use
cmdUse = -1
commands = []
commands[cmdUse = commands.length] = _INTL("Use") if useType && useType != 0
commands[commands.length] = _INTL("Cancel")
command = bag_screen.show_choice_message(_INTL("{1} is selected.", itemName), commands)
next unless cmdUse >= 0 && command == cmdUse # Use
# Use types:
# 0 = not usable in battle
# 1 = use on Pokémon (lots of items, Blue Flute)
# 2 = use on Pokémon's move (Ethers)
# 3 = use on battler (X items, Persim Berry, Red/Yellow Flutes)
# 4 = use on opposing battler (Poké Balls)
# 5 = use no target (Poké Doll, Guard Spec., Poké Flute, Launcher items)
case useType
when 1 # Use on Pokémon
if @battle.pbTeamLengthFromBattlerIndex(idxBattler) == 1
if yield item.id, useType, @battle.battlers[idxBattler].pokemonIndex, -1, itemScene
break
else
next
when 1, 2, 3 # Use on Pokémon/Pokémon's move/battler
# Auto-choose the Pokémon/battler whose action is being decided if they
# are the only available Pokémon/battler to use the item on
case useType
when 1 # Use on Pokémon
if @battle.pbTeamLengthFromBattlerIndex(idxBattler) == 1
if yield item.id, useType, @battle.battlers[idxBattler].pokemonIndex, -1, bag_screen
break
else
next
end
end
when 3 # Use on battler
if @battle.pbPlayerBattlerCount == 1
if yield item.id, useType, @battle.battlers[idxBattler].pokemonIndex, -1, bag_screen
break
else
next
end
end
end
when 3 # Use on battler
if @battle.pbPlayerBattlerCount == 1
if yield item.id, useType, @battle.battlers[idxBattler].pokemonIndex, -1, itemScene
break
else
next
end
end
end
# Fade out and hide Bag screen
itemScene.pbFadeOutScene
# Get player's party
party = @battle.pbParty(idxBattler)
partyPos = @battle.pbPartyOrder(idxBattler)
partyStart, _partyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
modParty = @battle.pbPlayerDisplayParty(idxBattler)
# Start party screen
party_idx = -1
party_screen = UI::Party.new(modParty, mode: :battle_use_item)
party_screen.choose_pokemon do |pkmn, party_index|
party_idx = party_index
next true if party_index < 0
# Use the item on the selected Pokémon
real_party_index = -1
partyPos.each_with_index do |pos, i|
next if pos != party_index + partyStart
real_party_index = i
break
end
next false if real_party_index < 0
next false if !pkmn || pkmn.egg?
move_index = -1
if useType == 2 # Use on Pokémon's move
move_index = party_screen.choose_move(pkmn, _INTL("Restore which move?"))
next false if move_index < 0
end
next true if yield item.id, useType, real_party_index, move_index, party_screen
next false
end
break if party_idx >= 0 # Item was used; close the Bag screen
# Cancelled choosing a Pokémon; show the Bag screen again
itemScene.pbFadeInScene
when 4 # Use on opposing battler (Poké Balls)
idxTarget = -1
if @battle.pbOpposingBattlerCount(idxBattler) == 1
@battle.allOtherSideBattlers(idxBattler).each { |b| idxTarget = b.index }
break if yield item.id, useType, idxTarget, -1, itemScene
else
wasTargeting = true
# Fade out and hide Bag screen
itemScene.pbFadeOutScene
# Fade in and show the battle screen, choosing a target
tempVisibleSprites = visibleSprites.clone
tempVisibleSprites["commandWindow"] = false
tempVisibleSprites["targetWindow"] = true
idxTarget = pbChooseTarget(idxBattler, GameData::Target.get(:Foe), tempVisibleSprites)
if idxTarget >= 0
break if yield item.id, useType, idxTarget, -1, self
bag_sprites_status = pbFadeOutAndHide(bag_screen.sprites)
# Get player's party
party = @battle.pbParty(idxBattler)
partyPos = @battle.pbPartyOrder(idxBattler)
partyStart, _partyEnd = @battle.pbTeamIndexRangeFromBattlerIndex(idxBattler)
modParty = @battle.pbPlayerDisplayParty(idxBattler)
# Start party screen
party_idx = -1
party_screen = UI::Party.new(modParty, mode: :battle_use_item)
party_screen.choose_pokemon do |pkmn, party_index|
party_idx = party_index
next true if party_index < 0
# Use the item on the selected Pokémon
real_party_index = -1
partyPos.each_with_index do |pos, i|
next if pos != party_index + partyStart
real_party_index = i
break
end
next false if real_party_index < 0
next false if !pkmn || pkmn.egg?
move_index = -1
if useType == 2 # Use on Pokémon's move
move_index = party_screen.choose_move(pkmn, _INTL("Restore which move?"))
next false if move_index < 0
end
if yield item.id, useType, real_party_index, move_index, party_screen
bag_screen.silent_end_screen
next true
end
party_idx = -1
next false
end
# Target invalid/cancelled choosing a target; show the Bag screen again
wasTargeting = false
pbFadeOutAndHide(@sprites)
itemScene.pbFadeInScene
break if party_idx >= 0 # Item was used; close the Bag screen
# Cancelled choosing a Pokémon; show the Bag screen again
pbFadeInAndShow(bag_screen.sprites, bag_sprites_status)
when 4 # Use on opposing battler (Poké Balls)
idxTarget = -1
if @battle.pbOpposingBattlerCount(idxBattler) == 1
@battle.allOtherSideBattlers(idxBattler).each { |b| idxTarget = b.index }
break if yield item.id, useType, idxTarget, -1, bag_screen
else
wasTargeting = true
# Fade out and hide Bag screen
bag_sprites_status = pbFadeOutAndHide(bag_screen.sprites)
# Fade in and show the battle screen, choosing a target
tempVisibleSprites = visibleSprites.clone
tempVisibleSprites["commandWindow"] = false
tempVisibleSprites["targetWindow"] = true
idxTarget = pbChooseTarget(idxBattler, GameData::Target.get(:Foe), tempVisibleSprites)
if idxTarget >= 0
break if yield item.id, useType, idxTarget, -1, self
end
# Target invalid/cancelled choosing a target; show the Bag screen again
wasTargeting = false
pbFadeOutAndHide(@sprites)
pbFadeInAndShow(bag_screen.sprites, bag_sprites_status)
end
when 5 # Use with no target
break if yield item.id, useType, idxBattler, -1, bag_screen
end
when 5 # Use with no target
break if yield item.id, useType, idxBattler, -1, itemScene
end
next true
end
@bagLastPocket = $bag.last_viewed_pocket
@bagChoices = $bag.last_pocket_selections.clone
$bag.last_viewed_pocket = oldLastPocket
$bag.last_pocket_selections = oldChoices
# Close Bag screen
itemScene.pbEndScene
# Fade back into battle screen (if not already showing it)
pbFadeInAndShow(@sprites, visibleSprites) if !wasTargeting
end

View File

@@ -382,9 +382,9 @@ def pbBerryPlant
when 0 # Fertilize
mulch = nil
pbFadeOutIn do
scene = PokemonBag_Scene.new
screen = PokemonBagScreen.new(scene, $bag)
mulch = screen.pbChooseItemScreen(proc { |item| GameData::Item.get(item).is_mulch? })
bag_screen = UI::Bag.new($bag, mode: :choose_item)
bag_screen.set_filter_proc(proc { |item| GameData::Item.get(item).is_mulch? })
mulch = bag_screen.choose_item
end
return if !mulch
mulch_data = GameData::Item.get(mulch)
@@ -409,9 +409,9 @@ def pbBerryPlant
end
if !ask_to_plant || pbConfirmMessage(_INTL("Want to plant a Berry?"))
pbFadeOutIn do
scene = PokemonBag_Scene.new
screen = PokemonBagScreen.new(scene, $bag)
berry = screen.pbChooseItemScreen(proc { |item| GameData::Item.get(item).is_berry? })
bag_screen = UI::Bag.new($bag, mode: :choose_item)
bag_screen.set_filter_proc(proc { |item| GameData::Item.get(item).is_berry? })
berry = bag_screen.choose_item
end
if berry
$stats.berries_planted += 1

View File

@@ -58,8 +58,8 @@ module ItemHandlers
# 0 - Item not used
# 1 - Item used, don't end screen
# 2 - Item used, end screen
def triggerUseFromBag(item)
return UseFromBag.trigger(item) if UseFromBag[item]
def triggerUseFromBag(item, bag_screen = nil)
return UseFromBag.trigger(item, bag_screen) if UseFromBag[item]
# No UseFromBag handler exists; check the UseInField handler if present
if UseInField[item]
return (UseInField.trigger(item)) ? 1 : 0
@@ -618,8 +618,11 @@ def pbLearnMove(pkmn, move, ignore_if_known = false, by_machine = false, screen
if by_machine && Settings::TAUGHT_MACHINES_KEEP_OLD_PP
pkmn.moves[move_index].pp = [old_move_pp, pkmn.moves[move_index].total_pp].min
end
# TODO: The last \\wt[16] is skipped in instant text speed. Or rather,
# the text after it is shown at the same time as the text before
# it, but the SE waits for that duration and then plays.
pbMessage(_INTL("1, 2, and...\\wt[16] ...\\wt[16] ...\\wt[16] Ta-da!") + "\\se[Battle ball drop]\1", &block)
pbMessage(_INTL("{1} forgot how to use {2}.\nAnd..." + "\1", pkmn_name, old_move_name), &block)
pbMessage(_INTL("{1} forgot how to use {2}.\nAnd...", pkmn_name, old_move_name) + "\1", &block)
pbMessage("\\se[]" + _INTL("{1} learned {2}!", pkmn_name, move_name) + "\\se[Pkmn move learnt]", &block)
pkmn.changeHappiness("machine") if by_machine
return true
@@ -646,6 +649,8 @@ end
#===============================================================================
# Use an item from the Bag and/or on a Pokémon.
#===============================================================================
# Called from the Bag screen and also when prompted to use a Repel when one runs
# out (bag_scene will be nil for the latter).
# @return [Integer] 0 = item wasn't used; 1 = item used; 2 = close Bag to use in field
def pbUseItem(bag, item, bag_scene = nil)
itm = GameData::Item.get(item)
@@ -697,7 +702,7 @@ def pbUseItem(bag, item, bag_scene = nil)
end
return (ret) ? 1 : 0
elsif useType == 2 || itm.is_machine? # Item is usable from Bag or teaches a move
intret = ItemHandlers.triggerUseFromBag(item)
intret = ItemHandlers.triggerUseFromBag(item, bag_scene)
if intret >= 0
bag.remove(item) if intret == 1 && itm.consumed_after_use?
return intret
@@ -796,7 +801,7 @@ def pbGiveItemToPokemon(item, pkmn, scene, pkmnid = 0)
end
if pkmn.hasItem?
olditemname = pkmn.item.portion_name
if newitemname.starts_with_vowel?
if olditemname.starts_with_vowel?
scene.pbDisplay(_INTL("{1} is already holding an {2}.", pkmn.name, olditemname) + "\1")
else
scene.pbDisplay(_INTL("{1} is already holding a {2}.", pkmn.name, olditemname) + "\1")
@@ -869,9 +874,8 @@ end
def pbChooseItem(var = 0, *args)
ret = nil
pbFadeOutIn do
scene = PokemonBag_Scene.new
screen = PokemonBagScreen.new(scene, $bag)
ret = screen.pbChooseItemScreen
bag_screen = UI::Bag.new($bag, mode: :choose_item)
ret = bag_screen.choose_item
end
$game_variables[var] = ret || :NONE if var > 0
return ret
@@ -880,9 +884,9 @@ end
def pbChooseApricorn(var = 0)
ret = nil
pbFadeOutIn do
scene = PokemonBag_Scene.new
screen = PokemonBagScreen.new(scene, $bag)
ret = screen.pbChooseItemScreen(proc { |item| GameData::Item.get(item).is_apricorn? })
bag_screen = UI::Bag.new($bag, mode: :choose_item)
bag_screen.set_filter_proc(proc { |item| GameData::Item.get(item).is_apricorn? })
ret = bag_screen.choose_item
end
$game_variables[var] = ret || :NONE if var > 0
return ret
@@ -891,9 +895,9 @@ end
def pbChooseFossil(var = 0)
ret = nil
pbFadeOutIn do
scene = PokemonBag_Scene.new
screen = PokemonBagScreen.new(scene, $bag)
ret = screen.pbChooseItemScreen(proc { |item| GameData::Item.get(item).is_fossil? })
bag_screen = UI::Bag.new($bag, mode: :choose_item)
bag_screen.set_filter_proc(proc { |item| GameData::Item.get(item).is_fossil? })
ret = bag_screen.choose_item
end
$game_variables[var] = ret || :NONE if var > 0
return ret

View File

@@ -25,11 +25,11 @@ ItemHandlers::UseText.add(:EXPALL, proc { |item|
# a Pokémon), calls the UseInField handler for it instead.
#===============================================================================
ItemHandlers::UseFromBag.add(:HONEY, proc { |item|
ItemHandlers::UseFromBag.add(:HONEY, proc { |item, bag_screen|
next 2
})
ItemHandlers::UseFromBag.add(:ESCAPEROPE, proc { |item|
ItemHandlers::UseFromBag.add(:ESCAPEROPE, proc { |item, bag_screen|
if !$game_player.can_map_transfer_with_follower?
pbMessage(_INTL("It can't be used when you have someone with you."))
next 0
@@ -41,13 +41,13 @@ ItemHandlers::UseFromBag.add(:ESCAPEROPE, proc { |item|
next 0
})
ItemHandlers::UseFromBag.add(:BICYCLE, proc { |item|
ItemHandlers::UseFromBag.add(:BICYCLE, proc { |item, bag_screen|
next (pbBikeCheck) ? 2 : 0
})
ItemHandlers::UseFromBag.copy(:BICYCLE, :MACHBIKE, :ACROBIKE)
ItemHandlers::UseFromBag.add(:OLDROD, proc { |item|
ItemHandlers::UseFromBag.add(:OLDROD, proc { |item, bag_screen|
notCliff = $game_map.passable?($game_player.x, $game_player.y, $game_player.direction, $game_player)
next 2 if $game_player.pbFacingTerrainTag.can_fish && ($PokemonGlobal.surfing || notCliff)
pbMessage(_INTL("Can't use that here."))
@@ -56,26 +56,28 @@ ItemHandlers::UseFromBag.add(:OLDROD, proc { |item|
ItemHandlers::UseFromBag.copy(:OLDROD, :GOODROD, :SUPERROD)
ItemHandlers::UseFromBag.add(:ITEMFINDER, proc { |item|
ItemHandlers::UseFromBag.add(:ITEMFINDER, proc { |item, bag_screen|
next 2
})
ItemHandlers::UseFromBag.copy(:ITEMFINDER, :DOWSINGMCHN, :DOWSINGMACHINE)
ItemHandlers::UseFromBag.add(:TOWNMAP, proc { |item|
pbFadeOutIn do
ItemHandlers::UseFromBag.add(:TOWNMAP, proc { |item, bag_screen|
pbFadeOutInWithUpdate(bag_screen&.sprites) do
scene = PokemonRegionMap_Scene.new(-1, false)
screen = PokemonRegionMapScreen.new(scene)
ret = screen.pbStartScreen
$game_temp.fly_destination = ret if ret
next 99999 if ret # Ugly hack to make Bag scene not reappear if flying
if ret
$game_temp.fly_destination = ret
bag_screen&.silent_end_screen
end
end
next ($game_temp.fly_destination) ? 2 : 0
})
ItemHandlers::UseFromBag.addIf(:move_machines,
proc { |item| GameData::Item.get(item).is_machine? },
proc { |item|
proc { |item, bag_screen|
if $player.pokemon_count == 0
pbMessage(_INTL("There is no Pokémon."))
next 0
@@ -159,10 +161,13 @@ EventHandlers.add(:on_player_step_taken, :repel_counter,
next if !pbConfirmMessage(_INTL("The repellent's effect wore off! Would you like to use another one?"))
ret = nil
pbFadeOutIn do
scene = PokemonBag_Scene.new
screen = PokemonBagScreen.new(scene, $bag)
ret = screen.pbChooseItemScreen(proc { |item| repels.include?(item) })
bag_screen = UI::Bag.new($bag, mode: :choose_item)
bag_screen.set_filter_proc(proc { |item| repels.include?(item) })
ret = bag_screen.choose_item
end
# TODO: Would be nice if this didn't call pbUseItem, so that pbUseItem would
# be exclusively called from the Bag screen and could rely on that
# screen existing.
pbUseItem($bag, ret) if ret
}
)

View File

@@ -264,6 +264,6 @@ ItemHandlers::UseInField.add(:POKERADAR, proc { |item|
next pbUsePokeRadar
})
ItemHandlers::UseFromBag.add(:POKERADAR, proc { |item|
ItemHandlers::UseFromBag.add(:POKERADAR, proc { |item, bag_screen|
next (pbCanUsePokeRadar?) ? 2 : 0
})

View File

@@ -82,7 +82,7 @@ class ReadyMenuButton < Sprite
textpos.push([_INTL(">99"), 230, 24, :right,
Color.new(248, 248, 248), Color.new(40, 40, 40), :outline])
else
textpos.push([_INTL("x{1}", qty), 230, 24, :right,
textpos.push([_INTL("×{1}", qty), 230, 24, :right,
Color.new(248, 248, 248), Color.new(40, 40, 40), :outline])
end
end

View File

@@ -1237,9 +1237,9 @@ class PokemonStorageScene
def pbChooseItem(bag)
ret = nil
pbFadeOutIn do
scene = PokemonBag_Scene.new
screen = PokemonBagScreen.new(scene, bag)
ret = screen.pbChooseItemScreen(proc { |item| GameData::Item.get(item).can_hold? })
bag_screen = UI::Bag.new(bag, mode: :choose_item)
bag_screen.set_filter_proc(proc { |itm| GameData::Item.get(itm).can_hold? })
ret = bag_screen.choose_item
end
return ret
end

View File

@@ -1,3 +1,5 @@
# TODO: Could inherit from class UI::BagVisuals and just change some graphics.
#===============================================================================
#
#===============================================================================
@@ -39,7 +41,7 @@ class Window_PokemonItemStorage < Window_DrawableCommand
baseColor = (index == @sortIndex) ? Color.new(248, 24, 24) : self.baseColor
textpos.push([itemname, rect.x, rect.y, :left, self.baseColor, self.shadowColor])
if GameData::Item.get(item).show_quantity?
qty = _ISPRINTF("x{1: 2d}", @bag[index][1])
qty = _ISPRINTF("×{1: 2d}", @bag[index][1])
sizeQty = self.contents.text_size(qty).width
xQty = rect.x + rect.width - sizeQty - 2
textpos.push([qty, xQty, rect.y, :left, baseColor, self.shadowColor])
@@ -190,6 +192,93 @@ class TossItemScene < ItemStorage_Scene
end
end
#===============================================================================
#
#===============================================================================
class ItemStorageScreen
def initialize(scene, bag)
@bag = bag
@scene = scene
end
def pbDisplay(text)
@scene.pbDisplay(text)
end
def pbConfirm(text)
return @scene.pbConfirm(text)
end
# UI logic for withdrawing an item in the item storage screen.
def pbWithdrawItemScreen
if !$PokemonGlobal.pcItemStorage
$PokemonGlobal.pcItemStorage = PCItemStorage.new
end
storage = $PokemonGlobal.pcItemStorage
@scene.pbStartScene(storage)
loop do
item = @scene.pbChooseItem
break if !item
itm = GameData::Item.get(item)
qty = storage.quantity(item)
if qty > 1 && !itm.is_important?
qty = @scene.pbChooseNumber(_INTL("How many do you want to withdraw?"), qty)
end
next if qty <= 0
if @bag.can_add?(item, qty)
if !storage.remove(item, qty)
raise "Can't delete items from storage"
end
if !@bag.add(item, qty)
raise "Can't withdraw items from storage"
end
@scene.pbRefresh
dispqty = (itm.is_important?) ? 1 : qty
itemname = (dispqty > 1) ? itm.portion_name_plural : itm.portion_name
pbDisplay(_INTL("Withdrew {1} {2}.", dispqty, itemname))
else
pbDisplay(_INTL("There's no more room in the Bag."))
end
end
@scene.pbEndScene
end
# UI logic for tossing an item in the item storage screen.
def pbTossItemScreen
if !$PokemonGlobal.pcItemStorage
$PokemonGlobal.pcItemStorage = PCItemStorage.new
end
storage = $PokemonGlobal.pcItemStorage
@scene.pbStartScene(storage)
loop do
item = @scene.pbChooseItem
break if !item
itm = GameData::Item.get(item)
if itm.is_important?
@scene.pbDisplay(_INTL("That's too important to toss out!"))
next
end
qty = storage.quantity(item)
itemname = itm.portion_name
itemnameplural = itm.portion_name_plural
if qty > 1
qty = @scene.pbChooseNumber(_INTL("Toss out how many {1}?", itemnameplural), qty)
end
next if qty <= 0
itemname = itemnameplural if qty > 1
next if !pbConfirm(_INTL("Is it OK to throw away {1} {2}?", qty, itemname))
if !storage.remove(item, qty)
raise "Can't delete items from storage"
end
@scene.pbRefresh
pbDisplay(_INTL("Threw away {1} {2}.", qty, itemname))
end
@scene.pbEndScene
end
end
#===============================================================================
# Common UI functions used in both the Bag and item storage screens.
# Displays messages and allows the user to choose a number/command.
@@ -282,10 +371,10 @@ module UIHelper
helpwindow.letterbyletter = false
curnumber = initnum
ret = 0
numwindow = Window_UnformattedTextPokemon.new("x000")
numwindow = Window_UnformattedTextPokemon.new("×000")
numwindow.viewport = helpwindow.viewport
numwindow.letterbyletter = false
numwindow.text = _ISPRINTF("x{1:03d}", curnumber)
numwindow.text = _ISPRINTF("×{1:03d}", curnumber)
numwindow.resizeToFit(numwindow.text, Graphics.width)
pbBottomRight(numwindow)
helpwindow.resizeHeightToFit(helpwindow.text, Graphics.width - numwindow.width)
@@ -309,28 +398,28 @@ module UIHelper
curnumber += 1
curnumber = 1 if curnumber > maximum
if curnumber != oldnumber
numwindow.text = _ISPRINTF("x{1:03d}", curnumber)
numwindow.text = _ISPRINTF("×{1:03d}", curnumber)
pbPlayCursorSE
end
elsif Input.repeat?(Input::DOWN)
curnumber -= 1
curnumber = maximum if curnumber < 1
if curnumber != oldnumber
numwindow.text = _ISPRINTF("x{1:03d}", curnumber)
numwindow.text = _ISPRINTF("×{1:03d}", curnumber)
pbPlayCursorSE
end
elsif Input.repeat?(Input::LEFT)
curnumber -= 10
curnumber = 1 if curnumber < 1
if curnumber != oldnumber
numwindow.text = _ISPRINTF("x{1:03d}", curnumber)
numwindow.text = _ISPRINTF("×{1:03d}", curnumber)
pbPlayCursorSE
end
elsif Input.repeat?(Input::RIGHT)
curnumber += 10
curnumber = maximum if curnumber > maximum
if curnumber != oldnumber
numwindow.text = _ISPRINTF("x{1:03d}", curnumber)
numwindow.text = _ISPRINTF("×{1:03d}", curnumber)
pbPlayCursorSE
end
end

View File

@@ -15,34 +15,50 @@ def pbPCItemStorage
_INTL("Go back to the previous menu.")], -1, command)
case command
when 0 # Withdraw Item
if !$PokemonGlobal.pcItemStorage
$PokemonGlobal.pcItemStorage = PCItemStorage.new
end
$PokemonGlobal.pcItemStorage ||= PCItemStorage.new
if $PokemonGlobal.pcItemStorage.empty?
pbMessage(_INTL("There are no items."))
else
pbFadeOutIn do
scene = WithdrawItemScene.new
screen = PokemonBagScreen.new(scene, $bag)
screen = ItemStorageScreen.new(scene, $bag)
screen.pbWithdrawItemScreen
end
end
when 1 # Deposit Item
$PokemonGlobal.pcItemStorage ||= PCItemStorage.new
item_storage = $PokemonGlobal.pcItemStorage
pbFadeOutIn do
scene = PokemonBag_Scene.new
screen = PokemonBagScreen.new(scene, $bag)
screen.pbDepositItemScreen
bag_screen = UI::Bag.new($bag, mode: :choose_item)
given_item = bag_screen.choose_item do |item|
item_data = GameData::Item.get(item)
qty = $bag.quantity(item)
if qty > 1 && !item_data.is_important?
qty = bag_screen.choose_number(_INTL("How many do you want to deposit?"), qty)
end
if qty > 0
if item_storage.can_add?(item, qty)
raise "Can't delete items from Bag" if !$bag.remove(item, qty)
raise "Can't deposit items to storage" if !item_storage.add(item, qty)
bag_screen.refresh
disp_qty = (item_data.is_important?) ? 1 : qty
item_name = (disp_qty > 1) ? item_data.portion_name_plural : item_data.portion_name
bag_screen.show_message(_INTL("Deposited {1} {2}.", disp_qty, item_name))
else
bag_screen.show_message(_INTL("There's no room to store items."))
end
end
next false
end
end
when 2 # Toss Item
if !$PokemonGlobal.pcItemStorage
$PokemonGlobal.pcItemStorage = PCItemStorage.new
end
$PokemonGlobal.pcItemStorage ||= PCItemStorage.new
if $PokemonGlobal.pcItemStorage.empty?
pbMessage(_INTL("There are no items."))
else
pbFadeOutIn do
scene = TossItemScene.new
screen = PokemonBagScreen.new(scene, $bag)
screen = ItemStorageScreen.new(scene, $bag)
screen.pbTossItemScreen
end
end

View File

@@ -27,12 +27,7 @@ class PokemonMartAdapter
end
def getDisplayName(item)
item_name = GameData::Item.get(item).name
if GameData::Item.get(item).is_machine?
machine = GameData::Item.get(item).move
item_name = _INTL("{1} {2}", item_name, GameData::Move.get(machine).name)
end
return item_name
return GameData::Item.get(item).display_name
end
def getDisplayNamePlural(item)
@@ -162,7 +157,7 @@ class SellAdapter
def getDisplayPrice(item)
if @adapter.showQuantity?(item)
return sprintf("x%d", @adapter.getQuantity(item))
return sprintf("×%d", @adapter.getQuantity(item))
else
return ""
end
@@ -309,6 +304,10 @@ class PokemonMart_Scene
end
def pbStartSellScene2(bag, adapter)
# TODO: Don't have a subscene. Make a new BagVisuals class for choosing an
# item to sell, and open that. It can inherit from class
# UI::BagVisuals and add the money window (that may be all that needs
# adding).
@subscene = PokemonBag_Scene.new
@adapter = adapter
@viewport2 = Viewport.new(0, 0, Graphics.width, Graphics.height)
@@ -487,7 +486,7 @@ class PokemonMart_Scene
numwindow.height = 64
numwindow.baseColor = Color.new(88, 88, 80)
numwindow.shadowColor = Color.new(168, 184, 184)
numwindow.text = _INTL("x{1}<r>$ {2}", curnumber, (curnumber * itemprice).to_s_formatted)
numwindow.text = _INTL("×{1}<r>$ {2}", curnumber, (curnumber * itemprice).to_s_formatted)
pbBottomRight(numwindow)
numwindow.y -= helpwindow.height
loop do
@@ -500,28 +499,28 @@ class PokemonMart_Scene
curnumber -= 10
curnumber = 1 if curnumber < 1
if curnumber != oldnumber
numwindow.text = _INTL("x{1}<r>$ {2}", curnumber, (curnumber * itemprice).to_s_formatted)
numwindow.text = _INTL("×{1}<r>$ {2}", curnumber, (curnumber * itemprice).to_s_formatted)
pbPlayCursorSE
end
elsif Input.repeat?(Input::RIGHT)
curnumber += 10
curnumber = maximum if curnumber > maximum
if curnumber != oldnumber
numwindow.text = _INTL("x{1}<r>$ {2}", curnumber, (curnumber * itemprice).to_s_formatted)
numwindow.text = _INTL("×{1}<r>$ {2}", curnumber, (curnumber * itemprice).to_s_formatted)
pbPlayCursorSE
end
elsif Input.repeat?(Input::UP)
curnumber += 1
curnumber = 1 if curnumber > maximum
if curnumber != oldnumber
numwindow.text = _INTL("x{1}<r>$ {2}", curnumber, (curnumber * itemprice).to_s_formatted)
numwindow.text = _INTL("×{1}<r>$ {2}", curnumber, (curnumber * itemprice).to_s_formatted)
pbPlayCursorSE
end
elsif Input.repeat?(Input::DOWN)
curnumber -= 1
curnumber = maximum if curnumber < 1
if curnumber != oldnumber
numwindow.text = _INTL("x{1}<r>$ {2}", curnumber, (curnumber * itemprice).to_s_formatted)
numwindow.text = _INTL("×{1}<r>$ {2}", curnumber, (curnumber * itemprice).to_s_formatted)
pbPlayCursorSE
end
elsif Input.trigger?(Input::USE)

View File

@@ -343,7 +343,7 @@ class BattlePointShop_Scene
numwindow.height = 64
numwindow.baseColor = Color.new(88, 88, 80)
numwindow.shadowColor = Color.new(168, 184, 184)
numwindow.text = _INTL("x{1}<r>{2} BP", curnumber, (curnumber * itemprice).to_s_formatted)
numwindow.text = _INTL("×{1}<r>{2} BP", curnumber, (curnumber * itemprice).to_s_formatted)
pbBottomRight(numwindow)
numwindow.y -= helpwindow.height
loop do
@@ -356,28 +356,28 @@ class BattlePointShop_Scene
curnumber -= 10
curnumber = 1 if curnumber < 1
if curnumber != oldnumber
numwindow.text = _INTL("x{1}<r>{2} BP", curnumber, (curnumber * itemprice).to_s_formatted)
numwindow.text = _INTL("×{1}<r>{2} BP", curnumber, (curnumber * itemprice).to_s_formatted)
pbPlayCursorSE
end
elsif Input.repeat?(Input::RIGHT)
curnumber += 10
curnumber = maximum if curnumber > maximum
if curnumber != oldnumber
numwindow.text = _INTL("x{1}<r>{2} BP", curnumber, (curnumber * itemprice).to_s_formatted)
numwindow.text = _INTL("×{1}<r>{2} BP", curnumber, (curnumber * itemprice).to_s_formatted)
pbPlayCursorSE
end
elsif Input.repeat?(Input::UP)
curnumber += 1
curnumber = 1 if curnumber > maximum
if curnumber != oldnumber
numwindow.text = _INTL("x{1}<r>{2} BP", curnumber, (curnumber * itemprice).to_s_formatted)
numwindow.text = _INTL("×{1}<r>{2} BP", curnumber, (curnumber * itemprice).to_s_formatted)
pbPlayCursorSE
end
elsif Input.repeat?(Input::DOWN)
curnumber -= 1
curnumber = maximum if curnumber < 1
if curnumber != oldnumber
numwindow.text = _INTL("x{1}<r>{2} BP", curnumber, (curnumber * itemprice).to_s_formatted)
numwindow.text = _INTL("×{1}<r>{2} BP", curnumber, (curnumber * itemprice).to_s_formatted)
pbPlayCursorSE
end
elsif Input.trigger?(Input::USE)

View File

@@ -315,6 +315,12 @@ module UI
#---------------------------------------------------------------------------
def index
return 0
end
#---------------------------------------------------------------------------
def show_message(text)
@sprites[:speech_box].visible = true
@sprites[:speech_box].text = text
@@ -451,8 +457,16 @@ module UI
end
# TODO: Rewrite this.
def choose_number(help_text, maximum, init_num = 1)
return UIHelper.pbChooseNumber(@sprites[:speech_box], help_text, maximum, init_num) { update_visuals }
def choose_number(help_text, maximum, init_value = 1)
if maximum.is_a?(ChooseNumberParams)
return pbMessageChooseNumber(help_text, maximum) { update_visuals }
end
return UIHelper.pbChooseNumber(@sprites[:speech_box], help_text, maximum, init_value) { update_visuals }
end
#---------------------------------------------------------------------------
def refresh_on_index_changed(old_index)
end
#---------------------------------------------------------------------------
@@ -471,8 +485,12 @@ module UI
loop do
Graphics.update
Input.update
old_index = index
update_visuals
refresh_on_index_changed(old_index) if index != old_index
old_index = index
ret = update_input
refresh_on_index_changed(old_index) if index != old_index
break if ret
end
return ret
@@ -483,8 +501,8 @@ module UI
# The logic class.
#=============================================================================
class BaseScreen
attr_reader :visuals
attr_reader :result
attr_reader :visuals
attr_accessor :result
def initialize
@disposed = false
@@ -515,6 +533,16 @@ module UI
#-----------------------------------------------------------------------------
def sprites
return @visuals.sprites
end
def index
return @visuals.index
end
#-----------------------------------------------------------------------------
def show_message(text)
@visuals.show_message(text)
end
@@ -546,8 +574,8 @@ module UI
return show_choice(commands)
end
def choose_number(help_text, maximum, init_num = 1)
return @visuals.choose_number(help_text, maximum, init_num)
def choose_number(help_text, maximum, init_value = 1)
return @visuals.choose_number(help_text, maximum, init_value)
end
alias pbChooseNumber choose_number
@@ -597,7 +625,7 @@ module UI
return nil if action_hash[:condition] && !action_hash[:condition].call(self)
if action_hash[:menu]
choice = show_choice_from_menu_handler(action_hash[:menu], action_hash[:menu_message]&.call(self))
perform_action(choice) if choice
return perform_action(choice) if choice
elsif action_hash[:effect]
return perform_action_effect(action_hash)
end

View File

@@ -221,9 +221,9 @@ MenuHandlers.add(:pause_menu, :bag, {
pbPlayDecisionSE
item = nil
pbFadeOutIn do
scene = PokemonBag_Scene.new
screen = PokemonBagScreen.new(scene, $bag)
item = screen.pbStartScreen
bag_screen = UI::Bag.new($bag)
bag_screen.main
item = bag_screen.result
(item) ? menu.silent_end_screen : menu.refresh
end
next false if !item
@@ -267,7 +267,7 @@ MenuHandlers.add(:pause_menu, :town_map, {
})
MenuHandlers.add(:pause_menu, :trainer_card, {
"name" => proc { next $player.name },
"name" => proc { |menu| next $player.name },
"order" => 50,
"effect" => proc { |menu|
pbPlayDecisionSE

View File

@@ -326,8 +326,8 @@ end
#
#===============================================================================
class UI::PartyVisuals < UI::BaseVisuals
attr_reader :index
attr_reader :sprites
attr_reader :index
attr_reader :sub_mode
GRAPHICS_FOLDER = "Party/" # Subfolder in Graphics/UI
@@ -783,10 +783,6 @@ class UI::Party < UI::BaseScreen
#-----------------------------------------------------------------------------
def index
return @visuals.index
end
def set_index(new_index)
@visuals.set_index(new_index)
end
@@ -795,10 +791,6 @@ class UI::Party < UI::BaseScreen
return (index < @party.length) ? @party[index] : nil
end
def sprites
return @visuals.sprites
end
def can_access_storage?
return @visuals.can_access_storage?
end
@@ -1194,18 +1186,18 @@ UIActionHandlers.add(UI::Party::SCREEN_ID, :item_use, {
pkmn = screen.pokemon
used_item = nil
pbFadeOutInWithUpdate(screen.sprites) do
bag_scene = PokemonBag_Scene.new
bag_screen = PokemonBagScreen.new(bag_scene, $bag)
used_item = bag_screen.pbChooseItemScreen(proc { |item|
itm = GameData::Item.get(item)
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 !pbCanUseOnPokemon?(itm)
next false if pkmn.hyper_mode && !GameData::Item.get(item)&.is_scent?
if itm.is_machine?
move = itm.move
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)
@@ -1219,9 +1211,9 @@ UIActionHandlers.add(UI::Party::SCREEN_ID, :item_give, {
pkmn = screen.pokemon
given_item = nil
pbFadeOutInWithUpdate(screen.sprites) do
bag_scene = PokemonBag_Scene.new
bag_screen = PokemonBagScreen.new(bag_scene, $bag)
given_item = bag_screen.pbChooseItemScreen(proc { |item| GameData::Item.get(item).can_hold? })
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)
@@ -1360,6 +1352,31 @@ UIActionHandlers.add(UI::Party::SCREEN_ID, :use_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
@@ -1450,31 +1467,6 @@ MenuHandlers.add(:party_screen_interact, :cancel, {
"order" => 9999
})
#-------------------------------------------------------------------------------
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
})
#===============================================================================
# Open the party screen.
#===============================================================================

View File

@@ -1489,10 +1489,10 @@ UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :interact_menu, {
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :give_item, {
:effect => proc { |screen|
item = nil
pbFadeOutIn do
bag_scene = PokemonBag_Scene.new
bag_screen = PokemonBagScreen.new(bag_scene, $bag)
item = bag_screen.pbChooseItemScreen(proc { |itm| GameData::Item.get(itm).can_hold? })
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)
}
@@ -1507,7 +1507,7 @@ UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :take_item, {
UIActionHandlers.add(UI::PokemonSummary::SCREEN_ID, :pokedex, {
:effect => proc { |screen|
$player.pokedex.register_last_seen(screen.pokemon)
pbFadeOutIn do
pbFadeOutInWithUpdate(screen.sprites) do
dex_scene = PokemonPokedexInfo_Scene.new
dex_screen = PokemonPokedexInfoScreen.new(dex_scene)
dex_screen.pbStartSceneSingle(screen.pokemon.species)

View File

@@ -0,0 +1,883 @@
#===============================================================================
#
#===============================================================================
class UI::BagVisualsList < Window_DrawableCommand
attr_accessor :sorting
def initialize(bag, x, y, width, height, viewport)
@bag = bag
@sorting = false
super(x, y, width, height, viewport)
@selarrow = AnimatedBitmap.new(bag_folder + "cursor")
@swaparrow = AnimatedBitmap.new(bag_folder + "cursor_swap")
self.windowskin = nil
end
def dispose
@swaparrow.dispose
super
end
#-----------------------------------------------------------------------------
def page_row_max; return UI::BagVisuals::ITEMS_VISIBLE; end
def page_item_max; return page_row_max; end
def itemCount
return (@items&.length || 0) + 1 # The extra 1 is the Close Bag option
end
def bag_folder
return UI::BagVisuals::UI_FOLDER + UI::BagVisuals::GRAPHICS_FOLDER
end
def items=(value)
@items = value
refresh
end
def item_id
return (@items[self.index]) ? @items[self.index][0] : nil
end
#-----------------------------------------------------------------------------
# Custom method that allows for an extra option to be displayed above and
# below the main visible list.
def itemRect(item)
if item < 0 || item >= @item_max || item < self.top_item - 1 ||
item > self.top_item + self.page_item_max
return Rect.new(0, 0, 0, 0)
end
cursor_width = (self.width - self.borderX - ((@column_max - 1) * @column_spacing)) / @column_max
x = item % @column_max * (cursor_width + @column_spacing)
y = (item / @column_max * @row_height) - @virtualOy
return Rect.new(x, y, cursor_width, @row_height)
end
#-----------------------------------------------------------------------------
# This draws all the visible options first, and then draws the cursor. It also
# draws an additional option above the main visible ones.
def refresh
@item_max = itemCount
update_cursor_rect
dwidth = self.width - self.borderX
dheight = self.height - self.borderY
self.contents = pbDoEnsureBitmap(self.contents, dwidth, dheight)
self.contents.clear
@item_max.times do |i|
next if i < self.top_item - 1 || i > self.top_item + self.page_item_max
drawItem(i, @item_max, itemRect(i))
end
drawCursor(self.index, itemRect(self.index))
end
def switching_base_color=(value)
@switching_base_color = value
end
def switching_shadow_color=(value)
@switching_shadow_color = value
end
def drawItem(index, _count, rect)
textpos = []
rect = Rect.new(rect.x + 16, rect.y + 16, rect.width - 16, rect.height)
if index == self.itemCount - 1
textpos.push([_INTL("CLOSE BAG"), rect.x, rect.y + 2, :left, self.baseColor, self.shadowColor])
else
this_item_id = @items[index][0]
item_data = GameData::Item.get(this_item_id)
baseColor = self.baseColor
shadowColor = self.shadowColor
if @sorting && index == self.index
baseColor = @switching_base_color || self.baseColor
shadowColor = @switching_shadow_color || self.shadowColor
end
# Draw item name
textpos.push(
[item_data.display_name, rect.x, rect.y + 2, :left, baseColor, shadowColor]
)
# Draw register icon
showing_register_icon = false
if item_data.is_important?
if @bag.registered?(this_item_id)
pbDrawImagePositions(
self.contents,
[[bag_folder + _INTL("icon_register"), rect.x + rect.width - 72, rect.y + 8, 0, 0, -1, 24]]
)
showing_register_icon = true
elsif pbCanRegisterItem?(this_item_id)
pbDrawImagePositions(
self.contents,
[[bag_folder + _INTL("icon_register"), rect.x + rect.width - 72, rect.y + 8, 0, 24, -1, 24]]
)
showing_register_icon = true
end
end
# Draw quantity
if item_data.show_quantity? && !showing_register_icon
qty = @items[index][1]
qtytext = _ISPRINTF("×{1: 3d}", qty)
xQty = rect.x + rect.width - self.contents.text_size(qtytext).width - 16
textpos.push([qtytext, xQty, rect.y + 2, :left, baseColor, shadowColor])
end
end
pbDrawTextPositions(self.contents, textpos)
end
def drawCursor(index, rect)
if self.index == index
bmp = (@sorting) ? @swaparrow.bitmap : @selarrow.bitmap
pbCopyBitmap(self.contents, bmp, rect.x, rect.y + 2)
end
end
#-----------------------------------------------------------------------------
def update
super
@uparrow.visible = false
@downarrow.visible = false
end
end
#===============================================================================
#
#===============================================================================
class UI::BagVisuals < UI::BaseVisuals
attr_reader :sprites
GRAPHICS_FOLDER = "Bag/" # 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)], # Summary screen's white
# :black => [Color.new(64, 64, 64), Color.new(176, 176, 176)] # Summary screen's black
:white => [Color.new(248, 248, 248), Color.new(0, 0, 0)],
:black => [Color.new(88, 88, 80), Color.new(168, 184, 184)],
:switching => [Color.new(224, 0, 0), Color.new(248, 144, 144)]
}
ITEMS_VISIBLE = 7
def initialize(bag, mode = :normal)
@bag = bag
@mode = mode
@pocket = @bag.last_viewed_pocket
super()
end
def initialize_bitmaps
@bitmaps[:slider] = AnimatedBitmap.new(graphics_folder + "icon_slider")
@bitmaps[:pocket_icons] = AnimatedBitmap.new(graphics_folder + "icon_pocket")
end
def initialize_overlay
super
add_overlay(:slider_overlay, 36, 470 + 38 + 38)
@sprites[:slider_overlay].x = 470
@sprites[:slider_overlay].y = 54 - 38
end
def initialize_sprites
initialize_pocket_sprites
initialize_item_list
initialize_item_sprites
end
def initialize_pocket_sprites
@sprites[:bag] = IconSprite.new(30, 20, @viewport)
@sprites[:pocket_icons] = BitmapSprite.new(186, 32, @viewport)
@sprites[:pocket_icons].x = 0
@sprites[:pocket_icons].y = 224
# TODO: I won't need these two animated arrows once new ones are put next to
# the pocket icons.
# Arrow to indicate more ribbons are above the ones visible when navigating ribbons
add_animated_arrow(:left_arrow, -4, 76, :left)
@sprites[:left_arrow].z = 1700
# TODO: Set visibility.
# @sprites[:left_arrow].visible = (!@choosing || numfilledpockets > 1)
# Arrow to indicate more ribbons are below the ones visible when navigating ribbons
add_animated_arrow(:right_arrow, 150, 76, :right)
@sprites[:right_arrow].z = 1700
# TODO: Set visibility.
# @sprites[:right_arrow].visible = (!@choosing || numfilledpockets > 1)
end
def initialize_item_list
@sprites[:item_list] = UI::BagVisualsList.new(@bag, 168, -8, 314, 40 + 32 + (ITEMS_VISIBLE * 32), @viewport)
@sprites[:item_list].baseColor = TEXT_COLOR_THEMES[:black][0]
@sprites[:item_list].shadowColor = TEXT_COLOR_THEMES[:black][1]
@sprites[:item_list].switching_base_color = TEXT_COLOR_THEMES[:switching][0]
@sprites[:item_list].switching_shadow_color = TEXT_COLOR_THEMES[:switching][1]
@sprites[:item_list].items = @bag.pockets[@pocket]
@sprites[:item_list].index = @bag.last_viewed_index(@pocket)
@sprites[:item_list].active = false
end
def initialize_item_sprites
# Selected item's icon
@sprites[:item_icon] = ItemIconSprite.new(48, Graphics.height - 48, nil, @viewport)
# Selected item's description text box
@sprites[:item_description] = Window_UnformattedTextPokemon.newWithSize(
"", 72, 272, Graphics.width - 96, 128, @viewport
)
@sprites[:item_description].baseColor = TEXT_COLOR_THEMES[:white][0]
@sprites[:item_description].shadowColor = TEXT_COLOR_THEMES[:white][1]
@sprites[:item_description].visible = true
@sprites[:item_description].windowskin = nil
end
#-----------------------------------------------------------------------------
def background_filename
return gendered_filename(self.class::BACKGROUND_FILENAME + "_" + @pocket.to_s)
end
def index
return @sprites[:item_list].index
end
def item
return @sprites[:item_list].item_id
end
#-----------------------------------------------------------------------------
def set_filter_proc(filter_proc)
@filter_proc = filter_proc
# Create filtered pocket lists
@filtered_list = []
(1...@bag.pockets.length).each do |pckt|
@filtered_list[pckt] = []
@bag.pockets[pckt].length.times do |j|
@filtered_list[pckt].push(@bag.pockets[pckt][j]) if @filter_proc.call(@bag.pockets[pckt][j][0])
end
end
# Ensure current pocket is one that isn't empty
new_pocket = 1
if @mode == :choose_item_in_battle && !@filtered_list[@bag.last_viewed_pocket].empty?
new_pocket = @bag.last_viewed_pocket
end
num_pockets = PokemonBag.pocket_count
num_pockets.times do |i|
next_pocket = new_pocket + i
next_pocket -= num_pockets if next_pocket > num_pockets
next if @filtered_list[next_pocket].empty?
new_pocket = next_pocket
break
end
new_pocket = 1 if @filtered_list[new_pocket].empty? # In case all pockets are empty
# Set the new pocket
set_pocket(new_pocket)
@sprites[:item_list].index = 0
end
def set_pocket(new_pocket)
@pocket = new_pocket
@bag.last_viewed_pocket = @pocket
if @filtered_list
@sprites[:item_list].items = @filtered_list[@pocket]
else
@sprites[:item_list].items = @bag.pockets[@pocket]
end
@sprites[:item_list].index = @bag.last_viewed_index(@pocket)
refresh
end
def go_to_next_pocket
new_pocket = @pocket
loop do
new_pocket = (new_pocket >= PokemonBag.pocket_count) ? 1 : new_pocket + 1
break if ![:choose_item, :choose_item_in_battle].include?(@mode)
break if new_pocket == @pocket # Bag is empty somehow
if @filtered_list
break if @filtered_list[new_pocket].length > 0
else
break if @bag.pockets[new_pocket].length > 0
end
end
return if new_pocket == @pocket
pbPlayCursorSE
set_pocket(new_pocket)
end
def go_to_previous_pocket
new_pocket = @pocket
loop do
new_pocket = (new_pocket <= 1) ? PokemonBag.pocket_count : new_pocket - 1
break if ![:choose_item, :choose_item_in_battle].include?(@mode)
break if new_pocket == @pocket # Bag is empty somehow
if @filtered_list
break if @filtered_list[new_pocket].length > 0
else
break if @bag.pockets[new_pocket].length > 0
end
end
return if new_pocket == @pocket
pbPlayCursorSE
set_pocket(new_pocket)
end
def switch_index
return @switch_index || -1
end
def can_switch?
return false if @mode != :normal || @filtered_list
return false if @bag.pockets[@pocket].length <= 1
return false if index >= @bag.pockets[@pocket].length
return false if Settings::BAG_POCKET_AUTO_SORT[@pocket - 1]
return true
end
def switching?
return switch_index >= 0
end
def start_switching(sw_index)
@switch_index = sw_index
@sprites[:item_list].sorting = true
refresh_item_list
end
def end_switching
@switch_index = -1
@sprites[:item_list].sorting = false
refresh_item_list
end
def cancel_switching
this_pocket = @bag.pockets[@pocket]
this_pocket.insert(@switch_index, this_pocket.delete_at(@sprites[:item_list].index))
@sprites[:item_list].items = this_pocket
@sprites[:item_list].index = @switch_index
end_switching
refresh
end
#-----------------------------------------------------------------------------
def refresh
super
refresh_background
refresh_pocket_icons
refresh_pocket
refresh_item_list
refresh_slider
refresh_selected_item
end
def refresh_background
@sprites[:background].setBitmap(graphics_folder + background_filename)
end
def refresh_pocket_icons
@sprites[:pocket_icons].bitmap.clear
# Draw the pocket icons
if [:choose_item, :choose_item_in_battle].include?(@mode) && @filtered_list
(1...@bag.pockets.length).each do |i|
next if @filtered_list[i].length > 0
draw_image(@bitmaps[:pocket_icons], 6 + ((i - 1) * 22), 6,
(i - 1) * 20, 28, 20, 20, overlay: :pocket_icons)
end
end
draw_image(@bitmaps[:pocket_icons], 2 + ((@pocket - 1) * 22), 2,
(@pocket - 1) * 28, 0, 28, 28, overlay: :pocket_icons)
# TODO: Draw the left and right arrows, if @mode != :choose_item ||
# @filtered_list has 2+ non-empty pockets.
end
def refresh_pocket
# Draw pocket's name
draw_text(PokemonBag.pocket_names[@pocket - 1], 94, 186, align: :center, theme: :black)
# Set the bag sprite
bag_sprite_filename = graphics_folder + gendered_filename(sprintf("bag_%d", @pocket))
@sprites[:bag].setBitmap(bag_sprite_filename)
end
def refresh_item_list
@sprites[:item_list].refresh
end
# TODO: Probably turn this method into a helper method. Put the defined values
# in constants so that initializing the slider overlay can use them to
# automatically determine its size.
def refresh_slider
@sprites[:slider_overlay].bitmap.clear
# Define useful values
slider_rects = {
:up_arrow => [0, 0, 36, 38],
:down_arrow => [0, 38, 36, 38],
:box_top => [36, 0, 36, 4],
:box_middle => [36, 4, 36, 16],
:box_bottom => [36, 20, 36, 18]
}
slider_x = 0
slider_y = slider_rects[:up_arrow][3]
slider_height = 174
min_box_height = slider_rects[:box_top][3] + slider_rects[:box_middle][3] + slider_rects[:box_bottom][3]
# Draw things
show_slider = false
# Draw slider up arrow
if @sprites[:item_list].top_row > 0
draw_image(@bitmaps[:slider], slider_x, slider_y - slider_rects[:up_arrow][3],
*slider_rects[:up_arrow], overlay: :slider_overlay)
show_slider = true
end
# Draw slider down arrow
if @sprites[:item_list].top_item + @sprites[:item_list].page_item_max < @sprites[:item_list].itemCount
draw_image(@bitmaps[:slider], slider_x, slider_y + slider_height,
*slider_rects[:down_arrow], overlay: :slider_overlay)
show_slider = true
end
# Draw slider box
if show_slider
box_height = (slider_height * @sprites[:item_list].page_row_max / @sprites[:item_list].row_max).floor
box_height += [(slider_height - box_height) / 2, slider_height / 6].min # Make it bigger than expected
box_height = [box_height.floor, min_box_height].max
box_y = slider_rects[:up_arrow][3]
box_y += ((slider_height - box_height) * @sprites[:item_list].top_row / (@sprites[:item_list].row_max - @sprites[:item_list].page_row_max)).floor
# Draw slider box top
draw_image(@bitmaps[:slider], slider_x, box_y,
*slider_rects[:box_top], overlay: :slider_overlay)
# Draw slider box middle
middle_height = box_height - slider_rects[:box_top][3] - slider_rects[:box_bottom][3]
iterations = (middle_height / slider_rects[:box_middle][3].to_f).ceil
iterations.times do |i|
segment_y = box_y + slider_rects[:box_top][3] + (i * slider_rects[:box_middle][3])
box_middle_rect = slider_rects[:box_middle].clone
if i == iterations - 1 # Last part
box_middle_rect[3] = middle_height - (i * slider_rects[:box_middle][3])
end
draw_image(@bitmaps[:slider], slider_x, segment_y,
*box_middle_rect, overlay: :slider_overlay)
end
# Draw slider box bottom
draw_image(@bitmaps[:slider], slider_x, box_y + box_height - slider_rects[:box_bottom][3],
*slider_rects[:box_bottom], overlay: :slider_overlay)
end
end
def refresh_selected_item
selected_item = item
# Set the selected item's icon
@sprites[:item_icon].item = selected_item
# Set the selected item's description
if selected_item
@sprites[:item_description].text = GameData::Item.get(selected_item).description
else
@sprites[:item_description].text = _INTL("Close bag.")
end
end
def refresh_on_index_changed(old_index)
if switching?
# Skip past "Cancel"
this_pocket = @bag.pockets[@pocket]
if index >= this_pocket.length
@sprites[:item_list].index = (old_index == this_pocket.length - 1) ? 0 : this_pocket.length - 1
end
# Move the item being switched
this_pocket.insert(index, this_pocket.delete_at(old_index))
@sprites[:item_list].items = this_pocket
end
@bag.set_last_viewed_index(@pocket, index)
refresh_slider
refresh_selected_item
end
#-----------------------------------------------------------------------------
def update_input
# Check for movement to a new pocket
if Input.repeat?(Input::LEFT)
go_to_previous_pocket if !switching?
elsif Input.repeat?(Input::RIGHT)
go_to_next_pocket if !switching?
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_interaction(input)
case input
when Input::USE
if switching?
pbPlayDecisionSE
return :switch_item_end
end
if !item # "CLOSE BAG"
pbPlayCloseMenuSE
return :quit
end
pbPlayDecisionSE
return :interact_menu
when Input::ACTION
if switching?
pbPlayDecisionSE
return :switch_item_end
elsif can_switch?
pbPlayDecisionSE
return :switch_item_start
end
when Input::SPECIAL
# TODO: May be unused. Originally toggled registering an item, but this
# has been disabled for ages.
when Input::BACK
if switching?
pbPlayCancelSE
return :switch_item_cancel
end
pbPlayCloseMenuSE
return :quit
end
return nil
end
def navigate
@sprites[:item_list].active = true
ret = super
@sprites[:item_list].active = false
return ret
end
#-----------------------------------------------------------------------------
def update_input_choose_item
# Check for movement to a new pocket
if Input.repeat?(Input::LEFT)
go_to_previous_pocket
elsif Input.repeat?(Input::RIGHT)
go_to_next_pocket
end
# Check for interaction
if Input.trigger?(Input::USE)
return update_interaction_choose_item(Input::USE)
elsif Input.trigger?(Input::BACK)
return update_interaction_choose_item(Input::BACK)
end
return nil
end
def update_interaction_choose_item(input)
case input
when Input::USE
if !item # "CLOSE BAG"
pbPlayCloseMenuSE
return :quit
end
pbPlayDecisionSE
return :chosen
when Input::BACK
pbPlayCloseMenuSE
return :quit
end
return nil
end
def navigate_choose_item
@sprites[:item_list].active = true
ret = nil
loop do
Graphics.update
Input.update
old_index = index
update_visuals
refresh_on_index_changed(old_index) if index != old_index
ret = update_input_choose_item
break if ret
end
@sprites[:item_list].active = false
return ret
end
end
#===============================================================================
#
#===============================================================================
class UI::Bag < UI::BaseScreen
attr_reader :bag
SCREEN_ID = :bag_screen
def initialize(bag, mode: :normal)
@bag = bag
@mode = mode
super()
end
def initialize_visuals
@visuals = UI::BagVisuals.new(@bag, @mode)
end
#-----------------------------------------------------------------------------
def item
return nil if @visuals.item.nil?
return GameData::Item.get(@visuals.item)
end
def set_filter_proc(filter_proc)
@visuals.set_filter_proc(filter_proc)
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 cancel_switching
@visuals.cancel_switching
end
def switch_items(index1, index2)
@visuals.end_switching
end
#-----------------------------------------------------------------------------
def choose_item
start_screen
loop do
on_start_main_loop
chosen_item = choose_item_core
if chosen_item && block_given?
next if !yield chosen_item
end
@result = chosen_item
break
end
end_screen
return @result
end
def choose_item_core
command = @visuals.navigate_choose_item
return item.id if command == :chosen
return nil
end
end
#===============================================================================
#
#===============================================================================
# Shows a choice menu using the MenuHandlers options below.
UIActionHandlers.add(UI::Bag::SCREEN_ID, :screen_menu, {
:menu => :bag_screen_menu,
:menu_message => proc { |screen| _INTL("Choose an option.") }
})
#-------------------------------------------------------------------------------
# Shows a choice menu using the MenuHandlers options below.
UIActionHandlers.add(UI::Bag::SCREEN_ID, :interact_menu, {
:menu => :bag_screen_interact,
:menu_message => proc { |screen| _INTL("{1} is selected.", screen.item.name) }
})
UIActionHandlers.add(UI::Bag::SCREEN_ID, :read_mail, {
:effect => proc { |screen|
pbFadeOutInWithUpdate(screen.sprites) do
pbDisplayMail(Mail.new(screen.item.id, "", ""))
end
}
})
UIActionHandlers.add(UI::Bag::SCREEN_ID, :use, {
:returns_value => true,
:effect => proc { |screen|
item = screen.item.id
ret = pbUseItem(screen.bag, item, screen)
# ret: 0=Item wasn't used; 1=Item used; 2=Close Bag to use in field
if ret == 2
screen.result = item
next :quit
end
screen.refresh
next nil
}
})
UIActionHandlers.add(UI::Bag::SCREEN_ID, :give, {
:effect => proc { |screen|
if $player.pokemon_count == 0
screen.show_message(_INTL("There is no Pokémon."))
elsif screen.item.is_important?
screen.show_message(_INTL("The {1} can't be held.", screen.item.portion_name))
else
pbFadeOutInWithUpdate(screen.sprites) do
party_screen = UI::Party.new($player.party, mode: :choose_pokemon)
party_screen.choose_pokemon do |pkmn, party_index|
pbGiveItemToPokemon(screen.item.id, party_screen.pokemon, party_screen, party_index) if party_index >= 0
next true
end
screen.refresh
end
end
}
})
UIActionHandlers.add(UI::Bag::SCREEN_ID, :toss, {
:effect => proc { |screen|
qty = screen.bag.quantity(screen.item.id)
if qty > 1
help_text = _INTL("Toss out how many {1}?", screen.item.portion_name_plural)
qty = screen.choose_number(help_text, qty)
end
if qty > 0
item_name = (qty > 1) ? screen.item.portion_name_plural : screen.item.portion_name
if screen.show_confirm_message(_INTL("Is it OK to throw away {1} {2}?", qty, item_name))
qty.times { screen.bag.remove(screen.item.id) }
screen.refresh
screen.show_message(_INTL("Threw away {1} {2}.", qty, item_name))
end
end
}
})
# Handles both registering and unregistering the item.
UIActionHandlers.add(UI::Bag::SCREEN_ID, :register, {
:effect => proc { |screen|
if screen.bag.registered?(screen.item.id)
screen.bag.unregister(screen.item.id)
else
screen.bag.register(screen.item.id)
end
screen.refresh
}
})
UIActionHandlers.add(UI::Bag::SCREEN_ID, :debug, {
:effect => proc { |screen|
command = 0
loop do
command = screen.show_choice_message(
_INTL("Do what with {1}?", screen.item.name),
[_INTL("Change quantity"), _INTL("Make Mystery Gift"), _INTL("Cancel")], command)
case command
when 0 # Change quantity
qty = screen.bag.quantity(screen.item.id)
item_name_plural = screen.item.name_plural
params = ChooseNumberParams.new
params.setRange(0, Settings::BAG_MAX_PER_SLOT)
params.setDefaultValue(qty)
new_qty = screen.choose_number(
_INTL("Choose new quantity of {1} (max. {2}).", item_name_plural, Settings::BAG_MAX_PER_SLOT), params
)
if new_qty > qty
screen.bag.add(screen.item.id, new_qty - qty)
elsif new_qty < qty
screen.bag.remove(screen.item.id, qty - new_qty)
end
screen.refresh
break if new_qty == 0
when 1 # Make Mystery Gift
pbCreateMysteryGift(1, screen.item.id)
else
break
end
end
}
})
#-------------------------------------------------------------------------------
UIActionHandlers.add(UI::Bag::SCREEN_ID, :switch_item_start, {
:effect => proc { |screen|
screen.start_switching
}
})
UIActionHandlers.add(UI::Bag::SCREEN_ID, :switch_item_end, {
:effect => proc { |screen|
screen.switch_items(screen.switch_index, screen.index)
}
})
UIActionHandlers.add(UI::Bag::SCREEN_ID, :switch_item_cancel, {
:effect => proc { |screen|
screen.cancel_switching
}
})
#===============================================================================
# Menu options for choice menus that exist in the party screen.
#===============================================================================
MenuHandlers.add(:bag_screen_menu, :open_storage, {
"name" => _INTL("Access Pokémon Boxes"),
"order" => 10,
# "condition" => proc { |screen| next screen.can_access_storage? }
})
MenuHandlers.add(:bag_screen_menu, :cancel, {
"name" => _INTL("Cancel"),
"order" => 9999
})
#-------------------------------------------------------------------------------
MenuHandlers.add(:bag_screen_interact, :read_mail, {
"name" => _INTL("Read"),
"order" => 10,
"condition" => proc { |screen| next screen.item.is_mail? }
})
MenuHandlers.add(:bag_screen_interact, :use, {
"name" => proc { |screen|
next ItemHandlers.getUseText(screen.item.id) if ItemHandlers.hasUseText(screen.item.id)
next _INTL("Use")
},
"order" => 20,
"condition" => proc { |screen|
next ItemHandlers.hasOutHandler(screen.item.id) || (screen.item.is_machine? && $player.party.length > 0)
}
})
MenuHandlers.add(:bag_screen_interact, :give, {
"name" => _INTL("Give"),
"order" => 30,
"condition" => proc { |screen| next $player.pokemon_party.length > 0 && screen.item.can_hold? }
})
MenuHandlers.add(:bag_screen_interact, :toss, {
"name" => _INTL("Toss"),
"order" => 40,
"condition" => proc { |screen| next !screen.item.is_important? || $DEBUG }
})
MenuHandlers.add(:bag_screen_interact, :register, {
"name" => proc { |screen|
next _INTL("Deselect") if $bag.registered?(screen.item.id)
next _INTL("Select")
},
"order" => 50,
"condition" => proc { |screen| next pbCanRegisterItem?(screen.item.id) }
})
MenuHandlers.add(:bag_screen_interact, :debug, {
"name" => _INTL("Debug"),
"order" => 60,
"condition" => proc { |screen| next $DEBUG }
})
MenuHandlers.add(:bag_screen_interact, :cancel, {
"name" => _INTL("Cancel"),
"order" => 9999
})