Update 6.8

This commit is contained in:
chardub
2026-07-10 15:42:06 -04:00
parent 5b85e72cb2
commit 6a6f126a18
7871 changed files with 493194 additions and 224826 deletions
@@ -0,0 +1,209 @@
class ContactsAppScene < PokeNavAppScene
INFO_TEXT_Y = 270
def cursor_x_offset
return 16
end
def cursor_y_offset
return -16
end
def header_name
return _INTL("Trainers")
end
def cursor_path
return "Graphics/Pictures/Pokegear/Trainers/icon_button_static"
end
def header_path
return "Graphics/Pictures/Pokegear/Trainers/bg_header_trainers"
end
def display_mode
return :LIST
end
def x_gap
return 74;
end
def y_gap
return 48;
end
def columns
return 1
end
def visible_rows
return 3
end
def start_x
return 40;
end
def start_y
return 80;
end
def pbStartScene(screen)
@screen = screen
buttons = []
@trainers = []
@contacts_list = screen.list_contacts
@contacts_list.each do |location, trainers_list|
next unless location
buttons << ContactsAppLocationButton.new(location, nil, location)
next unless trainers_list && trainers_list.size > 0
trainers_list.each do |trainer|
next unless trainer
next unless screen.can_be_listed(trainer)
trainerClassName = GameData::TrainerType.get(trainer.trainerType).name
trainerName = MessageTypes.getFromHash(MessageTypes::TrainerNames, trainer.trainerName) || trainer.trainerName
trainer_name = _INTL("{1} {2}", trainerClassName, trainerName)
@trainers << trainer.id
button = ContactsAppTrainerButton.new(trainer.id, trainer.overworld_sprite, trainer_name)
$Trainer.pokenav.viewed_trainers = [] unless $Trainer&.pokenav&.viewed_trainers
button.set_trade_available(trainer.can_trade?)
button.set_new(!$Trainer.pokenav.viewed_trainers.include?(trainer.id))
buttons << button
end
end
super(buttons)
@index = 1
scroll_to_current_location
displayTextElements
@buttons[@index].hover
end
def updateInput
cols = columns
total = @buttons.length
move_delay = 2
if Input.trigger?(Input::BACK)
pbPlayCloseMenuSE
@exiting = true
return
elsif Input.trigger?(Input::USE)
pbPlayDecisionSE
click(@buttons[@index]&.id)
elsif Input.repeat?(Input::LEFT)
prev_location = find_previous_location_index
move_to_index(prev_location+1)
pbWait(move_delay)
elsif Input.repeat?(Input::RIGHT)
next_location = find_next_location_index
move_to_index(next_location+1)
pbWait(move_delay)
elsif Input.repeat?(Input::UP)
move_index(-cols)
pbWait(move_delay)
elsif Input.repeat?(Input::DOWN)
move_index(cols)
pbWait(move_delay)
end
end
def scroll_to_current_location
current_location_name = getMapName($game_map.map_id)
for i in @index..@buttons.length-1
if @buttons[i].is_a?(ContactsAppLocationButton) && @buttons[i].id == current_location_name
new_index = i+1 #next index for the first trainer in that location
move_to_index(new_index)
end
end
end
def find_next_location_index
for i in @index..@buttons.length-1
if @buttons[i].is_a?(ContactsAppLocationButton)
return i
end
end
return @buttons.length-1
end
def find_previous_location_index
found_current_location = false
for i in @index.downto(0)
if @buttons[i].is_a?(ContactsAppLocationButton)
return i if found_current_location
found_current_location = true
end
end
return 1
end
def move_index(delta)
return if @buttons.empty?
pbPlayCursorSE
new_index = (@index + delta) % @buttons.length
if @buttons[new_index].is_a?(ContactsAppLocationButton)
new_index = new_index + delta
end
new_index = @buttons.length - 1 if new_index < 0
@index = new_index
hover(@buttons[@index]&.id)
end
def createCursor
super
@sprites["cursor"].x=16
@sprites["cursor"].y=-32
end
def click(button_id)
super
@screen.view_trainer_page(button_id, @trainers)
# cmd_info = _INTL("Info")
# cmd_team = _INTL("View Team")
# cmd_cancel = _INTL("Cancel")
# commands = [cmd_info, cmd_team, cmd_cancel]
# choice = pbMessage(_INTL("What would you like to do?"), commands, commands.size)
# case commands[choice]
# when cmd_info
# @screen.view_trainer_page(button_id, @trainers)
# when cmd_team
# @screen.view_trainer_team(button_id)
# end
end
def layoutButtons
return if @exiting
current_row = @index
scroll_row = [current_row - visible_rows + 1, 0].max
y_positions = []
cumulative_y = 0
@buttons.each do |btn|
y_positions << cumulative_y
cumulative_y += btn.get_height + (btn.respond_to?(:bottom_margin) ? btn.bottom_margin : 0)
end
scroll_pixels = y_positions[[scroll_row, @buttons.length - 1].min]
@buttons.each_with_index do |btn, i|
btn.x = start_x
btn.y = start_y + y_positions[i] - scroll_pixels
btn.visible = (btn.y >= start_y - btn.get_height && btn.y <= Graphics.height)
btn.selected = (i == @index)
end
updateCursor
updateHeader(scroll_row)
end
def pbUpdate
super
@buttons.each { |btn| btn.update if btn.respond_to?(:update) }
end
def hover(button_id)
super
end
end
@@ -0,0 +1,99 @@
#===============================================================================
#
#===============================================================================
class ContactsAppScreen
def initialize(scene)
@scene = scene
end
UNLISTABLE_TRAINER_TYPES = [:TEAM_AQUA_GRUNT_M, :TEAM_AQUA_GRUNT_F,
:TEAM_MAGMA_GRUNT_M, :TEAM_MAGMA_GRUNT_F,
:TEAM_MAGMAQUA_GRUNT_M, :TEAM_MAGMAQUA_GRUNT_F,
:TEAM_AQUA_EXEC_M, :TEAM_AQUA_EXEC_F,
:TEAM_MAGMA_EXEC_M, :TEAM_MAGMA_EXEC_F,
:TEAM_AQUA_BOSS, :TEAM_MAGMA_BOSS,
:ADVENTURER_KANTO, :ADVENTURER_JOHTO,
:ADVENTURER_SINNOH, :ADVENTURER_UNOVA,
:ADVENTURER_KALOS, :ADVENTURER_ALOLA,
]
def pbStartScreen(main_menu_scene, screen)
@main_menu_scene = main_menu_scene
@scene.pbStartScene(self)
@scene.pbScene
@scene.pbEndScene
@screen = screen
end
def list_contacts
contacts_list_by_location = {}
$PokemonGlobal.battledTrainers = {} unless $PokemonGlobal.battledTrainers
$PokemonGlobal.battledTrainers.each do |id, trainer|
next unless can_be_listed(trainer)
if trainer.favorite
contacts_list_by_location[_INTL("Favorites")] ||= []
contacts_list_by_location[_INTL("Favorites")] << trainer
else
location = trainer.location
contacts_list_by_location[location] ||= []
contacts_list_by_location[location] << trainer
end
end
contacts_list_by_location.each_value do |trainer_array|
trainer_array.sort_by! { |t| t.trainerName }
end
# Move Favorites to front
favorites = contacts_list_by_location.delete(_INTL("Favorites"))
if favorites
contacts_list_by_location = { _INTL("Favorites") => favorites }.merge(contacts_list_by_location)
end
return contacts_list_by_location
end
def can_be_listed(trainer)
trainerType = trainer.trainerType
return false if UNLISTABLE_TRAINER_TYPES.include?(trainerType)
return true
end
def view_trainer_page(trainer_id, trainers_list)
trainer= getRebattledTrainerFromKey(trainer_id)
if trainer
pbFadeOutIn {
scene = ContactsAppInfoPageScene.new
screen = ContactsAppInfoPageScreen.new
screen.pbStartScreen(scene, trainer, trainers_list)
}
else
pbSEPlay("buzzer", 80)
pbWait(4)
end
end
def view_trainer_team(trainer_id)
trainer= getRebattledTrainerFromKey(trainer_id)
if trainer
team = trainer.currentTeam
pbFadeOutIn {
scene = PokemonSummary_Scene.new
screen = PokemonSummaryScreen.new(scene)
screen.pbStartScreen(team,0, false)
}
else
pbSEPlay("buzzer", 80)
pbWait(4)
end
end
def pbSummary(list, index)
visibleSprites = pbFadeOutAndHide(@sprites) { pbUpdate }
scene = PokemonSummary_Scene.new
screen = PokemonSummaryScreen.new(scene)
@sprites["list"].index = screen.pbStartScreen(list, index ,false)
pbFadeInAndShow(@sprites, visibleSprites) { pbUpdate }
end
end
@@ -0,0 +1,335 @@
class ContactsAppInfoPageScene < PokeNavAppScene
attr_accessor :trainer
SPRITE_POSITION_X = 375
SPRITE_POSITION_Y = 230
TITLE_TEXT_X = 380
TITLE_TEXT_Y = 50
INFO_HEADER_X = 40
INFO_TEXT_X = 60
INFO_TEXT_START_Y = 50
INFO_TEXT_GAP = 40
INFO_HEADER_GAP = 30
FRIENDSHIP_ICON = "Graphics/Pictures/Pokegear/Trainers/friendship"
FRIENDSHIP_ICON_GAP = 4
ICON_SIZE = 16
FRIENDSHIP_ICON_START_X = 40
FRIENDSHIP_ICON_START_Y = 20
INFO_TEXT_LINES_PER_PAGE = 2
def header_name
return _INTL("Trainers")
end
def cursor_path
return "Graphics/Pictures/Pokeradar/icon_button"
end
def header_path
return "Graphics/Pictures/Pokegear/bg_header_trainers"
end
def bg_path
return "Graphics/Pictures/Pokegear/Trainers/bg_summary"
end
def display_mode
return :LIST
end
def x_gap
return 75;
end
def y_gap
return 64;
end
def columns
return 1
end
def visible_rows
return 3
end
def start_x
return 40;
end
def start_y
return 80;
end
def initialize
super
@value_color_base = Color.new(24, 112, 216)
@value_color_shadow = Color.new(136, 168, 208)
if isDarkMode
@value_color_base, @value_color_shadow = @value_color_shadow, @value_color_base
end
@info_text_page = 0
@info_text_pages = []
end
def pbStartScene(screen, trainer)
echoln "start"
@screen = screen
buttons = []
@trainer = trainer
super(buttons)
if @trainer
showTrainerSprite
showTrainerInfo
showFriendshipIcons
else
pbEndScene
end
end
def showTrainerSprite
if @trainer.id == BATTLED_TRAINER_RIVAL_KEY
bitmap = generate_front_trainer_sprite_bitmap_from_appearance($Trainer.rival_appearance)
@sprites["trainer"] = IconSprite.new(0, 0, @viewport)
@sprites["trainer"].setBitmapDirectly(bitmap)
else
trainerFile = GameData::TrainerType.front_sprite_filename(@trainer.trainerType)
@sprites["trainer"] = IconSprite.new(0, 0, @viewport)
@sprites["trainer"].setBitmap(trainerFile)
end
if @sprites["trainer"].bitmap &&
@sprites["trainer"].bitmap.width > @sprites["trainer"].bitmap.height * 2
@sprites["trainer"].src_rect.x = 0
@sprites["trainer"].src_rect.width = @sprites["trainer"].bitmap.width / 5
end
@sprites["trainer"].ox = @sprites["trainer"].src_rect.width / 2
@sprites["trainer"].oy = @sprites["trainer"].bitmap.height
@sprites["trainer"].z = 50
@sprites["trainer"].x = SPRITE_POSITION_X
@sprites["trainer"].y = SPRITE_POSITION_Y
end
def showFriendshipIcons
@friendship_icons&.each(&:dispose)
@friendship_icons = []
(@trainer.friendship_level || 0).times do |i|
sprite = IconSprite.new(0, 0, @viewport)
sprite.setBitmap(FRIENDSHIP_ICON)
sprite.x = FRIENDSHIP_ICON_START_X + i * (ICON_SIZE + FRIENDSHIP_ICON_GAP)
sprite.y = FRIENDSHIP_ICON_START_Y
sprite.z = 100002
@friendship_icons << sprite
@sprites["friendship_icon_#{i}"] = sprite
end
end
def showTrainerInfo
Kernel.pbClearText
showHeaderName
trainerClassName = GameData::TrainerType.get(@trainer.trainerType).name
trainerName = MessageTypes.getFromHash(MessageTypes::TrainerNames, @trainer.trainerName) || @trainer.trainerName
trainer_name = _INTL("{1} {2}", trainerClassName, trainerName)
level_sum = 0
if @trainer.currentTeam.size > 0
@trainer.currentTeam.each do |pokemon|
level_sum += pokemon.level
end
average_level = (level_sum / @trainer.currentTeam.length).round
else
average_level = _INTL("N/A")
end
location = @trainer.location
if @trainer.id == BATTLED_TRAINER_RIVAL_KEY
favorite_type = _INTL("All")
elsif @trainer.id == BATTLED_TRAINER_WALLY_KEY
favorite_type = _INTL("Unknown")
else
favorite_type = (GameData::Type.try_get(@trainer.favorite_type)&.name) || _INTL("Unknown")
end
Kernel.pbDisplayText(trainer_name, TITLE_TEXT_X, TITLE_TEXT_Y, 999999, @text_color_base, @text_color_shadow)
current_y = INFO_TEXT_START_Y
displayText(_INTL("Location:"), INFO_HEADER_X, current_y)
current_y += INFO_HEADER_GAP
displayValue(location, INFO_TEXT_X, current_y)
current_y += INFO_TEXT_GAP
displayText(_INTL("Average team level:"), INFO_HEADER_X, current_y)
current_y += INFO_HEADER_GAP
displayValue(average_level.to_s, INFO_TEXT_X, current_y)
current_y += INFO_TEXT_GAP
displayText(_INTL("Favorite type:"), INFO_HEADER_X, current_y)
current_y += INFO_HEADER_GAP
displayValue(favorite_type, INFO_TEXT_X, current_y)
trainer_data = GameData::Trainer.try_get(@trainer.trainerType, @trainer.trainerName, 0)
current_y += INFO_TEXT_GAP * 1.5
if @trainer.previous_random_events
action = getBestMatchingPreviousRandomEvent(trainer_data, @trainer.previous_random_events)
if action
case action.eventType
when :CATCH
action_text = _INTL("Recently caught a {1}.",
GameData::Species.get(action.caught_pokemon).name)
when :EVOLVE
action_text = _INTL("Recently evolved their {1}.",
GameData::Species.get(action.evolved_pokemon).name)
when :FUSE
action_text = _INTL("Recently fused their {1} and {2}\.",
GameData::Species.get(action.fusion_head_pokemon).name,
GameData::Species.get(action.fusion_body_pokemon).name)
when :UNFUSE
action_text = _INTL("Recently unfused their {1}.",
GameData::Species.get(action.unfused_pokemon).name)
when :REVERSE
action_text = _INTL("Recently reversed their {1}.",
GameData::Species.get(action.reversed_pokemon).name)
end
end
end
displayInfoText(current_y, trainer_data, action_text)
end
def displayInfoText(current_y, trainer_data, action_text = nil)
infoText = trainer_data&.infoText
if infoText || action_text
if infoText
full_text = infoText.gsub("___", "")
full_text = full_text.gsub("<PLAYER_NAME>", $Trainer.name)
full_text = "\"#{full_text}\"" unless full_text.empty?
end
max_width = SPRITE_POSITION_X - INFO_HEADER_X - 20
temp_bitmap = Bitmap.new(1, 1)
all_lines = full_text ? wrap_text(full_text, temp_bitmap, max_width) : []
temp_bitmap.dispose
@info_text_pages = all_lines.each_slice(INFO_TEXT_LINES_PER_PAGE).to_a
#insert action page as first
@info_text_pages.unshift([action_text]) if action_text
@info_text_page = @info_text_page.clamp(0, [@info_text_pages.size - 1, 0].max)
page_lines = @info_text_pages[@info_text_page] || []
page_lines.each do |line|
displayText(line, INFO_HEADER_X, current_y)
current_y += INFO_TEXT_GAP
end
if @info_text_pages.size > 1
indicator = "#{@info_text_page + 1} / #{@info_text_pages.size}"
displayText(indicator, Graphics.width - 80, 340)
end
else
@info_text_pages = []
@info_text_page = 0
end
end
def rebuildInfoTextPage(all_lines)
@info_text_pages = all_lines.each_slice(INFO_TEXT_LINES_PER_PAGE).to_a
@info_text_page = @info_text_page.clamp(0, [@info_text_pages.size - 1, 0].max)
end
def updateInput
if Input.trigger?(Input::UP)
change_trainer(-1)
pbSEPlay("GUI naming tab swap start")
elsif Input.trigger?(Input::DOWN)
change_trainer(1)
pbSEPlay("GUI naming tab swap start")
elsif Input.trigger?(Input::LEFT)
if @info_text_pages.size > 1 && @info_text_page > 0
@info_text_page -= 1
pbSEPlay("GUI naming tab swap start")
refresh_info_text
end
elsif Input.trigger?(Input::RIGHT)
if @info_text_pages.size > 1 && @info_text_page < @info_text_pages.size - 1
@info_text_page += 1
pbSEPlay("GUI naming tab swap start")
refresh_info_text
end
elsif Input.trigger?(Input::BACK)
pbPlayCloseMenuSE
@exiting = true
return
elsif Input.trigger?(Input::USE)
pbPlayDecisionSE
click(@buttons[@index]&.id)
end
end
# Re-renders only the info text area without rebuilding the whole scene.
def refresh_info_text
Kernel.pbClearText
showHeaderName
showTrainerInfo
end
def change_trainer(index_delta)
@info_text_page = 0
@info_text_pages = []
@screen.change_trainer(index_delta)
@sprites["trainer"]&.dispose
@friendship_icons&.each_with_index { |_, i| @sprites.delete("friendship_icon_#{i}") }
Kernel.pbClearText()
showTrainerSprite
showTrainerInfo
showFriendshipIcons
end
def displayText(text, x_position, y_position)
Kernel.pbDisplayText(text, x_position, y_position, nil, @text_color_base, @text_color_shadow, 3)
end
def displayValue(text, x_position, y_position)
Kernel.pbDisplayText(text, x_position, y_position, nil, @value_color_base, @value_color_shadow, 3)
end
# def Kernel.pbDisplayText(message,xposition,yposition,z=nil, baseColor=nil, shadowColor=nil,alignment=2)
def createCursor
return if $PokemonTemp.pokeradar
super
end
def click(button_id)
super
echoln @trainer.id
cmd_team = _INTL("View Team")
cmd_cancel = _INTL("Cancel")
commands = []
commands << cmd_team if @trainer.currentTeam.size > 0
commands << cmd_cancel
#choice = pbMessage(nil, commands, commands.size)
choice = pbShowCommands(nil, commands, commands.size, 0, 330,200)
case commands[choice]
when cmd_team
Kernel.pbClearText()
@screen.view_trainer_team(@trainer.id)
showTrainerInfo
end
end
def hover(button_id)
super
end
def pbEndScene
super
#Re-write the header name since going back to trainers list which has already been initialized
Kernel.pbDisplayText(header_name, Graphics.width/2 , HEADER_HEIGHT)
end
end
@@ -0,0 +1,54 @@
class ContactsAppInfoPageScreen
def pbStartScreen(scene, trainer, trainers_list)
@trainers_list = trainers_list
@scene = scene
@index = get_current_index(trainer.id)
@scene.pbStartScene(self, trainer)
@scene.pbScene
@scene.pbEndScene
updateStatus
end
def updateStatus
trainer_id = @trainers_list[@index]
unless $Trainer.pokenav.viewed_trainers.include?(trainer_id)
$Trainer.pokenav.viewed_trainers << trainer_id
end
end
def view_trainer_team(trainer_id)
trainer = getRebattledTrainerFromKey(trainer_id)
if trainer
team = trainer.currentTeam
pbFadeOutIn {
scene = PokemonSummary_Scene.new
screen = PokemonSummaryScreen.new(scene)
screen.pbStartScreen(team, 0, false)
}
else
pbSEPlay("buzzer", 80)
pbWait(4)
end
end
def get_current_index(current_trainer_id)
@trainers_list.each_with_index do |trainer_id, i|
if trainer_id == current_trainer_id
return i
end
end
return 0
end
def change_trainer(delta)
new_index = @index + delta
new_index = @trainers_list.length - 1 if new_index >= @trainers_list.length
new_index = 0 if new_index < 0
@index = new_index
new_trainer_id = @trainers_list[new_index]
@scene.trainer = getRebattledTrainerFromKey(new_trainer_id)
updateStatus
end
end
@@ -0,0 +1,19 @@
class ContactsAppLocationButton < PokenavButton
BOTTOM_MARGIN = 8 # tune this
def get_height
return 40
end
def get_width
return 200
end
def get_text
return @id.to_s
end
def bottom_margin
return BOTTOM_MARGIN
end
end
@@ -0,0 +1,168 @@
class ContactsAppTrainerButton < PokenavButton
IMAGE_TEXT_GAP = 128
DEFAULT_SPRITE_PATH = "000"
IMAGE_X_OFFSET = 32
SOURCE_IMAGE_Y_CROP = 24
ICON_SIZE = 24
ICON_X_MARGIN = 8
ICON_GAP = 4
TRADE_AVAILABLE_ICON = "Graphics/Pictures/Pokegear/Trainers/tradeIcon"
IS_NEW_ICON = "Graphics/Pictures/Pokegear/Trainers/dialogIcon"
ICON_X_OFFSET = -20
FADE_SPEED = 16
def get_width
return Graphics.width-72
end
def get_height
return 56
end
def background_image
if isDarkMode
return "Graphics/Pictures/Pokegear/Trainers/trainer_list_button_dark.png"
else
return "Graphics/Pictures/Pokegear/Trainers/trainer_list_button.png"
end
end
def set_trade_available(value)
@is_trade_available = value
create_icon_sprites
end
def set_new(value)
@is_new = value
create_icon_sprites
end
def initialize(id, image = nil, text = nil, viewport = nil)
@image_path = image
@image_path = DEFAULT_SPRITE_PATH if @image_path.nil?
super(id, nil, text, viewport)
@is_trade_available = false
@is_new = false
refresh
end
def viewport=(vp)
super(vp)
create_image_sprite
create_icon_sprites
end
def create_image_sprite
return unless @image_path && self.viewport
@image_sprite = IconSprite.new(0, 0, self.viewport)
if @id == BATTLED_TRAINER_RIVAL_KEY
rival_bitmap = AnimatedBitmap.new(getBaseOverworldSpriteFilename())
rival_bitmap.bitmap = generateNPCClothedBitmapStatic($Trainer.rival_appearance)
@image_sprite.setBitmapDirectly(rival_bitmap)
else
@image_sprite.setBitmap("Graphics/Characters/#{@image_path}")
end
frame_w = (@image_sprite.bitmap.width / 4).round
frame_h = (@image_sprite.bitmap.height / 4).round
@image_sprite.src_rect.width = frame_w
@image_sprite.src_rect.height = [frame_h - SOURCE_IMAGE_Y_CROP, get_height].min
@image_sprite.src_rect.x = 16
@image_sprite.src_rect.y = 16
@image_sprite.z = self.z + 1
end
def create_icon_sprites
return unless self.viewport
@icon_sprites&.each(&:dispose)
@icon_sprites = []
icons = []
icons << TRADE_AVAILABLE_ICON if @is_trade_available
icons << IS_NEW_ICON if @is_new
icons.each do |path|
sprite = IconSprite.new(0, 0, self.viewport)
sprite.setBitmap(path)
sprite.z = self.z + 1
@icon_sprites << sprite
end
update_icon_positions
end
def update_icon_positions
return unless @icon_sprites
right_edge = (self.x || 0) + get_width - ICON_X_MARGIN
@icon_sprites.each_with_index do |sprite, i|
sprite.x = right_edge - ICON_SIZE - (i * (ICON_SIZE + ICON_GAP)) + ICON_X_OFFSET
sprite.y = (self.y || 0) + (get_height - ICON_SIZE) / 2
sprite.visible = self.visible
end
end
def hover
if @is_new
$Trainer.pokenav.viewed_trainers << @id
@is_new = false
@fading_new_icon = @icon_sprites.last # IS_NEW_ICON is last in array
end
super
end
def update
return unless @fading_new_icon
@fading_new_icon.opacity -= FADE_SPEED
if @fading_new_icon.opacity <= 0
@fading_new_icon.dispose
@icon_sprites.delete(@fading_new_icon)
@fading_new_icon = nil
update_icon_positions # reflow remaining icons
end
end
def x=(value)
super
@image_sprite&.x = value + IMAGE_X_OFFSET
update_icon_positions
end
def y=(value)
super
frame_h = @image_sprite ? @image_sprite.src_rect.height : 0
@image_sprite&.y = value + (get_height - frame_h) / 2 - 8
update_icon_positions
end
def visible=(value)
super
@image_sprite&.visible = value
@icon_sprites&.each { |s| s.visible = value }
end
def dispose
@image_sprite&.dispose
@icon_sprites&.each(&:dispose)
super
end
def draw_text(x_offset = 0)
return unless self.bitmap && @text && @text != ""
frame_w = @image_sprite ? @image_sprite.src_rect.width : 0
text_x = IMAGE_TEXT_GAP
max_width = self.bitmap.width - text_x
lines = wrap_text(@text, self.bitmap, max_width)
lines.each_with_index do |line, i|
y_pos = (get_height / 2) - (LINE_HEIGHT * lines.size / 2) + (LINE_HEIGHT * i)
self.bitmap.font.color = @shadow_color
self.bitmap.draw_text(text_x + 1, y_pos + 1, max_width, LINE_HEIGHT, line)
self.bitmap.font.color = @text_color
self.bitmap.draw_text(text_x, y_pos, max_width, LINE_HEIGHT, line)
end
end
end