Rewrote load game screen inc. supporting multiple save files, allowed \PN and \v[42] in map names and Town Map point names

This commit is contained in:
Maruno17
2024-10-20 23:46:56 +01:00
parent 89c344dc00
commit 6152b75cb1
21 changed files with 781 additions and 60 deletions

View File

@@ -6,6 +6,8 @@
# @see SaveData.register_conversion # @see SaveData.register_conversion
#=============================================================================== #===============================================================================
module SaveData module SaveData
DIRECTORY = (File.directory?(System.data_directory)) ? System.data_directory : "./"
FILENAME_REGEX = /Game(\d*)\.rxdata$/
# Contains the file path of the save file. # Contains the file path of the save file.
FILE_PATH = if File.directory?(System.data_directory) FILE_PATH = if File.directory?(System.data_directory)
System.data_directory + "/Game.rxdata" System.data_directory + "/Game.rxdata"
@@ -18,6 +20,19 @@ module SaveData
return File.file?(FILE_PATH) return File.file?(FILE_PATH)
end end
# @return[Array] array of filenames in the save folder that are save files
def self.all_save_files
files = Dir.get(DIRECTORY, "*", false)
ret = []
files.each do |file|
next if !file[FILENAME_REGEX]
ret.push([$~[1].to_i, file])
end
ret.sort! { |a, b| a[0] <=> b[0] }
ret.map! { |val| val[1] }
return ret
end
# Fetches the save data from the given file. # Fetches the save data from the given file.
# Returns an Array in the case of a pre-v19 save file. # Returns an Array in the case of a pre-v19 save file.
# @param file_path [String] path of the file to load from # @param file_path [String] path of the file to load from
@@ -64,9 +79,9 @@ module SaveData
# Deletes the save file (and a possible .bak backup file if one exists) # Deletes the save file (and a possible .bak backup file if one exists)
# @raise [Error::ENOENT] # @raise [Error::ENOENT]
def self.delete_file def self.delete_file(filename)
File.delete(FILE_PATH) File.delete(DIRECTORY + filename)
File.delete(FILE_PATH + ".bak") if File.file?(FILE_PATH + ".bak") File.delete(DIRECTORY + filename + ".bak") if File.file?(DIRECTORY + filename + ".bak")
end end
# Converts the pre-v19 format data to the new format. # Converts the pre-v19 format data to the new format.

View File

@@ -259,9 +259,9 @@ module SaveData
# been set to be loaded during bootup. Done when a save file exists. # been set to be loaded during bootup. Done when a save file exists.
# @param save_data [Hash] save data to load # @param save_data [Hash] save data to load
# @raise [InvalidValueError] if an invalid value is being loaded # @raise [InvalidValueError] if an invalid value is being loaded
def self.load_bootup_values(save_data) def self.load_bootup_values(save_data, reload = false)
validate save_data => Hash validate save_data => Hash
load_values(save_data) { |value| !value.loaded? && value.load_in_bootup? } load_values(save_data) { |value| (reload || !value.loaded?) && value.load_in_bootup? }
end end
# Goes through each value with {Value#load_in_bootup} enabled and loads their # Goes through each value with {Value#load_in_bootup} enabled and loads their

View File

@@ -10,16 +10,17 @@ SaveData.register(:player) do
end end
SaveData.register(:game_system) do SaveData.register(:game_system) do
load_in_bootup # TODO: Am I sure this doesn't need to be loaded in bootup?
# load_in_bootup
ensure_class :Game_System ensure_class :Game_System
save_value { $game_system } save_value { $game_system }
load_value { |value| $game_system = value } load_value { |value| $game_system = value }
new_game_value { Game_System.new } new_game_value { Game_System.new }
reset_on_new_game # reset_on_new_game
end end
SaveData.register(:pokemon_system) do SaveData.register(:pokemon_system) do
load_in_bootup load_in_bootup # Because this contains values for the Options screen
ensure_class :PokemonSystem ensure_class :PokemonSystem
save_value { $PokemonSystem } save_value { $PokemonSystem }
load_value { |value| $PokemonSystem = value } load_value { |value| $PokemonSystem = value }
@@ -96,7 +97,6 @@ SaveData.register(:storage_system) do
end end
SaveData.register(:essentials_version) do SaveData.register(:essentials_version) do
load_in_bootup
ensure_class :String ensure_class :String
save_value { Essentials::VERSION } save_value { Essentials::VERSION }
load_value { |value| $save_engine_version = value } load_value { |value| $save_engine_version = value }
@@ -104,7 +104,6 @@ SaveData.register(:essentials_version) do
end end
SaveData.register(:game_version) do SaveData.register(:game_version) do
load_in_bootup
ensure_class :String ensure_class :String
save_value { Settings::GAME_VERSION } save_value { Settings::GAME_VERSION }
load_value { |value| $save_game_version = value } load_value { |value| $save_game_version = value }
@@ -112,10 +111,8 @@ SaveData.register(:game_version) do
end end
SaveData.register(:stats) do SaveData.register(:stats) do
load_in_bootup
ensure_class :GameStats ensure_class :GameStats
save_value { $stats } save_value { $stats }
load_value { |value| $stats = value } load_value { |value| $stats = value }
new_game_value { GameStats.new } new_game_value { GameStats.new }
reset_on_new_game
end end

View File

