Final Fantasy IX Save Menu
By BigEd781
By BigEd781
Introduction
I have been on a Final Fantasy IX kick lately, so I decided to overhaul the save menu. Just place the script in the "Materials" section and download the images to your "Graphics\Pictures" folder
Features
⇨ Adds a new look modeled after Final Fantasy IX.
⇨ Displays the party member's faces instead of their sprites.
⇨ Uses a cursor for selection instead of the rectangle.
⇨ Adds the current gold amount to the display.
⇨ Adds the current location amount to the display.
⇨ Adds the current level of the party leader to the display.
⇨ Adds a gold and time icon to the display.
Updates / Bug Fixes
Configuration / Use
First off, know that this will not work with old save files!!!
It will not crash, but you must first create a new save to see the menu effects. It will look screwed up at first.
Copy the script and drop it into the "Materials" section above "Main".
Download all of the images below into your "Graphics\Pictures" folder.
Here is the configuration section. You can play with some of the layout options like font, font color, size, icon positioning, etc.:
Screenshots
Script
CODE
=begin
BigEd781's Final Fantasy IX Save Screen
Rewritten Methods
-----------------
Scene_File
write_save_data
create_savefile_windows
start
-----------------
=end
module FFIXSave
# use an image instead of the hazy map background
USE_CUSTOM_BACK = true
# the name of the background image file (if above is set to 'true')
BACK_NAME = 'StoneBackground'
# the name of the 'time' image file
TIMER_NAME = 'SaveTimer'
# the name of the 'gold' image file
GOLDCOIN_NAME = 'GoldCoin'
# the font to use. Example: 'Times New Roman'
FONT_NAME = Font.default_name
FONT_SIZE = 20
# the color of the font. (red, green, blue)
FONT_COLOR = Color.new(238, 238, 238)
# changing this will move the timer and gold
# icons left or right (for - or + values).
# this is useful if you have long names, or use a smaller font.
ICON_OFFSET_X = 0
end
class Rect
def shift_y(value)
new_y = self.y + value
return Rect.new(self.x, new_y, self.width, self.height)
end
end
#==============================================================================
# ** Sprite_HeaderBar
#------------------------------------------------------------------------------
# This is the top right window in the save/load menu
#==============================================================================
class Sprite_LoadSave < Sprite_Base
def initialize(x, y, text, viewport=nil)
super(viewport)
@bg_image = Cache.picture('LoadSaveDisplay')
@text = text
self.bitmap = Bitmap.new(@bg_image.width, @bg_image.height)
self.x = x
self.y = y
update
end
def update
self.bitmap.blt(0, 0, @bg_image, @bg_image.rect)
self.bitmap.draw_text(self.bitmap.rect.shift_y(3), @text, 1)
end
end
#==============================================================================
# ** Sprite_HeaderBar
#------------------------------------------------------------------------------
# This is the top left window in the save/load menu
#==============================================================================
class Sprite_HeaderBar < Sprite_Base
#--------------------------------------------------------------------------
# * initialize
#--------------------------------------------------------------------------
def initialize(viewport=nil)
@image = Cache.picture('FF9_HeaderBar')
@slot = 1
super(viewport)
self.bitmap = Bitmap.new(@image.width, @image.height)
self.x = 16
self.y = 18
update
end
#--------------------------------------------------------------------------
# * slot=
#--------------------------------------------------------------------------
def slot=(value)
@slot = value
end
#--------------------------------------------------------------------------
# * update
#--------------------------------------------------------------------------
def update
self.bitmap.blt(0, 0, @image, @image.rect)
text_y = 10
text_h = 24
self.bitmap.draw_text(16, text_y, 220, text_h, "Select a block.")
self.bitmap.draw_text(self.width - 72, text_y, 64, text_h, "Slot #{@slot}")
end
end
#==============================================================================
# ** Scene_File
#------------------------------------------------------------------------------
#
#==============================================================================
class Scene_File < Scene_Base
#--------------------------------------------------------------------------
# * Start processing
#--------------------------------------------------------------------------
alias :eds_pre_ff9_save_start :start
def start
super
create_menu_background
@help_window = Sprite_HeaderBar.new
create_loadsave_sprite
create_savefile_windows
@index = @saving ? $game_temp.last_file_index : self.latest_file_index
@savefile_windows[@index].selected = true
end
#--------------------------------------------------------------------------
# * create_menu_background (only if USE_CUSTOM_BACK == true)
#--------------------------------------------------------------------------
if FFIXSave::USE_CUSTOM_BACK
def create_menu_background
@menuback_sprite = Sprite.new
@menuback_sprite.bitmap = Cache.picture(FFIXSave::BACK_NAME)
@menuback_sprite.color.set(16, 16, 16, 128)
update_menu_background
end
end
#--------------------------------------------------------------------------
# * create_loadsave_sprite
#--------------------------------------------------------------------------
def create_loadsave_sprite
sx = @help_window.x + @help_window.width + 6
sy = @help_window.y
text = @saving ? "Save" : "Load"
@loadsave_sprite = Sprite_LoadSave.new(sx, sy, text)
end
#--------------------------------------------------------------------------
# * Update Save File Selection
#--------------------------------------------------------------------------
alias :eds_pre_ff9_save_update_savefile_selection :update_savefile_selection
def update_savefile_selection
eds_pre_ff9_save_update_savefile_selection
@help_window.slot = @index + 1
end
#--------------------------------------------------------------------------
# * write_save_data
#--------------------------------------------------------------------------
def write_save_data(file)
characters = []
map_name = load_data("Data/MapInfos.rvdata")[$game_map.map_id].name
for actor in $game_party.members
characters.push([ actor.character_name,
actor.character_index,
actor.face_name, # line added
actor.face_index, # line added
actor.name, # line added
actor.level, # line added
$game_party.gold, # line added
map_name ]) # line added
end
$game_system.save_count += 1
$game_system.version_id = $data_system.version_id
@last_bgm = RPG::BGM::last
@last_bgs = RPG::BGS::last
Marshal.dump(characters, file)
Marshal.dump(Graphics.frame_count, file)
Marshal.dump(@last_bgm, file)
Marshal.dump(@last_bgs, file)
Marshal.dump($game_system, file)
Marshal.dump($game_message, file)
Marshal.dump($game_switches, file)
Marshal.dump($game_variables, file)
Marshal.dump($game_self_switches, file)
Marshal.dump($game_actors, file)
Marshal.dump($game_party, file)
Marshal.dump($game_troop, file)
Marshal.dump($game_map, file)
Marshal.dump($game_player, file)
end
#--------------------------------------------------------------------------
# * Create Save File Window
#--------------------------------------------------------------------------
def create_savefile_windows
@savefile_windows = []
for i in 0..3
@savefile_windows.push(Window_FFIXSaveFile.new(i, make_filename(i)))
end
@item_max = 4
end
alias :eds_pre_ff9_terminate :terminate
def terminate
eds_pre_ff9_terminate
@loadsave_sprite.dispose
end
end
#==============================================================================
# ** Window_FFIXSaveFile
#------------------------------------------------------------------------------
# This is the window displayed for each save slot
#==============================================================================
class Window_FFIXSaveFile < Window_SaveFile
# the x , y position of the first column and line of text
TEXT_COL1_X = 298
TEXT_LINE1_Y = 0
# the y position of the second line of text, Level and Gold display
TEXT_LINE2_Y = 20
#--------------------------------------------------------------------------
# * initialize
#--------------------------------------------------------------------------
def initialize(file_index, filename)
unless FileTest.exist?(filename)
image_name = 'FF9_SaveFileWindow_no_overlay'
else
image_name = 'FF9_SaveFileWindow'
end
@bg_image = Cache.picture(image_name)
@timer_image = Cache.picture(FFIXSave::TIMER_NAME)
@gold_image = Cache.picture(FFIXSave::GOLDCOIN_NAME)
super(file_index, filename)
self.x = 0
self.height = @bg_image.height + 32
# do this so that they images can be close together,
# ignoring the 16 pixel window border
self.y -= self.y * 0.1
self.contents.dispose
self.contents = Bitmap.new(512, @bg_image.height)
self.contents.font.name = FFIXSave::FONT_NAME
self.contents.font.size = FFIXSave::FONT_SIZE
self.opacity = 0
@arrow_sprite = Sprite.new
@arrow_sprite.bitmap = Cache.picture('pointer')
@arrow_sprite.x = 0
@arrow_sprite.y = self.y + (0.4 * self.height)
@arrow_sprite.visible = false
@arrow_sprite.z = self.z + 1
refresh
end
def selected=(value)
@arrow_sprite.visible = value
end
#--------------------------------------------------------------------------
# * refresh
#--------------------------------------------------------------------------
def refresh
self.contents.clear
self.contents.font.color = FFIXSave::FONT_COLOR
draw_background_panel
begin
if @file_exist
draw_party_characters
draw_playtime
draw_leader_name
draw_leader_level
draw_gold
draw_location
draw_icons
end
rescue
# do nothing or the game will crash
# before the first save file is created.
end
end
#--------------------------------------------------------------------------
# * draw_background_panel
#--------------------------------------------------------------------------
def draw_background_panel
self.contents.blt(0, 0, @bg_image, @bg_image.rect)
end
#--------------------------------------------------------------------------
# * draw_party_characters
#--------------------------------------------------------------------------
def draw_party_characters
x, y, size = 5, 5, (self.contents.height - 10)
for i in 0...@characters.size
name = @characters[i][2]
index = @characters[i][3]
draw_face(name, index, x, y, size)
x += size + 2
end
end
#--------------------------------------------------------------------------
# * draw_playtime
#--------------------------------------------------------------------------
def draw_playtime
hour = @total_sec / 60 / 60
min = @total_sec / 60 % 60
sec = @total_sec % 60
time_string = sprintf("%02d:%02d:%02d", hour, min, sec)
len = contents.text_size(time_string).width + 6
contents.font.color = FFIXSave::FONT_COLOR
contents.draw_text(contents.width - len, TEXT_LINE1_Y, contents.width - 4, WLH, time_string)
end
#--------------------------------------------------------------------------
# * draw_leader_name
#--------------------------------------------------------------------------
def draw_leader_name
name = @characters[0][4]
self.contents.draw_text(TEXT_COL1_X, TEXT_LINE1_Y, 128, WLH, name)
end
#--------------------------------------------------------------------------
# * draw_leader_level
#--------------------------------------------------------------------------
def draw_leader_level
level = "Level #{@characters[0][5]}"
self.contents.draw_text(TEXT_COL1_X, TEXT_LINE2_Y, 128, WLH, level)
end
#--------------------------------------------------------------------------
# * draw_gold
#--------------------------------------------------------------------------
def draw_gold
gold = "#{@characters[0][6]} #{Vocab.gold[0,1]}"
text_x = contents.width - (contents.text_size(gold).width + 6)
self.contents.draw_text(text_x, TEXT_LINE2_Y, 96, WLH, gold)
end
#--------------------------------------------------------------------------
# * draw_location
#--------------------------------------------------------------------------
def draw_location
loc = @characters[0][7]
text_y = TEXT_LINE2_Y + 28
self.contents.draw_text(308, text_y, contents.width - 312, WLH, loc)
end
#--------------------------------------------------------------------------
# * draw_icons
#--------------------------------------------------------------------------
def draw_icons
self.contents.blt(TEXT_COL1_X + 93 + FFIXSave::ICON_OFFSET_X,
TEXT_LINE1_Y + 7,
@timer_image,
@timer_image.rect)
self.contents.blt(TEXT_COL1_X + 93 + FFIXSave::ICON_OFFSET_X,
TEXT_LINE2_Y + 7,
@gold_image,
@gold_image.rect)
end
#--------------------------------------------------------------------------
# * update_cursor
#--------------------------------------------------------------------------
def update_cursor
# not needed
end
#--------------------------------------------------------------------------
# * dispose
#--------------------------------------------------------------------------
def dispose
super
@bg_image.dispose
@timer_image.dispose
@arrow_sprite.dispose
end
end
BigEd781's Final Fantasy IX Save Screen
Rewritten Methods
-----------------
Scene_File
write_save_data
create_savefile_windows
start
-----------------
=end
module FFIXSave
# use an image instead of the hazy map background
USE_CUSTOM_BACK = true
# the name of the background image file (if above is set to 'true')
BACK_NAME = 'StoneBackground'
# the name of the 'time' image file
TIMER_NAME = 'SaveTimer'
# the name of the 'gold' image file
GOLDCOIN_NAME = 'GoldCoin'
# the font to use. Example: 'Times New Roman'
FONT_NAME = Font.default_name
FONT_SIZE = 20
# the color of the font. (red, green, blue)
FONT_COLOR = Color.new(238, 238, 238)
# changing this will move the timer and gold
# icons left or right (for - or + values).
# this is useful if you have long names, or use a smaller font.
ICON_OFFSET_X = 0
end
class Rect
def shift_y(value)
new_y = self.y + value
return Rect.new(self.x, new_y, self.width, self.height)
end
end
#==============================================================================
# ** Sprite_HeaderBar
#------------------------------------------------------------------------------
# This is the top right window in the save/load menu
#==============================================================================
class Sprite_LoadSave < Sprite_Base
def initialize(x, y, text, viewport=nil)
super(viewport)
@bg_image = Cache.picture('LoadSaveDisplay')
@text = text
self.bitmap = Bitmap.new(@bg_image.width, @bg_image.height)
self.x = x
self.y = y
update
end
def update
self.bitmap.blt(0, 0, @bg_image, @bg_image.rect)
self.bitmap.draw_text(self.bitmap.rect.shift_y(3), @text, 1)
end
end
#==============================================================================
# ** Sprite_HeaderBar
#------------------------------------------------------------------------------
# This is the top left window in the save/load menu
#==============================================================================
class Sprite_HeaderBar < Sprite_Base
#--------------------------------------------------------------------------
# * initialize
#--------------------------------------------------------------------------
def initialize(viewport=nil)
@image = Cache.picture('FF9_HeaderBar')
@slot = 1
super(viewport)
self.bitmap = Bitmap.new(@image.width, @image.height)
self.x = 16
self.y = 18
update
end
#--------------------------------------------------------------------------
# * slot=
#--------------------------------------------------------------------------
def slot=(value)
@slot = value
end
#--------------------------------------------------------------------------
# * update
#--------------------------------------------------------------------------
def update
self.bitmap.blt(0, 0, @image, @image.rect)
text_y = 10
text_h = 24
self.bitmap.draw_text(16, text_y, 220, text_h, "Select a block.")
self.bitmap.draw_text(self.width - 72, text_y, 64, text_h, "Slot #{@slot}")
end
end
#==============================================================================
# ** Scene_File
#------------------------------------------------------------------------------
#
#==============================================================================
class Scene_File < Scene_Base
#--------------------------------------------------------------------------
# * Start processing
#--------------------------------------------------------------------------
alias :eds_pre_ff9_save_start :start
def start
super
create_menu_background
@help_window = Sprite_HeaderBar.new
create_loadsave_sprite
create_savefile_windows
@index = @saving ? $game_temp.last_file_index : self.latest_file_index
@savefile_windows[@index].selected = true
end
#--------------------------------------------------------------------------
# * create_menu_background (only if USE_CUSTOM_BACK == true)
#--------------------------------------------------------------------------
if FFIXSave::USE_CUSTOM_BACK
def create_menu_background
@menuback_sprite = Sprite.new
@menuback_sprite.bitmap = Cache.picture(FFIXSave::BACK_NAME)
@menuback_sprite.color.set(16, 16, 16, 128)
update_menu_background
end
end
#--------------------------------------------------------------------------
# * create_loadsave_sprite
#--------------------------------------------------------------------------
def create_loadsave_sprite
sx = @help_window.x + @help_window.width + 6
sy = @help_window.y
text = @saving ? "Save" : "Load"
@loadsave_sprite = Sprite_LoadSave.new(sx, sy, text)
end
#--------------------------------------------------------------------------
# * Update Save File Selection
#--------------------------------------------------------------------------
alias :eds_pre_ff9_save_update_savefile_selection :update_savefile_selection
def update_savefile_selection
eds_pre_ff9_save_update_savefile_selection
@help_window.slot = @index + 1
end
#--------------------------------------------------------------------------
# * write_save_data
#--------------------------------------------------------------------------
def write_save_data(file)
characters = []
map_name = load_data("Data/MapInfos.rvdata")[$game_map.map_id].name
for actor in $game_party.members
characters.push([ actor.character_name,
actor.character_index,
actor.face_name, # line added
actor.face_index, # line added
actor.name, # line added
actor.level, # line added
$game_party.gold, # line added
map_name ]) # line added
end
$game_system.save_count += 1
$game_system.version_id = $data_system.version_id
@last_bgm = RPG::BGM::last
@last_bgs = RPG::BGS::last
Marshal.dump(characters, file)
Marshal.dump(Graphics.frame_count, file)
Marshal.dump(@last_bgm, file)
Marshal.dump(@last_bgs, file)
Marshal.dump($game_system, file)
Marshal.dump($game_message, file)
Marshal.dump($game_switches, file)
Marshal.dump($game_variables, file)
Marshal.dump($game_self_switches, file)
Marshal.dump($game_actors, file)
Marshal.dump($game_party, file)
Marshal.dump($game_troop, file)
Marshal.dump($game_map, file)
Marshal.dump($game_player, file)
end
#--------------------------------------------------------------------------
# * Create Save File Window
#--------------------------------------------------------------------------
def create_savefile_windows
@savefile_windows = []
for i in 0..3
@savefile_windows.push(Window_FFIXSaveFile.new(i, make_filename(i)))
end
@item_max = 4
end
alias :eds_pre_ff9_terminate :terminate
def terminate
eds_pre_ff9_terminate
@loadsave_sprite.dispose
end
end
#==============================================================================
# ** Window_FFIXSaveFile
#------------------------------------------------------------------------------
# This is the window displayed for each save slot
#==============================================================================
class Window_FFIXSaveFile < Window_SaveFile
# the x , y position of the first column and line of text
TEXT_COL1_X = 298
TEXT_LINE1_Y = 0
# the y position of the second line of text, Level and Gold display
TEXT_LINE2_Y = 20
#--------------------------------------------------------------------------
# * initialize
#--------------------------------------------------------------------------
def initialize(file_index, filename)
unless FileTest.exist?(filename)
image_name = 'FF9_SaveFileWindow_no_overlay'
else
image_name = 'FF9_SaveFileWindow'
end
@bg_image = Cache.picture(image_name)
@timer_image = Cache.picture(FFIXSave::TIMER_NAME)
@gold_image = Cache.picture(FFIXSave::GOLDCOIN_NAME)
super(file_index, filename)
self.x = 0
self.height = @bg_image.height + 32
# do this so that they images can be close together,
# ignoring the 16 pixel window border
self.y -= self.y * 0.1
self.contents.dispose
self.contents = Bitmap.new(512, @bg_image.height)
self.contents.font.name = FFIXSave::FONT_NAME
self.contents.font.size = FFIXSave::FONT_SIZE
self.opacity = 0
@arrow_sprite = Sprite.new
@arrow_sprite.bitmap = Cache.picture('pointer')
@arrow_sprite.x = 0
@arrow_sprite.y = self.y + (0.4 * self.height)
@arrow_sprite.visible = false
@arrow_sprite.z = self.z + 1
refresh
end
def selected=(value)
@arrow_sprite.visible = value
end
#--------------------------------------------------------------------------
# * refresh
#--------------------------------------------------------------------------
def refresh
self.contents.clear
self.contents.font.color = FFIXSave::FONT_COLOR
draw_background_panel
begin
if @file_exist
draw_party_characters
draw_playtime
draw_leader_name
draw_leader_level
draw_gold
draw_location
draw_icons
end
rescue
# do nothing or the game will crash
# before the first save file is created.
end
end
#--------------------------------------------------------------------------
# * draw_background_panel
#--------------------------------------------------------------------------
def draw_background_panel
self.contents.blt(0, 0, @bg_image, @bg_image.rect)
end
#--------------------------------------------------------------------------
# * draw_party_characters
#--------------------------------------------------------------------------
def draw_party_characters
x, y, size = 5, 5, (self.contents.height - 10)
for i in 0...@characters.size
name = @characters[i][2]
index = @characters[i][3]
draw_face(name, index, x, y, size)
x += size + 2
end
end
#--------------------------------------------------------------------------
# * draw_playtime
#--------------------------------------------------------------------------
def draw_playtime
hour = @total_sec / 60 / 60
min = @total_sec / 60 % 60
sec = @total_sec % 60
time_string = sprintf("%02d:%02d:%02d", hour, min, sec)
len = contents.text_size(time_string).width + 6
contents.font.color = FFIXSave::FONT_COLOR
contents.draw_text(contents.width - len, TEXT_LINE1_Y, contents.width - 4, WLH, time_string)
end
#--------------------------------------------------------------------------
# * draw_leader_name
#--------------------------------------------------------------------------
def draw_leader_name
name = @characters[0][4]
self.contents.draw_text(TEXT_COL1_X, TEXT_LINE1_Y, 128, WLH, name)
end
#--------------------------------------------------------------------------
# * draw_leader_level
#--------------------------------------------------------------------------
def draw_leader_level
level = "Level #{@characters[0][5]}"
self.contents.draw_text(TEXT_COL1_X, TEXT_LINE2_Y, 128, WLH, level)
end
#--------------------------------------------------------------------------
# * draw_gold
#--------------------------------------------------------------------------
def draw_gold
gold = "#{@characters[0][6]} #{Vocab.gold[0,1]}"
text_x = contents.width - (contents.text_size(gold).width + 6)
self.contents.draw_text(text_x, TEXT_LINE2_Y, 96, WLH, gold)
end
#--------------------------------------------------------------------------
# * draw_location
#--------------------------------------------------------------------------
def draw_location
loc = @characters[0][7]
text_y = TEXT_LINE2_Y + 28
self.contents.draw_text(308, text_y, contents.width - 312, WLH, loc)
end
#--------------------------------------------------------------------------
# * draw_icons
#--------------------------------------------------------------------------
def draw_icons
self.contents.blt(TEXT_COL1_X + 93 + FFIXSave::ICON_OFFSET_X,
TEXT_LINE1_Y + 7,
@timer_image,
@timer_image.rect)
self.contents.blt(TEXT_COL1_X + 93 + FFIXSave::ICON_OFFSET_X,
TEXT_LINE2_Y + 7,
@gold_image,
@gold_image.rect)
end
#--------------------------------------------------------------------------
# * update_cursor
#--------------------------------------------------------------------------
def update_cursor
# not needed
end
#--------------------------------------------------------------------------
# * dispose
#--------------------------------------------------------------------------
def dispose
super
@bg_image.dispose
@timer_image.dispose
@arrow_sprite.dispose
end
end
Compatibility / Bugs
Should be fine as there are not many mods for the save/load menu. Let me know of any bugs.











