查看: 6976|回复: 2

[整合系统] 【RMXP】免dll截图存档+覆盖存档提示

[复制链接]

路人党员

梦石
0
星屑
52
在线时间
2276 小时
注册时间
2010-12-30
回帖
3011
发表于 2014-3-13 01:30:38 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 英顺的马甲 于 2016-7-31 02:24 编辑

[20160731]抛弃禁术,改用移花接木
[20160723]改善禁术,减少冲突率
[20160617]调整了禁术,解决了会因插入脚本位置而导致的潜在bug
[20160608]稍微调整了API,解决了一些潜在的BUG

此东西乃无聊产物,整合了以下这两只脚本==
免dll截图存档 by lim前辈
覆盖存档提示 by 佚名

整合后的新特色:
  • 人性化的设置
  • 减少并尽量避免重定义
  • 移花接木


整合这货的主要目的是学习交流,与其他脚本有任何冲突本人一概不理==

[pre lang="ruby"]#-----------------------------------------------------------------------------#
# 脚本设定                                                                    #
#-----------------------------------------------------------------------------#
# 说明:                                                                      #
# ①主要采用了Snap To Bitmap这脚本省去了DLL。                                  #
# ②采用了禁术,各位不用到处改脚本了,不过冲突率也大增==                       #
# ③脚本当中采用的脚本归原作者所有。                                           #
# ④设置区域的变量请自行更改。                                                 #
# ⑤调整了截图的API,解决一些潜在的截图BUG。                                   #
#-----------------------------------------------------------------------------#
SaveSettings = Struct.new(:max_save_file,:save_file_dir,:slot_name_format,
:save_help,:load_help,:override_question,:snap_no_found,:no_save_file).new
#-----------------------------------------------------------------------------#
# 设置区域
#-----------------------------------------------------------------------------#
# 最大的存档量
SaveSettings.max_save_file = 20
# 存档所在文件夹
SaveSettings.save_file_dir = "Save/"
# 存档槽的名称的格式,具体请参考sprintf
SaveSettings.slot_name_format = "%03d号存档"
# 存档界面所显示的字符串
SaveSettings.save_help = "要保存哪号存档呢?"
# 读档界面所显示的字符串
SaveSettings.load_help = "要读取哪号存档呢?"
# 需要覆盖存档时所发问的问题
SaveSettings.override_question = "存档已在,是否覆盖?新档替代,莫能重来。"
# 找不到截图时的信息
SaveSettings.snap_no_found = "哎哟,截图找不到啦!你都干了啥啊?"
# 存档槽没有存档时显示的信息
SaveSettings.no_save_file = "这里还没存档喔"

#-----------------------------------------------------------------------------#
# 创建存档文件夹                                                              #
#-----------------------------------------------------------------------------#
Dir.mkdir(SaveSettings.save_file_dir) unless FileTest.directory?(SaveSettings.save_file_dir)

#-----------------------------------------------------------------------------#
# 游戏核心处理类                                                              #
#-----------------------------------------------------------------------------#
class Game_System
  #---------------------------------------------------------------------------#
  # 实例变量                                                                  #
  #---------------------------------------------------------------------------#
  attr_accessor :snap_bitmap
end

#-----------------------------------------------------------------------------#
# 强改存档界面                                                                #
#-----------------------------------------------------------------------------#
class Scene_Save
  def self.new(*a)
    return Scene_Save_Load.new
  end
end

#-----------------------------------------------------------------------------#
# 强改读档界面                                                                #
#-----------------------------------------------------------------------------#
class Scene_Load
  def self.new(*a)
    return Scene_Save_Load.new(1)
  end
end

#-----------------------------------------------------------------------------#
# 地图界面钩子                                                                #
#-----------------------------------------------------------------------------#
Scene_Map = Class.new(Scene_Map) {
  superclass.constants.each{|c|const_set(c, superclass.const_get(c))}
  const_set(:NoDllSLHook, true)
  define_method(:main) do
    super
    $game_system.snap_bitmap = Graphics.snap_to_bitmap if $scene.is_a?(Scene_Menu)
  end
} unless Scene_Map.const_defined?(:NoDllSLHook)