@@ -23,16 +23,12 @@ module Game
# Loads bootup data from save file (if it exists) or creates bootup data (if # Loads bootup data from save file (if it exists) or creates bootup data (if
# it doesn't). # it doesn't).
def set_up_system def set_up_system
save_data = (SaveData.exists?) ? SaveData.read_from_file(SaveData::FILE_PATH) : {} SaveData.initialize_bootup_values
if save_data.empty?
SaveData.initialize_bootup_values
else
SaveData.load_bootup_values(save_data)
end
# Set resize factor
pbSetResizeFactor([$PokemonSystem.screensize, 4].min) pbSetResizeFactor([$PokemonSystem.screensize, 4].min)
# Set language (and choose language if there is no save file) # Set language (and choose language if there is no save file)
if !Settings::LANGUAGES.empty? if !Settings::LANGUAGES.empty?
# TODO: Change this to check for any save file.
save_data = (SaveData.exists?) ? SaveData.read_from_file(SaveData::FILE_PATH) : {}
$PokemonSystem.language = pbChooseLanguage if save_data.empty? && Settings::LANGUAGES.length >= 2 $PokemonSystem.language = pbChooseLanguage if save_data.empty? && Settings::LANGUAGES.length >= 2
MessageTypes.load_message_files(Settings::LANGUAGES[$PokemonSystem.language][1]) MessageTypes.load_message_files(Settings::LANGUAGES[$PokemonSystem.language][1])
end end

View File

@@ -61,6 +61,7 @@ class GameStats
attr_writer :play_time # In seconds; the reader also updates the value attr_writer :play_time # In seconds; the reader also updates the value
attr_accessor :play_sessions attr_accessor :play_sessions
attr_accessor :time_last_saved # In seconds attr_accessor :time_last_saved # In seconds
attr_reader :real_time_saved
def initialize def initialize
# Travel # Travel
@@ -149,6 +150,7 @@ class GameStats
@play_time = 0 @play_time = 0
@play_sessions = 0 @play_sessions = 0
@time_last_saved = 0 @time_last_saved = 0
@real_time_saved = 0
end end
def distance_moved def distance_moved
@@ -189,6 +191,7 @@ class GameStats
def set_time_last_saved def set_time_last_saved
@time_last_saved = play_time @time_last_saved = play_time
@real_time_saved = Time.now.to_i
end end
def time_since_last_save def time_since_last_save

View File

@@ -243,17 +243,15 @@ end
def pbRepositionMessageWindow(msgwindow, linecount = 2) def pbRepositionMessageWindow(msgwindow, linecount = 2)
msgwindow.height = (32 * linecount) + msgwindow.borderY msgwindow.height = (32 * linecount) + msgwindow.borderY
msgwindow.y = (Graphics.height) - (msgwindow.height) msgwindow.y = (Graphics.height) - (msgwindow.height)
if $game_system case $game_system&.message_position || 2
case $game_system.message_position when 0 # top
when 0 # up msgwindow.y = 0
msgwindow.y = 0 when 1 # middle
when 1 # middle msgwindow.y = (Graphics.height - msgwindow.height) / 2
msgwindow.y = (Graphics.height / 2) - (msgwindow.height / 2) when 2 # bottom
when 2 msgwindow.y = Graphics.height - msgwindow.height
msgwindow.y = (Graphics.height) - (msgwindow.height)
end
msgwindow.opacity = 0 if $game_system.message_frame != 0
end end
msgwindow.opacity = 0 if ($game_system&.message_frame || 0) != 0
end end
# internal function # internal function

View File

@@ -254,9 +254,10 @@ end
def pbGetMapNameFromId(id) def pbGetMapNameFromId(id)
name = GameData::MapMetadata.try_get(id)&.name name = GameData::MapMetadata.try_get(id)&.name
if nil_or_empty?(name) name = pbGetBasicMapNameFromId(id) if nil_or_empty?(name)
name = pbGetBasicMapNameFromId(id) name = name.gsub(/\\PN/, $player.name) if $player
name.gsub!(/\\PN/, $player.name) if $player if $game_variables
name = name.gsub(/\\v\[(\d+)\]/) { |num| $game_variables[$~[1].to_i].to_s }
end end
return name return name
end end

View File

@@ -102,6 +102,7 @@ module GameData
return false return false
end end
# TODO: Make the below depend on a Setting rather than quoting it out.
def display_type(pkmn, move = nil) def display_type(pkmn, move = nil)
=begin =begin
case @function_code case @function_code
@@ -187,6 +188,7 @@ module GameData
return @type return @type
end end
# TODO: Make the below depend on a Setting rather than quoting it out.
def display_damage(pkmn, move = nil) def display_damage(pkmn, move = nil)
=begin =begin
case @function_code case @function_code

View File

@@ -159,6 +159,7 @@ class Battle::Move
if battler.isSpecies?(:MORPEKO) || battler.effects[PBEffects::TransformSpecies] == :MORPEKO if battler.isSpecies?(:MORPEKO) || battler.effects[PBEffects::TransformSpecies] == :MORPEKO
return pbBaseType(battler) return pbBaseType(battler)
end end
# TODO: Make the below depend on a Setting rather than quoting it out.
=begin =begin
when "TypeDependsOnUserPlate", "TypeDependsOnUserMemory", when "TypeDependsOnUserPlate", "TypeDependsOnUserMemory",
"TypeDependsOnUserDrive", "TypeAndPowerDependOnUserBerry", "TypeDependsOnUserDrive", "TypeAndPowerDependOnUserBerry",
@@ -170,6 +171,7 @@ class Battle::Move
return @realMove.display_type(battler.pokemon) return @realMove.display_type(battler.pokemon)
end end
# TODO: Make the below depend on a Setting rather than quoting it out.
def display_damage(battler) def display_damage(battler)
=begin =begin
case @function_code case @function_code
@@ -185,6 +187,7 @@ class Battle::Move
return @realMove.display_damage(battler.pokemon) return @realMove.display_damage(battler.pokemon)
end end
# TODO: Make the below depend on a Setting rather than quoting it out.
def display_category(battler) def display_category(battler)
=begin =begin
case @function_code case @function_code

