Files
infinitefusion-e18/Data/Scripts/016b_UI redesign/020_UI_PokeMart.rb

687 lines
22 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#===============================================================================
#
#===============================================================================
class UI::MartStockWrapper
def initialize(stock)
@stock = stock
refresh
end
def length
return @stock.length
end
def [](index)
return @stock[index]
end
def buy_price(item)
return 0 if item.nil?
if $game_temp.mart_prices && $game_temp.mart_prices[item]
return $game_temp.mart_prices[item][0] if $game_temp.mart_prices[item][0] > 0
end
return GameData::Item.get(item).price
end
def buy_price_string(item)
price = buy_price(item)
return _INTL("${1}", price.to_s_formatted)
end
def sell_price(item)
return 0 if item.nil?
if $game_temp.mart_prices && $game_temp.mart_prices[item]
return $game_temp.mart_prices[item][1] if $game_temp.mart_prices[item][1] >= 0
end
return GameData::Item.get(item).sell_price
end
def refresh
@stock.delete_if { |itm| GameData::Item.get(itm).is_important? && $bag.has?(itm) }
end
end
#===============================================================================
# Pokémon Mart.
#===============================================================================
class UI::MartVisualsList < Window_DrawableCommand
def initialize(stock, x, y, width, height, viewport = nil)
@stock = stock
super(x, y, width, height, viewport)
@selarrow = AnimatedBitmap.new(bag_folder + "cursor")
@baseColor = UI::MartVisuals::TEXT_COLOR_THEMES[:black][0]
@shadowColor = UI::MartVisuals::TEXT_COLOR_THEMES[:black][1]
self.windowskin = nil
end
#-----------------------------------------------------------------------------
def itemCount
return @stock.length + 1 # The extra 1 is the Cancel option
end
def bag_folder
return UI::MartVisuals::UI_FOLDER + UI::MartVisuals::GRAPHICS_FOLDER
end
def item_id
return (self.index >= @stock.length) ? nil : @stock[self.index]
end
def expensive_base_color=(value)
@expensive_base_color = value
end
def expensive_shadow_color=(value)
@expensive_shadow_color = value
end
#-----------------------------------------------------------------------------
# This draws all the visible options first, and then draws the cursor.
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 || i > self.top_item + self.page_item_max
drawItem(i, @item_max, itemRect(i))
end
drawCursor(self.index, itemRect(self.index))
end
def drawItem(index, count, rect)
textpos = []
rect = drawCursor(index, rect)
ypos = rect.y
this_item = @stock[index]
if this_item
# Draw item name
item_name = GameData::Item.get(this_item).display_name
textpos.push([item_name, rect.x, ypos + 2, :left, self.baseColor, self.shadowColor])
# Draw item price
price = @stock.buy_price_string(this_item)
price_width = self.contents.text_size(price).width
price_x = rect.x + rect.width - price_width - 2 - 16
expensive = @stock.buy_price(this_item) > $player.money
price_base_color = (expensive) ? @expensive_base_color || self.baseColor : self.baseColor
price_shadow_color = (expensive) ? @expensive_shadow_color || self.shadowColor : self.shadowColor
textpos.push([price, price_x, ypos + 2, :left, price_base_color, price_shadow_color])
else
textpos.push([_INTL("CANCEL"), rect.x, ypos + 2, :left, self.baseColor, self.shadowColor])
end
pbDrawTextPositions(self.contents, textpos)
end
end
#===============================================================================
#
#===============================================================================
class UI::MartVisuals < UI::BaseVisuals
attr_reader :sprites
attr_reader :pocket
GRAPHICS_FOLDER = "Mart/" # Subfolder in Graphics/UI
TEXT_COLOR_THEMES = { # These color themes are added to @sprites[:overlay]
:default => [Color.new(248, 248, 248), Color.new(56, 56, 56)], # Base and shadow colour
:white => [Color.new(248, 248, 248), Color.new(56, 56, 56)],
:black => [Color.new(88, 88, 80), Color.new(168, 184, 184)],
:expensive => [Color.new(224, 0, 0), Color.new(248, 144, 144)]
}
ITEMS_VISIBLE = 7
def initialize(stock, bag)
@stock = stock
@bag = bag
super()
end
def initialize_sprites
initialize_item_list
initialize_item_sprites
initialize_money_window
initialize_bag_quantity_window
end
def initialize_item_list
@sprites[:item_list] = UI::MartVisualsList.new(@stock, 152, 10, 374, 38 + (ITEMS_VISIBLE * 32), @viewport)
@sprites[:item_list].expensive_base_color = TEXT_COLOR_THEMES[:expensive][0]
@sprites[:item_list].expensive_shadow_color = TEXT_COLOR_THEMES[:expensive][1]
@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(
"", 76 + 4, 272, Graphics.width - 98, 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 initialize_money_window
@sprites[:money_window] = Window_AdvancedTextPokemon.newWithSize("", 0, 0, 162, 96, @viewport)
@sprites[:money_window].setSkin("Graphics/Windowskins/goldskin")
@sprites[:money_window].baseColor = TEXT_COLOR_THEMES[:black][0]
@sprites[:money_window].shadowColor = TEXT_COLOR_THEMES[:black][1]
@sprites[:money_window].letterbyletter = false
@sprites[:money_window].visible = true
end
def initialize_bag_quantity_window
@sprites[:bag_quantity_window] = Window_AdvancedTextPokemon.newWithSize(
_INTL("In Bag:<r>{1}", @bag.quantity(item)), 0, 0, 162, 64, @viewport
)
@sprites[:bag_quantity_window].setSkin("Graphics/Windowskins/goldskin")
@sprites[:bag_quantity_window].baseColor = TEXT_COLOR_THEMES[:black][0]
@sprites[:bag_quantity_window].shadowColor = TEXT_COLOR_THEMES[:black][1]
@sprites[:bag_quantity_window].letterbyletter = false
@sprites[:bag_quantity_window].visible = true
@sprites[:bag_quantity_window].y = Graphics.height - 102 - @sprites[:bag_quantity_window].height
end
#-----------------------------------------------------------------------------
def index
return @sprites[:item_list].index
end
def set_index(value)
@sprites[:item_list].index = value
refresh_on_index_changed(nil)
end
def item
return @sprites[:item_list].item_id
end
def show_money_window
@sprites[:money_window].visible = true
end
def hide_money_window
@sprites[:money_window].visible = false
end
def show_bag_quantity_window
@sprites[:bag_quantity_window].visible = true
end
def hide_bag_quantity_window
@sprites[:bag_quantity_window].visible = false
end
#-----------------------------------------------------------------------------
def choose_item_quantity(help_text, price_per_unit, maximum, init_value = 1)
@sprites[:speech_box].visible = true
@sprites[:speech_box].text = help_text
pbBottomLeftLines(@sprites[:speech_box], 2)
# Show the help text
loop do
Graphics.update
Input.update
update_visuals
if @sprites[:speech_box].busy?
if Input.trigger?(Input::USE)
pbPlayDecisionSE if @sprites[:speech_box].pausing?
@sprites[:speech_box].resume
end
else
break
end
end
# Choose a quantity
item_price = price_per_unit
quantity = init_value
using(num_window = Window_AdvancedTextPokemon.newWithSize(
_INTL("×{1}<r>${2}", quantity, (quantity * item_price).to_s_formatted),
0, 0, 224, 64, @viewport)) do
num_window.z = 2000
num_window.visible = true
num_window.letterbyletter = false
num_window.baseColor = TEXT_COLOR_THEMES[:black][0]
num_window.shadowColor = TEXT_COLOR_THEMES[:black][1]
pbBottomRight(num_window)
num_window.y -= @sprites[:speech_box].height
loop do
Graphics.update
Input.update
update
num_window.update
# Change quantity
old_quantity = quantity
if Input.repeat?(Input::LEFT)
quantity = [quantity - 10, 1].max
elsif Input.repeat?(Input::RIGHT)
quantity = [quantity + 10, maximum].min
elsif Input.repeat?(Input::UP)
quantity += 1
quantity = 1 if quantity > maximum
elsif Input.repeat?(Input::DOWN)
quantity -= 1
quantity = maximum if quantity < 1
end
if quantity != old_quantity
num_window.text = _INTL("×{1}<r>${2}", quantity, (quantity * item_price).to_s_formatted)
pbPlayCursorSE
end
# Finish choosing a quantity
if Input.trigger?(Input::USE)
pbPlayDecisionSE
break
elsif Input.trigger?(Input::BACK)
pbPlayCancelSE
quantity = 0
break
end
end
end
@sprites[:speech_box].visible = false
return quantity
end
#-----------------------------------------------------------------------------
def refresh
refresh_item_list
refresh_selected_item
refresh_money_window
end
def refresh_item_list
@sprites[:item_list].refresh
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("Quit shopping.")
end
refresh_bag_quantity_window
end
def refresh_bag_quantity_window
@sprites[:bag_quantity_window].text = _INTL("In Bag:<r>{1}", @bag.quantity(item))
(item) ? show_bag_quantity_window : hide_bag_quantity_window
end
def refresh_money_window
@sprites[:money_window].text = _INTL("Money:\n<r>${1}", $player.money.to_s_formatted)
end
def refresh_on_index_changed(old_index)
refresh_selected_item
end
#-----------------------------------------------------------------------------
def update_input
# Check for interaction
if Input.trigger?(Input::USE)
return update_interaction(Input::USE)
elsif Input.trigger?(Input::BACK)
return update_interaction(Input::BACK)
end
return nil
end
def update_interaction(input)
case input
when Input::USE
if item
pbPlayDecisionSE
return :interact
end
pbPlayCloseMenuSE
return :quit
when Input::BACK
pbPlayCloseMenuSE
return :quit
end
return nil
end
def navigate
@sprites[:item_list].active = true
ret = super
@sprites[:item_list].active = false
return ret
end
end
#===============================================================================
#
#===============================================================================
class UI::Mart < UI::BaseScreen
attr_reader :stock, :bag
SCREEN_ID = :mart_screen
def initialize(stock, bag)
pbScrollMap(6, 5, 5) # Direction 6 (right), 5 tiles, speed 5 (cycling speed, 10 tiles/second)
@stock = UI::MartStockWrapper.new(stock)
@bag = bag
super()
end
def initialize_visuals
@visuals = UI::MartVisuals.new(@stock, @bag)
end
def start_screen
pbSEPlay("GUI menu open")
end
def end_screen
return if @disposed
pbPlayCloseMenuSE
silent_end_screen
pbScrollMap(4, 5, 5) # Direction 4 (left), 5 tiles, speed 5 (cycling speed, 10 tiles/second)
end
#-----------------------------------------------------------------------------
def item
return nil if @visuals.item.nil?
return GameData::Item.get(@visuals.item)
end
#-----------------------------------------------------------------------------
def choose_item_quantity(help_text, price_per_unit, maximum, init_value = 1)
return @visuals.choose_item_quantity(help_text, price_per_unit, maximum, init_value)
end
end
#===============================================================================
#
#===============================================================================
UIActionHandlers.add(UI::Mart::SCREEN_ID, :interact, {
:effect => proc { |screen|
item = screen.item
item_price = screen.stock.buy_price(item)
# Check affordability
if $player.money < item_price
screen.show_message(_INTL("You don't have enough money."))
next
end
# Choose how many of the item to buy
quantity = 0
if item.is_important?
quantity = 1
next if !screen.show_confirm_message(
_INTL("So you want the {1}?\nIt'll be {2}. All right?",
item.portion_name, screen.stock.buy_price_string(item.id))
)
else
max_quantity = (item_price <= 0) ? PokemonBag::MAX_PER_SLOT : $player.money / item_price
max_quantity = [max_quantity, PokemonBag::MAX_PER_SLOT].min
quantity = screen.choose_item_quantity(
_INTL("So how many {1}?", item.portion_name_plural), item_price, max_quantity
)
next if quantity == 0
item_price *= quantity
if quantity > 1
next if !screen.show_confirm_message(
_INTL("So you want {1} {2}?\nThey'll be {3}. All right?",
quantity, item.portion_name_plural, screen.stock.buy_price_string(item.id))
)
elsif quantity > 0
next if !screen.show_confirm_message(
_INTL("So you want {1} {2}?\nIt'll be {3}. All right?",
quantity, item.portion_name, screen.stock.buy_price_string(item.id))
)
end
end
# Check affordability (should always be possible, but just make sure)
if $player.money < item_price
screen.show_message(_INTL("You don't have enough money."))
next
end
# Check the item can be put in the Bag
if !screen.bag.can_add?(item.id, quantity)
screen.show_message(_INTL("You have no room in your Bag."))
next
end
# Add the bought item(s)
screen.bag.add(item.id, quantity)
$stats.money_spent_at_marts += item_price
$stats.mart_items_bought += quantity
$player.money -= item_price
screen.stock.refresh
screen.refresh
screen.show_message(_INTL("Here you are! Thank you!")) { pbSEPlay("Mart buy item") }
# Give bonus Premier Ball(s)
if quantity >= 10 && item.is_poke_ball? && GameData::Item.exists?(:PREMIERBALL)
if Settings::MORE_BONUS_PREMIER_BALLS || item.id == :POKEBALL
premier_balls_earned = (Settings::MORE_BONUS_PREMIER_BALLS) ? (quantity / 10) : 1
premier_balls_added = 0
premier_balls_earned.times do
break if !screen.bag.add(:PREMIERBALL)
premier_balls_added += 1
end
if premier_balls_added > 0
$stats.premier_balls_earned += premier_balls_added
if premier_balls_added > 1
ball_name = GameData::Item.get(:PREMIERBALL).portion_name_plural
else
ball_name = GameData::Item.get(:PREMIERBALL).portion_name
end
screen.show_message(_INTL("And have {1} {2} on the house!", premier_balls_added, ball_name))
end
end
end
}
})
#===============================================================================
#
#===============================================================================
class UI::BagSellVisuals < UI::BagVisuals
def initialize(bag, stock, mode = :choose_item)
@stock = stock
super(bag, mode: mode)
end
def initialize_sprites
super
@sprites[:money_window] = Window_AdvancedTextPokemon.newWithSize("", 0, 36, 184, 96, @viewport)
@sprites[:money_window].setSkin("Graphics/Windowskins/goldskin")
@sprites[:money_window].z = 2000
@sprites[:money_window].baseColor = TEXT_COLOR_THEMES[:black][0]
@sprites[:money_window].shadowColor = TEXT_COLOR_THEMES[:black][1]
@sprites[:money_window].letterbyletter = false
@sprites[:money_window].visible = true
@sprites[:unit_price_window] = Window_AdvancedTextPokemon.newWithSize("", 0, 184, 184, 96, @viewport)
@sprites[:unit_price_window].setSkin("Graphics/Windowskins/goldskin")
@sprites[:unit_price_window].z = 2000
@sprites[:unit_price_window].baseColor = TEXT_COLOR_THEMES[:black][0]
@sprites[:unit_price_window].shadowColor = TEXT_COLOR_THEMES[:black][1]
@sprites[:unit_price_window].letterbyletter = false
@sprites[:unit_price_window].visible = true
end
def choose_item_quantity(help_text, price_per_unit, maximum, init_value = 1)
@sprites[:speech_box].visible = true
@sprites[:speech_box].text = help_text
pbBottomLeftLines(@sprites[:speech_box], 2)
# Show the help text
loop do
Graphics.update
Input.update
update_visuals
if @sprites[:speech_box].busy?
if Input.trigger?(Input::USE)
pbPlayDecisionSE if @sprites[:speech_box].pausing?
@sprites[:speech_box].resume
end
else
break
end
end
# Choose a quantity
item_price = price_per_unit
quantity = init_value
using(num_window = Window_AdvancedTextPokemon.newWithSize(
_INTL("×{1}<r>${2}", quantity, (quantity * item_price).to_s_formatted),
0, 0, 224, 64, @viewport)) do
num_window.z = 2000
num_window.visible = true
num_window.letterbyletter = false
num_window.baseColor = TEXT_COLOR_THEMES[:black][0]
num_window.shadowColor = TEXT_COLOR_THEMES[:black][1]
pbBottomRight(num_window)
num_window.y -= @sprites[:speech_box].height
loop do
Graphics.update
Input.update
update
num_window.update
# Change quantity
old_quantity = quantity
if Input.repeat?(Input::LEFT)
quantity = [quantity - 10, 1].max
elsif Input.repeat?(Input::RIGHT)
quantity = [quantity + 10, maximum].min
elsif Input.repeat?(Input::UP)
quantity += 1
quantity = 1 if quantity > maximum
elsif Input.repeat?(Input::DOWN)
quantity -= 1
quantity = maximum if quantity < 1
end
if quantity != old_quantity
num_window.text = _INTL("×{1}<r>${2}", quantity, (quantity * item_price).to_s_formatted)
pbPlayCursorSE
end
# Finish choosing a quantity
if Input.trigger?(Input::USE)
pbPlayDecisionSE
break
elsif Input.trigger?(Input::BACK)
pbPlayCancelSE
quantity = 0
break
end
end
end
@sprites[:speech_box].visible = false
return quantity
end
def refresh
super
@sprites[:money_window].text = _INTL("Money:\n<r>${1}", $player.money.to_s_formatted)
refresh_unit_price_window
end
def refresh_input_indicators; end
def refresh_unit_price_window
@sprites[:unit_price_window].visible = (!item.nil?)
return if item.nil?
price = @stock.sell_price(item)
if GameData::Item.get(item).is_important? || price == 0
@sprites[:unit_price_window].text = _INTL("You can't sell this item.")
else
@sprites[:unit_price_window].text = _INTL("Price each:\n<r>${1}", price.to_s_formatted)
end
end
def refresh_on_index_changed(old_index)
refresh_unit_price_window
end
end
#===============================================================================
#
#===============================================================================
class UI::BagSell < UI::Bag
def initialize(bag, mode: :choose_item)
@stock = UI::MartStockWrapper.new([])
super(bag, mode: mode)
end
def initialize_visuals
@visuals = UI::BagSellVisuals.new(@bag, @stock, mode: @mode)
end
def choose_item_quantity(help_text, price_per_unit, maximum, init_value = 1)
return @visuals.choose_item_quantity(help_text, price_per_unit, maximum, init_value)
end
def sell_items
choose_item do |item|
item_data = GameData::Item.get(item)
item_name = item_data.portion_name
item_name_plural = item_data.portion_name_plural
price = @stock.sell_price(item)
# Ensure item can be sold
if item_data.is_important? || price == 0
show_message(_INTL("Oh, no. I can't buy {1}.", item_name_plural))
next
end
# Choose a quantity of the item to sell
quantity = @bag.quantity(item)
if quantity > 1
quantity = choose_item_quantity(
_INTL("How many {1} would you like to sell?", item_name_plural), price, quantity
)
end
next if quantity == 0
# Sell the item(s)
price *= quantity
if show_confirm_message(_INTL("I can pay ${1}.\nWould that be OK?", price.to_s_formatted))
@bag.remove(item, quantity)
old_money = $player.money
$player.money += price
$stats.money_earned_at_marts += $player.money - old_money
refresh
sold_item_name = (quantity > 1) ? item_name_plural : item_name
show_message(_INTL("You turned over the {1} and got ${2}.",
sold_item_name, price.to_s_formatted)) { pbSEPlay("Mart buy item") }
end
next false
end
end
end
#===============================================================================
#
#===============================================================================
def pbPokemonMart(stock, speech = nil, cannot_sell = false)
commands = []
cmdBuy = -1
cmdSell = -1
cmdQuit = -1
commands[cmdBuy = commands.length] = _INTL("I'm here to buy")
commands[cmdSell = commands.length] = _INTL("I'm here to sell") if !cannot_sell
commands[cmdQuit = commands.length] = _INTL("No, thanks")
cmd = pbMessage(speech || _INTL("Welcome! How may I help you?"), commands, cmdQuit + 1)
loop do
if cmdBuy >= 0 && cmd == cmdBuy
UI::Mart.new(stock, $bag).main
elsif cmdSell >= 0 && cmd == cmdSell
pbFadeOutIn { UI::BagSell.new($bag).sell_items }
else
pbMessage(_INTL("Do come again!"))
break
end
cmd = pbMessage(_INTL("Is there anything else I can do for you?"), commands, cmdQuit + 1)
end
$game_temp.clear_mart_prices
end