#-----------------------------------------------------------------------------#
# 移花接木大法                                                                #
#-----------------------------------------------------------------------------#
class Scene_Title
  attr_accessor :command_window, :continue_enabled
  class Window_Command < ::Window_Command
    def disable_item(*a)
      super(*a) unless $scene.any_save? or a[0] != 1
    end
  end
  module Graphics
    def self.transition(*a)
      $scene.command_window.index = ($scene.continue_enabled = $scene.any_save?) ? 1 : 0
      begin
        ::Graphics.transition(*a)
      rescue Exception
        raise $!.class, $!.message, caller
      end
    end
    def self.method_missing(m, *args, &block)
      begin
        ::Graphics.send(m, *args, &block)
      rescue Exception
        raise $!.class, $!.message, caller
      end
    end
  end
  def any_save?
    for i in 1..SaveSettings.max_save_file
      return true if File.exist?(SaveSettings.save_file_dir + "Save#{i}.rxdata")
    end
    return false
  end
end

#-----------------------------------------------------------------------------#
# 存档界面用窗口                                                              #
#-----------------------------------------------------------------------------#
class Window_Save < Window_Base
  #---------------------------------------------------------------------------#
  # 初始化                                                                    #
  #---------------------------------------------------------------------------#
  def initialize
    super(160,64,480,416)
    self.contents = Bitmap.new(width-32,height-32)
    @_index = -1
    refresh(0)
  end
  #---------------------------------------------------------------------------#
  # 刷新                                                                      #
  #---------------------------------------------------------------------------#
  def refresh(index)
    if index != @_index
      # 清除原有资料
      self.contents.clear
      @sprite.dispose if @sprite != nil
      @sprite = nil
      # 开始读取资料
      @file_index = index
      @filename = SaveSettings.save_file_dir + "Save#{@file_index + 1}.rxdata"
      @time_stamp = Time.at(0)
      @file_exist = FileTest.exist?(@filename)
      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)
        @game_self_switches = Marshal.load(file)
        @game_screen        = Marshal.load(file)
        @game_actors        = Marshal.load(file)
        @game_party         = Marshal.load(file)
        @game_troop         = Marshal.load(file)
        @game_map           = Marshal.load(file)
        @game_player        = Marshal.load(file)
        @bitmap             = @game_system.snap_bitmap
        @total_sec = @frame_count / Graphics.frame_rate
        file.close
        # 描绘截图的框与影子
        self.contents.fill_rect(34,11,400,300,Color.new(0,0,0))
        self.contents.fill_rect(23,0,402,302,system_color)
        self.contents.fill_rect(24,1,400,300,Color.new(0,0,0,0))
        # 如截图不为空的话
        if @bitmap != nil
          # 描绘截图
          @sprite = Sprite.new
          @sprite.bitmap = @bitmap
          @sprite.x = 160+16+24
          @sprite.y = 64+16+1
          @sprite.zoom_x = @sprite.zoom_y = 0.625
          @sprite.z = 99999
        else
          self.contents.draw_text(0,96,448,32,SaveSettings.snap_no_found,1)
        end
        # 描绘角色
        @characters.size.times do |i|
          x = i*56 + 24
          bitmap = RPG::Cache.character(@characters[0], @characters[1])
          self.contents.blt(x,328,bitmap,Rect.new(0,0,bitmap.width/4,bitmap.height/4))
        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(0,320,448,32,time_string,2)
        # 描绘时间标记
        self.contents.font.color = normal_color
        time_string = @time_stamp.strftime("%Y/%m/%d %H:%M")
        self.contents.draw_text(0,352,448,32,time_string, 2)
      else
        self.contents.draw_text(0,320,448,32,SaveSettings.no_save_file,1)
      end
      @_index = index
    end
  end
  #---------------------------------------------------------------------------#
  # 释放                                                                      #
  #---------------------------------------------------------------------------#
  def dispose
    super
    @sprite.dispose if @sprite != nil
    @sprite = nil
  end
end