View File

@@ -90,16 +90,7 @@ class IntroEventScene < EventScene
def close_title_screen(scene, *args) def close_title_screen(scene, *args)
fade_out_title_screen(scene) fade_out_title_screen(scene)
sscene = PokemonLoad_Scene.new UI::Load.new.main
sscreen = PokemonLoadScreen.new(sscene)
sscreen.pbStartLoadScreen
end
def close_title_screen_delete(scene, *args)
fade_out_title_screen(scene)
sscene = PokemonLoad_Scene.new
sscreen = PokemonLoadScreen.new(sscene)
sscreen.pbStartDeleteScreen
end end
def title_screen_update(scene, args) def title_screen_update(scene, args)
@@ -108,11 +99,6 @@ class IntroEventScene < EventScene
@pic2.moveOpacity(TICKS_PER_ENTER_FLASH * 2 / 10, TICKS_PER_ENTER_FLASH * 4 / 10, 0) @pic2.moveOpacity(TICKS_PER_ENTER_FLASH * 2 / 10, TICKS_PER_ENTER_FLASH * 4 / 10, 0)
@pic2.moveOpacity(TICKS_PER_ENTER_FLASH * 6 / 10, TICKS_PER_ENTER_FLASH * 4 / 10, 255) @pic2.moveOpacity(TICKS_PER_ENTER_FLASH * 6 / 10, TICKS_PER_ENTER_FLASH * 4 / 10, 255)
end end
if Input.press?(Input::DOWN) &&
Input.press?(Input::BACK) &&
Input.press?(Input::CTRL)
close_title_screen_delete(scene, args)
end
end end
end end

View File

@@ -186,6 +186,8 @@ class PokemonRegionMap_Scene
next if point[0] != x || point[1] != y next if point[0] != x || point[1] != y
return "" if point[7] && (@wallmap || point[7] <= 0 || !$game_switches[point[7]]) return "" if point[7] && (@wallmap || point[7] <= 0 || !$game_switches[point[7]])
name = pbGetMessageFromHash(MessageTypes::REGION_LOCATION_NAMES, point[2]) name = pbGetMessageFromHash(MessageTypes::REGION_LOCATION_NAMES, point[2])
name = name.gsub(/\\PN/, $player.name)
name = name.gsub(/\\v\[(\d+)\]/) { |num| $game_variables[$~[1].to_i].to_s }
return (@editor) ? point[2] : name return (@editor) ? point[2] : name
end end
return "" return ""
@@ -336,7 +338,7 @@ class PokemonRegionMapScreen
end end
def pbStartScreen def pbStartScreen
@scene.pbStartScene($DEBUG) @scene.pbStartScene # ($DEBUG)
ret = @scene.pbMapScene ret = @scene.pbMapScene
@scene.pbEndScene @scene.pbEndScene
return ret return ret

View File

