#=============================================================================
# 改良存读档优化版 Ver 1.21
#-----------------------------------------------------------------------------
# By : RyanBern
#-----------------------------------------------------------------------------
# 功能说明:
# 在原始的存读档系统上做了一些加强,提供了一些扩展内容,界面比较简单,可以
# 试着在游戏中使用。
# 1.屏蔽了 Window_SaveFile,而选择了可以滚动的 Window_Selectable,存档数量
# 想存多少就存多少。
# 2.自动存档功能,这个是为一些没有存档意识的玩家写的系统,有了它系统就可以
# 按照一定规律自动保存进度,再也不用担心进度丢失了。存档的时机为主角切换
# 到另一个地图之后,可以通过更改下面的常量来控制。
# 3.存档覆盖提示功能,这个也是为一些粗心玩家设定的系统,当存储的位置已经有
# 了文件时,再存档会跳出覆盖提示,避免误操作。
#-----------------------------------------------------------------------------
# 另外里面的 Window_Selectable 是我扩展的内容,可以单独拿出来使用,扩展的功能
# 是可以指定每一行的行高,这样 Window_Selectable 滚动内容的行高就可以不必限制
# 为 32 了。
#-----------------------------------------------------------------------------
# 更新记录:
# 2014.12.25 精简代码,修复多了 end 的 BUG
# 2016.03.12 缓解 F12 冲突的问题
#=============================================================================
module RB
# 存档数最大值
SAVES_MAX = 20
# 如果打开12号开关,将不会自动存档
NO_AUTOSAVE = 12
# 禁止自动存档的地图ID数组,如果主角移动前的地图的ID在数组中
# 那么切换地图后将不会自动存档
NO_AUTOSAVE_MAPS = []
end
#=============================================================================
# ■ Window_Selectable
#-----------------------------------------------------------------------------
# 拥有光标的移动以及滚动功能的窗口类。
# RB 加强版,可以设定一行的高度
# 指定某一行的高度可以这样 Window_Selectable.new(0, 0, 120, 120, 54)
# 表示该窗口一行高度为 54,如果不写,默认为 32(原始默认高度)
#=============================================================================
class Window_Selectable < Window_Base
#--------------------------------------------------------------------------
# ● 定义实例变量
#--------------------------------------------------------------------------
attr_reader :index # 光标位置
attr_reader :help_window # 帮助窗口
attr_accessor :row_height # 行高
#--------------------------------------------------------------------------
# ● 初始画对像
# x : 窗口的 X 坐标
# y : 窗口的 Y 坐标
# width : 窗口的宽
# height : 窗口的高
#--------------------------------------------------------------------------
def initialize(x, y, width, height, row_height=32)
super(x, y, width, height)
@item_max = 1
@column_max = 1
@index = -1
@row_height = row_height
end
#--------------------------------------------------------------------------
# ● 获取开头行
#--------------------------------------------------------------------------
def top_row
# 将窗口内容的传送源 Y 坐标、1 行的高 @row_height 等分
return self.oy / @row_height
end
#--------------------------------------------------------------------------
# ● 设置开头行
# row : 显示开头的行
#--------------------------------------------------------------------------
def top_row=(row)
# row 未满 0 的场合更正为 0
if row < 0
row = 0
end
# row 超过 row_max - 1 的情况下更正为 row_max - 1
if row > row_max - 1
row = row_max - 1
end
# row 1 行高的 @row_height 倍、窗口内容的传送源 Y 坐标
self.oy = row * @row_height
end
#--------------------------------------------------------------------------
# ● 获取 1 页可以显示的行数
#--------------------------------------------------------------------------
def page_row_max
# 窗口的高度,设置画面的高度减去 32 ,除以 1 行的高度 @row_height
return (self.height - 32) / @row_height
end
#--------------------------------------------------------------------------
# ● 更新光标矩形
#--------------------------------------------------------------------------
def update_cursor_rect
# 光标位置不满 0 的情况下
if @index < 0
self.cursor_rect.empty
return
end
# 获取当前的行
row = @index / @column_max
# 当前行被显示开头行前面的情况下
if row < self.top_row
# 从当前行向开头行滚动
self.top_row = row
end
# 当前行被显示末尾行之后的情况下
if row > self.top_row + (self.page_row_max - 1)
# 从当前行向末尾滚动
self.top_row = row - (self.page_row_max - 1)
end
# 计算光标的宽
cursor_width = self.width / @column_max - 32
# 计算光标坐标
x = @index % @column_max * (cursor_width + 32)
y = @index / @column_max * @row_height - self.oy
# 更新光标矩形
self.cursor_rect.set(x, y, cursor_width, @row_height)
end
end
class Window_File < Window_Selectable
#--------------------------------------------------------------------------
# ● 初始化目标
#--------------------------------------------------------------------------
def initialize
super(0, 64, 640, 416 ,96)
@column_max = 1
case $scene
when Scene_Save
@item_max = RB::SAVES_MAX
when Scene_Load
@item_max = RB::SAVES_MAX + 1
end
refresh
self.index = 0
end
#--------------------------------------------------------------------------
# ● 刷新
#--------------------------------------------------------------------------
def refresh
if @item_max == 0
return
end
self.contents = Bitmap.new(width - 32, @item_max * 96)
self.contents.clear
for j in 0...@item_max
x = 64
y = j * 96
self.contents.font.color = normal_color
case $scene
when Scene_Save
name = "文件 #{j + 1}"
self.contents.draw_text(4, y, 600, 32, name)
@filename = "Save#{j + 1}.rxdata"
@name_width = contents.text_size(name).width
@time_stamp = Time.at(0)
@file_exist = FileTest.exist?(@filename)
when Scene_Load
if j == 0
name = "自动存档"
else
name = "文件 #{j}"
end
self.contents.draw_text(4, y, 600, 32, name)
@filename = "Save#{j}.rxdata"
@name_width = contents.text_size(name).width
@time_stamp = Time.at(0)
@file_exist = FileTest.exist?(@filename)
end
if @file_exist
file = File.open(@filename, "r")
@time_stamp = file.mtime
@characters = Marshal.load(file)
@frame_count = Marshal.load(file)
@game_system = Marshal.load(file)
@game_switches = Marshal.load(file)
@game_variables = Marshal.load(file)
@total_sec = @frame_count / Graphics.frame_rate
file.close
end
# 存档文件存在的情况下
if @file_exist
# 描绘角色
for i in 0...@characters.size
bitmap = RPG::Cache.character(@characters[i][0], @characters[i][1])
cw = bitmap.rect.width / 4
ch = bitmap.rect.height / 4
src_rect = Rect.new(0, 0, cw, ch)
x = 300 - @characters.size * 32 + i * 64 - cw / 2
self.contents.blt(x, 68 - ch + y, bitmap, src_rect)
end
# 描绘游戏时间
hour = @total_sec / 60 / 60
min = @total_sec / 60 % 60
sec = @total_sec % 60
time_string = sprintf("%02d:%02d:%02d", hour, min, sec)
self.contents.font.color = normal_color
self.contents.draw_text(4, 8 + y, 600, 32, time_string, 2)
# 描绘时间标记
self.contents.font.color = normal_color
time_string = @time_stamp.strftime("%Y/%m/%d %H:%M")
self.contents.draw_text(4, 40 + y, 600, 32, time_string, 2)
end
end
end
end
#==============================================================================
# ■ Window_ReplaceConfirm
#------------------------------------------------------------------------------
# 确认是否覆盖原始存档的窗口
#==============================================================================
class Window_ReplaceConfirm < Window_Selectable
#--------------------------------------------------------------------------
# ● 初始化对像
#--------------------------------------------------------------------------
def initialize
super(160, 176, 320, 128)
self.contents = Bitmap.new(width - 32, height - 32)
@item_max = 2
@column_max = 2
@commands = ["是的", "不要"]
refresh
self.index = 0
end
#--------------------------------------------------------------------------
# ● 刷新
#--------------------------------------------------------------------------
def refresh
self.contents.clear
self.contents.font.color = system_color
self.contents.draw_text(4, 0, 328, 32, "当前位置已经存在存档文件")
self.contents.draw_text(4, 32, 328, 32, "确认要覆盖当前存档文件吗?")
self.contents.font.color = normal_color
for i in 0...@item_max
draw_item(i)
end
end
#--------------------------------------------------------------------------
# ● 描绘项目
# index : 项目编号
#--------------------------------------------------------------------------
def draw_item(index)
x = 4 + index * 144
self.contents.draw_text(x, 64, 128, 32, @commands[index])
end
def update_cursor_rect
if @index < 0
self.cursor_rect.empty
else
self.cursor_rect.set(@index * 144, 64, (self.width - 32) / 2, 32)
end
end
end
#==============================================================================
# ■ Scene_File
#------------------------------------------------------------------------------
# 存档画面及读档画面的超级类。
#==============================================================================
class Scene_File
#--------------------------------------------------------------------------
# ● 主处理
#--------------------------------------------------------------------------
def main
# 生成帮助窗口
@help_window = Window_Help.new
@help_window.set_text(@help_text)
@file_window = Window_File.new
@file_window.index = $game_temp.last_file_index
@file_window.active = true
@confirm_window = Window_ReplaceConfirm.new
@confirm_window.active = false
@confirm_window.visible = false
@confirm_window.z = @file_window.z + 10
# 执行过渡
Graphics.transition
# 主循环
loop do
# 刷新游戏画面
Graphics.update
# 刷新输入信息
Input.update
# 刷新画面
update
# 如果画面被切换的话就中断循环
if $scene != self
break
end
end
# 准备过渡
Graphics.freeze
# 释放窗口
@help_window.dispose
@file_window.dispose
@confirm_window.dispose
end
#--------------------------------------------------------------------------
# ● 刷新画面
#--------------------------------------------------------------------------
def update
# 刷新窗口
@help_window.update
@file_window.update
@confirm_window.update
case $scene
when Scene_Save
if @file_window.active
update_file
return
end
if @confirm_window.active
update_confirm
return
end
when Scene_Load
# 按下 C 键的情况下
if Input.trigger?(Input::C)
# 调用过程 on_decision (定义继承目标)
on_decision(make_filename_load(@file_window.index))
$game_temp.last_file_index = @file_window.index
return
end
# 按下 B 键的情况下
if Input.trigger?(Input::B)
# 调用过程 on_cancel (定义继承目标)
on_cancel
return
end
end
end
#--------------------------------------------------------------------------
# ● 生成文件名
# file_index : 文件名的索引 (0~3)
#--------------------------------------------------------------------------
def make_filename(file_index)
return "Save#{file_index + 1}.rxdata"
end
def make_filename_load(file_index)
return "Save#{file_index}.rxdata"
end
end
class Scene_Save < Scene_File
#--------------------------------------------------------------------------
# ● 初始化对像
#--------------------------------------------------------------------------
def initialize
super("要储存到哪个文件?")
end
def update_file
filename = make_filename(@file_window.index)
if Input.trigger?(Input::C)
$game_system.se_play($data_system.decision_se)
if FileTest.exist?(filename)
@file_window.active = false
@confirm_window.active = true
@confirm_window.visible = true
else
# 演奏存档 SE
$game_system.se_play($data_system.save_se)
# 写入存档数据
file = File.open(filename, "wb")
write_save_data(file)
file.close
# 如果被事件调用
if $game_temp.save_calling
# 清除存档调用标志
$game_temp.save_calling = false
# 切换到地图画面
$scene = Scene_Map.new
return
end
# 切换到菜单画面
$scene = Scene_Menu.new(4)
end
end
if Input.trigger?(Input::B)
# 演奏取消 SE
$game_system.se_play($data_system.cancel_se)
# 如果被事件调用
if $game_temp.save_calling
# 清除存档调用标志
$game_temp.save_calling = false
# 切换到地图画面
$scene = Scene_Map.new
return
end
# 切换到菜单画面
$scene = Scene_Menu.new(4)
end
end
def update_confirm
filename = make_filename(@file_window.index)
if Input.trigger?(Input::C)
case @confirm_window.index
when 0
# 演奏存档 SE
$game_system.se_play($data_system.save_se)
# 写入存档数据
file = File.open(filename, "wb")
write_save_data(file)
file.close
# 如果被事件调用
if $game_temp.save_calling
# 清除存档调用标志
$game_temp.save_calling = false
# 切换到地图画面
$scene = Scene_Map.new
return
end
# 切换到菜单画面
$scene = Scene_Menu.new(4)
when 1
$game_system.se_play($data_system.cancel_se)
@file_window.active = true
@confirm_window.active = false
@confirm_window.visible = false
end
end
if Input.trigger?(Input::B)
$game_system.se_play($data_system.cancel_se)
@file_window.active = true
@confirm_window.active = false
@confirm_window.visible = false
end
end
def autosave
filename = "Save0.rxdata"
file = File.open(filename, "wb")
write_save_data(file)
file.close
end
end
class Scene_Load < Scene_File
#--------------------------------------------------------------------------
# ● 初始化对像
#--------------------------------------------------------------------------
def initialize
# 再生成临时对像
$game_temp = Game_Temp.new
# 选择存档时间最新的文件
$game_temp.last_file_index = 0
latest_time = Time.at(0)
for i in 0..RB::SAVES_MAX
filename = make_filename_load(i)
if FileTest.exist?(filename)
file = File.open(filename, "r")
if file.mtime > latest_time
latest_time = file.mtime
$game_temp.last_file_index = i
end
file.close
end
end
super("要载入哪个文件?")
end
end
class Scene_Map
unless method_defined? :rb_transfer_player_20141225
alias rb_transfer_player_20141225 transfer_player
def transfer_player
rb_transfer_player_20141225
unless RB::NO_AUTOSAVE_MAPS.include?($game_map.map_id) || $game_switches[RB::NO_AUTOSAVE]
save = Scene_Save.new
save.autosave
end
end
end
end