#-----------------------------------------------------------------------------#
# 存档界面                                                                    #
#-----------------------------------------------------------------------------#
class Scene_Save_Load
  #---------------------------------------------------------------------------#
  # 初始化                                                                    #
  #---------------------------------------------------------------------------#
  def initialize(type=0)
    @type = type == 0 ? "save" : "load"
    case @type
    when "save"
      @word = SaveSettings.save_help
    when "load"
      @word = SaveSettings.load_help
      $game_temp = Game_Temp.new
    end
  end
  #---------------------------------------------------------------------------#
  # 主执行                                                                    #
  #---------------------------------------------------------------------------#
  def main
    # 生成帮助窗口
    @help_window = Window_Help.new
    @help_window.set_text(@word)
    # 选项窗口
    choose = []
    for i in 1..SaveSettings.max_save_file
      choose.push(SaveSettings.slot_name_format % i)
    end
    @command_window = Window_Command.new(160,choose)
    @command_window.height = 416
    @command_window.y = 64
    @command_window.active = true
    # 存档资料窗口
    @save_window = Window_Save.new
    # 确认窗口
    @confirm_window = Window_Base.new(80,176,480,128)
    @confirm_window.contents = Bitmap.new(448,96)
    @confirm_window.contents.draw_text(0,0,448,32,SaveSettings.override_question,1)
    @confirm_window.contents.draw_text(0,64,216,32,"确定",1)
    @confirm_window.contents.draw_text(232,64,216,32,"取消",1)
    @confirm_window.cursor_rect.set(232,64,216,32)
    @confirm_window.z = 999999
    @confirm_window.visible = false
    @confirm_ok = false
    # 执行过渡
    Graphics.transition
    # 主循环
    loop do
      # 刷新游戏画面
      Graphics.update
      # 刷新输入信息
      Input.update
      # 刷新画面
      update
      # 如果画面切换的话的就中断循环
      if $scene != self
        break
      end
    end
    # 准备过渡
    Graphics.freeze
    # 释放窗口
    @help_window.dispose
    @save_window.dispose
    @command_window.dispose
    @confirm_window.dispose
  end
  #---------------------------------------------------------------------------#
  # 刷新                                                                      #
  #---------------------------------------------------------------------------#
  def update
    # 选择存档窗口激活
    unless @confirm_window.visible
      update_command
    else
      update_confirm
    end
  end
  #---------------------------------------------------------------------------#
  # 选择存档窗口刷新                                                          #
  #---------------------------------------------------------------------------#
  def update_command
    @command_window.update
    @save_window.refresh(@command_window.index)
    # 按下取消键
    if Input.trigger?(Input::B)
      case @type
      when "save"
        # 演奏取消 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)
      when "load"
        # 演奏取消 SE
        $game_system.se_play($data_system.cancel_se)
        # 切换到标题画面
        $scene = Scene_Title.new
      end
    end
    # 按下确定键
    if Input.trigger?(Input::C)
      # 计算存档编号
      filename = SaveSettings.save_file_dir + "Save#{@command_window.index+1}.rxdata"
      case @type
      when "save"
        if File.exist?(filename)
          @confirm_window.visible = true
          @confirm_window.cursor_rect.set(232,64,216,32)
          @confirm_ok = false
        else
          process_save
        end
      when "load"
        # 文件不存在的情况下
        unless FileTest.exist?(filename)
          # 演奏冻结 SE
          $game_system.se_play($data_system.buzzer_se)
          return
        end
        # 演奏读档 SE
        $game_system.se_play($data_system.load_se)
        # 写入存档数据
        file = File.open(filename, "rb")
        read_save_data(file)
        file.close
        # 还原 BGM、BGS
        $game_system.bgm_play($game_system.playing_bgm)
        $game_system.bgs_play($game_system.playing_bgs)
        # 刷新地图 (执行并行事件)
        $game_map.update
        # 切换到地图画面
        $scene = Scene_Map.new
      end
    end
  end
  #---------------------------------------------------------------------------#
  # 确认窗口刷新                                                              #
  #---------------------------------------------------------------------------#
  def update_confirm
    @confirm_window.update
    if Input.trigger?(Input::LEFT) or Input.trigger?(Input::UP)
      $game_system.se_play($data_system.cursor_se)
      @confirm_ok = true
    elsif Input.trigger?(Input::RIGHT) or Input.trigger?(Input::DOWN)
      $game_system.se_play($data_system.cursor_se)
      @confirm_ok = false
    elsif Input.trigger?(Input::C)
      $game_system.se_play($data_system.decision_se)
      process_save if @confirm_ok
      @confirm_window.visible = false
    elsif Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      @confirm_window.visible = false
    end
    @confirm_window.cursor_rect.set(232,64,216,32)
    @confirm_window.cursor_rect.set(0,64,216,32) if @confirm_ok
  end
  #---------------------------------------------------------------------------#
  # 执行存档                                                                  #
  #---------------------------------------------------------------------------#
  def process_save
    # 计算存档编号
    filename = SaveSettings.save_file_dir + "Save#{@command_window.index+1}.rxdata"
    # 演奏存档 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
  #---------------------------------------------------------------------------#
  # 存档                                                                      #
  #---------------------------------------------------------------------------#
  def write_save_data(file)
    # 生成描绘存档文件用的角色图形
    characters = []
    for i in 0...$game_party.actors.size
      actor = $game_party.actors
      characters.push([actor.character_name, actor.character_hue])
    end
    # 写入描绘存档文件用的角色数据
    Marshal.dump(characters, file)
    # 写入测量游戏时间用画面计数
    Marshal.dump(Graphics.frame_count, file)
    # 增加 1 次存档次数
    $game_system.save_count += 1
    # 保存魔法编号
    # (将编辑器保存的值以随机值替换)
    $game_system.magic_number = $data_system.magic_number
    # 写入各种游戏对像
    Marshal.dump($game_system, file)
    Marshal.dump($game_switches, file)
    Marshal.dump($game_variables, file)
    Marshal.dump($game_self_switches, file)
    Marshal.dump($game_screen, 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
  #---------------------------------------------------------------------------#
  # 读档                                                                      #
  #---------------------------------------------------------------------------#
  def read_save_data(file)
    # 读取描绘存档文件用的角色数据
    characters = Marshal.load(file)
    # 读取测量游戏时间用画面计数
    Graphics.frame_count = Marshal.load(file)
    # 读取各种游戏对像
    $game_system        = Marshal.load(file)
    $game_switches      = Marshal.load(file)
    $game_variables     = Marshal.load(file)
    $game_self_switches = Marshal.load(file)
    $game_screen        = Marshal.load(file)
    $game_actors        = Marshal.load(file)
    $game_party         = Marshal.load(file)
    $game_troop         = Marshal.load(file)
    $game_map           = Marshal.load(file)
    $game_player        = Marshal.load(file)
    # 魔法编号与保存时有差异的情况下
    # (加入编辑器的编辑过的数据)
    if $game_system.magic_number != $data_system.magic_number
      # 重新装载地图
      $game_map.setup($game_map.map_id)
      $game_player.center($game_player.x, $game_player.y)
    end
    # 刷新同伴成员
    $game_party.refresh
  end
end

#-----------------------------------------------------------------------------#
# Graphics.snap_to_bitmap(优化版)                                           #
# 作者:神思                                                                  #
# 优化:釣到一隻猴子@_@  (  AAM@_@  )                                         #
#-----------------------------------------------------------------------------#
#-----------------------------------------------------------------------------#
# Graphics                                                                    #
#-----------------------------------------------------------------------------#
class << Graphics
  SRCCOPY = 0xCC0020
  BitBlt = Win32API.new("gdi32", "BitBlt", "lllllllll", "l")
  CreateCompatibleBitmap = Win32API.new("gdi32", "CreateCompatibleBitmap", "lll", "l")
  CreateCompatibleDC = Win32API.new("gdi32", "CreateCompatibleDC", "l", "l")
  DeleteDC = Win32API.new("gdi32", "DeleteDC", "l", "l")
  DeleteObject = Win32API.new("gdi32", "DeleteObject", "l", "l")
  SelectObject = Win32API.new("gdi32", "SelectObject", "ll", "l")
  GetBitmapBits = Win32API.new("gdi32", "GetBitmapBits", "llp", "l")
  
  GetWindowDC = Win32API.new("user32", "GetWindowDC", "l", "l")
  ReleaseDC = Win32API.new("user32", "ReleaseDC", "ll", "l")
  GetWindowRect = Win32API.new("user32", "GetWindowRect", "lp", "l")
  GetClientRect = Win32API.new("user32", "GetClientRect", "lp", "l")
  ClientToScreen = Win32API.new("user32", "ClientToScreen", "ip", "i")
  
  RtlMoveMemory = Win32API.new('kernel32', 'RtlMoveMemory', 'ipi', 'i')
  HWnd = Win32API.new("user32", "GetActiveWindow", nil, 'l').call
  #---------------------------------------------------------------------------#
  # snap_to_bitmap                                                            #
  #---------------------------------------------------------------------------#
  def snap_to_bitmap
    width, height, rgbs = self.bitmap_data
    hb = Bitmap.new(width, height)
    len = width * 4
    ptr = [rgbs].pack("p").unpack("l")[0]
    ptrs = []
    height.times{|i| ptrs[height -1 -i] = ptr + (len * i)}
    pdest = hb.address
    height.times do |y|
      RtlMoveMemory.call(pdest, ptrs[y], len)
      pdest += len
    end
    return hb
  end
  #---------------------------------------------------------------------------#
  # bitmap_data                                                               #
  #---------------------------------------------------------------------------#
  def bitmap_data
    hdcSrc = GetWindowDC.call(HWnd)
    wrect = [0,0,0,0].pack("l*")
    crect = wrect.clone
    point = [0, 0].pack("l*")
    GetWindowRect.call(HWnd, wrect)
    GetClientRect.call(HWnd, crect)
    ClientToScreen.call(HWnd, point)
    wrect = wrect.unpack("l*")
    crect = crect.unpack("l*")
    point = point.unpack("l*")
    wrect[0] -= point[0]
    wrect[1] -= point[1]
    hdcDest = CreateCompatibleDC.call(hdcSrc)
    hBitmap = CreateCompatibleBitmap.call(hdcSrc, crect[2], crect[3])
    hOld = SelectObject.call(hdcDest, hBitmap)
    BitBlt.call(hdcDest, wrect[0], wrect[1], crect[2] - wrect[0], crect[3] - wrect[1], hdcSrc, 0, 0, SRCCOPY)
    SelectObject.call(hdcDest, hOld)
    DeleteDC.call(hdcDest)
    ReleaseDC.call(HWnd, hdcSrc)
    dwCount = crect[2] * crect[3]
    lpBits = ([0] * dwCount).pack("l*")
    GetBitmapBits.call(hBitmap, dwCount * 4, lpBits)
    DeleteObject.call(hBitmap)
    return crect[2], crect[3], lpBits
  end
end

#-----------------------------------------------------------------------------#
# Font                                                                        #
#-----------------------------------------------------------------------------#
class Font
  #---------------------------------------------------------------------------#
  # marshal_dump                                                              #
  #---------------------------------------------------------------------------#
  def marshal_dump
  end
  #---------------------------------------------------------------------------#
  # marshal_load                                                              #
  #---------------------------------------------------------------------------#
  def marshal_load(obj)
  end
end

#-----------------------------------------------------------------------------#
# Bitmap                                                                      #
#-----------------------------------------------------------------------------#
class Bitmap
  RtlMoveMemory = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
  # 传送到内存的API函数
  RtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
  RtlMoveMemory_ip = Win32API.new('kernel32', 'RtlMoveMemory', 'ipi', 'i')
  #---------------------------------------------------------------------------#
  # _dump                                                                     #
  #---------------------------------------------------------------------------#
  def _dump(limit)
    data = "rgba" * width * height
    RtlMoveMemory_pi.call(data, address, data.length)
    [width, height, Zlib::Deflate.deflate(data)].pack("LLa*") # 压缩
  end
  #---------------------------------------------------------------------------#
  # _load                                                                     #
  #---------------------------------------------------------------------------#
  def self._load(str)
    w, h, zdata = str.unpack("LLa*")
    b = self.new(w, h)
    RtlMoveMemory_ip.call(b.address, Zlib::Inflate.inflate(zdata), w * h * 4)
    return b
  end
  #---------------------------------------------------------------------------#
  # address                                                                   #
  #---------------------------------------------------------------------------#
  def address
    # [[[bitmap.object_id * 2 + 16] + 8] + 16] == 数据的开头
    buffer, ad = "xxxx", object_id * 2 + 16
    RtlMoveMemory.call(buffer, ad, 4)
    ad = buffer.unpack("L")[0] + 8
    RtlMoveMemory.call(buffer, ad, 4)
    ad = buffer.unpack("L")[0] + 16
    RtlMoveMemory.call(buffer, ad, 4)
    return buffer.unpack("L")[0]
  end
end[/pre]

由于只是技术交流用,因此不提供截图,所以截图请自行补脑==

评分

参与人数 2星屑 +65 收起 理由
daosi + 20 感谢更新
gonglinyuan + 45 技术贴竟然没人塞糖,真是心寒…….

查看全部评分

本人擅长XP,如果有脚本或者Ruby方面的问题欢迎发电邮到engshun@1ppl.me咨询,本人很少检查电邮所以不一定会及时回复,本人不会直接出手解决问题只会提供一个方向,所以谢绝伸手党

老黄鸡

梦石
4
星屑
45692
在线时间
7947 小时
注册时间
2009-7-6
回帖
13335

MZ评测员RM创作大赛01组委会开拓者贵宾

发表于 2014-5-15 10:35:16 | 显示全部楼层
不同屏幕分辨率比如16位色和32位色一个像素所占字节数不同,所以这个脚本只能在32位色下正常工作。

点评

额,这就不关我的事了==  发表于 2014-5-15 10:36
RGDirect - DirectX驱动的RGSS,点我了解.
(排满,暂停)RM全系列成套系统定制请联系QQ1213237796
不接受对其他插件维护的委托
回复

使用道具 举报

梦石
0
星屑
50
在线时间
28 小时
注册时间
2016-9-30
回帖
9
发表于 2016-12-16 23:52:47 | 显示全部楼层
额,看起来还不错
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部