mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2026-07-22 07:37:00 +00:00
Update 6.8
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
class PokeNavAppScene
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Configuration (override in child classes)
|
||||
#--------------------------------------------------------------------------
|
||||
HEADER_HEIGHT = -6
|
||||
|
||||
def display_mode
|
||||
return :LIST # :LIST or :GRID
|
||||
end
|
||||
|
||||
def start_x
|
||||
return 60;
|
||||
end
|
||||
|
||||
def start_y
|
||||
return 80;
|
||||
end
|
||||
|
||||
def x_gap
|
||||
return 220;
|
||||
end
|
||||
|
||||
def y_gap
|
||||
return 100;
|
||||
end
|
||||
|
||||
def visible_rows
|
||||
return 4;
|
||||
end
|
||||
|
||||
def cursor_x_offset
|
||||
return 0
|
||||
end
|
||||
|
||||
def cursor_y_offset
|
||||
return 0
|
||||
end
|
||||
|
||||
def header_path
|
||||
return "Graphics/Pictures/Pokegear/bg_header"
|
||||
end
|
||||
|
||||
def bg_path
|
||||
return ""
|
||||
end
|
||||
|
||||
def cursor_path
|
||||
return "Graphics/Pictures/Pokegear/icon_button"
|
||||
end
|
||||
|
||||
def header_name
|
||||
return _INTL("PokeNav App")
|
||||
end
|
||||
|
||||
def columns
|
||||
return (display_mode == :GRID) ? 2 : 1
|
||||
end
|
||||
|
||||
def initialize
|
||||
@text_color_base = pbColor(:DARK_TEXT_MAIN_COLOR)
|
||||
@text_color_shadow = pbColor(:DARK_TEXT_SHADOW_COLOR)
|
||||
if isDarkMode
|
||||
@text_color_base, @text_color_shadow = @text_color_shadow, @text_color_base
|
||||
end
|
||||
end
|
||||
|
||||
# buttons should be a list of PokeNavButton
|
||||
def pbStartScene(buttons = [])
|
||||
@buttons = buttons
|
||||
@index = 0
|
||||
@mode = display_mode
|
||||
@exiting = false
|
||||
@viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
|
||||
@viewport.z = 99999
|
||||
@sprites = {}
|
||||
|
||||
createBackground
|
||||
createHeader if use_header?
|
||||
|
||||
@buttons.each_with_index do |b, i|
|
||||
b.viewport = @viewport
|
||||
b.z=1
|
||||
@sprites["button#{i}"] = b
|
||||
end
|
||||
if @buttons && !@buttons.empty?
|
||||
createCursor
|
||||
layoutButtons
|
||||
end
|
||||
init_graphics
|
||||
pbUpdateSpriteHash(@sprites)
|
||||
pbFadeInAndShow(@sprites) { pbUpdate }
|
||||
|
||||
end
|
||||
|
||||
def init_graphics
|
||||
@sprites["bg"] = IconSprite.new(0, 0, @viewport)
|
||||
@sprites["bg"].setBitmap(bg_path)
|
||||
@sprites["bg"].z=0
|
||||
end
|
||||
|
||||
def pbUpdate
|
||||
layoutButtons
|
||||
pbUpdateSpriteHash(@sprites)
|
||||
end
|
||||
|
||||
def layoutButtons
|
||||
return if @exiting
|
||||
cols = columns
|
||||
rows_visible = visible_rows
|
||||
|
||||
# Current selected row
|
||||
current_row = @index / cols
|
||||
|
||||
# Scroll offset in rows
|
||||
scroll_row = 0
|
||||
if current_row >= rows_visible
|
||||
scroll_row = current_row - rows_visible + 1
|
||||
end
|
||||
|
||||
scroll_pixels = scroll_row * y_gap
|
||||
|
||||
@buttons.each_with_index do |btn, i|
|
||||
row = i / cols
|
||||
col = i % cols
|
||||
|
||||
btn.x = start_x + col * x_gap
|
||||
btn.y = start_y + row * y_gap - scroll_pixels
|
||||
# Only show buttons fully within the scrollable content area, never over the header
|
||||
btn.visible = (btn.y >= start_y && btn.y <= Graphics.height)
|
||||
btn.selected = (i == @index)
|
||||
end
|
||||
updateCursor
|
||||
updateHeader(scroll_row)
|
||||
end
|
||||
|
||||
def updateCursor
|
||||
cursor = @sprites["cursor"]
|
||||
return unless cursor
|
||||
return if @buttons.empty?
|
||||
|
||||
btn = @buttons[@index]
|
||||
return unless btn
|
||||
|
||||
cursor.x = btn.x + cursor_x_offset
|
||||
cursor.y = btn.y + cursor_y_offset
|
||||
cursor.visible = btn.visible
|
||||
end
|
||||
|
||||
def set_cursor_visible(value)
|
||||
if @sprites["cursor"]
|
||||
@sprites["cursor"].visible = value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def createCursor
|
||||
return unless cursor_path
|
||||
@sprites["cursor"] = IconSprite.new(0, 0, @viewport)
|
||||
@sprites["cursor"].setBitmap(cursor_path)
|
||||
@sprites["cursor"].z = 100000
|
||||
end
|
||||
|
||||
def createHeader
|
||||
@sprites["header"] = IconSprite.new(0, 0, @viewport)
|
||||
@sprites["header"].setBitmap(header_path)
|
||||
# Ensure header always renders above buttons and cursor
|
||||
@sprites["header"].z = 100001
|
||||
end
|
||||
|
||||
def createBackground
|
||||
@sprites["background"] = IconSprite.new(0, 0, @viewport)
|
||||
if isDarkMode
|
||||
@sprites["background"].setBitmap("Graphics/Pictures/Pokegear/bg_dark")
|
||||
else
|
||||
@sprites["background"].setBitmap("Graphics/Pictures/Pokegear/bg")
|
||||
end
|
||||
end
|
||||
|
||||
def updateHeader(scroll_row)
|
||||
return unless use_header?
|
||||
return unless @sprites["header"]
|
||||
end
|
||||
|
||||
def displayTextElements
|
||||
Kernel.pbClearText
|
||||
showHeaderName
|
||||
end
|
||||
|
||||
def use_header?
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
|
||||
def showHeaderName
|
||||
Kernel.pbDisplayText(header_name, Graphics.width/2 , HEADER_HEIGHT)
|
||||
end
|
||||
def pbScene
|
||||
loop do
|
||||
Graphics.update
|
||||
Input.update
|
||||
|
||||
updateInput
|
||||
layoutButtons
|
||||
pbUpdateSpriteHash(@sprites)
|
||||
|
||||
break if @exiting
|
||||
end
|
||||
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) && cols > 1
|
||||
move_index(-1)
|
||||
pbWait(move_delay)
|
||||
elsif Input.repeat?(Input::RIGHT) && cols > 1
|
||||
move_index(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 hover(button_id)
|
||||
@buttons[@index]&.hover
|
||||
end
|
||||
|
||||
def click(button_id)
|
||||
@buttons[@index]&.click
|
||||
end
|
||||
def move_index(delta)
|
||||
echoln "old: #{@index}"
|
||||
|
||||
return if @buttons.empty?
|
||||
pbPlayCursorSE
|
||||
@index = (@index + delta) % @buttons.length
|
||||
hover(@buttons[@index]&.id)
|
||||
end
|
||||
|
||||
def move_to_index(index)
|
||||
return if @buttons.empty?
|
||||
pbPlayCursorSE
|
||||
@index = index
|
||||
echoln "new: #{@index}"
|
||||
|
||||
hover(@buttons[@index]&.id)
|
||||
|
||||
|
||||
end
|
||||
|
||||
def pbEndScene
|
||||
@exiting = true
|
||||
pbFadeOutAndHide(@sprites) { pbUpdate }
|
||||
pbDisposeSpriteHash(@sprites)
|
||||
@buttons.each do |button|
|
||||
button.dispose
|
||||
end
|
||||
Kernel.pbClearText
|
||||
@viewport.dispose
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,209 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class PokenavButton < SpriteWrapper
|
||||
DESC_Y = 10
|
||||
LINE_HEIGHT = 22
|
||||
REWARD_LINE = DESC_Y + LINE_HEIGHT * 2
|
||||
|
||||
attr_reader :id
|
||||
|
||||
attr_accessor :height
|
||||
attr_accessor :width
|
||||
attr_accessor :image_path
|
||||
attr_accessor :icon_bitmap
|
||||
attr_accessor :x
|
||||
attr_accessor :y
|
||||
attr_accessor :crop_width
|
||||
attr_accessor :crop_height
|
||||
|
||||
attr_accessor :text_color
|
||||
attr_accessor :shadow_color
|
||||
|
||||
DEFAULT_WIDTH = 200
|
||||
DEFAULT_HEIGHT = 40
|
||||
|
||||
def initialize(id, icon = nil, text = nil, viewport = nil)
|
||||
super(viewport)
|
||||
|
||||
@id = id
|
||||
@selected = false
|
||||
@text = text || get_text
|
||||
@crop_width = nil
|
||||
@crop_height = nil
|
||||
|
||||
@text_color = pbColor(:DARK_TEXT_MAIN_COLOR)
|
||||
@shadow_color = pbColor(:DARK_TEXT_SHADOW_COLOR)
|
||||
@text_padding = text_padding
|
||||
if isDarkMode
|
||||
@text_color, @shadow_color = @shadow_color, @text_color
|
||||
end
|
||||
@bg = IconSprite.new(0, 0, @viewport)
|
||||
@bg.setBitmap(background_image)
|
||||
@bg.z = self.z - 1
|
||||
# Determine source bitmap
|
||||
if icon.is_a?(String)
|
||||
@icon_bitmap = AnimatedBitmap.new(icon)
|
||||
bmp = @icon_bitmap.bitmap
|
||||
elsif icon.is_a?(AnimatedBitmap)
|
||||
@icon_bitmap = icon
|
||||
bmp = icon.bitmap
|
||||
elsif icon.is_a?(Bitmap)
|
||||
bmp = icon
|
||||
else
|
||||
bmp = nil
|
||||
end
|
||||
|
||||
# Create display bitmap
|
||||
if bmp
|
||||
self.bitmap = Bitmap.new(bmp.width, bmp.height)
|
||||
self.bitmap.blt(0, 0, bmp, Rect.new(0, 0, bmp.width, bmp.height))
|
||||
else
|
||||
create_empty_bitmap
|
||||
end
|
||||
refresh
|
||||
end
|
||||
|
||||
def text_alignment
|
||||
return 0 #Left
|
||||
#1 for centered
|
||||
end
|
||||
|
||||
def text_padding
|
||||
return 4
|
||||
end
|
||||
def viewport=(vp)
|
||||
super(vp)
|
||||
@bg&.dispose
|
||||
@bg = IconSprite.new(0, 0, vp)
|
||||
@bg.setBitmap(background_image)
|
||||
@bg.z = self.z - 1
|
||||
create_empty_bitmap if self.bitmap.nil?
|
||||
refresh
|
||||
end
|
||||
|
||||
def background_image
|
||||
return ""
|
||||
end
|
||||
|
||||
def create_empty_bitmap
|
||||
self.bitmap = Bitmap.new(get_width, get_height)
|
||||
pbSetSystemFont(self.bitmap)
|
||||
|
||||
# Draw background image directly onto bitmap
|
||||
bg_path = background_image
|
||||
if bg_path && bg_path != ""
|
||||
bg_bmp = AnimatedBitmap.new(bg_path).bitmap
|
||||
self.bitmap.stretch_blt(
|
||||
Rect.new(0, 0, get_width, get_height),
|
||||
bg_bmp,
|
||||
Rect.new(0, 0, bg_bmp.width, bg_bmp.height)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def x=(value)
|
||||
@x = value
|
||||
super(value)
|
||||
@bg.x = value if @bg
|
||||
end
|
||||
|
||||
def y=(value)
|
||||
@y = value
|
||||
super(value)
|
||||
@bg.y = value if @bg
|
||||
end
|
||||
|
||||
def z=(value)
|
||||
super(value)
|
||||
@bg.z = value - 1 if @bg
|
||||
end
|
||||
|
||||
def get_height
|
||||
return DEFAULT_HEIGHT
|
||||
end
|
||||
|
||||
def get_width
|
||||
return DEFAULT_WIDTH
|
||||
end
|
||||
|
||||
def get_default_image_path
|
||||
return ""
|
||||
end
|
||||
|
||||
def get_text
|
||||
return ""
|
||||
end
|
||||
|
||||
def dispose
|
||||
dispose_graphics
|
||||
super
|
||||
end
|
||||
|
||||
def dispose_graphics
|
||||
@icon_bitmap.dispose if @icon_bitmap
|
||||
@bg.dispose if @bg
|
||||
@icon_bitmap = nil
|
||||
@bg =nil
|
||||
end
|
||||
|
||||
def click
|
||||
echoln "clicked #{@id}"
|
||||
end
|
||||
|
||||
def hover
|
||||
echoln "hovering over #{@id}"
|
||||
end
|
||||
|
||||
def selected=(val)
|
||||
oldsel = @selected
|
||||
@selected = val
|
||||
refresh if oldsel != val
|
||||
end
|
||||
|
||||
def refresh
|
||||
return unless self.bitmap
|
||||
self.bitmap.clear
|
||||
|
||||
# Redraw background
|
||||
bg_path = background_image
|
||||
if bg_path && bg_path != ""
|
||||
bg_bmp = AnimatedBitmap.new(bg_path).bitmap
|
||||
self.bitmap.stretch_blt(
|
||||
Rect.new(0, 0, self.bitmap.width, self.bitmap.height),
|
||||
bg_bmp,
|
||||
Rect.new(0, 0, bg_bmp.width, bg_bmp.height)
|
||||
)
|
||||
end
|
||||
|
||||
if @icon_bitmap
|
||||
bmp = @icon_bitmap.bitmap
|
||||
width = @crop_width || bmp.width
|
||||
height = @crop_height || bmp.height
|
||||
self.bitmap.blt(0, 0, bmp, Rect.new(0, 0, width, height))
|
||||
end
|
||||
|
||||
draw_text if @text && @text != ""
|
||||
end
|
||||
|
||||
def draw_text
|
||||
return unless self.bitmap && @text && @text != ""
|
||||
padding = @text_padding
|
||||
max_width = self.bitmap.width - (padding * 2)
|
||||
|
||||
lines = wrap_text(@text, self.bitmap, max_width)
|
||||
|
||||
total_text_height = lines.length * LINE_HEIGHT
|
||||
start_y = (self.bitmap.height - total_text_height) / 2
|
||||
|
||||
lines.each_with_index do |line, i|
|
||||
y_pos = start_y + (LINE_HEIGHT * i)
|
||||
self.bitmap.font.color = @shadow_color
|
||||
self.bitmap.draw_text(padding + 1, y_pos + 1, max_width, LINE_HEIGHT, line, text_alignment)
|
||||
self.bitmap.font.color = @text_color
|
||||
self.bitmap.draw_text(padding, y_pos, max_width, LINE_HEIGHT, line, text_alignment)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
def checkTrainerRematchChallenges
|
||||
$Trainer.complete_challenge(:rematch_trainer)
|
||||
end
|
||||
|
||||
class PokeBattle_Battler
|
||||
def checkChallengesAfterTurn()
|
||||
@battle.eachBattler do |battler|
|
||||
if battler.pbOwnedByPlayer?
|
||||
|
||||
else
|
||||
# # 1 HP left check
|
||||
if battler.hp == 1
|
||||
$Trainer.complete_challenge(:battle_enemy_1_hp)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def checkStatRaiseBattleChallenge(stat, increment)
|
||||
$Trainer.complete_challenge(:battle_stat_boost)
|
||||
$Trainer.complete_challenge(:battle_stat_boost_sharp) if increment >= 2
|
||||
if statStageAtMax?(stat)
|
||||
$Trainer.complete_challenge(:battle_stat_boost_max)
|
||||
end
|
||||
end
|
||||
|
||||
alias challenge_pbFlinch pbFlinch
|
||||
|
||||
def pbFlinch(_user = nil)
|
||||
challenge_pbFlinch(_user)
|
||||
if _user&.pbOwnedByPlayer?
|
||||
$Trainer.complete_challenge(:battle_flinch)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class PokeBattle_Move
|
||||
alias challenge_pbInflictHPDamage pbInflictHPDamage
|
||||
|
||||
def pbInflictHPDamage(target)
|
||||
challenge_pbInflictHPDamage(target)
|
||||
|
||||
return if target.pbOwnedByPlayer?
|
||||
# Not very effective 1 hit KO
|
||||
if target.fainted? &&
|
||||
target.damageState.initialHP == target.totalhp &&
|
||||
Effectiveness.not_very_effective?(target.damageState.typeMod)
|
||||
$Trainer.complete_challenge(:defeat_1_not_very_effective)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
class PokemonTemp
|
||||
attr_accessor :pokemon_is_weather_encounter
|
||||
end
|
||||
module PokeBattle_BattleCommon
|
||||
def checkCatchChallenge(pokeball, battle, caught_pokemon)
|
||||
#Caught in 1 try
|
||||
if battle.balls_thrown == 0 #It's incremented after this method gets checked
|
||||
$Trainer.complete_challenge(:catch_first_try)
|
||||
end
|
||||
|
||||
#Catching at full health
|
||||
if caught_pokemon.hp == caught_pokemon.totalhp
|
||||
$Trainer.complete_challenge(:catch_full_health)
|
||||
end
|
||||
|
||||
#Without receiving any damage
|
||||
if battle.damage_received ==0
|
||||
$Trainer.complete_challenge(:catch_no_damage)
|
||||
end
|
||||
|
||||
if pokeball == :PREMIERBALL
|
||||
$Trainer.complete_challenge(:catch_premierball)
|
||||
end
|
||||
|
||||
if caught_pokemon.isFusion?
|
||||
$Trainer.complete_challenge(:catch_fused)
|
||||
end
|
||||
|
||||
if $PokemonBag.pbQuantity(pokeball) == 0
|
||||
$Trainer.complete_challenge(:catch_last_pokeball)
|
||||
end
|
||||
|
||||
if $PokemonTemp.pokemon_is_weather_encounter
|
||||
$Trainer.complete_challenge(:catch_weather_encounter)
|
||||
end
|
||||
end
|
||||
end
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
##
|
||||
## Hooks for challenges about encountering overworld Pokemon
|
||||
##
|
||||
|
||||
def all_same_pokemon?(pokemon_array)
|
||||
species_array = pokemon_array.map {|pokemon| pokemon.species}
|
||||
different_species = species_array.uniq
|
||||
return different_species.length == 1
|
||||
end
|
||||
|
||||
def all_different_pokemon?(pokemon_array)
|
||||
species_array = pokemon_array.map {|pokemon| pokemon.species}
|
||||
different_species = species_array.uniq
|
||||
return different_species.length > 1
|
||||
end
|
||||
|
||||
def checkEncounterChallenges(encountered_pokemon)
|
||||
case encountered_pokemon.length
|
||||
when 1
|
||||
when 2
|
||||
$Trainer.complete_challenge(:encounter_2_pokemon_at_once)
|
||||
if all_different_pokemon?(encountered_pokemon)
|
||||
$Trainer.complete_challenge(:encounter_2_different_pokemon_at_once)
|
||||
elsif all_same_pokemon?(encountered_pokemon)
|
||||
$Trainer.complete_challenge(:encounter_2_same_pokemon_at_once)
|
||||
end
|
||||
when 3
|
||||
$Trainer.complete_challenge(:encounter_3_pokemon_at_once)
|
||||
if all_different_pokemon?(encountered_pokemon)
|
||||
$Trainer.complete_challenge(:encounter_3_different_pokemon_at_once)
|
||||
elsif all_same_pokemon?(encountered_pokemon)
|
||||
$Trainer.complete_challenge(:encounter_3_same_pokemon_at_once)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def checkWildFusePokemonChallenge(pokemon1,pokemon2)
|
||||
$Trainer.complete_challenge(:fuse_wild_pokemon)
|
||||
if $game_switches[SWITCH_WILD_FUSION_QUEST]
|
||||
current_nb = pbGet(VAR_NB_WILD_FUSING_SEEN)
|
||||
current_nb+=1
|
||||
pbSet(VAR_NB_WILD_FUSING_SEEN,current_nb)
|
||||
if current_nb <= 3
|
||||
pbMEPlay("Register phone")
|
||||
pbCallBub(3)
|
||||
pbMessage(_INTL("Wild Fusion Study: {1} / 3",current_nb))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,41 @@
|
||||
class PokemonTemp
|
||||
attr_accessor :fuse_count_today
|
||||
attr_accessor :unfuse_count_today
|
||||
end
|
||||
|
||||
def checkFuseChallenges(head_pokemon, body_pokemon)
|
||||
case $PokemonTemp.fuse_count_today
|
||||
when 1
|
||||
$Trainer.complete_challenge(:fuse_1_pokemon)
|
||||
when 2
|
||||
$Trainer.complete_challenge(:fuse_2_pokemon)
|
||||
when 5
|
||||
$Trainer.complete_challenge(:fuse_5_pokemon)
|
||||
end
|
||||
|
||||
echoln head_pokemon.species
|
||||
echoln body_pokemon.species
|
||||
echoln head_pokemon.species == body_pokemon.species
|
||||
if head_pokemon.species == body_pokemon.species
|
||||
$Trainer.complete_challenge(:fuse_same_species)
|
||||
end
|
||||
|
||||
species_data_head = GameData::Species.get(head_pokemon.species)
|
||||
species_data_body = GameData::Species.get(body_pokemon.species)
|
||||
for type in species_data_head&.types
|
||||
if species_data_body.hasType?(type)
|
||||
$Trainer.complete_challenge(:fuse_same_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def checkUnfuseChallenges(unfused_pokemon)
|
||||
case $PokemonTemp.unfuse_count_today
|
||||
when 1
|
||||
$Trainer.complete_challenge(:unfuse_1_pokemon)
|
||||
when 2
|
||||
$Trainer.complete_challenge(:unfuse_2_pokemon)
|
||||
when 5
|
||||
$Trainer.complete_challenge(:unfuse_5_pokemon)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,154 @@
|
||||
def openChallengeApp
|
||||
pbFadeOutIn {
|
||||
scene = PokemonChallenges_Scene.new
|
||||
screen = PokemonChallenges_Screen.new(scene)
|
||||
screen.pbStartScreen
|
||||
# scene.pbRefresh
|
||||
}
|
||||
end
|
||||
|
||||
class Player
|
||||
|
||||
def add_challenge(challenge_id)
|
||||
@challenges = {} unless @challenges
|
||||
challenge_template = CHALLENGES[challenge_id]
|
||||
return unless challenge_template
|
||||
|
||||
reward = pick_challenge_reward(challenge_template.category)
|
||||
@challenges[challenge_id] = PlayerChallenge.new(challenge_id, reward)
|
||||
end
|
||||
|
||||
def complete_challenge(challenge_id)
|
||||
@challenges = {} unless @challenges
|
||||
if @challenges.has_key?(challenge_id)
|
||||
challenge = @challenges[challenge_id]
|
||||
challenge.completed = true
|
||||
@challenges[challenge_id] = challenge
|
||||
$Trainer.nb_completed_challenges = 0 unless $Trainer.nb_completed_challenges
|
||||
$Trainer.nb_completed_challenges += 1
|
||||
pbSEPlay("challenge_complete")
|
||||
end
|
||||
end
|
||||
|
||||
def completed_challenge?(challenge_id)
|
||||
if @challenges.has_key?(challenge_id)
|
||||
return @challenges[challenge_id]
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def select_random_challenge(category)
|
||||
candidates = CHALLENGES.select { |_k, v| v.category == category }.keys
|
||||
return nil if candidates.empty?
|
||||
return candidates.sample
|
||||
end
|
||||
|
||||
def refresh_challenges()
|
||||
@challenges = {} unless @challenges
|
||||
@challenges.each do |challenge_id, challenge|
|
||||
unless challenge.completed
|
||||
remove_challenge(challenge.id)
|
||||
end
|
||||
end
|
||||
categories = [:encounter, :battle, :catch, :fusion]
|
||||
categories.each do |category|
|
||||
unclaimed_challenges = listPlayerChallengesOfCategory(category)
|
||||
if unclaimed_challenges.empty?
|
||||
challenge_id = select_random_challenge(category)
|
||||
add_challenge(challenge_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def listPlayerChallengesOfCategory(category)
|
||||
challenges_list = []
|
||||
@challenges.each do |challenge_id, challenge|
|
||||
if challenge.category == category
|
||||
challenges_list << challenge.id
|
||||
end
|
||||
end
|
||||
return challenges_list
|
||||
end
|
||||
|
||||
def remove_challenge(challenge_id)
|
||||
@challenges.delete(challenge_id)
|
||||
end
|
||||
|
||||
def clear_all_challenges
|
||||
@challenges = {} unless @challenges
|
||||
for id in @challenges
|
||||
remove_challenge(id)
|
||||
end
|
||||
end
|
||||
|
||||
def pick_challenge_reward(category)
|
||||
tier = rand(3) == 0 ? :TIER2 : :TIER1
|
||||
items_list = []
|
||||
case category
|
||||
when :encounter
|
||||
items_list = tier == :TIER1 ? CHALLENGE_ENCOUNTER_REWARDS_TIER1 : CHALLENGE_ENCOUNTER_REWARDS_TIER2
|
||||
when :battle
|
||||
items_list = tier == :TIER1 ? CHALLENGE_BATTLE_REWARDS_TIER1 : CHALLENGE_BATTLE_REWARDS_TIER2
|
||||
when :catch
|
||||
items_list = tier == :TIER1 ? CHALLENGE_CATCH_REWARDS_TIER1 : CHALLENGE_CATCH_REWARDS_TIER2
|
||||
when :fusion
|
||||
items_list = tier == :TIER1 ? CHALLENGE_FUSE_REWARDS_TIER1 : CHALLENGE_FUSE_REWARDS_TIER2
|
||||
end
|
||||
item = items_list.sample
|
||||
if tier == :TIER1
|
||||
quantity = rand(1..3)
|
||||
else
|
||||
quantity = 1
|
||||
end
|
||||
reward = []
|
||||
for i in 1..quantity
|
||||
reward << item
|
||||
end
|
||||
return reward
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class PlayerChallenge
|
||||
attr_reader :id
|
||||
attr_reader :item_reward
|
||||
attr_reader :template
|
||||
attr_accessor :completed
|
||||
|
||||
def initialize(id, item_reward = [])
|
||||
@id = id
|
||||
@item_reward = item_reward
|
||||
@template = CHALLENGES[id]
|
||||
@completed = false
|
||||
end
|
||||
|
||||
def description
|
||||
return _INTL(@template.description)
|
||||
end
|
||||
|
||||
def category
|
||||
return _INTL(@template.category)
|
||||
end
|
||||
|
||||
def money_reward
|
||||
return _INTL(@template.money_reward)
|
||||
end
|
||||
end
|
||||
|
||||
class ChallengeTemplate
|
||||
attr_reader :id, :description, :category, :money_reward
|
||||
|
||||
def initialize(id, description, category, money_reward)
|
||||
@id = id
|
||||
@description = description
|
||||
@money_reward = money_reward
|
||||
@category = category
|
||||
end
|
||||
end
|
||||
|
||||
CHALLENGES = {}
|
||||
|
||||
def define_challenge(id, description:, category:, money_reward:)
|
||||
CHALLENGES[id] = ChallengeTemplate.new(id, description, category, money_reward)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
CHALLENGE_ENCOUNTER_REWARDS_TIER1 = [:REPEL, :POTION, :ANTIDOTE]
|
||||
CHALLENGE_ENCOUNTER_REWARDS_TIER2 = [:SUPERREPEL, :SUPERPOTION, :REVIVE, :DAWNSTONE, :DUSKSTONE]
|
||||
|
||||
CHALLENGE_BATTLE_REWARDS_TIER1 = [:XATTACK, :XDEFENSE, :ETHER, :FULLHEAL]
|
||||
CHALLENGE_BATTLE_REWARDS_TIER2 = [:ELIXIR, :SITRUSBERRY]
|
||||
|
||||
CHALLENGE_CATCH_REWARDS_TIER1 = [:POKEBALL, :GREATBALL, :PREMIERBALL, :HEALBALL]
|
||||
CHALLENGE_CATCH_REWARDS_TIER2 = [:ULTRABALL, :QUICKBALL, :DUSKBALL]
|
||||
|
||||
CHALLENGE_FUSE_REWARDS_TIER1 = [:DNASPLICERS, :DNAREVERSER]
|
||||
CHALLENGE_FUSE_REWARDS_TIER2 = [:FUSIONBALL, :FUSIONREPEL]
|
||||
|
||||
########################
|
||||
# Encounter challenges
|
||||
########################
|
||||
|
||||
define_challenge :encounter_2_pokemon_at_once,
|
||||
description: _INTL("Get into a battle against 2 wild Pokémon at once"),
|
||||
category: :encounter,
|
||||
money_reward: 200
|
||||
|
||||
define_challenge :encounter_3_pokemon_at_once,
|
||||
description: _INTL("Get into a battle against 3 wild Pokémon at once"),
|
||||
category: :encounter,
|
||||
money_reward: 750
|
||||
|
||||
define_challenge :encounter_2_different_pokemon_at_once,
|
||||
description: _INTL("Get into a battle against 2 different wild Pokémon at once"),
|
||||
category: :encounter,
|
||||
money_reward: 350
|
||||
|
||||
define_challenge :encounter_3_different_pokemon_at_once,
|
||||
description: _INTL("Get into a battle against 3 different wild Pokémon at once"),
|
||||
category: :encounter,
|
||||
money_reward: 750
|
||||
|
||||
define_challenge :encounter_2_fused_pokemon_at_once,
|
||||
description: _INTL("Get into a battle against 2 wild fused Pokémon at once"),
|
||||
category: :encounter,
|
||||
money_reward: 1000
|
||||
|
||||
define_challenge :encounter_2_same_pokemon_at_once,
|
||||
description: _INTL("Get into a battle against 2 of the same Pokémon at once"),
|
||||
category: :encounter,
|
||||
money_reward: 350
|
||||
|
||||
define_challenge :encounter_3_same_pokemon_at_once,
|
||||
description: _INTL("Get into a battle against 3 of the same species of Pokémon at once"),
|
||||
category: :encounter,
|
||||
money_reward: 750
|
||||
|
||||
|
||||
define_challenge :encounter_offguard_any,
|
||||
description: _INTL("Catch a Pokémon off guard"),
|
||||
category: :encounter,
|
||||
money_reward: 200
|
||||
|
||||
define_challenge :encounter_offguard_curious,
|
||||
description: _INTL("Catch a curious Pokémon off guard"),
|
||||
category: :encounter,
|
||||
money_reward: 350
|
||||
|
||||
define_challenge :encounter_offguard_aggressive,
|
||||
description: _INTL("Catch an aggressive Pokémon off guard"),
|
||||
category: :encounter,
|
||||
money_reward: 500
|
||||
|
||||
define_challenge :encounter_offguard_skittish,
|
||||
description: _INTL("Catch a skittish Pokémon off guard"),
|
||||
category: :encounter,
|
||||
money_reward: 500
|
||||
|
||||
|
||||
# define_challenge :wild_pokemon_chase_20_steps,
|
||||
# description: _INTL("Get a wild Pokémon to chase you for 20 steps"),
|
||||
# money_reward: 1000
|
||||
|
||||
|
||||
########################
|
||||
# Battle challenges
|
||||
########################
|
||||
define_challenge :defeat_1_not_very_effective,
|
||||
description: _INTL("Defeat a Pokémon in one move using a not-very-effective move"),
|
||||
category: :battle,
|
||||
money_reward: 400
|
||||
#
|
||||
# define_challenge :defeat_1_indirect_damage,
|
||||
# description: _INTL("Defeat a wild Pokémon without inflicting any direct damage"),
|
||||
# money_reward: 500
|
||||
#
|
||||
define_challenge :battle_enemy_1_hp,
|
||||
description: _INTL("Get an opposing Pokémon to exactly 1 HP"),
|
||||
category: :battle,
|
||||
money_reward: 1000
|
||||
|
||||
define_challenge :battle_flinch,
|
||||
description: _INTL("Make your opponent flinch during a battle"),
|
||||
category: :battle,
|
||||
money_reward: 250
|
||||
|
||||
define_challenge :rematch_trainer,
|
||||
description: _INTL("Rematch a trainer"),
|
||||
category: :battle,
|
||||
money_reward: 400
|
||||
|
||||
# define_challenge :battle_use_healing_item,
|
||||
# description: _INTL("Use a healing item during a battle."),
|
||||
# category: :battle,
|
||||
# money_reward: 200
|
||||
#
|
||||
define_challenge :battle_stat_boost,
|
||||
description: _INTL("Use a stat-boosting move or item during a battle"),
|
||||
category: :battle,
|
||||
money_reward: 300
|
||||
|
||||
define_challenge :battle_stat_boost_sharp,
|
||||
description: _INTL("Sharply boost your Pokémon's stats in a battle"),
|
||||
category: :battle,
|
||||
money_reward: 500
|
||||
|
||||
define_challenge :battle_stat_boost_max,
|
||||
description: _INTL("Max out one of your stats during a battle"),
|
||||
category: :battle,
|
||||
money_reward: 1000
|
||||
|
||||
#
|
||||
# define_challenge :battle_berry,
|
||||
# description: _INTL("Have a Pokémon use a berry during a battle"),
|
||||
# category: :battle,
|
||||
# money_reward: 300
|
||||
|
||||
# define_challenge :battle_one_hit_ko,
|
||||
# description: _INTL("Land a One-Hit-KO move"),
|
||||
# category: :fight,
|
||||
# money_reward: 1000
|
||||
#
|
||||
|
||||
########################
|
||||
# Catching challenges
|
||||
########################
|
||||
|
||||
|
||||
define_challenge :catch_first_try,
|
||||
description: _INTL("Catch a Pokémon on the first try"),
|
||||
category: :catch,
|
||||
money_reward: 400
|
||||
|
||||
define_challenge :catch_full_health,
|
||||
description: _INTL("Catch a Pokémon at full health"),
|
||||
category: :catch,
|
||||
money_reward: 400
|
||||
|
||||
define_challenge :catch_no_damage,
|
||||
description: _INTL("Catch a Pokémon without receiving any damage"),
|
||||
category: :catch,
|
||||
money_reward: 400
|
||||
|
||||
define_challenge :catch_premierball,
|
||||
description: _INTL("Catch a Pokémon using a Premier Ball"),
|
||||
category: :catch,
|
||||
money_reward: 150
|
||||
|
||||
define_challenge :catch_fused,
|
||||
description: _INTL("Catch a wild fused Pokémon"),
|
||||
category: :catch,
|
||||
money_reward: 300
|
||||
|
||||
define_challenge :catch_last_pokeball,
|
||||
description: _INTL("Catch a Pokémon on your very last ball"),
|
||||
category: :catch,
|
||||
money_reward: 1000
|
||||
|
||||
define_challenge :catch_weather_encounter,
|
||||
description: _INTL("Catch a Pokémon that appears in special weather conditions"),
|
||||
category: :catch,
|
||||
money_reward: 750
|
||||
|
||||
|
||||
########################
|
||||
# Fusing challenges
|
||||
########################
|
||||
|
||||
|
||||
#
|
||||
define_challenge :fuse_same_species,
|
||||
description: _INTL("Fuse two Pokémon of the same species together"),
|
||||
category: :fusion,
|
||||
money_reward: 500
|
||||
|
||||
define_challenge :fuse_same_type,
|
||||
description: _INTL("Fuse two Pokémon that share the same type"),
|
||||
category: :fusion,
|
||||
money_reward: 500
|
||||
|
||||
|
||||
|
||||
define_challenge :fuse_5_pokemon,
|
||||
description: _INTL("Fuse Pokémon 5 times"),
|
||||
category: :fusion,
|
||||
money_reward: 750
|
||||
|
||||
define_challenge :fuse_1_pokemon,
|
||||
description: _INTL("Fuse a Pokémon"),
|
||||
category: :fusion,
|
||||
money_reward: 200
|
||||
|
||||
define_challenge :fuse_2_pokemon,
|
||||
description: _INTL("Fuse Pokémon 2 times"),
|
||||
category: :fusion,
|
||||
money_reward: 300
|
||||
|
||||
define_challenge :unfuse_5_pokemon,
|
||||
description: _INTL("Unfuse Pokémon 5 times"),
|
||||
category: :fusion,
|
||||
money_reward: 750
|
||||
|
||||
define_challenge :unfuse_2_pokemon,
|
||||
description: _INTL("Unfuse Pokémon 2 times"),
|
||||
category: :fusion,
|
||||
money_reward: 300
|
||||
|
||||
define_challenge :unfuse_1_pokemon,
|
||||
description: _INTL("Unfuse a Pokémon"),
|
||||
category: :fusion,
|
||||
money_reward: 200
|
||||
|
||||
define_challenge :fuse_wild_pokemon,
|
||||
description: _INTL("Get two wild Pokémon to fuse before a battle"),
|
||||
category: :fusion,
|
||||
money_reward: 400
|
||||
@@ -0,0 +1,156 @@
|
||||
class PokemonChallenges_Scene
|
||||
VISIBLE_BUTTONS = 3 # Number of buttons visible at once
|
||||
Y_START = 41
|
||||
Y_GAP = 108
|
||||
BUTTONS_X = 60
|
||||
|
||||
def pbStartScene(challenges)
|
||||
@challenges = []
|
||||
@index = 0
|
||||
@viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
|
||||
@viewport.z = 99999
|
||||
@sprites = {}
|
||||
|
||||
# Background
|
||||
@sprites["background"] = IconSprite.new(0, 0, @viewport)
|
||||
if isDarkMode
|
||||
@sprites["background"].setBitmap("Graphics/Pictures/Pokegear/bg_dark")
|
||||
else
|
||||
@sprites["background"].setBitmap("Graphics/Pictures/Pokegear/bg")
|
||||
end
|
||||
@sprites["header"] = IconSprite.new(0, 0, @viewport)
|
||||
@sprites["header"].setBitmap("Graphics/Pictures/Challenges/bg_header")
|
||||
|
||||
# Buttons
|
||||
@buttons = []
|
||||
|
||||
$Trainer.challenges.keys.each_with_index do |challenge_id, i|
|
||||
challenge = $Trainer.challenges[challenge_id]
|
||||
next unless challenge
|
||||
@challenges << challenge
|
||||
btn = ChallengeButton.new(challenge, BUTTONS_X, Y_START + i * Y_GAP, @viewport)
|
||||
@sprites["button#{i}"] = btn
|
||||
@buttons << btn
|
||||
end
|
||||
displayTextElements
|
||||
pbFadeInAndShow(@sprites) { pbUpdate }
|
||||
end
|
||||
|
||||
def displayTextElements
|
||||
Kernel.pbClearText
|
||||
Kernel.pbDisplayText(_INTL("POKÉCHALLENGE"), 256,-8,99999, pbColor(:ORANGE), pbColor(:BROWN))
|
||||
Kernel.pbDisplayText($Trainer.nb_completed_challenges.to_s, 450,-6,99999, pbColor(:GREEN), pbColor(:DARKGREEN))
|
||||
if @buttons.empty?
|
||||
showEmptyMessage
|
||||
end
|
||||
end
|
||||
def showEmptyMessage
|
||||
text_pos_x =240
|
||||
text_pos_y =100
|
||||
text_color = pbColor(:WHITE)
|
||||
line_height = 36
|
||||
Kernel.pbDisplayText(_INTL("You finished all your PokéChallenges!"), text_pos_x, text_pos_y,99999, text_color)
|
||||
Kernel.pbDisplayText(_INTL("You'll be assigned new ones tomorrow."), text_pos_x, text_pos_y+line_height,99999, text_color)
|
||||
end
|
||||
def pbUpdate
|
||||
# Update selection
|
||||
@buttons.each_with_index do |btn, idx|
|
||||
btn.selected = (idx == @index)
|
||||
end
|
||||
|
||||
# Scroll calculation
|
||||
scroll_offset = 0
|
||||
if @index >= VISIBLE_BUTTONS
|
||||
Kernel.pbClearText
|
||||
@sprites["header"].visible=false
|
||||
scroll_offset = (@index - VISIBLE_BUTTONS + 1) * Y_GAP
|
||||
else
|
||||
displayTextElements
|
||||
@sprites["header"].visible=true
|
||||
end
|
||||
|
||||
# Move buttons according to scroll
|
||||
@buttons.each_with_index do |btn, idx|
|
||||
btn.y = Y_START + idx * Y_GAP - scroll_offset
|
||||
end
|
||||
if @buttons.empty?
|
||||
showEmptyMessage
|
||||
end
|
||||
|
||||
pbUpdateSpriteHash(@sprites)
|
||||
end
|
||||
|
||||
def pbScene
|
||||
loop do
|
||||
Graphics.update
|
||||
Input.update
|
||||
pbUpdate
|
||||
|
||||
if Input.trigger?(Input::BACK)
|
||||
pbPlayCloseMenuSE
|
||||
break
|
||||
elsif Input.trigger?(Input::USE)
|
||||
if @challenges.empty?
|
||||
pbPlayCloseMenuSE
|
||||
break
|
||||
end
|
||||
pbPlayDecisionSE
|
||||
challenge = @challenges[@index]
|
||||
|
||||
if @buttons[@index].can_claim_reward
|
||||
$Trainer.remove_challenge(challenge.id)
|
||||
removeChallengeAt(@index)
|
||||
pbSEPlay("GUI storage show party panel")
|
||||
receiveChallengeReward(challenge)
|
||||
pbUpdate
|
||||
else
|
||||
pbSEPlay("GUI sel buzzer",80)
|
||||
pbMessage(_INTL("The reward can only be collected once you complete the challenge!"))
|
||||
end
|
||||
elsif Input.trigger?(Input::UP)
|
||||
pbPlayCursorSE if @challenges.length > 1
|
||||
@index -= 1
|
||||
@index = @challenges.length - 1 if @index < 0
|
||||
elsif Input.trigger?(Input::DOWN)
|
||||
pbPlayCursorSE if @challenges.length > 1
|
||||
@index += 1
|
||||
@index = 0 if @index >= @challenges.length
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def receiveChallengeReward(challenge)
|
||||
money_reward = challenge.money_reward
|
||||
item_reward = challenge.item_reward
|
||||
quantity = item_reward.length
|
||||
pbReceiveMoney(money_reward)
|
||||
pbReceiveItem(item_reward[0], quantity)
|
||||
$Trainer.money += money_reward
|
||||
|
||||
end
|
||||
|
||||
def challengeCompleted?(challenge_id)
|
||||
return $Trainer.completed_challenge?(challenge_id)
|
||||
end
|
||||
|
||||
#Removes the challenge button sprite
|
||||
def removeChallengeAt(index)
|
||||
btn = @buttons[index]
|
||||
btn.dispose
|
||||
@sprites.delete_if { |_, v| v == btn }
|
||||
@buttons.delete_at(index)
|
||||
@challenges.delete_at(index)
|
||||
|
||||
if @index >= @buttons.length
|
||||
@index = @buttons.length - 1
|
||||
end
|
||||
@index = 0 if @index < 0
|
||||
end
|
||||
|
||||
def pbEndScene
|
||||
pbFadeOutAndHide(@sprites) { pbUpdate }
|
||||
pbDisposeSpriteHash(@sprites)
|
||||
Kernel.pbClearText
|
||||
@viewport.dispose
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class PokemonChallenges_Screen
|
||||
def initialize(scene)
|
||||
@scene = scene
|
||||
end
|
||||
|
||||
def pbStartScreen
|
||||
challenges = getChallengesList
|
||||
$Trainer.pokenav.last_opened_challenges = pbGetTimeNow
|
||||
@scene.pbStartScene(challenges)
|
||||
@scene.pbScene
|
||||
@scene.pbEndScene
|
||||
end
|
||||
|
||||
|
||||
def getChallengesList
|
||||
last_opened = $Trainer.pokenav.last_opened_challenges
|
||||
if shouldRefreshChallenges(last_opened)
|
||||
$Trainer.refresh_challenges()
|
||||
$PokemonTemp.fuse_count_today = 0
|
||||
$PokemonTemp.unfuse_count_today = 0
|
||||
end
|
||||
|
||||
return $Trainer.challenges.values
|
||||
end
|
||||
|
||||
|
||||
def shouldRefreshChallenges(last_opened_date)
|
||||
return true if last_opened_date.nil?
|
||||
#return Time.now.to_date > last_opened_date
|
||||
return timeDateGreaterThan(pbGetTimeNow,last_opened_date)
|
||||
#return pbGetTimeNow.to_date > last_opened_date #Replace Time.now by pbGetTimeNow everywhere to use in-game days for testing
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class ChallengeButton < SpriteWrapper
|
||||
attr_reader :challenge
|
||||
attr_reader :selected
|
||||
attr_reader :can_claim_reward
|
||||
|
||||
DESC_Y = 10
|
||||
LINE_HEIGHT = 22
|
||||
REWARD_LINE = DESC_Y + LINE_HEIGHT * 2
|
||||
|
||||
def initialize(challenge, x, y, viewport=nil)
|
||||
super(viewport)
|
||||
@challenge = challenge
|
||||
@can_claim_reward = @challenge.completed
|
||||
|
||||
@selected = false
|
||||
|
||||
graphics_completed = "Graphics/Pictures/Challenges/button_complete_#{@challenge.category.to_s}"
|
||||
graphics_incompleted = "Graphics/Pictures/Challenges/button_incomplete_#{@challenge.category.to_s}"
|
||||
|
||||
image_path = @can_claim_reward ? graphics_completed : graphics_incompleted
|
||||
@cursor = AnimatedBitmap.new(image_path)
|
||||
@contents = BitmapWrapper.new(@cursor.width, @cursor.height)
|
||||
self.bitmap = @contents
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
pbSetSystemFont(self.bitmap)
|
||||
refresh
|
||||
end
|
||||
|
||||
def dispose
|
||||
@cursor.dispose
|
||||
@contents.dispose
|
||||
super
|
||||
end
|
||||
|
||||
def selected=(val)
|
||||
oldsel = @selected
|
||||
@selected = val
|
||||
refresh if oldsel != val
|
||||
end
|
||||
|
||||
def refresh
|
||||
self.bitmap.clear
|
||||
|
||||
# Draw background
|
||||
rect = Rect.new(0, 0, @cursor.width, @cursor.height / 2)
|
||||
rect.y = @cursor.height / 2 if @selected
|
||||
self.bitmap.blt(0, 0, @cursor.bitmap, rect)
|
||||
|
||||
# Text colors
|
||||
text_color = @can_claim_reward ? Color.new(0,255,0) : Color.new(248,248,248)
|
||||
shadow_color = Color.new(40,40,40)
|
||||
|
||||
# Description text
|
||||
desc_lines = wrap_text(@challenge.description, @contents, @cursor.width - 40)[0, 2]
|
||||
textpos = []
|
||||
y_offset = DESC_Y
|
||||
desc_lines.each do |line|
|
||||
textpos << [line, 10, y_offset, 0, text_color, shadow_color, false]
|
||||
y_offset += LINE_HEIGHT
|
||||
end
|
||||
|
||||
reward_y = REWARD_LINE
|
||||
reward_text = @can_claim_reward ? _INTL("Collect") : _INTL("Reward")
|
||||
claim_color = @can_claim_reward ? Color.new(80, 220, 255) : Color.new(255, 215, 120)
|
||||
|
||||
textpos << [
|
||||
"#{reward_text} $#{@challenge.money_reward}",
|
||||
10, reward_y, 0,
|
||||
claim_color, shadow_color, false
|
||||
]
|
||||
|
||||
pbDrawTextPositions(self.bitmap, textpos)
|
||||
|
||||
# --------------------------------------------------
|
||||
# Draw item reward icons
|
||||
# --------------------------------------------------
|
||||
return if !@challenge.item_reward || @challenge.item_reward.empty?
|
||||
|
||||
icon_x = 170 # starting X (adjust to taste)
|
||||
icon_y = reward_y #- 4 # align with text nicely
|
||||
icon_size = 48
|
||||
icon_gap = 8
|
||||
|
||||
@challenge.item_reward.each do |item|
|
||||
icon_path = GameData::Item.icon_filename(item)
|
||||
next if !icon_path
|
||||
|
||||
icon = Bitmap.new(icon_path)
|
||||
src_rect = Rect.new(0, 0, icon.width, icon.height)
|
||||
|
||||
self.bitmap.stretch_blt(
|
||||
Rect.new(icon_x, icon_y, icon_size, icon_size),
|
||||
icon,
|
||||
src_rect
|
||||
)
|
||||
|
||||
icon.dispose
|
||||
icon_x += icon_size + icon_gap
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Helper method to wrap text into multiple lines
|
||||
def wrap_text(text, bitmap, max_width)
|
||||
words = text.split(" ")
|
||||
lines = []
|
||||
line = ""
|
||||
words.each do |word|
|
||||
test_line = line.empty? ? word : "#{line} #{word}"
|
||||
if bitmap.text_size(test_line).width > max_width
|
||||
lines << line
|
||||
line = word
|
||||
else
|
||||
line = test_line
|
||||
end
|
||||
end
|
||||
lines << line unless line.empty?
|
||||
return lines
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user