mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2026-07-22 15:47: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
|
||||
@@ -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
|
||||
@@ -0,0 +1,107 @@
|
||||
class FusionQuizAppScene < PokeNavAppScene
|
||||
attr_accessor :playing
|
||||
attr_accessor :difficulty
|
||||
|
||||
def initialize
|
||||
super
|
||||
@playing = false
|
||||
end
|
||||
|
||||
def y_gap
|
||||
return 64;
|
||||
end
|
||||
|
||||
def start_x
|
||||
return Graphics.width-200;
|
||||
end
|
||||
|
||||
def pbStartScene(buttons = nil)
|
||||
$game_system.bgm_memorize
|
||||
super(buttons)
|
||||
pbBGMPlay("game_corner")
|
||||
displayTextElements
|
||||
end
|
||||
|
||||
def bg_path
|
||||
if @playing
|
||||
if @difficulty == :ADVANCED
|
||||
return "Graphics/Pictures/Pokegear/FusionQuiz/bg_play_advanced"
|
||||
else
|
||||
return "Graphics/Pictures/Pokegear/FusionQuiz/bg_play"
|
||||
end
|
||||
else
|
||||
return "Graphics/Pictures/Pokegear/FusionQuiz/bg_menu"
|
||||
end
|
||||
end
|
||||
|
||||
def cursor_path
|
||||
return "Graphics/Pictures/Pokegear/FusionQuiz/cursor"
|
||||
end
|
||||
|
||||
def header_name
|
||||
return _INTL("Who's That Fusion!")
|
||||
end
|
||||
|
||||
def header_path
|
||||
return "Graphics/Pictures/Pokegear/FusionQuiz/bg_header_quiz"
|
||||
end
|
||||
|
||||
def click(button_id)
|
||||
case button_id
|
||||
when "play"
|
||||
@selected = :play
|
||||
@exiting = true
|
||||
when "score"
|
||||
@selected = :score
|
||||
@exiting = true
|
||||
else
|
||||
pbPlayCloseMenuSE
|
||||
@selected = :exit
|
||||
@exiting = true
|
||||
end
|
||||
end
|
||||
|
||||
def updateInput
|
||||
if Input.trigger?(Input::BACK)
|
||||
pbPlayCloseMenuSE
|
||||
@selected = :exit
|
||||
@exiting = true
|
||||
return
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def selected_action
|
||||
return @selected
|
||||
end
|
||||
|
||||
def updateBackground
|
||||
echoln bg_path
|
||||
@sprites["bg"].setBitmap(bg_path) unless @sprites["bg"].disposed?
|
||||
end
|
||||
|
||||
def pbEndSceneKeepBg
|
||||
@exiting = true
|
||||
sprites_without_bg = @sprites.reject { |k, _| ["bg", "background", "header"].include?(k) }
|
||||
pbFadeOutAndHide(sprites_without_bg) { pbUpdate }
|
||||
pbDisposeSpriteHash(sprites_without_bg)
|
||||
@buttons.each(&:dispose)
|
||||
Kernel.pbClearText
|
||||
showHeaderName
|
||||
end
|
||||
|
||||
def disposeBg
|
||||
@sprites["bg"]&.dispose
|
||||
@sprites["background"]&.dispose
|
||||
@sprites["header"]&.dispose
|
||||
@viewport&.dispose
|
||||
end
|
||||
|
||||
def pbEndScene
|
||||
echoln "ENDING"
|
||||
$game_system.bgm_stop
|
||||
$game_system.bgm_restore
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,183 @@
|
||||
class FusionQuizAppScreen
|
||||
def initialize(scene)
|
||||
@scene = scene
|
||||
end
|
||||
|
||||
POINTS_TO_UNLOCK_MODES = {
|
||||
:regular_3_rounds => 0,
|
||||
:regular_5_rounds => 2000,
|
||||
:regular_10_rounds => 4000,
|
||||
:advanced_3_rounds => 5000,
|
||||
:advanced_5_rounds => 12000,
|
||||
:advanced_10_rounds => 16000,
|
||||
}
|
||||
|
||||
def pbStartScreen(main_menu_scene, screen)
|
||||
@main_menu_scene = main_menu_scene
|
||||
@screen = screen
|
||||
#Possible modes:
|
||||
# :regular_3_rounds
|
||||
# :regular_5_rounds
|
||||
# :regular_10_rounds
|
||||
# :advanced_3_rounds
|
||||
# :advanced_5_rounds
|
||||
# :advanced_10_rounds
|
||||
$Trainer&.pokenav&.fusion_quiz_unlocked_modes = [:regular_3_rounds] unless $Trainer&.pokenav&.fusion_quiz_unlocked_modes
|
||||
loop do
|
||||
btn_play = FusionQuizMenuButton.new("play", nil, _INTL("Play"))
|
||||
btn_score = FusionQuizMenuButton.new("score", nil, _INTL("Score"))
|
||||
btn_close = FusionQuizMenuButton.new("exit", nil, _INTL("Exit"))
|
||||
|
||||
@scene.pbStartScene([btn_play, btn_score, btn_close])
|
||||
@scene.pbScene
|
||||
|
||||
case @scene.selected_action
|
||||
when :play
|
||||
@scene.pbEndSceneKeepBg
|
||||
launch_quiz
|
||||
@scene.playing = false
|
||||
@scene.disposeBg
|
||||
when :score
|
||||
@scene.pbEndSceneKeepBg
|
||||
show_high_score
|
||||
@scene.disposeBg
|
||||
when :exit, nil
|
||||
@scene.pbEndScene
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def launch_quiz
|
||||
difficulty = prompt_difficulty
|
||||
high_score = pbGet(VAR_STAT_FUSION_QUIZ_HIGHEST_SCORE)
|
||||
@scene.difficulty = difficulty
|
||||
if difficulty
|
||||
nb_rounds = prompt_nb_rounds(difficulty)
|
||||
if nb_rounds > 0
|
||||
@scene.playing = true
|
||||
@scene.updateBackground
|
||||
quiz = FusionQuiz.new(difficulty)
|
||||
quiz.silhouette_color = Color.new(0, 0, 0, 200)
|
||||
quiz.windowed = false
|
||||
if difficulty == :ADVANCED
|
||||
quiz.picture_offset_x = -30
|
||||
quiz.picture_offset_y = 32
|
||||
else
|
||||
quiz.picture_offset_x = -40
|
||||
quiz.picture_offset_y = 32
|
||||
end
|
||||
quiz.start_quiz(nb_rounds)
|
||||
unless quiz.player_abandonned
|
||||
score = quiz.get_score
|
||||
if score > high_score
|
||||
pbMEPlay("Level Up")
|
||||
pbMessage(_INTL("You beat your previous high score!", score))
|
||||
end
|
||||
unlock_new_modes(score)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_mode_name(mode_id)
|
||||
case mode_id
|
||||
when :regular_3_rounds
|
||||
return _INTL("Regular (3 Rounds)")
|
||||
when :regular_5_rounds
|
||||
return _INTL("Regular (5 Rounds)")
|
||||
when :regular_10_rounds
|
||||
return _INTL("Regular (10 Rounds)")
|
||||
when :advanced_3_rounds
|
||||
return _INTL("Advanced (3 Rounds)")
|
||||
when :advanced_5_rounds
|
||||
return _INTL("Advanced (5 Rounds)")
|
||||
when :advanced_10_rounds
|
||||
return _INTL("Advanced (10 Rounds)")
|
||||
end
|
||||
end
|
||||
|
||||
def unlock_new_modes(score)
|
||||
POINTS_TO_UNLOCK_MODES.keys.each do |mode_id|
|
||||
points_to_unlock = POINTS_TO_UNLOCK_MODES[mode_id]
|
||||
next if $Trainer&.pokenav&.fusion_quiz_unlocked_modes&.include?(mode_id)
|
||||
if score >= points_to_unlock
|
||||
$Trainer&.pokenav&.fusion_quiz_unlocked_modes = [:regular_3_rounds] unless $Trainer&.pokenav&.fusion_quiz_unlocked_modes
|
||||
$Trainer&.pokenav&.fusion_quiz_unlocked_modes << mode_id
|
||||
pbSEPlay("itemlevel", 80)
|
||||
pbMessage(_INTL("Unlocked a new difficulty: \\C[3]{1}",get_mode_name(mode_id)))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def prompt_difficulty
|
||||
advanced_difficulties = [:advanced_3_rounds, :advanced_5_rounds, :advanced_10_rounds]
|
||||
echoln $Trainer&.pokenav&.fusion_quiz_unlocked_modes
|
||||
cmd_regular = _INTL("Regular")
|
||||
cmd_advanced = _INTL("Advanced")
|
||||
cmd_cancel = _INTL("Cancel")
|
||||
options = []
|
||||
options << cmd_regular
|
||||
options << cmd_advanced if ($Trainer&.pokenav&.fusion_quiz_unlocked_modes & advanced_difficulties)&.any?
|
||||
options << cmd_cancel
|
||||
choice = pbMessage(
|
||||
_INTL("Choose a difficulty:"),
|
||||
options,3
|
||||
)
|
||||
|
||||
case options[choice]
|
||||
when cmd_regular
|
||||
return :REGULAR
|
||||
when cmd_advanced
|
||||
return :ADVANCED
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def prompt_nb_rounds(difficulty)
|
||||
options = []
|
||||
|
||||
cmd_3_rounds = _INTL("3 Rounds")
|
||||
cmd_5_rounds = _INTL("5 Rounds")
|
||||
cmd_10_rounds = _INTL("10 Rounds")
|
||||
cmd_cancel = _INTL("Cancel")
|
||||
|
||||
case difficulty
|
||||
when :ADVANCED
|
||||
options << cmd_3_rounds if $Trainer&.pokenav&.fusion_quiz_unlocked_modes&.include?(:advanced_3_rounds)
|
||||
options << cmd_5_rounds if $Trainer&.pokenav&.fusion_quiz_unlocked_modes&.include?(:advanced_5_rounds)
|
||||
options << cmd_10_rounds if $Trainer&.pokenav&.fusion_quiz_unlocked_modes&.include?(:advanced_10_rounds)
|
||||
when :REGULAR
|
||||
options << cmd_3_rounds if $Trainer&.pokenav&.fusion_quiz_unlocked_modes&.include?(:regular_3_rounds)
|
||||
options << cmd_5_rounds if $Trainer&.pokenav&.fusion_quiz_unlocked_modes&.include?(:regular_5_rounds)
|
||||
options << cmd_10_rounds if $Trainer&.pokenav&.fusion_quiz_unlocked_modes&.include?(:regular_10_rounds)
|
||||
end
|
||||
options << cmd_cancel
|
||||
choice = pbMessage(
|
||||
_INTL("Choose the number of rounds:"),
|
||||
options,4
|
||||
)
|
||||
|
||||
case options[choice]
|
||||
when cmd_3_rounds
|
||||
nb_rounds = 3
|
||||
when cmd_5_rounds
|
||||
nb_rounds = 5
|
||||
when cmd_10_rounds
|
||||
nb_rounds = 10
|
||||
else
|
||||
nb_rounds = 0
|
||||
end
|
||||
return nb_rounds
|
||||
end
|
||||
|
||||
def show_high_score
|
||||
high = pbGet(VAR_STAT_FUSION_QUIZ_HIGHEST_SCORE)
|
||||
total = pbGet(VAR_STAT_FUSION_QUIZ_TOTAL_PTS)
|
||||
times = pbGet(VAR_STAT_FUSION_QUIZ_NB_TIMES)
|
||||
pbMessage(_INTL("High Score: {1} pts", high))
|
||||
pbMessage(_INTL("Total Points: {1}\\nGames Played: {2}", total, times))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
class FusionQuizMenuButton < 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
|
||||
|
||||
def initialize(id, icon = nil, text = nil, viewport = nil)
|
||||
super
|
||||
@text_color = pbColor(:LIGHT_TEXT_MAIN_COLOR)
|
||||
@shadow_color = pbColor(:LIGHT_TEXT_SHADOW_COLOR)
|
||||
end
|
||||
def get_width
|
||||
return 180
|
||||
end
|
||||
|
||||
def get_height
|
||||
return 52
|
||||
end
|
||||
|
||||
def text_padding
|
||||
return 40
|
||||
end
|
||||
|
||||
def text_alignment
|
||||
return 1 #Centered
|
||||
end
|
||||
def background_image
|
||||
return "Graphics/Pictures/Pokegear/FusionQuiz/icon_button"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,176 @@
|
||||
class Trainer
|
||||
attr_accessor :pokenav
|
||||
|
||||
def install_app(app_id)
|
||||
@pokenav = Pokenav.new unless $Trainer.pokenav
|
||||
@pokenav.install_app(app_id)
|
||||
end
|
||||
end
|
||||
|
||||
class Pokenav
|
||||
attr_accessor :installed_apps
|
||||
attr_accessor :last_opened_challenges #date
|
||||
attr_accessor :darkMode
|
||||
attr_accessor :viewed_trainers
|
||||
attr_accessor :last_opened_quest_mode
|
||||
|
||||
attr_accessor :fusion_quiz_unlocked_modes
|
||||
|
||||
|
||||
AVAILABLE_APPS = {
|
||||
# Starting apps
|
||||
:QUESTS => "Quests",
|
||||
:MAP => "Town Map",
|
||||
:DAYNIGHT => "Toggle Dark/Light",
|
||||
:REARRANGE => "Rearrange",
|
||||
|
||||
# Unlockable apps
|
||||
:CONTACTS => "Trainers", # obtained after rematch quest TODO
|
||||
:JUKEBOX => "Jukebox", # obtained at devon corp
|
||||
:WEATHER => "Weather Map", # obtained at TV Mauville
|
||||
:POKERADAR => "PokéRadar", # given by the rival somewhere?
|
||||
:POKECHALLENGE => "PokéChallenge", #Obtained at Slateport fan club
|
||||
:BOXLINK => "Box Link",
|
||||
:FUSIONQUIZ => "Who's That Fusion?",
|
||||
:BERRYDEX => "BerryDex",
|
||||
|
||||
#To implement
|
||||
# Statistics (shows you a bunch of statstics. nb of times you fused, nb of steps taken, etc.)
|
||||
#
|
||||
#Other ideas
|
||||
# Guess That Fusion -> same game as in pif 1, but in app form
|
||||
# Slot machine
|
||||
# Weather
|
||||
# Secret Bases map (a map that lists all the secret bases)
|
||||
# Something to check your Pokemon's EVs / IVs
|
||||
# Daycare app that tells you the status of your eggs
|
||||
}
|
||||
|
||||
def self.app_name(app_id)
|
||||
return _INTL(AVAILABLE_APPS[app_id] || app_id.to_s)
|
||||
end
|
||||
|
||||
def initialize
|
||||
@installed_apps = [:MAP, :QUESTS, :DAYNIGHT, :REARRANGE]
|
||||
@last_opened_challenges = nil
|
||||
@viewed_trainers = []
|
||||
@exiting = false
|
||||
@last_opened_quest_mode = :MAP
|
||||
end
|
||||
|
||||
def install_app(app_id)
|
||||
return unless Pokenav::AVAILABLE_APPS.keys.include?(app_id)
|
||||
initialize unless @installed_apps
|
||||
@installed_apps << app_id unless @installed_apps.include?(app_id)
|
||||
app_name = Pokenav.app_name(app_id)
|
||||
pbMEPlay("match_call")
|
||||
pbMessage(_INTL("The \\C[3]{1} App\\C[0] was installed in the PokéNav!", app_name))
|
||||
end
|
||||
|
||||
def has_app(app_id)
|
||||
return @installed_apps.include?(app_id)
|
||||
end
|
||||
end
|
||||
|
||||
class PokemonPokegearScreen
|
||||
|
||||
def update_commands
|
||||
commands = []
|
||||
$Trainer.pokenav&.installed_apps.each do |app|
|
||||
commands << [app.to_s, Pokenav.app_name(app)]
|
||||
end
|
||||
return commands
|
||||
end
|
||||
def pbStartScreen
|
||||
$Trainer.pokenav = Pokenav.new unless $Trainer.pokenav
|
||||
commands = update_commands
|
||||
@scene.pbStartScene(commands)
|
||||
loop do
|
||||
cmd = @scene.pbScene
|
||||
commands = update_commands #in case they're reordered
|
||||
chosen = commands[cmd][0].to_sym
|
||||
if cmd < 0
|
||||
break
|
||||
elsif chosen == :MAP
|
||||
pbWeatherMap
|
||||
# pbShowMap(-1, false)
|
||||
elsif chosen == :JUKEBOX
|
||||
pbFadeOutIn {
|
||||
scene = PokemonJukebox_Scene.new
|
||||
screen = PokemonJukeboxScreen.new(scene)
|
||||
screen.pbStartScreen
|
||||
}
|
||||
elsif chosen == :QUESTS
|
||||
pbQuestlog()
|
||||
elsif chosen == :CONTACTS
|
||||
openContactsApp
|
||||
next
|
||||
elsif chosen == :WEATHER
|
||||
pbWeatherMap
|
||||
elsif chosen == :POKECHALLENGE
|
||||
openChallengeApp
|
||||
elsif chosen == :POKERADAR
|
||||
openPokeRadarApp
|
||||
elsif chosen == :REARRANGE
|
||||
@scene.rearrange_order
|
||||
elsif chosen == :DAYNIGHT
|
||||
toggleDarkMode
|
||||
elsif chosen == :BOXLINK
|
||||
openBoxLinkApp
|
||||
elsif chosen == :FUSIONQUIZ
|
||||
openGuessThatFusionApp
|
||||
elsif chosen == :BERRYDEX
|
||||
pbBerryDex
|
||||
# elsif cmdPhone>=0 && cmd==cmdPhone
|
||||
# pbFadeOutIn {
|
||||
# PokemonPhoneScene.new.start
|
||||
# }
|
||||
end
|
||||
end
|
||||
@scene.pbEndScene
|
||||
end
|
||||
|
||||
def openBoxLinkApp
|
||||
blacklisted_maps = []
|
||||
if blacklisted_maps.include?($game_map.map_id)
|
||||
Kernel.pbMessage(_INTL("There doesn't seem to be any network coverage here..."))
|
||||
else
|
||||
pbFadeOutIn {
|
||||
scene = PokemonStorageScene.new
|
||||
screen = PokemonStorageScreen.new(scene, $PokemonStorage)
|
||||
screen.pbStartScreen(0) # Boot PC in organize mode
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def openContactsApp
|
||||
pbFadeOutIn {
|
||||
scene = ContactsAppScene.new
|
||||
screen = ContactsAppScreen.new(scene)
|
||||
screen.pbStartScreen(@scene,screen)
|
||||
}
|
||||
end
|
||||
|
||||
def openPokeRadarApp
|
||||
pbFadeOutIn {
|
||||
scene = PokeRadarAppScene.new
|
||||
screen = PokeRadarAppScreen.new(scene)
|
||||
screen.pbStartScreen(@scene)
|
||||
}
|
||||
end
|
||||
|
||||
def openGuessThatFusionApp
|
||||
pbFadeOutIn {
|
||||
scene = FusionQuizAppScene.new
|
||||
screen = FusionQuizAppScreen.new(scene)
|
||||
screen.pbStartScreen(@scene,screen)
|
||||
}
|
||||
end
|
||||
|
||||
def toggleDarkMode
|
||||
pbSEPlay("GUI storage show party panel")
|
||||
$Trainer.pokenav.darkMode = false if $Trainer.pokenav.darkMode.nil?
|
||||
$Trainer.pokenav.darkMode = !$Trainer.pokenav.darkMode
|
||||
@scene.reloadBackground
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,520 @@
|
||||
# todo:
|
||||
# When all pokemon in the route are seen, can "scan" for species
|
||||
# - spawns a pokemon of that species nearby with notice behavior to flee
|
||||
class PokeRadarAppScene < PokeNavAppScene
|
||||
INFO_TEXT_Y = 270
|
||||
|
||||
def header_name
|
||||
return _INTL("PokéRadar")
|
||||
end
|
||||
|
||||
def cursor_path
|
||||
return "Graphics/Pictures/Pokeradar/icon_button"
|
||||
end
|
||||
|
||||
def header_path
|
||||
return "Graphics/Pictures/Pokeradar/bg_header"
|
||||
end
|
||||
|
||||
def display_mode
|
||||
return :GRID
|
||||
end
|
||||
|
||||
def x_gap
|
||||
return 75;
|
||||
end
|
||||
|
||||
def y_gap
|
||||
return 50;
|
||||
end
|
||||
|
||||
def columns
|
||||
return 6
|
||||
end
|
||||
|
||||
def visible_rows
|
||||
return 3
|
||||
end
|
||||
|
||||
def start_x
|
||||
return 40;
|
||||
end
|
||||
|
||||
def start_y
|
||||
return 80;
|
||||
end
|
||||
|
||||
def pbStartScene(main_menu_scene)
|
||||
@unseenPokemon = [] unless @unseenPokemon
|
||||
@seenPokemon = [] unless @seenPokemon
|
||||
@pokenav_main_menu_scene = main_menu_scene
|
||||
if $PokemonTemp.pokeradar
|
||||
buttons = showCurrentScanningTarget
|
||||
super(buttons)
|
||||
else
|
||||
buttons = showWildPokemonList
|
||||
super(buttons)
|
||||
showSelectedPokemonInfo
|
||||
end
|
||||
showHeaderInfo
|
||||
end
|
||||
|
||||
def showSelectedPokemonInfo
|
||||
hover(@buttons[@index]&.id)
|
||||
if @unseenPokemon.empty? && @seenPokemon.empty?
|
||||
showEmpty
|
||||
end
|
||||
end
|
||||
|
||||
def showHeaderInfo
|
||||
showBattery
|
||||
showHeaderName
|
||||
showAreaName
|
||||
showWeatherIcon
|
||||
end
|
||||
|
||||
def showWildPokemonList
|
||||
@encounter_type = $PokemonEncounters.encounter_type
|
||||
@weatherEncounters = $PokemonEncounters.list_weather_encounters_species(@encounter_type)
|
||||
|
||||
@unseenPokemon = listPokemonInCurrentRoute(@encounter_type, false, true, true)
|
||||
@seenPokemon = listPokemonInCurrentRoute(@encounter_type, true, false, true)
|
||||
buttons = []
|
||||
@seenPokemon.each do |pokemon_species|
|
||||
icon_path = pbCheckPokemonIconFiles(pokemon_species)
|
||||
bmp = load_bitmap(icon_path, false)
|
||||
button = PokenavButton.new(pokemon_species, bmp)
|
||||
button.crop_width = button.icon_bitmap.bitmap.width / 2
|
||||
button.refresh
|
||||
buttons << button if button
|
||||
end
|
||||
@unseenPokemon.each do |pokemon_species|
|
||||
icon_path = pbCheckPokemonIconFiles(pokemon_species)
|
||||
bmp = load_bitmap(icon_path, true)
|
||||
button = PokenavButton.new(pokemon_species, bmp)
|
||||
button.crop_width = button.icon_bitmap.bitmap.width / 2
|
||||
button.refresh
|
||||
buttons << button
|
||||
end
|
||||
return buttons
|
||||
end
|
||||
|
||||
def createCursor
|
||||
return if $PokemonTemp.pokeradar
|
||||
super
|
||||
end
|
||||
|
||||
def showEmpty
|
||||
Kernel.pbDisplayText(_INTL("No wild Pokémon found nearby."), Graphics.width / 2, Graphics.height / 2, nil, @text_color_base, @text_color_shadow)
|
||||
Kernel.pbDisplayText(_INTL("Stand in tall grass to start scanning."), Graphics.width / 2,( Graphics.height / 2)+32, nil, @text_color_base, @text_color_shadow)
|
||||
|
||||
end
|
||||
|
||||
def showCurrentScanningTarget
|
||||
scanningPokemon = $PokemonTemp.pokeradar[0]
|
||||
icon_path = pbCheckPokemonIconFiles(scanningPokemon)
|
||||
bmp = load_bitmap(icon_path, false)
|
||||
button = PokenavButton.new(scanningPokemon, bmp)
|
||||
button.refresh
|
||||
species_name = GameData::Species.get(scanningPokemon).name
|
||||
Kernel.pbDisplayText(_INTL("Currently scanning for {1}.", species_name), Graphics.width / 2, 200, 500000, @text_color_base, @text_color_shadow)
|
||||
Kernel.pbDisplayText(_INTL("Current chain: {1}.", $PokemonTemp.pokeradar[2]), Graphics.width / 2, 230, 500000, @text_color_base, @text_color_shadow)
|
||||
return [button]
|
||||
end
|
||||
|
||||
def load_bitmap(path, dark = false)
|
||||
return nil unless path && path != ""
|
||||
bmp = Bitmap.new(path)
|
||||
|
||||
if dark
|
||||
darken_bitmap(bmp, 220) # strength 0–255
|
||||
end
|
||||
|
||||
return AnimatedBitmap.from_bitmap(bmp)
|
||||
end
|
||||
|
||||
def darken_bitmap(bmp, amount)
|
||||
for x in 0...bmp.width
|
||||
for y in 0...bmp.height
|
||||
pixel = bmp.get_pixel(x, y)
|
||||
next if pixel.alpha == 0
|
||||
|
||||
factor = (255 - amount) / 255.0
|
||||
r = (pixel.red * factor).to_i
|
||||
g = (pixel.green * factor).to_i
|
||||
b = (pixel.blue * factor).to_i
|
||||
|
||||
bmp.set_pixel(x, y, Color.new(r, g, b, pixel.alpha))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#[rareness, species, minLevel, maxLevel]
|
||||
def get_encounter(species,include_weather=true)
|
||||
for encounter in $PokemonEncounters.listPossibleEncounters(@encounter_type, include_weather)
|
||||
if encounter[1] == species
|
||||
return encounter
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def get_weather_encounter(species)
|
||||
for encounter in $PokemonEncounters.list_weather_encounters(@encounter_type)
|
||||
if encounter[1] == species
|
||||
return encounter
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
def get_energy_for_scan(species)
|
||||
encounter = get_encounter(species)
|
||||
if @weatherEncounters.include?(species)
|
||||
base_rareness = encounter[0]
|
||||
rareness =calculate_weather_encounter_rareness(encounter,base_rareness)
|
||||
else
|
||||
rareness = encounter[0]
|
||||
end
|
||||
energy = (110 - rareness) * 5
|
||||
(energy / 50.0).round * 50
|
||||
end
|
||||
|
||||
def get_rarity_flavor_text(species)
|
||||
encounter = get_encounter(species,false)
|
||||
weather_encounter = get_weather_encounter(species)
|
||||
|
||||
return unless encounter || weather_encounter
|
||||
if (encounter && encounter[1] == species) || (weather_encounter && weather_encounter[1] == species)
|
||||
base_rareness =0
|
||||
base_rareness = encounter[0] if encounter
|
||||
base_rareness = weather_encounter[0] if weather_encounter
|
||||
if @weatherEncounters.include?(species)
|
||||
rareness = calculate_weather_encounter_rareness(weather_encounter,base_rareness)
|
||||
else
|
||||
rareness = base_rareness
|
||||
end
|
||||
if rareness < 5
|
||||
text = _INTL("Very rare")
|
||||
elsif rareness < 10
|
||||
text = _INTL("Rare")
|
||||
elsif rareness < 25
|
||||
text = _INTL("Uncommon")
|
||||
elsif rareness < 40
|
||||
text = _INTL("Common")
|
||||
else
|
||||
text = _INTL("Very Common")
|
||||
end
|
||||
return text
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_weather_encounter_rareness(encounter,base_rareness)
|
||||
map_weather = $game_weather.current_weather[$game_map.map_id]
|
||||
if map_weather
|
||||
weather_intensity = map_weather[1]
|
||||
weather_intensity = 0 if !weather_intensity
|
||||
chance_of_weather_encounter = $PokemonEncounters.weather_encounter_chance(weather_intensity)
|
||||
weather_rareness = encounter[0]
|
||||
return weather_rareness * (chance_of_weather_encounter.to_f / 100) * (base_rareness/100)
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
def click(button_id)
|
||||
species = button_id
|
||||
if $PokemonTemp.pokeradar
|
||||
click_stop_scan(species)
|
||||
else
|
||||
if @seenPokemon.include?(species)
|
||||
click_seen(species)
|
||||
else
|
||||
click_unseen
|
||||
end
|
||||
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def hover(button_id)
|
||||
return if $PokemonTemp.pokeradar
|
||||
if @seenPokemon.include?(button_id)
|
||||
hover_seen(button_id)
|
||||
else
|
||||
hover_unseen
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def click_stop_scan(species)
|
||||
species_name = GameData::Species.get(species).name
|
||||
options = []
|
||||
cmd_stop_scan = _INTL("Stop Scanning")
|
||||
cmd_cancel = _INTL("Cancel")
|
||||
options << cmd_stop_scan
|
||||
options << cmd_cancel
|
||||
Kernel.pbClearText()
|
||||
showHeaderInfo
|
||||
chosen = pbMessage(_INTL("You are currently scanning for {1}.", species_name), options, options.length)
|
||||
case options[chosen]
|
||||
when cmd_stop_scan
|
||||
$PokemonTemp.pokeradar = nil
|
||||
pbEndScene
|
||||
pbStartScene(@pokenav_main_menu_scene)
|
||||
else
|
||||
showCurrentScanningTarget
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def click_seen(species)
|
||||
options = []
|
||||
cmd_scan = _INTL("Scan")
|
||||
cmd_cancel = _INTL("Cancel")
|
||||
options << cmd_scan
|
||||
options << cmd_cancel
|
||||
Kernel.pbClearText()
|
||||
showHeaderInfo
|
||||
chosen = pbMessage(_INTL("What would you like to do?"), options, options.length)
|
||||
case options[chosen]
|
||||
when cmd_scan
|
||||
energy_needed = get_energy_for_scan(species)
|
||||
if Settings::POKERADAR_BATTERY_STEPS - $PokemonGlobal.pokeradarBattery >= energy_needed
|
||||
$PokemonGlobal.pokeradarBattery += energy_needed
|
||||
displayTextElements
|
||||
@exiting = true
|
||||
|
||||
pbEndScene
|
||||
@pokenav_main_menu_scene.exiting = true
|
||||
@pokenav_main_menu_scene.pbEndScene
|
||||
|
||||
encounter = get_encounter(species)
|
||||
min_level = encounter[2]
|
||||
max_level = encounter[3]
|
||||
level = rand(min_level..max_level)
|
||||
pbWait(16)
|
||||
pbMessage(_INTL("Scanning for {1}...\\wtnp[5]", GameData::Species.get(species).name))
|
||||
possible_tiles = getTerrainTilesNearPlayer(getTerrainType, 3)
|
||||
position = possible_tiles.sample
|
||||
if position
|
||||
set_pokeradar_data(species, level, position)
|
||||
spawn_pokeradar_pokemon(species, level)
|
||||
else
|
||||
pbPokeRadarCancel
|
||||
pbMessage(_INTL("The Pokéradar scan failed... Try again somewhere else"))
|
||||
end
|
||||
else
|
||||
pbPokeRadarCancel
|
||||
Kernel.pbClearText()
|
||||
showHeaderInfo
|
||||
pbMessage(_INTL("The battery is not charged enough for this scan!"))
|
||||
showSelectedPokemonInfo
|
||||
end
|
||||
else
|
||||
showSelectedPokemonInfo
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def click_unseen
|
||||
return unless @unseenPokemon.any?
|
||||
Kernel.pbClearText()
|
||||
showHeaderInfo
|
||||
pbMessage(_INTL("You need to encounter the Pokémon before you can scan for it."))
|
||||
hover_unseen
|
||||
end
|
||||
|
||||
def hover_seen(species)
|
||||
displayTextElements
|
||||
pokemon_name = GameData::Species.get(species).name
|
||||
Kernel.pbDisplayText(pokemon_name, Graphics.width / 2, INFO_TEXT_Y, 99999, @text_color_base, @text_color_shadow)
|
||||
Kernel.pbDisplayText(get_rarity_flavor_text(species), Graphics.width / 2, INFO_TEXT_Y + 30, 99999, @text_color_base, @text_color_shadow)
|
||||
Kernel.pbDisplayText(_INTL("Battery for scan: {1}", get_energy_for_scan(species)), Graphics.width / 2, INFO_TEXT_Y + 60, 99999, @text_color_base, @text_color_shadow)
|
||||
end
|
||||
|
||||
def hover_unseen()
|
||||
return unless @unseenPokemon.any?
|
||||
displayTextElements
|
||||
pokemon_name = _INTL("Unknown Pokémon")
|
||||
Kernel.pbDisplayText(pokemon_name, Graphics.width / 2, INFO_TEXT_Y, 999999, @text_color_base, @text_color_shadow)
|
||||
end
|
||||
|
||||
def displayTextElements
|
||||
super
|
||||
showBattery
|
||||
showAreaName
|
||||
|
||||
end
|
||||
|
||||
def showBattery
|
||||
$PokemonGlobal.pokeradarBattery = Settings::POKERADAR_BATTERY_STEPS unless $PokemonGlobal.pokeradarBattery
|
||||
battery_power = Settings::POKERADAR_BATTERY_STEPS - $PokemonGlobal.pokeradarBattery
|
||||
Kernel.pbDisplayText(_INTL("{1}/1000", battery_power), 450, HEADER_HEIGHT,nil, @text_color_base, @text_color_shadow)
|
||||
end
|
||||
|
||||
def showAreaName
|
||||
map_name = getMapName($game_map.map_id)
|
||||
text = _INTL("{1}", map_name)
|
||||
encounter_type = get_encounter_type_name
|
||||
if encounter_type && !encounter_type.empty?
|
||||
text += _INTL(" ({1})", encounter_type)
|
||||
end
|
||||
Kernel.pbDisplayText(text, Graphics.width / 2, 40,nil, @text_color_base, @text_color_shadow)
|
||||
end
|
||||
|
||||
def showWeatherIcon
|
||||
icon_path = get_current_weather_icon
|
||||
return unless icon_path
|
||||
@sprites["weather"] = IconSprite.new(400, 48, @viewport)
|
||||
@sprites["weather"].setBitmap(icon_path)
|
||||
@sprites["weather"].z = 100000
|
||||
end
|
||||
|
||||
def get_encounter_type_name
|
||||
encounter_type = $PokemonEncounters.encounter_type
|
||||
case encounter_type
|
||||
when :Land
|
||||
return _INTL("Grass")
|
||||
when :Land1
|
||||
return _INTL("Clovers")
|
||||
when :Land2
|
||||
return _INTL("Dry Grass")
|
||||
when :Land3
|
||||
return _INTL("Flowers")
|
||||
when :LandMorning, :LandDay, :LandNight
|
||||
return _INTL("Grass")
|
||||
when :WaterMorning, :WaterDay, :WaterNight
|
||||
return _INTL("Water")
|
||||
when :TallGrass
|
||||
return _INTL("Tall Grass")
|
||||
else
|
||||
return encounter_type.to_s
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def getTerrainType
|
||||
encounter_type = $PokemonEncounters.encounter_type
|
||||
case encounter_type
|
||||
when :Land, :Land1, :Land2, :Land3, :LandMorning, :LandDay, :LandNight, :TallGrass
|
||||
return :Grass
|
||||
else
|
||||
return encounter_type
|
||||
end
|
||||
end
|
||||
|
||||
def continue_pokeradar_app_chain()
|
||||
pbWait(12)
|
||||
species = $PokemonTemp.pokeradar[0]
|
||||
level = $PokemonTemp.pokeradar[1]
|
||||
possible_tiles = getTerrainTilesNearPlayer(getTerrainType, 3)
|
||||
position = possible_tiles.sample
|
||||
if position
|
||||
set_pokeradar_data(species, level, position)
|
||||
spawn_pokeradar_pokemon(species, level)
|
||||
else
|
||||
pbPokeRadarCancel
|
||||
pbMessage(_INTL("The Pokéradar scan failed... Try again somewhere else"))
|
||||
end
|
||||
end
|
||||
|
||||
def update_pokeradar_overworld_ui
|
||||
if $PokemonTemp.pokeradar
|
||||
current_chain = $PokemonTemp.pokeradar[2]
|
||||
return unless current_chain >= 1
|
||||
species = $PokemonTemp.pokeradar[0]
|
||||
$PokemonTemp.pokeradar_ui.dispose if $PokemonTemp.pokeradar_ui
|
||||
$PokemonTemp.pokeradar_ui = PokeRadar_UI.new([species], [], [])
|
||||
if current_chain >= 40
|
||||
$PokemonTemp.pokeradar_ui.set_text(_INTL("PokéRadar Chain: {1}", current_chain), 72, 12, pbColor(:GREEN), pbColor(:DARKGREEN))
|
||||
else
|
||||
$PokemonTemp.pokeradar_ui.set_text(_INTL("PokéRadar Chain: {1}", current_chain), 72, 12)
|
||||
end
|
||||
else
|
||||
$PokemonTemp.pokeradar_ui.dispose if $PokemonTemp.pokeradar_ui
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def spawn_pokeradar_pokemon(species, level)
|
||||
max_attempts = 50
|
||||
return unless species && level
|
||||
|
||||
pbWait(20)
|
||||
playAnimation(Settings::POKERADAR_LIGHT_ANIMATION_RED_ID, $game_player.x, $game_player.y)
|
||||
pbWait(10)
|
||||
|
||||
spawned_events = nil
|
||||
radius = 4
|
||||
max_radius = 8
|
||||
max_attempts.times do |current_attempt|
|
||||
radius += 1 if current_attempt > 0 && current_attempt % 10 == 0
|
||||
radius += (current_attempt / 10).floor
|
||||
radius = [radius, max_radius].min
|
||||
spawned_events = spawn_ow_pokemon(species, level, 1, radius)
|
||||
break if spawned_events&.length.to_i > 0
|
||||
end
|
||||
|
||||
if spawned_events&.length.to_i > 0
|
||||
event = spawned_events[0]
|
||||
grass = $PokemonTemp&.pokeradar[3]
|
||||
|
||||
if grass && grass[3] == 2
|
||||
pbSEPlay("shiny", 60)
|
||||
playAnimation(Settings::SPARKLE_SHORT_ANIMATION_ID, event.x, event.y)
|
||||
event.make_shiny
|
||||
end
|
||||
|
||||
#todo: MAYBE increase the move frequency past a certain chain number (50?) to make it more difficult
|
||||
event.behavior_roaming = :look_around_player
|
||||
event.behavior_noticed = event.pokemon.shiny? ? :curious : :flee
|
||||
event.update_movement_type
|
||||
event.turn_away_from_player
|
||||
playAnimation(Settings::POKERADAR_LIGHT_ANIMATION_RED_ID, event.x, event.y)
|
||||
else
|
||||
echoln "failed to spawn after #{max_attempts} attempts"
|
||||
pbPokeRadarCancel
|
||||
pbMessage(_INTL("The Pokéradar scan failed... Try again somewhere else"))
|
||||
end
|
||||
|
||||
update_pokeradar_overworld_ui
|
||||
end
|
||||
|
||||
# Reusing the old pokeradar mechanics for chaining
|
||||
def set_pokeradar_data(species, level, position)
|
||||
x = position[0]
|
||||
y = position[1]
|
||||
|
||||
existing_chain = ($PokemonTemp.pokeradar && $PokemonTemp.pokeradar[2]) ? $PokemonTemp.pokeradar[2] : 0
|
||||
|
||||
$PokemonTemp.pokeradar = [0, 0, 0, []] unless $PokemonTemp.pokeradar
|
||||
$PokemonTemp.pokeradar[0] = species
|
||||
$PokemonTemp.pokeradar[1] = level
|
||||
$PokemonTemp.pokeradar[2] = existing_chain
|
||||
$PokemonTemp.pokeradar[3] = [x, y, 0, determine_shininess]
|
||||
end
|
||||
|
||||
def determine_shininess
|
||||
chain_length = ($PokemonTemp.pokeradar && $PokemonTemp.pokeradar[2]) ? $PokemonTemp.pokeradar[2] : 0
|
||||
rarity = (rand(100) < 25) ? 1 : 0
|
||||
if chain_length > 0
|
||||
capped_chain = [chain_length, 40].min
|
||||
shiny_threshold = [(65536 / Settings::SHINY_POKEMON_CHANCE) - capped_chain * 200, 200].max
|
||||
shiny_divisor = 0xFFFF / shiny_threshold
|
||||
shiny_roll = rand(65536) / shiny_divisor
|
||||
rarity = 2 if shiny_roll == 0
|
||||
end
|
||||
return rarity
|
||||
end
|
||||
|
||||
|
||||
Events.onMapChange += proc { |_sender, e|
|
||||
pbPokeRadarCancel
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class PokeRadarAppScreen
|
||||
def initialize(scene)
|
||||
@scene = scene
|
||||
end
|
||||
|
||||
def pbStartScreen(main_menu_scene)
|
||||
@scene.pbStartScene(main_menu_scene)
|
||||
@scene.pbScene
|
||||
@scene.pbEndScene
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
class TrainerStatistics
|
||||
attr_accessor :pokecenter_heals
|
||||
attr_accessor :pokemon_contests_participated_total
|
||||
attr_accessor :pokemon_contests_participated_category
|
||||
attr_accessor :pokemon_contests_participated_rank
|
||||
attr_accessor :pokemon_contests_participated_category_rank
|
||||
attr_accessor :pokemon_contests_won_total
|
||||
attr_accessor :pokemon_contests_won_category
|
||||
attr_accessor :pokemon_contests_won_rank
|
||||
attr_accessor :pokemon_contests_won_category_rank
|
||||
|
||||
attr_accessor :berries_planted
|
||||
attr_accessor :bike_hops_distance
|
||||
|
||||
def initialize
|
||||
@pokecenter_heals = 0
|
||||
@nb_trades = 0
|
||||
@nb_battles_won = 0
|
||||
@nb_battles_lost = 0
|
||||
|
||||
@nb_pokemon_defeated = 0
|
||||
@nb_pokemon_surprised = 0
|
||||
@berries_planted = 0
|
||||
@bike_hops_distance = 0
|
||||
initializeContestStats
|
||||
end
|
||||
|
||||
def initializeContestStats
|
||||
@pokemon_contests_participated_total ||= 0
|
||||
@pokemon_contests_participated_category ||= [0,0,0,0,0]
|
||||
@pokemon_contests_participated_rank ||= [0,0,0,0]
|
||||
@pokemon_contests_participated_category_rank ||= [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
|
||||
@pokemon_contests_won_total ||= 0
|
||||
@pokemon_contests_won_category ||= [0,0,0,0,0]
|
||||
@pokemon_contests_won_rank ||= [0,0,0,0]
|
||||
@pokemon_contests_won_category_rank ||= [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
|
||||
end
|
||||
|
||||
def contest_stats_initialized?
|
||||
!@pokemon_contests_participated_total.nil?
|
||||
end
|
||||
def incr_nb_pokemon_surprised
|
||||
@nb_pokemon_surprised = 1 unless @nb_pokemon_surprised
|
||||
@nb_pokemon_surprised += 1
|
||||
end
|
||||
|
||||
def incr_nb_pokemon_defeated
|
||||
@nb_pokemon_defeated = 1 unless @nb_pokemon_defeated
|
||||
@nb_pokemon_defeated += 1
|
||||
end
|
||||
def incr_nb_trades
|
||||
@nb_trades = 1 unless @nb_trades
|
||||
@nb_trades += 1
|
||||
end
|
||||
|
||||
def incr_nb_battles_won
|
||||
@nb_battles_won = 1 unless @nb_battles_won
|
||||
@nb_battles_won += 1
|
||||
end
|
||||
|
||||
def incr_nb_battles_lost
|
||||
@nb_battles_lost = 1 unless @nb_battles_lost
|
||||
@nb_battles_lost += 1
|
||||
end
|
||||
|
||||
def incr_nb_bike_hops_steps
|
||||
@bike_hops_distance = 1 unless @bike_hops_distance
|
||||
@bike_hops_distance += 1
|
||||
end
|
||||
|
||||
def nb_fusions
|
||||
return $game_variables[VAR_STAT_NB_FUSIONS]
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user