@@ -392,7 +392,7 @@ MenuHandlers.add(:options_menu, :bgm_volume, {
"set_proc" => proc { |value, scene| "set_proc" => proc { |value, scene|
next if $PokemonSystem.bgmvolume == value next if $PokemonSystem.bgmvolume == value
$PokemonSystem.bgmvolume = value $PokemonSystem.bgmvolume = value
next if scene.in_load_screen || $game_system.playing_bgm.nil? next if scene.in_load_screen || !$game_system || $game_system.playing_bgm.nil?
playingBGM = $game_system.getPlayingBGM playingBGM = $game_system.getPlayingBGM
$game_system.bgm_pause $game_system.bgm_pause
$game_system.bgm_resume(playingBGM) $game_system.bgm_resume(playingBGM)
@@ -409,7 +409,7 @@ MenuHandlers.add(:options_menu, :se_volume, {
"set_proc" => proc { |value, _scene| "set_proc" => proc { |value, _scene|
next if $PokemonSystem.sevolume == value next if $PokemonSystem.sevolume == value
$PokemonSystem.sevolume = value $PokemonSystem.sevolume = value
if $game_system.playing_bgs if $game_system && $game_system.playing_bgs
$game_system.playing_bgs.volume = value $game_system.playing_bgs.volume = value
playingBGS = $game_system.getPlayingBGS playingBGS = $game_system.getPlayingBGS
$game_system.bgs_pause $game_system.bgs_pause

View File

@@ -65,7 +65,7 @@ module UI
end end
def gendered_filename(base_filename) def gendered_filename(base_filename)
return filename_with_appendix(base_filename, "_f") if $player.female? return filename_with_appendix(base_filename, "_f") if $player&.female?
return base_filename return base_filename
end end
@@ -306,6 +306,8 @@ module UI
# The visuals class. # The visuals class.
#============================================================================= #=============================================================================
class BaseVisuals class BaseVisuals
attr_reader :sprites
BACKGROUND_FILENAME = "bg" BACKGROUND_FILENAME = "bg"
include SpriteContainerMixin include SpriteContainerMixin
@@ -800,6 +802,7 @@ module UI
end end
def main def main
return if @disposed
start_screen start_screen
loop do loop do
on_start_main_loop on_start_main_loop

View File

@@ -327,7 +327,6 @@ end
# #
#=============================================================================== #===============================================================================
class UI::PartyVisuals < UI::BaseVisuals class UI::PartyVisuals < UI::BaseVisuals
attr_reader :sprites
attr_reader :index attr_reader :index
attr_reader :sub_mode attr_reader :sub_mode

View File

@@ -164,7 +164,6 @@ end
# #
#=============================================================================== #===============================================================================
class UI::BagVisuals < UI::BaseVisuals class UI::BagVisuals < UI::BaseVisuals
attr_reader :sprites
attr_reader :pocket attr_reader :pocket
GRAPHICS_FOLDER = "Bag/" # Subfolder in Graphics/UI GRAPHICS_FOLDER = "Bag/" # Subfolder in Graphics/UI

View File

@@ -0,0 +1,717 @@
#===============================================================================
#
#===============================================================================
class UI::LoadPanel < UI::SpriteContainer
GRAPHICS_FOLDER = "Load/"
TEXT_COLOR_THEMES = { # These color themes are added to @sprites[:overlay]
:default => [Color.new(88, 88, 80), Color.new(168, 184, 184)] # Base and shadow colour
}
PANEL_WIDTH = 392
PANEL_HEIGHT = 56
def initialize(label, viewport)
@label = label
@selected = nil
super(viewport)
end
def initialize_sprites
initialize_panel_background
initialize_overlay
end
def initialize_panel_background
@sprites[:background] = ChangelingSprite.new(0, 0, @viewport)
panel_srcs.each_pair do |key, values|
@sprites[:background].add_bitmap(key, values)
end
@sprites[:background].change_bitmap(:default)
record_values(:background)
end
def initialize_overlay
add_overlay(:overlay, @sprites[:background].src_rect.width, @sprites[:background].src_rect.height)
@sprites[:overlay].z = 10
record_values(:overlay)
end
#-----------------------------------------------------------------------------
def width
return self.class::PANEL_WIDTH
end
def height
return self.class::PANEL_HEIGHT
end
def panel_srcs
return {
:default => [graphics_folder + "panels", 0, UI::LoadContinuePanel::PANEL_HEIGHT * 2,
self.class::PANEL_WIDTH, self.class::PANEL_HEIGHT],
:selected => [graphics_folder + "panels", 0, (UI::LoadContinuePanel::PANEL_HEIGHT * 2) + self.class::PANEL_HEIGHT,
self.class::PANEL_WIDTH, self.class::PANEL_HEIGHT]
}
end
def selected=(value)
return if @selected == value
@selected = value
@sprites[:background].change_bitmap((@selected) ? :selected : :default)
refresh
end
#-----------------------------------------------------------------------------
def refresh
super
draw_text(@label, 18, 18)
end
end
#===============================================================================
#
#===============================================================================
class UI::LoadContinuePanel < UI::LoadPanel
attr_reader :sprites
GRAPHICS_FOLDER = "Load/"
TEXT_COLOR_THEMES = { # These color themes are added to @sprites[:overlay]
:default => [Color.new(88, 88, 80), Color.new(168, 184, 184)], # Base and shadow colour
:male => [Color.new(0, 112, 248), Color.new(120, 184, 232)],
:female => [Color.new(232, 32, 16), Color.new(248, 168, 184)]
}
PANEL_WIDTH = 392
PANEL_HEIGHT = 248
def initialize(label, save_data, slot_index, total_slots, viewport)
@save_data = save_data
@slot_index = slot_index
@total_slots = total_slots
super(label, viewport)
refresh
end
def initialize_sprites
super
initialize_player_sprite
initialize_pokemon_icons
initialize_arrow_sprites
end
def initialize_player_sprite
meta = GameData::PlayerMetadata.get(@save_data[:player].character_ID)
filename = pbGetPlayerCharset(meta.walk_charset, @save_data[:player], true)
@sprites[:player] = TrainerWalkingCharSprite.new(filename, @viewport)
if !@sprites[:player].bitmap
raise _INTL("Player character {1}'s walking charset was not found (filename: \"{2}\").",
@save_data[:player].character_ID, filename)
end
@sprites[:player].x = 48 - (@sprites[:player].bitmap.width / 8)
@sprites[:player].y = 72 - (@sprites[:player].bitmap.height / 8)
@sprites[:player].z = 1
record_values(:player)
end
def initialize_pokemon_icons
Settings::MAX_PARTY_SIZE.times do |i|
@sprites["pokemon_#{i}"] = PokemonIconSprite.new(@save_data[:player].party[i], @viewport)
@sprites["pokemon_#{i}"].x, @sprites["pokemon_#{i}"].y = pokemon_coords(i)
@sprites["pokemon_#{i}"].z = 1
@sprites["pokemon_#{i}"].setOffset
record_values("pokemon_#{i}")
end
end
def initialize_arrow_sprites
@sprites[:left_arrow] = AnimatedSprite.new(UI_FOLDER + "left_arrow", 8, 40, 28, 2, @viewport)
@sprites[:left_arrow].x = -16
@sprites[:left_arrow].y = (height / 2) - 14
@sprites[:left_arrow].z = 20
@sprites[:left_arrow].play
record_values(:left_arrow)
@sprites[:right_arrow] = AnimatedSprite.new(UI_FOLDER + "right_arrow", 8, 40, 28, 2, @viewport)
@sprites[:right_arrow].x = width - 24
@sprites[:right_arrow].y = (height / 2) - 14
@sprites[:right_arrow].z = 20
@sprites[:right_arrow].play
record_values(:right_arrow)
end
#-----------------------------------------------------------------------------
def panel_srcs
return {
:default => [graphics_folder + "panels", 0, 0,
self.class::PANEL_WIDTH, self.class::PANEL_HEIGHT],
:selected => [graphics_folder + "panels", 0, self.class::PANEL_HEIGHT,
self.class::PANEL_WIDTH, self.class::PANEL_HEIGHT]
}
end
def pokemon_coords(index)
return 276 + (66 * (index % 2)),
74 + (50 * (index / 2))
end
def visible=(value)
super
@sprites[:left_arrow].visible = (@selected && @total_slots >= 2)
@sprites[:right_arrow].visible = (@selected && @total_slots >= 2)
end
def selected=(value)
@sprites[:left_arrow].visible = (value && @total_slots >= 2)
@sprites[:right_arrow].visible = (value && @total_slots >= 2)
super
end
def set_data(save_data, slot_index, total_slots)
@save_data = save_data
@slot_index = slot_index
@total_slots = total_slots
@sprites[:left_arrow].visible = (@selected && total_slots >= 2)
@sprites[:right_arrow].visible = (@selected && total_slots >= 2)
set_player_sprite
refresh
end
def set_player_sprite
meta = GameData::PlayerMetadata.get(@save_data[:player].character_ID)
filename = pbGetPlayerCharset(meta.walk_charset, @save_data[:player], true)
@sprites[:player].charset = filename
if !@sprites[:player].bitmap
raise _INTL("Player character {1}'s walking charset was not found (filename: \"{2}\").",
@save_data[:player].character_ID, filename)
end
end
#-----------------------------------------------------------------------------
def refresh
super
refresh_pokemon
draw_slot_number
draw_save_file_text
end
def refresh_pokemon
Settings::MAX_PARTY_SIZE.times do |i|
@sprites["pokemon_#{i}"].pokemon = @save_data[:player].party[i]
end
end
def draw_slot_number
return if @total_slots <= 1
draw_text(sprintf("%d/%d", @slot_index + 1, @total_slots), PANEL_WIDTH - 18, 18, align: :right)
end
def draw_save_file_text
gender_theme = :default
if @save_data[:player].male?
gender_theme = :male
elsif @save_data[:player].female?
gender_theme = :female
end
# Player's name
draw_text(@save_data[:player].name, 78, 66, theme: gender_theme)
# Location
map_id = @save_data[:map_factory].map.map_id
map_name = pbGetMapNameFromId(map_id)
map_name = map_name.gsub(/\\PN/, @save_data[:player].name)
map_name = map_name.gsub(/\\v\[(\d+)\]/) { |num| @save_data[:variables][$~[1].to_i].to_s }
draw_text(map_name, 18, 114, theme: gender_theme)
# Gym Badges
draw_text(_INTL("Badges:"), 18, 146)
draw_text(@save_data[:player].badge_count.to_s, 156, 146, theme: gender_theme)
# Pokédex owned count
draw_text(_INTL("Pokédex:"), 18, 178)
draw_text(@save_data[:player].pokedex.seen_count.to_s, 156, 178, theme: gender_theme)
# Time played
draw_text(_INTL("Time played:"), 18, 210)
play_time = @save_data[:stats]&.play_time.to_i || 0
hour = (play_time / 60) / 60
min = (play_time / 60) % 60
play_time_text = (hour > 0) ? _INTL("{1}h {2}m", hour, min) : _INTL("{1}m", min)
draw_text(play_time_text, 156, 210, theme: gender_theme)
save_time = @save_data[:stats]&.real_time_saved
if save_time
save_time = Time.at(save_time)
if System.user_language[3..4] == "US" # If the user is in the United States
save_text = save_time.strftime("%-m/&-d/%Y")
else
save_text = save_time.strftime("%-d/%-m/%Y")
end
draw_text(save_text, PANEL_WIDTH - 18, 210, align: :right, theme: gender_theme)
end
end
def refresh_existing_pokemon
Settings::MAX_PARTY_SIZE.times do |i|
@sprites["pokemon_#{i}"].pokemon = @save_data[:player].party[i]
end
end
end
#===============================================================================
#
#===============================================================================
class UI::LoadVisuals < UI::BaseVisuals
attr_reader :slot_index
GRAPHICS_FOLDER = "Load/" # Subfolder in Graphics/UI
PANEL_SPACING_EDGE = 4
PANEL_SPACING = PANEL_SPACING_EDGE * 2
# save_data here is an array of all save files' data. It has been compacted.
# commands is {:continue => _INTL("Continue"), :new_game => _INTL("New Game")}, etc.
def initialize(commands, save_data, default_slot_index = 0)
@save_data = save_data
@commands = commands
@index = @commands.keys.first # A symbol from @commands
@slot_index = default_slot_index # Which save slot is selected
super()
end
def initialize_sprites
initialize_continue_panels
initialize_other_panels
end
def initialize_continue_panels
return if @save_data.nil? || @commands.keys.first != :continue
# Continue panel in middle
this_slot_index = @slot_index
@sprites[:continue] = create_continue_panel(this_slot_index)
# Continue panel to left
if @save_data.length >= 2
previous_slot_index = this_slot_index - 1
@sprites[:continue_previous] = create_continue_panel(this_slot_index - 1)
@sprites[:continue_previous].x = @sprites[:continue].x - @sprites[:continue].width - PANEL_SPACING
# Continue panel to right
next_slot_index = this_slot_index + 1
@sprites[:continue_next] = create_continue_panel(this_slot_index + 1)
@sprites[:continue_next].x = @sprites[:continue].x + @sprites[:continue].width + PANEL_SPACING
end
end
def initialize_other_panels
@commands.each_pair do |key, text|
next if key == :continue
@sprites[key] = UI::LoadPanel.new(text, @viewport)
@sprites[key].x = (Graphics.width - @sprites[key].width) / 2
end
@sprites[:mystery_gift]&.visible = @save_data[@slot_index] && @save_data[@slot_index][1][:player].mystery_gift_unlocked
end
#-----------------------------------------------------------------------------
def create_continue_panel(slot_index)
slot_index += @save_data.length if slot_index < 0
slot_index -= @save_data.length if slot_index >= @save_data.length
ret = UI::LoadContinuePanel.new(@commands[:continue],
@save_data[slot_index][1], slot_index, @save_data.length, @viewport)
ret.x = (Graphics.width - ret.width) / 2
return ret
end
def set_index(new_index)
@index = new_index
refresh_on_index_changed(@index)
end
def go_to_next_option(play_se = true)
return if @commands.length == 1
old_index = @commands.keys.index(@index)
new_index = old_index
loop do
new_index = (new_index + 1) % @commands.length
break if @sprites[@commands.keys[new_index]] && @sprites[@commands.keys[new_index]].visible
break if new_index == old_index
end
return if new_index == old_index
pbPlayCursorSE if play_se
set_index(@commands.keys[new_index])
end
def go_to_previous_option(play_se = true)
return if @commands.length == 1
old_index = @commands.keys.index(@index)
new_index = old_index
loop do
new_index -= 1
new_index += @commands.length if new_index < 0
break if @sprites[@commands.keys[new_index]] && @sprites[@commands.keys[new_index]].visible
break if new_index == old_index
end
return if new_index == old_index
pbPlayCursorSE if play_se
set_index(@commands.keys[new_index])
end
#-----------------------------------------------------------------------------
def set_slot_index(new_index, forced = false)
while new_index < 0
new_index += @save_data.length
end
while new_index >= @save_data.length
new_index -= @save_data.length
end
return if !forced && @slot_index == new_index
# Set the new index
@slot_index = new_index
# Show the newly selected slot's information in the Continue panel
@sprites[:continue].set_data(@save_data[@slot_index][1], @slot_index, @save_data.length)
# Show the newly adjacent slots' information in the adjacent Continue panels
prev_index = @slot_index - 1
prev_index += @save_data.length if prev_index < 0
@sprites[:continue_previous]&.set_data(@save_data[prev_index][1], prev_index, @save_data.length)
next_index = (@slot_index + 1) % @save_data.length
@sprites[:continue_next]&.set_data(@save_data[next_index][1], next_index, @save_data.length)
# Determine whether the Mystery Gift option is visible
@sprites[:mystery_gift].visible = @save_data[@slot_index][1][:player].mystery_gift_unlocked
refresh_panel_positions
SaveData.load_bootup_values(@save_data[@slot_index][1], true)
pbPlayCursorSE if !forced
end
def go_to_next_save_slot
set_slot_index(@slot_index + 1)
end
def go_to_previous_save_slot
set_slot_index(@slot_index - 1)
end
#-----------------------------------------------------------------------------
def refresh
super
@commands.keys.each { |key| @sprites[key]&.refresh }
refresh_panel_positions
refresh_selected_panel
end
def full_refresh
refresh
@sprites.each_pair { |key, sprite| sprite.refresh if sprite.respond_to?(:refresh) }
end
def refresh_panel_positions
@panel_y_offset ||= 0
sprite_y = PANEL_SPACING_EDGE
# Determine the relative positions of all option panels
sprite_pos = {}
@commands.keys.each do |key|
next if !@sprites[key] || !@sprites[key].visible # If Mystery Gift option isn't available
sprite_pos[key] = sprite_y
sprite_y += @sprites[key].height + PANEL_SPACING
end
# Determine an offset that ensures the selected option panel is on-screen
screen_y = sprite_pos[@index] - @panel_y_offset
if screen_y < PANEL_SPACING_EDGE
@panel_y_offset = sprite_pos[@index] - PANEL_SPACING_EDGE
elsif screen_y + @sprites[@index].height > Graphics.height - PANEL_SPACING_EDGE
@panel_y_offset = sprite_pos[@index] + @sprites[@index].height + PANEL_SPACING_EDGE - Graphics.height
end
# Apply the calculated positions to all option panels
sprite_pos.each_pair do |key, value|
@sprites[key].y = value - @panel_y_offset
end
@sprites[:continue_previous]&.y = @sprites[:continue].y
@sprites[:continue_next]&.y = @sprites[:continue].y
end
def refresh_selected_panel
@commands.keys.each do |key|
@sprites[key]&.selected = (key == @index)
end
@sprites[:continue_previous]&.selected = false
@sprites[:continue_next]&.selected = false
end
def refresh_on_index_changed(old_index)
refresh_selected_panel
refresh_panel_positions
end
def refresh_after_save_file_deleted
@slot_index = [@slot_index, @save_data.length - 1].min
if @save_data.empty?
[:continue, :continue_previous, :continue_next].each do |key|
@sprites[key].dispose if @sprites[key] && !@sprites[key].disposed?
@sprites[key] = nil
end
@sprites[:mystery_gift].visible = false
go_to_next_option(false)
else
if @save_data.length == 1
[:continue_previous, :continue_next].each do |key|
@sprites[key].dispose if @sprites[key] && !@sprites[key].disposed?
@sprites[key] = nil
end
end
set_slot_index(@slot_index, true)
end
end
#-----------------------------------------------------------------------------
def update_input
# Check for movement to a new option
if Input.repeat?(Input::UP)
go_to_previous_option
elsif Input.repeat?(Input::DOWN)
go_to_next_option
end
# Check for movement to a different save slot
if @index == :continue && @save_data.length > 1
if Input.repeat?(Input::LEFT)
go_to_previous_save_slot
elsif Input.repeat?(Input::RIGHT)
go_to_next_save_slot
end
end
# Check for interaction
if Input.trigger?(Input::USE)
if @index == :continue && Input.press?(Input::ACTION) && Input.press?(Input::BACK)
pbPlayDecisionSE
return :delete_save
end
return update_interaction(Input::USE)
end
return nil
end
def update_interaction(input)
case input
when Input::USE
pbPlayDecisionSE if @index != :quit_game
return @index # This is a key from @commands
end
return nil
end
end
#===============================================================================
#
#===============================================================================
class UI::Load < UI::BaseScreen
attr_reader :save_data
SCREEN_ID = :load_screen
def initialize
load_save_data
if $DEBUG && !FileTest.exist?("Game.rgssad") && Settings::SKIP_CONTINUE_SCREEN
@disposed = true
perform_action((@save_data.empty?) ? :new_game : :continue)
return
end
set_commands
super
end
def initialize_visuals
@visuals = UI::LoadVisuals.new(@commands, @save_data, @default_slot_index)
end
#-----------------------------------------------------------------------------
def slot_index
return @visuals&.slot_index || @default_slot_index
end
def set_commands
@commands = {}
MenuHandlers.each_available(:load_screen, self) do |option, _hash, name|
@commands[option] = name
end
end
#-----------------------------------------------------------------------------
def load_save_data
@save_data = []
@default_slot_index = 0
last_edited_time = nil
files = SaveData.all_save_files
files.each do |file|
# Load the save file
this_save_data = SaveData.read_from_file(SaveData::DIRECTORY + file)
if !SaveData.valid?(this_save_data)
if File.file?(SaveData::DIRECTORY + file + ".bak")
show_message(_INTL("The save file is corrupt. A backup will be loaded."))
this_save_data = load_save_file(SaveData::FILE_PATH + ".bak")
else
prompt_corrupted_save_deletion(file)
end
end
@save_data.push([file, this_save_data])
# Find the most recently edited save file; default to selecting that one
save_time = this_save_data[:stats].real_time_saved || 0
if !last_edited_time || save_time > last_edited_time
last_edited_time = save_time
@default_slot_index = @save_data.length - 1
end
end
SaveData.load_bootup_values(@save_data[@default_slot_index][1], true) if !@save_data.empty?
end
def prompt_corrupted_save_deletion(filename)
show_message(_INTL("The save file is corrupt, or is incompatible with this game.") + "\1")
pbPlayDecisionSE
exit if !show_confirm_serious_message(_INTL("Do you want to delete the save file and start anew?"))
delete_save_data(filename)
$PokemonSystem = PokemonSystem.new
end
def prompt_save_deletion(filename)
if show_confirm_serious_message(_INTL("Delete this save file?"))
show_message(_INTL("Once a save file has been deleted, there is no way to recover it.") + "\1")
pbPlayDecisionSE
if show_confirm_serious_message(_INTL("Delete the save file anyway?"))
delete_save_data(filename) {
@save_data.delete_if { |save| save[0] == filename }
@visuals.refresh_after_save_file_deleted
}
end
end
end
def delete_save_data(filename)
begin
SaveData.delete_file(filename)
yield if block_given?
show_message(_INTL("The save file was deleted."))
rescue SystemCallError
show_message(_INTL("The save file could not be deleted."))
end
end
#-----------------------------------------------------------------------------
def full_refresh
@visuals.full_refresh
end
end
#===============================================================================
# Actions that can be triggered in the load screen.
#===============================================================================
UIActionHandlers.add(UI::Load::SCREEN_ID, :continue, {
:effect => proc { |screen|
screen.end_screen
Game.load(screen.save_data[screen.slot_index][1])
}
})
UIActionHandlers.add(UI::Load::SCREEN_ID, :mystery_gift, {
:effect => proc { |screen|
pbFadeOutInWithUpdate(screen.sprites) do
pbDownloadMysteryGift(screen.save_data[screen.slot_index][1][:player])
end
}
})
UIActionHandlers.add(UI::Load::SCREEN_ID, :new_game, {
:effect => proc { |screen|
screen.end_screen
Game.start_new
}
})
UIActionHandlers.add(UI::Load::SCREEN_ID, :options, {
:effect => proc { |screen|
pbFadeOutInWithUpdate(screen.sprites) do
options_scene = PokemonOption_Scene.new
options_screen = PokemonOptionScreen.new(options_scene)
options_screen.pbStartScreen(true)
screen.full_refresh
end
}
})
UIActionHandlers.add(UI::Load::SCREEN_ID, :language, {
:effect => proc { |screen|
screen.end_screen
$PokemonSystem.language = pbChooseLanguage
MessageTypes.load_message_files(Settings::LANGUAGES[$PokemonSystem.language][1])
if screen.save_data[screen.slot_index]
screen.save_data[screen.slot_index][1][:pokemon_system] = $PokemonSystem
File.open(SaveData::DIRECTORY + screen.save_data[screen.slot_index][0], "wb") do |file|
Marshal.dump(screen.save_data[screen.slot_index][1], file)
end
end
$scene = pbCallTitle
}
})
UIActionHandlers.add(UI::Load::SCREEN_ID, :debug, {
:effect => proc { |screen|
pbFadeOutInWithUpdate(screen.sprites) do
pbDebugMenu(false)
end
}
})
UIActionHandlers.add(UI::Load::SCREEN_ID, :quit_game, {
:effect => proc { |screen|
pbPlayCloseMenuSE
screen.end_screen
$scene = nil
}
})
UIActionHandlers.add(UI::Load::SCREEN_ID, :delete_save, {
:effect => proc { |screen|
screen.prompt_save_deletion(screen.save_data[screen.slot_index][0])
}
})
#===============================================================================
# Menu options that exist in the load screen.
#===============================================================================
MenuHandlers.add(:load_screen, :continue, {
"name" => _INTL("Continue"),
"order" => 10,
"condition" => proc { |screen| next screen.save_data && !screen.save_data.empty? }
})
# NOTE: Mystery Gift is always added as an option here, even if no save files
# have unlocked it. Whether it is shown depends on the selected save file,
# and its visibility is toggled elsewhere because of that.
MenuHandlers.add(:load_screen, :mystery_gift, {
"name" => _INTL("Mystery Gift"),
"order" => 20,
"condition" => proc { |screen| next screen.save_data && !screen.save_data.empty? }
})
MenuHandlers.add(:load_screen, :new_game, {
"name" => _INTL("New Game"),
"order" => 30
})
MenuHandlers.add(:load_screen, :options, {
"name" => _INTL("Options"),
"order" => 40
})
# TODO: Put language in the options screen?
MenuHandlers.add(:load_screen, :language, {
"name" => _INTL("Language"),
"order" => 50,
"condition" => proc { |screen| next Settings::LANGUAGES.length >= 2 }
})
MenuHandlers.add(:load_screen, :debug, {
"name" => _INTL("Debug"),
"order" => 60,
"condition" => proc { |screen| next $DEBUG }
})
MenuHandlers.add(:load_screen, :quit_game, {
"name" => _INTL("Quit Game"),
"order" => 9999
})

View File

@@ -701,7 +701,6 @@ class UI::PokemonStorageVisuals < UI::BaseVisuals
# 0+ = box number # 0+ = box number
attr_reader :box attr_reader :box
attr_reader :sub_mode attr_reader :sub_mode
attr_reader :sprites
GRAPHICS_FOLDER = "Storage/" # Subfolder in Graphics/UI GRAPHICS_FOLDER = "Storage/" # Subfolder in Graphics/UI
TEXT_COLOR_THEMES = { # These color themes are added to @sprites[:overlay] TEXT_COLOR_THEMES = { # These color themes are added to @sprites[:overlay]
@@ -1352,9 +1351,9 @@ class UI::PokemonStorageVisuals < UI::BaseVisuals
return update_interaction(Input::BACK) return update_interaction(Input::BACK)
elsif Input.trigger?(Input::ACTION) elsif Input.trigger?(Input::ACTION)
return update_interaction(Input::ACTION) return update_interaction(Input::ACTION)
elsif Input.trigger?(Input::JUMPUP) elsif Input.repeat?(Input::JUMPUP)
return update_interaction(Input::JUMPUP) return update_interaction(Input::JUMPUP)
elsif Input.trigger?(Input::JUMPDOWN) elsif Input.repeat?(Input::JUMPDOWN)
return update_interaction(Input::JUMPDOWN) return update_interaction(Input::JUMPDOWN)
end end
return nil return nil

View File

@@ -125,7 +125,6 @@ end
# #
#=============================================================================== #===============================================================================
class UI::MartVisuals < UI::BaseVisuals class UI::MartVisuals < UI::BaseVisuals
attr_reader :sprites
attr_reader :pocket attr_reader :pocket
GRAPHICS_FOLDER = "Mart/" # Subfolder in Graphics/UI GRAPHICS_FOLDER = "Mart/" # Subfolder in Graphics/UI

View File

@@ -1,3 +1,4 @@
=begin
#=============================================================================== #===============================================================================
# #
#=============================================================================== #===============================================================================
@@ -356,3 +357,4 @@ class PokemonLoadScreen
end end
end end
end end
=end

View File

@@ -1,3 +1,4 @@
=begin
#=============================================================================== #===============================================================================
# Pokémon icons. # Pokémon icons.
#=============================================================================== #===============================================================================
@@ -2029,3 +2030,4 @@ class PokemonStorageScreen
return retval return retval
end end
end end
=end

View File

@@ -4,9 +4,7 @@
class Scene_DebugIntro class Scene_DebugIntro
def main def main
Graphics.transition(0) Graphics.transition(0)
sscene = PokemonLoad_Scene.new UI::Load.new.main
sscreen = PokemonLoadScreen.new(sscene)
sscreen.pbStartLoadScreen
Graphics.freeze Graphics.freeze
end end
end end