From 07dd85c6fa491ff1da04d8fdf2166794c6a799ef Mon Sep 17 00:00:00 2001 From: bakustarver <66978329+bakustarver@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:17:57 +0200 Subject: [PATCH] Add files via upload --- mkxp-z/Kawariki-patches/README.md | 129 + mkxp-z/Kawariki-patches/libs/PreloadIni.rb | 439 +++ mkxp-z/Kawariki-patches/libs/Win32API.rb | 130 + mkxp-z/Kawariki-patches/libs/Win32APIl.rb | 346 +++ .../libs/XP_TilemapOverrideLib.rb | 136 + mkxp-z/Kawariki-patches/libs/ruby18.rb | 71 + mkxp-z/Kawariki-patches/patches.rb | 182 ++ mkxp-z/Kawariki-patches/ports/BasicMouse.rb | 158 + mkxp-z/Kawariki-patches/ports/CRDJ_Input.rb | 452 +++ .../ports/Glitchfinder_Keyboard_Stub.rb | 254 ++ mkxp-z/Kawariki-patches/ports/Mouse.rb | 1052 +++++++ .../ports/TH_EventTriggerLabels.rb | 315 ++ .../ports/TH_SimpleAudioEncryption.rb | 110 + .../ports/XP_CustomResolution.rb | 815 +++++ .../ports/Zeus_Fullscreen++.rb | 61 + .../ports/Zeus_Map_Effects.rb | 530 ++++ mkxp-z/Kawariki-patches/ports/achievements.rb | 386 +++ mkxp-z/Kawariki-patches/ports/debug/basewt.rb | 613 ++++ .../ports/debug/savebitmanwin32api.rb | 2723 +++++++++++++++++ mkxp-z/Kawariki-patches/ports/testencode.rb | 1078 +++++++ mkxp-z/Kawariki-patches/ports/wxexittest.rb | 191 ++ mkxp-z/Kawariki-patches/preload.rb | 602 ++++ mkxp-z/Kawariki-patches/versions.json | 31 + mkxp-z/mkxp.json | 529 ++++ 24 files changed, 11333 insertions(+) create mode 100644 mkxp-z/Kawariki-patches/README.md create mode 100644 mkxp-z/Kawariki-patches/libs/PreloadIni.rb create mode 100644 mkxp-z/Kawariki-patches/libs/Win32API.rb create mode 100644 mkxp-z/Kawariki-patches/libs/Win32APIl.rb create mode 100644 mkxp-z/Kawariki-patches/libs/XP_TilemapOverrideLib.rb create mode 100644 mkxp-z/Kawariki-patches/libs/ruby18.rb create mode 100644 mkxp-z/Kawariki-patches/patches.rb create mode 100644 mkxp-z/Kawariki-patches/ports/BasicMouse.rb create mode 100644 mkxp-z/Kawariki-patches/ports/CRDJ_Input.rb create mode 100644 mkxp-z/Kawariki-patches/ports/Glitchfinder_Keyboard_Stub.rb create mode 100644 mkxp-z/Kawariki-patches/ports/Mouse.rb create mode 100644 mkxp-z/Kawariki-patches/ports/TH_EventTriggerLabels.rb create mode 100644 mkxp-z/Kawariki-patches/ports/TH_SimpleAudioEncryption.rb create mode 100644 mkxp-z/Kawariki-patches/ports/XP_CustomResolution.rb create mode 100644 mkxp-z/Kawariki-patches/ports/Zeus_Fullscreen++.rb create mode 100644 mkxp-z/Kawariki-patches/ports/Zeus_Map_Effects.rb create mode 100644 mkxp-z/Kawariki-patches/ports/achievements.rb create mode 100644 mkxp-z/Kawariki-patches/ports/debug/basewt.rb create mode 100644 mkxp-z/Kawariki-patches/ports/debug/savebitmanwin32api.rb create mode 100644 mkxp-z/Kawariki-patches/ports/testencode.rb create mode 100644 mkxp-z/Kawariki-patches/ports/wxexittest.rb create mode 100644 mkxp-z/Kawariki-patches/preload.rb create mode 100644 mkxp-z/Kawariki-patches/versions.json create mode 100644 mkxp-z/mkxp.json diff --git a/mkxp-z/Kawariki-patches/README.md b/mkxp-z/Kawariki-patches/README.md new file mode 100644 index 0000000..a3ec7b2 --- /dev/null +++ b/mkxp-z/Kawariki-patches/README.md @@ -0,0 +1,129 @@ +Kawariki MKXP runtime +===================== + +The MKXP runtime handles games based on the `Ruby Game Scripting System`, RGSS. + +The RPG Maker editions based on RGSS are: +- RPGMaker XP (RGSS1) +- RPGMaker VX (RGSS2) +- RPGMaker VX Ace (RGSS3) + +[Official RPGMaker Website][rpgmakerweb] + +MKXP-Z +------ + +The specific implementation used in Kawariki is [mkxp-z][mkxp-z] by Roza/Struma, +itself based on the original [mkxp][mkxp-github] by Ancurio. + +Currently, a [repackaged distribution][mkxp-z-repack] of the [official releases][mkxp-z-releases] are used. +This is required mostly for ease of automatic downloading in Kawariki. + +Links: [mkxp-z GitHub][mkxp-z-github] + +Configuration +------------- + +### mkxp.json + +The mkxp-z configuration is merged from a number of files in order: +- `/mkxp/mkxp.json`: Global mkxp config +- `/kawariki-mkxp.json`: Overrides specific for this game + +See the [default included with mkxp-z for reference][mkxp-z-config] +Note that the runtime doesn't understand JSON comments for now, +unlike mkxp-z itself. + +#### RTPs +The situation with RTPs is not optimal at the moment, but they +can be specified globally in `/mkxp/mkxp.json`: +```json +{ + "RTP": ["/path/to/rtp"] +} +``` + +### Environment Variables +- `KAWARIKI_MKXP_DUMP_SCRIPTS=`: Dump scripts to before applying patches +- `KAWARIKI_MKXP_DUMP_PATCHED_SCRIPTS=`: Dump scripts to after applying patches +- `KAWARIKI_MKXP_FILTER_SCRIPTS=[,...]`: Blacklist scripts/plugins by name +- `KAWARIKI_MKXP_DRY_RUN=1`: Exit after patching scripts, don't run game +- `KAWARIKI_MKXP_NO_FONT_EFFECTS=1`: Disable all font effects. See relevant patch in `pathces.rb` +- `KAWARIKI_NO_OVERLAYNS=1` Disallow usage of overlayns-static + +### versions.json +This file specifies the mkxp-z versions known to the runtime: +- `variant` *required string* Must be `"mkxp-z"` for now +- `version` *required array-of-numbers* The version of this distribution (e.g. `[2,3,0]`) +- `dist` *required string* The name of the directory the distribution is stored in. +- `dist_url` *optional string* An URL to download the distribution from if not already available. It must point to a gzip/bzip2/xz compressed tar-archive. +- `name` *optional string* An optional name given to the distribution. Defaults to the value of `dist`. + +RGSS Plugins +------------ + +All functionality in RGSS-based games is derived from the core scripts +of it's respective RPGMaker edition and extended by usually rather large +numbers of third-party engine plugins. + +Unfortunately, as a consequence of RGSS being a rather simplistic engine +and also being Windows-only, a lot of these plugins rely on assumptions +that don't hold true on Linux (though MKXP already implements case-insensitive +path lookups) or depend on the Win32 API or other (possibly custom) native +Windows libraries by way of a FFI (`Win32API` in RGSS). +As such, the runtime must apply a considerable amount of patches to a game +to allow it to run on Linux/MXKP-Z. + +### Ports + +Ports are modifications or re-implementations of third-party plugins to make +them work on Linux/MKXP-Z. They are contained in the `ports/` directory. + +All ports retain their original license terms. If you are the original author +of one of the included scripts and want it removed, please open an issue on GitHub. + +### Patches + +A patch tries to identify a third-party plugin and then either modify it's code +or outright replace the plugin with a port from `ports/`. Patches are defined +in [`patches.rb`](patches.rb) + +### Win32API Stubs +Win32API stubs are implemented in [`libs/Win32API.rb`](libs/Win32API.rb). +They are automatically loaded if any reference to Win32API is found in plugin +code after all patches are applied. + +It is usually easier to port a heavily Win32API-dependent plugin instead of +trying to re-implement the relevant Win32 APIs. + +Currently, implementations are included for these common APIs: +- kernel32/GetPrivateProfileString +- kernel32/GetPrivateProfileInt +- kernel32/WritePrivateProfileString + +### Preload + +The preload script `preload.rb` is registered with MKXP-Z and is responsible for +applying the patches defined in `patches.rb`. + +Some methods are provided for use in ports/patches: +- `Preload::require()`: Require a library from the `libs/` directory (see below) +- `Preload::print()`: Print to stderr. Note that Kernel.print may open a message box depending on RGSS version + +### Libraries +The `libs/` directory contains a few libraries to support porting plugins. +- `ruby18.rb`: Some low-hanging compatibility modifications for RGSS1, which used Ruby 1.8 +- `Win32API.rb`: Wrapper around Win32API to intercept imports with included ruby implementations +- `PreloadIni.rb`: Simple INI parser/generator for implementing {Get,Write}PrivateProfileString +- `XP_TileMapOverrideLib.rb`: Workaround for a MKXP-Z issue related to GL texture sizes + + + +[mkxp-z]: https://roza-gb.gitbook.io/mkxp-z +[mkxp-z-config]: https://github.com/mkxp-z/mkxp-z/blob/release/mkxp.json +[mkxp-z-github]: https://github.com/mkxp-z/mkxp-z +[mkxp-z-releases]: https://github.com/mkxp-z/mkxp-z/releases +[mkxp-z-repack]: https://github.com/Orochimarufan/Kawariki/releases/tag/mkxp-2.3.0-kk + +[mkxp-github]: https://github.com/Ancurio/mkxp +[rpgmakerweb]: https://www.rpgmakerweb.com/ diff --git a/mkxp-z/Kawariki-patches/libs/PreloadIni.rb b/mkxp-z/Kawariki-patches/libs/PreloadIni.rb new file mode 100644 index 0000000..87896ff --- /dev/null +++ b/mkxp-z/Kawariki-patches/libs/PreloadIni.rb @@ -0,0 +1,439 @@ +# INI file tools for replacing Win32API usage +# Key and Section names are case-preserving +# Authors: Taeyeon Mori + +module Preload + module Ini + # ********** Machinery ********** + class DummyReadFile + def each_line(&p) + end + end + + class IniBase + def initialize(file) + @file = file + @section = "" + @section_lc = "" + end + + attr_reader :section, :section_lc + + def self.open(filename) + if block_given? then + File.open(filename, self::FileMode) do |file| + yield self.new file + end + else + return self.new File.new(filename, self::FileMode) + end + end + end + + class IniWriter < IniBase + FileMode = "wt" + + def initialize(file) + super file + @newline = 1 + end + + def writeLine(line=nil) + if line.nil? || line.empty? then + @file.write "\r\n" + @newline += 1 + else + @file.write "#{line}\r\n" + @newline = 0 + end + end + + def writeComment(text) + writeLine "; #{text}" + end + + def writeSection(name) + lc = name.downcase + return if lc == @section_lc + writeLine if @newline < 1 + writeLine "[#{name}]" + @section = name + @section_lc = lc + end + + def writeKey(key, value) + value = value.to_s + value = "\"#{value}\"" if value.strip != value + writeLine "#{key}=#{value}" + end + + def writeEntry(section, key, value) + writeSection section + writeKey key, value + end + + def forward(token, *args) + # Can receive tokens from IniReader directly + case token + when :comment + writeComment *args + when :section + writeSection *args + when :key + writeKey *args + when :line + writeLine *args + when :empty + writeLine + when :eof + else + raise "Unknown token: #{token}" + end + end + end + + class IniReader < IniBase + FileMode = "rt" + + def self.open(filename) + # Pretend file is empty if it doesn't exist + if !File.exist? filename then + if block_given? then + yield self.new DummyReadFile.new + else + return self.new DummyReadFile.new + end + else + return super + end + end + + def readComment(line) + line.slice!(0, line[1] == " " ? 2 : 1) + [line] + end + + def readSection(line) + raise "Malformed section header: '#{line}'" if line[0] != '[' || line[-1] != ']' + @section = line[1...-1] + @section_lc = @section.downcase + return [@section] + end + + def readKey(line) + key, value = line.split('=', 2) + value.strip! + # Allow quoting to keep surrounding whitespace + value = value[1...-1] if value.size > 1 && "'\"".include?(value[0]) && value[0] == value[-1] + [key.strip, value] + rescue ArgumentError => e + puts "Error processing line: #{line.inspect} - #{e.message}" + nil + end + + def readLine(line) + if line.valid_encoding? + line.strip! + else + # Optionally, handle the invalid encoding here: + # You can choose to skip the line, replace invalid characters, or log it. + line = line.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '?') + end + return [:empty] if line.empty? + return :comment, *readComment(line) if line[0] == ';' + return :section, *readSection(line) if line[0] == '[' + return :key, *readKey(line) if line.include? '=' + # Just pass it through as last resort + return :line, line + end + + def read + raise "Must be called with block" unless block_given? + last = nil + @file.each_line do |line| + tok = readLine line + # Collapse empty lines + next if tok[0] == :empty && last == :empty + last = tok[0] + yield tok + end + yield :eof + end + end + + # ********** Simple API ********** + # Read + def self.readIniString(filename, section, key) + # Intended to be compatible with ReadPrivateProfileString + section_lc = section.downcase + key_lc = key.downcase + found_section = section.empty? + IniReader.open filename do |ir| + ir.read do |type, *args| + case type + when :section + found_section = ir.section_lc == section_lc + when :key + fkey, fval = *args + return fval if found_section && fkey.downcase == key_lc + end + end + end + end + + def self.readIniStrings(filename, section, keys=nil) + result = {} + section_lc = section.downcase + keys_lc = {} + keys.each {|key| keys_lc[key.downcase] = key} unless keys.nil? + found_section = section.empty? + IniReader.open filename do |ir| + ir.read do |type, *args| + case type + when :section + found_section = ir.section_lc == section_lc + when :key + if found_section then + fkey, fval = *args + if keys.nil? then + result[fkey] = fval + else + zkey = keys_lc[fkey.downcase] + result[zkey] = fval unless zkey.nil? + end + end + end + end + end + return result + end + + def self.readIniPaths(filename, paths=nil) + result = {} + paths_lc = {} + paths.each {|path| lc = path.downcase; paths_lc[lc] = path unless paths_lc.include? lc} unless paths.nil? + section_all = paths.nil? || paths.include?("/*") + section_all_name = nil + IniReader.open filename do |ir| + ir.read do |type, *args| + case type + when :section + unless paths.nil? then + section_all_name = paths_lc["#{ir.section_lc}/*"] + section_all = !section_all_name.nil? + section_all_name.slice!(-2...) if section_all + end + when :key + fkey, fval = *args + if section_all then + fpath = "#{section_all_name.nil? ? ir.section : section_all_name}/#{fkey}" + result[fpath] = fval + else + fpath = "#{ir.section_lc}/#{fkey.downcase}" + result[paths_lc[fpath]] = fval if paths_lc.key? fpath + end + end + end + end + result + end + + def self.readIniSections(filename) + # Get a list of sections in the file + # Note: may contain (possibly case-mismatched) duplicates. + sections = [] + IniReader.open filename do |ir| + ir.read do |type, *args| + case type + when :section + sections.push args[0] + end + end + end + return sections + end + + def self.readIniKeys(filename, section) + # Get a list of keys in a section + # Note: may contain (possibly case-mismatched) duplicates. + keys = [] + section_lc = section.downcase + found_section = section.empty? + IniReader.open filename do |ir| + ir.read do |type, *args| + case type + when :section + found_section = ir.section_lc == section_lc + when :key + keys.push args[0] if found_section + end + end + end + return keys + end + + # Write + def self.writeIniString(filename, section, key, value) + # Intended to be compatible with WritePrivateProfileString + # Write to new file then rename over top. + section_lc = section.downcase + key_lc = key.downcase unless key.nil? + found_section = section.empty? + written = key.nil? || value.nil? # Delete instead if nil + temp_name = "#{filename}.tmp" + IniWriter.open temp_name do |iw| + IniReader.open filename do |ir| + ir.read do |type, *args| + case type + when :section + # Insert new key before leaving section + if found_section && !written then + iw.writeKey key, value + written = true + end + # Start new section or omit whole section if key == nil + found_section = ir.section_lc == section_lc + iw.writeSection ir.section unless found_section && key.nil? + when :key + fkey, fval = *args + if found_section then + if fkey.downcase == key_lc then + # Replace matching key + iw.writeKey key, value unless written + written = true + elsif !key.nil? then + # Copy other keys + iw.writeKey fkey, fval + end + else + iw.writeKey fkey, fval + end + when :eof + # Add to end of file if not found earlier + iw.writeEntry section, key, value unless written + else + iw.forward type, *args + end + end + end + end + File.rename(temp_name, filename) + end + + def self.writeIniStrings(filename, section, hash) + # Write to new file then rename over top. + section_lc = section.downcase + hash_lc = {} + written = [] + hash.each_pair do |key, value| + key_lc = key.downcase + hash_lc[key_lc] = [key, value] unless value.nil? + written.push key_lc if value.nil? + end + found_section = section.empty? + temp_name = "#{filename}.tmp" + IniWriter.open temp_name do |iw| + IniReader.open filename do |ir| + ir.read do |type, *args| + case type + when :section + # Insert new keys before leaving section + if found_section && !hash_lc.empty? then + hash_lc.each_pair {|key_lc, kv| iw.writeKey *kv} + written.push *hash_lc.keys + hash_lc.clear + end + # Start new section or omit whole section if key == nil + found_section = ir.section_lc == section_lc + iw.writeSection ir.section + when :key + fkey, fval = *args + if found_section then + fkey_lc = fkey.downcase + entry = hash_lc.delete fkey_lc + if !entry.nil? then + # Replace matching key + iw.writeKey *entry + written.push fkey_lc + next + elsif written.include? fkey_lc then + next + end + end + iw.writeKey fkey, fval + when :eof + # Add to end of file if not found earlier + if !hash_lc.empty? then + iw.writeSection section + hash_lc.each_value {|key, value| iw.writeKey key, value} + end + else + iw.forward type, *args + end + end + end + end + File.rename(temp_name, filename) + end + + def self.writeIniPaths(filename, hash) + # Write to new file then rename over top. + sections = {} + hash.each_pair do |path, value| + section, _, key = path.rpartition '/' + section_lc = section.downcase + sections[section_lc] = {name: section, written: [], keys: {}} unless sections.key? section_lc + sections[section_lc][:keys][key.downcase] = [key, value] + end + current_section = sections[""] + temp_name = "#{filename}.tmp" + IniWriter.open temp_name do |iw| + IniReader.open filename do |ir| + ir.read do |type, *args| + case type + when :section + # Write new keys before leaving section + if !current_section.nil? then + current_section[:keys].each_value {|key, value| iw.writeKey key, value} + current_section[:written].push *current_section[:keys].keys + current_section[:keys].clear + end + # new section + iw.writeSection ir.section + current_section = sections[ir.section_lc] + when :key + fkey, fval = *args + fkey_lc = fkey.downcase + # Replace matching key + if !current_section.nil? then + replacement = current_section[:keys].delete fkey_lc + if !replacement.nil? then + iw.writeKey *replacement unless replacement[1].nil? + current_section[:written].push fkey_lc + next + elsif current_section[:written].include? fkey_lc then + next + end + end + # Copy other keys + iw.writeKey fkey, fval + when :eof + # Add sections not previously seen + sections.each_value do |sect| + if !sect[:keys].empty? then + iw.writeSection sect[:name] + sect[:keys].each_value do |key, value| + iw.writeKey key, value + end + end + end + else + iw.forward type, *args + end + end + end + end + File.rename(temp_name, filename) + end + end +end diff --git a/mkxp-z/Kawariki-patches/libs/Win32API.rb b/mkxp-z/Kawariki-patches/libs/Win32API.rb new file mode 100644 index 0000000..0ecefaa --- /dev/null +++ b/mkxp-z/Kawariki-patches/libs/Win32API.rb @@ -0,0 +1,130 @@ +=begin +Win32API emulation + +MKXP-Z exposes an implementation of the Win32API module (called MiniFFI) by default. +However, this alias is only actually useful on Windows. Therefore, we replace it +with a pure-ruby version specifically implementing the most common imports. +Real native libraries can still be accessed through MiniFII.new (e.g. in ports) + +Lambdas are used for implementations as they replicate Win32API's #call interface. +=end + +# Don't expose MiniFFI as Win32API +Object.remove_const :Win32API + +module Win32API + module Kernel32 + GetPrivateProfileInt = GetPrivateProfileIntA = ->(appname, keyname, default, filename) do + Preload.require "PreloadIni.rb" + s = Preload::Ini.readIniString filename, appname, keyname + s.nil? ? default : s.to_i + end + GetPrivateProfileString = GetPrivateProfileStringA = ->(appname, keyname, default, ret, size, filename) do + Preload.require "PreloadIni.rb" + if appname.nil? then + res = Preload::Ini.readIniSections(filename).join("\0") + "\0" + elsif keyname.nil? then + res = Preload::Ini.readIniKeys(filename, appname).join("\0") + "\0" + else + s = Preload::Ini.readIniString filename, appname, keyname + res = s.nil? ? (default.nil? ? "" : default) : s + end + # C-String dance + size -= 1 + if res.size > size then + res.slice!(size...) + res[size-1] = "\0" if appname.nil? or keyname.nil? + end + ret[...res.size] = res + ret[res.size] = "\0" + res.size + end + WritePrivateProfileString = WritePrivateProfileStringA = ->(appname, keyname, value, filename) do + Preload.require "PreloadIni.rb" + Preload::Ini.writeIniString filename, appname, keyname, value + end + end + + module User32 + FindWindow = FindWindowA = ->(cls, wnd) do + return 1 + end + FindWindowEx = ->(parent, ca, cls, wnd) do + return 1 + end + GetAsyncKeyState = ->(key) do + # Very naive + return 128 if Input.pressex? key + return 0 + end + GetClientRect = ->(hWnd, out_rect) do + return 0 unless hWnd == 1 + # out_rect.byteslice(0, 16, [0, 0, Graphics.width, Graphics.height].pack("llll")) + out_rect[0, 16] = [0, 0, Graphics.width, Graphics.height].pack("llll") + return 1 + end + GetCursorPos = ->(out_point) do + # Pack mouse coordinates into a binary string + packed_coords = [Input.mouse_x, Input.mouse_y].pack("ll") + + # Update the out_point string with the packed data (overwrite first 8 bytes) + out_point[0, 8] = packed_coords + + return 1 + end + + + GetKeyboardLayout = ->(thread) do + return 0 + end + GetSystemMetrics = ->(index) do + return Graphics.width if index == 0 # SM_CXSCREEN - Primary screen width + return Graphics.height if index == 1 # SM_CYSCREEN - Primary screen height + return 0 if index == 4 # SM_CYCAPTION - Height of caption area (title bar?) + return 0 if index == 5 # SM_CXBORDER - Width of window borders + return 0 if index == 6 # SM_CYBORDER - Height of window borders + return 0 if index == 23 # SM_SWAPBUTTON - Swap left/right mouse buttons + return 0 if index == 45 # SM_CXEDGE - Width of 3D window borders + Preload.print("Warning: user32#GetSystemMetrics index #{index} not implemented") + return 0 + end + GetWindowRect = ->(hWnd, out_rect) do + return 0 unless hWnd == 1 + # out_rect.byteslice(0, 16, [0, 0, Graphics.width, Graphics.height].pack("llll")) + out_rect[0, 16] = [0, 0, Graphics.width, Graphics.height].pack("llll") + return 1 + end + MapVirtualKeyEx = ->(code, map, layout) do + return 0 unless layout == 0 + return code + end + ScreenToClient = ->(hWnd, point) do + return 1 unless hWnd != 1 + return 0 + end + ShowCursor = ->(show) do + Graphics.show_cursor = show == 1 + return show + end + end + + module SteamAPI + # TODO: Forward to native steamapi? + SteamAPI_Init = ->{1} + SteamAPI_Shutdown = ->{} + end + + Libraries = { + "kernel32" => Kernel32, + "user32" => User32, + "steam_api" => SteamAPI, + } + + def self.new(dllname, func, *rest) + dllname = dllname[...-4] if dllname[...-4] == ".dll" + lib = Libraries[dllname] + return lib.const_get(func, false) if lib.const_defined?(func, false) unless lib.nil? + Preload.print("Warning: Win32API not implemented: #{dllname}##{func}") + return ->(*args){Preload.print "(STUB) #{dllname}##{func}: #{args}"} + end +end diff --git a/mkxp-z/Kawariki-patches/libs/Win32APIl.rb b/mkxp-z/Kawariki-patches/libs/Win32APIl.rb new file mode 100644 index 0000000..af1cdef --- /dev/null +++ b/mkxp-z/Kawariki-patches/libs/Win32APIl.rb @@ -0,0 +1,346 @@ +=begin +Win32API emulation + +MKXP-Z exposes an implementation of the Win32API module (called MiniFFI) by default. +However, this alias is only actually useful on Windows. Therefore, we replace it +with a pure-ruby version specifically implementing the most common imports. +Real native libraries can still be accessed through MiniFII.new (e.g. in ports) + +Lambdas are used for implementations as they replicate Win32API's #call interface. +=end + + +module Scancodes + SDL = { :UNKNOWN => 0x00, + :A => 0x04, :B => 0x05, :C => 0x06, :D => 0x07, + :E => 0x08, :F => 0x09, :G => 0x0A, :H => 0x0B, + :I => 0x0C, :J => 0x0D, :K => 0x0E, :L => 0x0F, + :M => 0x10, :N => 0x11, :O => 0x12, :P => 0x13, + :Q => 0x14, :R => 0x15, :S => 0x16, :T => 0x17, + :U => 0x18, :V => 0x19, :W => 0x1A, :X => 0x1B, + :Y => 0x1C, :Z => 0x1D, :N1 => 0x1E, :N2 => 0x1F, + :N3 => 0x20, :N4 => 0x21, :N5 => 0x22, :N6 => 0x23, + :N7 => 0x24, :N8 => 0x25, :N9 => 0x26, :N0 => 0x27, + :RETURN => 0x28, :ESCAPE => 0x29, :BACKSPACE => 0x2A, :TAB => 0x2B, + :SPACE => 0x2C, :MINUS => 0x2D, :EQUALS => 0x2E, :LEFTBRACKET => 0x2F, + :RIGHTBRACKET => 0x30, :BACKSLASH => 0x31, :NONUSHASH => 0x32, :SEMICOLON => 0x33, + :APOSTROPHE => 0x34, :GRAVE => 0x35, :COMMA => 0x36, :PERIOD => 0x37, + :SLASH => 0x38, :CAPSLOCK => 0x39, :F1 => 0x3A, :F2 => 0x3B, + :F3 => 0x3C, :F4 => 0x3D, :F5 => 0x3E, :F6 => 0x3F, + :F7 => 0x40, :F8 => 0x41, :F9 => 0x42, :F10 => 0x43, + :F11 => 0x44, :F12 => 0x45, :PRINTSCREEN => 0x46, :SCROLLLOCK => 0x47, + :PAUSE => 0x48, :INSERT => 0x49, :HOME => 0x4A, :PAGEUP => 0x4B, + :DELETE => 0x4C, :END => 0x4D, :PAGEDOWN => 0x4E, :RIGHT => 0x4F, + :LEFT => 0x50, :DOWN => 0x51, :UP => 0x52, :NUMLOCKCLEAR => 0x53, + :KP_DIVIDE => 0x54, :KP_MULTIPLY => 0x55, :KP_MINUS => 0x56, :KP_PLUS => 0x57, + :KP_ENTER => 0x58, :KP_1 => 0x59, :KP_2 => 0x5A, :KP_3 => 0x5B, + :KP_4 => 0x5C, :KP_5 => 0x5D, :KP_6 => 0x5E, :KP_7 => 0x5F, + :KP_8 => 0x60, :KP_9 => 0x61, :KP_0 => 0x62, :KP_PERIOD => 0x63, + :NONUSBACKSLASH => 0x64, :APPLICATION => 0x65, :POWER => 0x66, :KP_EQUALS => 0x67, + :F13 => 0x68, :F14 => 0x69, :F15 => 0x6A, :F16 => 0x6B, + :F17 => 0x6C, :F18 => 0x6D, :F19 => 0x6E, :F20 => 0x6F, + :F21 => 0x70, :F22 => 0x71, :F23 => 0x72, :F24 => 0x73, + :EXECUTE => 0x74, :HELP => 0x75, :MENU => 0x76, :SELECT => 0x77, + :STOP => 0x78, :AGAIN => 0x79, :UNDO => 0x7A, :CUT => 0x7B, + :COPY => 0x7C, :PASTE => 0x7D, :FIND => 0x7E, :MUTE => 0x7F, + :VOLUMEUP => 0x80, :VOLUMEDOWN => 0x81, :LOCKINGCAPSLOCK => 0x82, :LOCKINGNUMLOCK => 0x83, + :LOCKINGSCROLLLOCK => 0x84, :KP_COMMA => 0x85, :KP_EQUALSAS400 => 0x86, :INTERNATIONAL1 => 0x87, + :INTERNATIONAL2 => 0x88, :INTERNATIONAL3 => 0x89, :INTERNATIONAL4 => 0x8A, :INTERNATIONAL5 => 0x8B, + :INTERNATIONAL6 => 0x8C, :INTERNATIONAL7 => 0x8D, :INTERNATIONAL8 => 0x8E, :INTERNATIONAL9 => 0x8F, + :LANG1 => 0x90, :LANG2 => 0x91, :LANG3 => 0x92, :LANG4 => 0x93, + :LANG5 => 0x94, :LANG6 => 0x95, :LANG7 => 0x96, :LANG8 => 0x97, + :LANG9 => 0x98, :ALTERASE => 0x99, :SYSREQ => 0x9A, :CANCEL => 0x9B, + :CLEAR => 0x9C, :PRIOR => 0x9D, :RETURN2 => 0x9E, :SEPARATOR => 0x9F, + :OUT => 0xA0, :OPER => 0xA1, :CLEARAGAIN => 0xA2, :CRSEL => 0xA3, + :EXSEL => 0xA4, :KP_00 => 0xB0, :KP_000 => 0xB1, :THOUSANDSSEPARATOR => 0xB2, + :DECIMALSEPARATOR => 0xB3, :CURRENCYUNIT => 0xB4, :CURRENCYSUBUNIT => 0xB5, :KP_LEFTPAREN => 0xB6, + :KP_RIGHTPAREN => 0xB7, :KP_LEFTBRACE => 0xB8, :KP_RIGHTBRACE => 0xB9, :KP_TAB => 0xBA, + :KP_BACKSPACE => 0xBB, :KP_A => 0xBC, :KP_B => 0xBD, :KP_C => 0xBE, + :KP_D => 0xBF, :KP_E => 0xC0, :KP_F => 0xC1, :KP_XOR => 0xC2, + :KP_POWER => 0xC3, :KP_PERCENT => 0xC4, :KP_LESS => 0xC5, :KP_GREATER => 0xC6, + :KP_AMPERSAND => 0xC7, :KP_DBLAMPERSAND => 0xC8, :KP_VERTICALBAR => 0xC9, :KP_DBLVERTICALBAR => 0xCA, + :KP_COLON => 0xCB, :KP_HASH => 0xCC, :KP_SPACE => 0xCD, :KP_AT => 0xCE, + :KP_EXCLAM => 0xCF, :KP_MEMSTORE => 0xD0, :KP_MEMRECALL => 0xD1, :KP_MEMCLEAR => 0xD2, + :KP_MEMADD => 0xD3, :KP_MEMSUBTRACT => 0xD4, :KP_MEMMULTIPLY => 0xD5, :KP_MEMDIVIDE => 0xD6, + :KP_PLUSMINUS => 0xD7, :KP_CLEAR => 0xD8, :KP_CLEARENTRY => 0xD9, :KP_BINARY => 0xDA, + :KP_OCTAL => 0xDB, :KP_DECIMAL => 0xDC, :KP_HEXADECIMAL => 0xDD, :LCTRL => 0xE0, + :LSHIFT => 0xE1, :LALT => 0xE2, :LGUI => 0xE3, :RCTRL => 0xE4, + :RSHIFT => 0xE5, :RALT => 0xE6, :RGUI => 0xE7, :MODE => 0x101, + :AUDIONEXT => 0x102, :AUDIOPREV => 0x103, :AUDIOSTOP => 0x104, :AUDIOPLAY => 0x105, + :AUDIOMUTE => 0x106, :MEDIASELECT => 0x107, :WWW => 0x108, :MAIL => 0x109, + :CALCULATOR => 0x10A, :COMPUTER => 0x10B, :AC_SEARCH => 0x10C, :AC_HOME => 0x10D, + :AC_BACK => 0x10E, :AC_FORWARD => 0x10F, :AC_STOP => 0x110, :AC_REFRESH => 0x111, + :AC_BOOKMARKS => 0x112, :BRIGHTNESSDOWN => 0x113, :BRIGHTNESSUP => 0x114, :DISPLAYSWITCH => 0x115, + :KBDILLUMTOGGLE => 0x116, :KBDILLUMDOWN => 0x117, :KBDILLUMUP => 0x118, :EJECT => 0x119, + :SLEEP => 0x11A, :APP1 => 0x11B, :APP2 => 0x11C + } + + SDL.default = SDL[:UNKNOWN] + + WIN32 = { + :LBUTTON => 0x01, :RBUTTON => 0x02, :MBUTTON => 0x04, + + :BACK => 0x08, :TAB => 0x09, :RETURN => 0x0D, :SHIFT => 0x10, + :CONTROL => 0x11, :MENU => 0x12, :PAUSE => 0x13, :CAPITAL => 0x14, + :ESCAPE => 0x1B, :SPACE => 0x20, :PRIOR => 0x21, :NEXT => 0x22, + :END => 0x23, :HOME => 0x24, :LEFT => 0x25, :UP => 0x26, + :RIGHT => 0x27, :DOWN => 0x28, :PRINT => 0x2A, :INSERT => 0x2D, + :DELETE => 0x2E, + + :N0 => 0x30, :N1 => 0x31, :N2 => 0x32, :N3 => 0x33, + :N4 => 0x34, :N5 => 0x35, :N6 => 0x36, :N7 => 0x37, :N8 => 0x38, + :N9 => 0x39, + + :A => 0x41, :B => 0x42, :C => 0x43, :D => 0x44, :E => 0x45, :F => 0x46, + :G => 0x47, :H => 0x48, :I => 0x49, :J => 0x4A, :K => 0x4B, :L => 0x4C, + :M => 0x4D, :N => 0x4E, :O => 0x4F, :P => 0x50, :Q => 0x51, :R => 0x52, + :S => 0x53, :T => 0x54, :U => 0x55, :V => 0x56, :W => 0x57, :X => 0x58, + :Y => 0x59, :Z => 0x5A, + + :LWIN => 0x5B, :RWIN => 0x5C, + + :NUMPAD0 => 0x60, :NUMPAD1 => 0x61, :NUMPAD2 => 0x62, :NUMPAD3 => 0x63, + :NUMPAD4 => 0x64, :NUMPAD5 => 0x65, :NUMPAD6 => 0x66, :NUMPAD7 => 0x67, + :NUMPAD8 => 0x68, :NUMPAD9 => 0x69, + :MULTIPLY => 0x6A, :ADD => 0x6B, :SEPARATOR => 0x6C, :SUBSTRACT => 0x6D, + :DECIMAL => 0x6E, :DIVIDE => 0x6F, + + :F1 => 0x70, :F2 => 0x71, :F3 => 0x72, :F4 => 0x73, + :F5 => 0x74, :F6 => 0x75, :F7 => 0x76, :F8 => 0x77, + :F9 => 0x78, :F10 => 0x79, :F11 => 0x7A, :F12 => 0x7B, + :F13 => 0x7C, :F14 => 0x7D, :F15 => 0x7E, :F16 => 0x7F, + :F17 => 0x80, :F18 => 0x81, :F19 => 0x82, :F20 => 0x83, + :F21 => 0x84, :F22 => 0x85, :F23 => 0x86, :F24 => 0x87, + + :NUMLOCK => 0x90, :SCROLL => 0x91, + :LSHIFT => 0xA0, :RSHIFT => 0xA1, :LCONTROL => 0xA2, :RCONTROL => 0xA3, + :LMENU => 0xA4, :RMENU => 0xA5, :OEM_1 => 0xBA, + :OEM_PLUS => 0xBB, :OEM_COMMA => 0xBC, :OEM_MINUS => 0xBD, :OEM_PERIOD => 0xBE, + :OEM_2 => 0xBF, :OEM_3 => 0xC0, :OEM_4 => 0xDB, :OEM_5 => 0xDC, + :OEM_6 => 0xDD, :OEM_7 => 0xDE + } + + WIN32INV = WIN32.invert + + WIN2SDL = { + :BACK => :BACKSPACE, + :CAPITAL => :CAPSLOCK, + :PRIOR => :PAGEUP, :NEXT => :PAGEDOWN, + :PRINT => :PRINTSCREEN, + + :LWIN => :LGUI, :RWIN => :RGUI, + + :NUMPAD0 => :KP_0, :NUMPAD1 => :KP_1, :NUMPAD2 => :KP_2, :NUMPAD3 => :KP_3, + :NUMPAD4 => :KP_4, :NUMPAD5 => :KP_5, :NUMPAD6 => :KP_6, :NUMPAD7 => :KP_7, + :NUMPAD8 => :KP_8, :NUMPAD9 => :KP_9, + :MULTIPLY => :KP_MULTIPLY, :ADD => :KP_PLUS, :SUBSTRACT => :KP_MINUS, + :DECIMAL => :KP_DECIMAL, :DIVIDE => :KP_DIVIDE, + + :NUMLOCK => :NUMLOCKCLEAR, :SCROLL => :SCROLLLOCK, + :LCONTROL => :LCTRL, :RCONTROL => :RCTRL, + # FIXME: Fill these out + :LMENU => :LALT, :RMENU => :RALT, :OEM_1 => :SEMICOLON, + :OEM_PLUS => :UNKNOWN, :OEM_COMMA => :UNKNOWN, :OEM_MINUS => :UNKNOWN, :OEM_PERIOD => :UNKNOWN, + :OEM_2 => :UNKNOWN, :OEM_3 => :UNKNOWN, :OEM_4 => :UNKNOWN, :OEM_5 => :UNKNOWN, + :OEM_6 => :UNKNOWN, :OEM_7 => :UNKNOWN + } + + WIN2SDL.default = :UNKNOWN +end + +$win32KeyStates = nil + +module Graphics + class << self + alias_method(:win32wrap_update, :update) + def update + win32wrap_update + $win32KeyStates = nil + end + end +end + +def get_raw_keystates + if $win32KeyStates == nil + $win32KeyStates = Input.raw_key_states + end + + return $win32KeyStates +end + +def common_keystate(vkey) + vkey_name = Scancodes::WIN32INV[vkey] + + states = get_raw_keystates + pressed = false + + if vkey_name == :LBUTTON + pressed = Input.press?(Input::MOUSELEFT) + elsif vkey_name == :RBUTTON + pressed = Input.press?(Input::MOUSERIGHT) + elsif vkey_name == :MBUTTON + pressed = Input.press?(Input::MOUSEMIDDLE) + elsif vkey_name == :SHIFT + pressed = double_state(states, :LSHIFT, :RSHIFT) + elsif vkey_name == :MENU + pressed = double_state(states, :LALT, :RALT) + elsif vkey_name == :CONTROL + pressed = double_state(states, :LCTRL, :RCTRL) + else + scan = nil + if Scancodes::SDL.key?(vkey_name) + scan = vkey_name + else + scan = Scancodes::WIN2SDL[vkey_name] + end + + pressed = state_pressed(states, scan) + end + + return pressed ? 1 : 0 +end + +def memcpy_string(dst, src) + i = 0 + src.each_byte do |b| + dst.setbyte(i, b) + i += 1 + end +end + +def state_pressed(states, sdl_scan) + return states[Scancodes::SDL[sdl_scan]] +end + +def double_state(states, left, right) + return state_pressed(states, left) || state_pressed(states, right) +end + +# Don't expose MiniFFI as Win32API +Object.remove_const :Win32API + +module Win32API + module Kernel32 + GetPrivateProfileInt = GetPrivateProfileIntA = ->(appname, keyname, default, filename) do + Preload.require "PreloadIni.rb" + s = Preload::Ini.readIniString filename, appname, keyname + s.nil? ? default : s.to_i + end + GetPrivateProfileString = GetPrivateProfileStringA = ->(appname, keyname, default, ret, size, filename) do + Preload.require "PreloadIni.rb" + if appname.nil? then + res = Preload::Ini.readIniSections(filename).join("\0") + "\0" + elsif keyname.nil? then + res = Preload::Ini.readIniKeys(filename, appname).join("\0") + "\0" + else + s = Preload::Ini.readIniString filename, appname, keyname + res = s.nil? ? (default.nil? ? "" : default) : s + end + # C-String dance + size -= 1 + if res.size > size then + res.slice!(size...) + res[size-1] = "\0" if appname.nil? or keyname.nil? + end + ret[...res.size] = res + ret[res.size] = "\0" + res.size + end + WritePrivateProfileString = WritePrivateProfileStringA = ->(appname, keyname, value, filename) do + Preload.require "PreloadIni.rb" + Preload::Ini.writeIniString filename, appname, keyname, value + end + MultiByteToWideChar = MultiByteToWideCharA = ->(codepage, flags, input_str, input_len, buffer, buffer_size) do + puts codepage, flags, input_str, input_len, buffer, buffer_size + + #Preload.require "PreloadIni.rb" + #Preload::Ini.writeIniString filename, appname, keyname, value + end + end + + module User32 + FindWindow = FindWindowA = ->(cls, wnd) do + return 1 + end + FindWindowEx = ->(parent, ca, cls, wnd) do + return 1 + end + GetAsyncKeyState = ->(key) do + # Very naive + return 128 if Input.pressex? key + return 0 + end + GetClientRect = ->(hWnd, out_rect) do + return 0 unless hWnd == 1 + # out_rect.byteslice(0, 16, [0, 0, Graphics.width, Graphics.height].pack("llll")) + out_rect[0, 16] = [0, 0, Graphics.width, Graphics.height].pack("llll") + return 1 + end + GetCursorPos = ->(out_point) do + # Pack mouse coordinates into a binary string + packed_coords = [Input.mouse_x, Input.mouse_y].pack("ll") + + # Update the out_point string with the packed data (overwrite first 8 bytes) + out_point[0, 8] = packed_coords + + return 1 + end + GetKeyState = ->(vkey) do + puts 'bbb' + return common_keystate(vkey[0]) + end + + GetKeyboardLayout = ->(thread) do + return 0 + end + GetSystemMetrics = ->(index) do + return Graphics.width if index == 0 # SM_CXSCREEN - Primary screen width + return Graphics.height if index == 1 # SM_CYSCREEN - Primary screen height + return 0 if index == 4 # SM_CYCAPTION - Height of caption area (title bar?) + return 0 if index == 5 # SM_CXBORDER - Width of window borders + return 0 if index == 6 # SM_CYBORDER - Height of window borders + return 0 if index == 23 # SM_SWAPBUTTON - Swap left/right mouse buttons + return 0 if index == 45 # SM_CXEDGE - Width of 3D window borders + Preload.print("Warning: user32#GetSystemMetrics index #{index} not implemented") + return 0 + end + GetWindowRect = ->(hWnd, out_rect) do + return 0 unless hWnd == 1 + # out_rect.byteslice(0, 16, [0, 0, Graphics.width, Graphics.height].pack("llll")) + out_rect[0, 16] = [0, 0, Graphics.width, Graphics.height].pack("llll") + return 1 + end + MapVirtualKeyEx = ->(code, map, layout) do + return 0 unless layout == 0 + return code + end + ScreenToClient = ->(hWnd, point) do + return 1 unless hWnd != 1 + return 0 + end + ShowCursor = ->(show) do + Graphics.show_cursor = show == 1 + return show + end + end + + module SteamAPI + # TODO: Forward to native steamapi? + SteamAPI_Init = ->{1} + SteamAPI_Shutdown = ->{} + end + + Libraries = { + "kernel32" => Kernel32, + "user32" => User32, + "steam_api" => SteamAPI, + } + + def self.new(dllname, func, *rest) + dllname = dllname[...-4] if dllname[...-4] == ".dll" + lib = Libraries[dllname] + return lib.const_get(func, false) if lib.const_defined?(func, false) unless lib.nil? + Preload.print("Warning: Win32API not implemented: #{dllname}##{func}") + return ->(*args){Preload.print "(STUB) #{dllname}##{func}: #{args}"} + end +end diff --git a/mkxp-z/Kawariki-patches/libs/XP_TilemapOverrideLib.rb b/mkxp-z/Kawariki-patches/libs/XP_TilemapOverrideLib.rb new file mode 100644 index 0000000..9eabdbf --- /dev/null +++ b/mkxp-z/Kawariki-patches/libs/XP_TilemapOverrideLib.rb @@ -0,0 +1,136 @@ +# ====================================================================== +# MKXP-Z Custom tilemap workaround tools +# +# Authors: Roza, Taeyeon Mori +# +# Contains library code only, must be added to custom +# tilemap classes using a preload patch or similar. +# ====================================================================== +# SUPER TILEMAP VERTICAL WRAPPER THING +# +# This is a little fix for Pokemon Essentials' custom tilemap code +# that works around MKXP's GPU texture size limit that would normally +# stop you from playing a lot of games. +# +# The concept is simple enough: If your tileset is too big, a new +# bitmap will be constructed with all the excess pixels sent to the +# image's right side. This basically means that you now have a limit +# far higher than you should ever actually need. +# +# 1024 -> 4096 +# 2048 -> 16384 (enough to get the normal limit) +# 4096 -> 65536 (enough to load pretty much any tileset) +# 8192 -> 262144 +# 16384 -> 1048576 (what most people have at this point) +# +# Because of the extra math the game will have to do to find the right +# pixels, this will probably cause a slight performance hit while on these +# maps which would normally be megasurfaces. +# +# This script was written for games based on 17.1. This workaround is +# already implemented in 19. +# +# ~Roza/Zoroark +#======================================================================= + +module TileWrap + + MAX_TEX_SIZE = Bitmap.max_size + TILESET_WIDTH = 0x100 + MAX_TEX_SIZE_BOOSTED = MAX_TEX_SIZE**2/TILESET_WIDTH + + def self.clamp(val, min, max) + val = max if val > max + val = min if val < min + return val + end + + def self.wrapTileset(originalbmp) + width = originalbmp.width + height = originalbmp.height + if width == TILESET_WIDTH && originalbmp.mega? + columns = (height / MAX_TEX_SIZE.to_f).ceil + + if columns * TILESET_WIDTH > MAX_TEX_SIZE + raise "Tilemap is too long!\n\nSIZE: #{originalbmp.height}px\nHARDWARE LIMIT: #{MAX_TEX_SIZE}px\nBOOSTED LIMIT: #{MAX_TEX_SIZE_BOOSTED}px" + end + bmp = Bitmap.new(TILESET_WIDTH*columns, MAX_TEX_SIZE) + remainder = height % MAX_TEX_SIZE + + columns.times{|col| + srcrect = Rect.new(0, col * MAX_TEX_SIZE, width, (col + 1 == columns) ? remainder : MAX_TEX_SIZE) + bmp.blt(col*TILESET_WIDTH, 0, originalbmp, srcrect) + } + return bmp + end + + return originalbmp + end + + def self.wrapRect(srcrect) + column, y = srcrect.y.divmod MAX_TEX_SIZE + raise "Rect split across column wrap!" if y + srcrect.height > MAX_TEX_SIZE + return srcrect if column == 0 + Rect.new(column * MAX_TEX_SIZE + srcrect.x, y, srcrect.width, srcrect.height) + end + + def self.wrapRect!(rect) + column, y = rect.y.divmod MAX_TEX_SIZE + raise "Rect split across column wrap!" if y + rect.height > MAX_TEX_SIZE + return if column == 0 + rect.x = column * MAX_TEX_SIZE + rect.x + rect.y = y + end + + def self.blitWrappedPixels(destX, destY, dest, src, srcrect) + if (srcrect.y + srcrect.width < MAX_TEX_SIZE) + # Save the processing power + return dest.blt(destX, destY, src, srcrect) + end + merge = (srcrect.y % MAX_TEX_SIZE) > ((srcrect.y + srcrect.height) % MAX_TEX_SIZE) + + srcrect.x = clamp(srcrect.x, 0,TILESET_WIDTH) + srcrect.width = clamp(srcrect.width, 0, TILESET_WIDTH - srcrect.x) + col = (srcrect.y / MAX_TEX_SIZE.to_f).floor + srcX = col * TILESET_WIDTH + srcrect.x + srcY = srcrect.y % MAX_TEX_SIZE + + if !merge + dest.blt(destX, destY, src, Rect.new(srcX, srcY, srcrect.width, srcrect.height)) + else + #FIXME won't work on heights longer than two columns, but nobody should need + # more than 32k pixels high at once anyway + side = {:a => MAX_TEX_SIZE - srcY, :b => srcrect.height - (MAX_TEX_SIZE - srcY)} + dest.blt(destX, destY, src, Rect.new(srcX, srcY, srcrect.width, side[:a])) + dest.blt(destX, destY + side[:a], src, Rect.new(srcX + TILESET_WIDTH, 0, srcrect.width, side[:b])) + end + end + + # May be applied using Module#prepend + # it's probably better to integrate custom tilemap code manually + # XXX: is it OK to dispose of the original bitmaps? + module TilemapPatch + def tileset=(value) + if value.mega? + super TileWrap::wrapTileset value + value.dispose + else + super value + end + end + end + + module SpritePatch + def bitmap=(bmp) + if bmp.mega? + super TileWrap.wrapTileset bmp + bmp.dispose + else + super bmp + end + end + def src_rect=(rect) + super TileWrap.wrapRect rect + end + end +end diff --git a/mkxp-z/Kawariki-patches/libs/ruby18.rb b/mkxp-z/Kawariki-patches/libs/ruby18.rb new file mode 100644 index 0000000..d80d367 --- /dev/null +++ b/mkxp-z/Kawariki-patches/libs/ruby18.rb @@ -0,0 +1,71 @@ +# Ruby 1.8 compat +module Ruby18 + module ObjectPatch + # Object#type used to be an alias to Object#class + def type + self.class + end + end + + class IncludeStringArray < Array + def include?(thing) + if thing.is_a?(String) then + super(thing.to_sym) + else + super + end + end + end + + module KernelPatch + # Used to return a string array + def methods(*) + IncludeStringArray.new super + end + + def singleton_methods(*) + IncludeStringArray.new super + end + end + + module ModulePatch + # Used to return string array. + # Fix instance_methods.include? use-case by patching it to work with strings + # The array will still be of symbols however + def instance_methods(*) + IncludeStringArray.new super + end + + def public_instance_methods(*) + IncludeStringArray.new super + end + + def private_instance_methods(*) + IncludeStringArray.new super + end + end + + module ArrayPatch + def nitems + count {|i| !i.nil?} + end + + def choice + sample + end + end + + module HashPatch + def index(value) + key value + end + end + + + # Apply Patches + Object.prepend ObjectPatch + Module.prepend ModulePatch + Kernel.prepend KernelPatch + Array.prepend ArrayPatch + Hash.prepend HashPatch +end diff --git a/mkxp-z/Kawariki-patches/patches.rb b/mkxp-z/Kawariki-patches/patches.rb new file mode 100644 index 0000000..6533077 --- /dev/null +++ b/mkxp-z/Kawariki-patches/patches.rb @@ -0,0 +1,182 @@ +# Kawariki MKXP patch/port collection +# See preload.rb for Patch implementation + +module Preload + Patches = [ + # Ports + Patch.new("Zeus Fullscreen: Use mkxp builtin fullscreen instead (Alt+Enter)") + .imported?(:Zeus_Fullscreen) + .replace!("Zeus_Fullscreen++.rb"), + Patch.new("Zeus_Map_Effects fix") + .imported?(:Zeus_Map_Effects) + .sub!(":zoom_blur_length, :motion_blur_rate", ":zoom_blur_length, :motion_blur_rate\ndef update_animation_Integer(str,str2,str3,str4,str5,str6) @wave_amp = 0.0; end"), + Patch.new("Zeus_Lights_Shadows fix") + .imported?(:Zeus_Lights_Shadows) + .sub!(/^RPG_VERSION =.*/, "RPG_VERSION ="+ ENV["vcode"]) + .sub!(" def update_animation_value", "def update_animation_Integer(str,str2,str3,str4,str5) @wave_amp = 0.0; end\ndef update_animation_value"), + Patch.new("cyanic-SteamUserStatsLite fix") + .include?('Win32API.new(self.steam_dll_name') + # .remove!, + .replace!("achievements.rb"), + Patch.new("XIV's Simple Reputation System (SRS) for RGSS3 fix") #XIV's Simple Reputation System (SRS) for RGSS3 Romance Level Version: 1.1 + .include?("Simple Reputation System (SRS)") + .sub!("$scene1 = Scene_Menu.new(0)", "$scene1 = Scene_Menu.new"), + Patch.new("Nergal's Item Finding patch fix") #Nergal's Item Finding + .include?("SEARCH_TYPES = [ ITEMS_TO_FIND_PERSON, ITEMS_TO_FIND_SCHOOL,") + .include?("$scene1 = Scene_Menu.new(0)") + .sub!("$scene1 = Scene_Menu.new(0)", "$scene1 = Scene_Menu.new"), + Patch.new("Nergal's XP Gain") #Nergal's XP Gain + .include?("CustomGab.display_message(stat + XpLevel::MAX_LVL_MESSAGE)") + .include?("$scene4 = Scene_Menu.new(0)") + .sub!("$scene4 = Scene_Menu.new(0)", "$scene4 = Scene_Menu.new"), + Patch.new("WF-RGSS base patch") #【WF-RGSS】共通rev29 base + .include?("'MultiByteToWideChar', %w(i l p i p i)") + # .remove!, + .replace!("testencode.rb"), #Window_Base + Patch.new("window check content height fix (mgq paradox)") + .if? {|script| script.name == "Window_Base"} + # .replace!("basewt.rb"), + .sub!("if contents_width > 0 && contents_height > 0", " if contents_height > 10000\nself.contents = Bitmap.new(1, 1)\nelsif contents_width > 0 && contents_height > 0"), + Patch.new("temp bitmap load crash fix monster girl paradox disable preview") #ベース/Module + #maybe the memory buffer is filled up too much?? + # .if? {|script| script.name == "Window_Base"} + .include?('thumnail_file = "Save/Save#{Regexp.last_match(1)}.png"') + .sub!("@thumbnails[Regexp.last_match(1).to_i - 1] = Bitmap.new(thumnail_file)", "@thumbnails[Regexp.last_match(1).to_i - 1] = @dummy_thumbnail #Bitmap.new(thumnail_file)"), + # .replace!("savebitmanwin32api.rb"), + Patch.new("WF-RGSS Exit-EX patch test") #▼ メイン【WF-RGSS】Exit-EX 終了処理 + .include?("Win32API.new('System/WFExit','hookExit','v','l')") + # .remove!, + .replace!("wxexittest.rb"), + Patch.new("HimeWorks' Event Trigger Labels: Fix any_key_pressed? implementation") + .imported?(:TH_EventTriggerLabels) + .replace!("TH_EventTriggerLabels.rb"), + Patch.new("HimeWorks' Simple Audio Encryption: Re-Implement with direct path detection") + .imported?(:TH_SimpleAudioEncryption) + .replace!("TH_SimpleAudioEncryption.rb"), + Patch.new("MOG_Anti_Lag: Fix visible type error") + .imported?(:mog_anti_lag) + .sub!("self.visible = @character.can_update", "self.visible = !!@character.can_update"), + Patch.new("KGC_BitmapExtension ? XP/VX ? error") + .imported?(:BitmapExtension) + # .sub!("class Win32API", "module Win32API"), + .remove!, + Patch.new("Galv's Event Pop-Ups: Fix bitmap constructor call") + .imported?("Galv_Map_Popups") + .match?(/\.font\.shadow\s*=\s*\d+/) + .sub!(/\.font\.shadow\s*=\s*\d+/, ".font.shadow = true"), + Patch.new("Basic Mouse Plugin test disable for now") + .imported?(nil) + .if? {|script| script.name == "BasicMouse"} + .include?("include IBasicMouse") + .sub!("def VZ; return @curr.lZ / 120; end", "def VZ; return 0 if @curr.nil? || @curr.lZ.nil?; return @curr.lZ / 120.0 end"), + Patch.new("Screenshot plugin") + .imported?(nil) + .include?('SAVE_NAME = "ScreenShot/%Y%m%d%H%M%S.png"') + # .sub!("def VZ; return @curr.lZ / 120; end", "def VZ; return 0 if @curr.nil? || @curr.lZ.nil?; return @curr.lZ / 120.0 end"), + # .replace!("BasicMouse.rb"), + .remove!, + Patch.new("Super simple mouse script: Use mkxp mouse API") + .imported?(nil) + .include?("SUPER SIMPLE MOUSE SCRIPT") + .replace!("Mouse.rb"), + Patch.new("RMXP CustomResolution plugin") + .imported?(nil) + .include?("def snapshot(filename = 'Data/snap', quality = 0)") + .replace!("XP_CustomResolution.rb"), + Patch.new("Glitchfinder's Key Input: Shim with MKXP builtins") + .imported?(nil) + .include?("unless method_defined?(:keyinputmodule_input_update)") + .replace!("Glitchfinder_Keyboard_Stub.rb"), + Patch.new("Auto Font Install: Already included in MKXP") + .imported?(nil) + .include?("# ** Auto Font Install") + .remove!, + Patch.new("Extended Music Script: MKXP already supports .mod. other formats aren't available.") + .imported?(nil) + .include?("# Extended Music Script Version 3.5") + .then!{|script| + return if script.context.flag? :incompatible_bgm_checked + unsupp = "wma,psf,minipsf,psf2,minipsf2,gsf,minigsf,usf,miniusf,hps,dsp,spc,gym,cym".split(",") + # TODO: Find unsupported files in Audio/BGM. Then show msgbox if no converted versions available + # Official MKXP-Z also doesn't support mp3 + script.context.mark :incompatible_bgm_checked + } + .remove!, + Patch.new("CRDJ Input script: Use MKXP-Z input extensions") + .imported?(nil) + .include?("# A module that handles input data from a gamepad or keyboard.\r\n# Managed by symbols rather than button numbers in RGSS3.") + .replace!("CRDJ_Input.rb"), + # Specific Inline Patches + Patch.new("Shokushu de sennou patch") + .imported?(nil) + .include?("alias _cao_log_terminate_message terminate_message") + #.replace!("oldregex1.rb"), + .sub!("@text.gsub!(/[", "#"), + Patch.new("Try to fix superclass mismatches from MP Scene_Base") + .imported?(nil) + .include?("======\nMoonpearl's Scene_Base\n-----") + .flag!(:redefinitions_overwrite_class), + # Generic Inline Patches + Patch.new("Disable all font effects") + .flag?(:no_font_effects) # KAWARIKI_MKXP_NO_FONT_EFFECTS + .match?(/(\.f|F)ont\.(default_)?(italic|outline|shadow|bold)/) + # Font is a built-in API, so it's already available in preload + .then!{Font.default_italic = Font.default_outline = Font.default_shadow = Font.default_bold = false} + .sub!(/Font\.default_(italic|outline|shadow|bold)\s*=/, "Font.default_\\1 = false &&") + .sub!(/\.font\.(italic|outline|shadow|bold)\s*\=/, ".font.\\1 = false &&"), + Patch.new("Improve Ruby 1.8 Compatibility") + .if?{|script| script.context[:rgss_version] < 3} # Never on VX Ace, which shipped 1.9 + .match?("self.type", /\Wtype\.to_s\W/, /\Winstance_methods\.include\?\(['"%]/) + .then!{require "ruby18.rb"}, + Patch.new("Game_Player fix") + .imported?(nil) + .if? {|script| script.name == "Game_Player"} + .if? {|script| script.source.include? "else return true"} + .sub!("else return true", "true"), + Patch.new("KGC Bitmap Extension fix") + .imported?(nil) + .if? {|script| script.source.include? "@@reel_stop = RPG::SE.new(CAO::PSLOT::SOUND_REEL_STOP"} + .sub!("@@reel_stop =", "@reel_stop ="), + Patch.new("Response improvement remove") + .imported?(nil) + .match?(/#.*_wdtk_resinp_update/) + .remove!, + Patch.new("Response improvement patch") + .imported?(nil) + # .if? {|script| script.name == "Response improvement script"} + #.include?('@@press_count.each do') + .if? {|script| script.source.include? "_wdtk_resinp_update" } + .sub!('@@press_count', "@press_count") + .sub!(/\b_wdtk_resinp_update\b(?!\s*update)/, "_wdtk_resinp_update;\n@press_count ||= {}"), + # .sub!(/^(?!#.*)\b_wdtk_resinp_update\b(?!\s*update)/, "_wdtk_resinp_update;\n@press_count ||= {}"), + # .sub!(/^(?!#.*)\b_wdtk_resinp_update\b(?!\s*update)/) { "_wdtk_resinp_update;\n@press_count ||= {}" }, + Patch.new("Item Script") + .imported?(nil) + # .if? {|script| script.name == "Response improvement script"} + #.include?('@@press_count.each do') + .include?("CATEGORIZE ITEM SCENE v1.0 ") + # .if? {|script| script.source.include? "_wdtk_resinp_update"} + .sub!("CATEGORY_IDENTIFIER.index(ITEM_DEFAULT_CATEGORY)", "CATEGORY_IDENTIFIER.keys.index(ITEM_DEFAULT_CATEGORY)") + .sub!("CATEGORY_IDENTIFIER.index(:", "CATEGORY_IDENTIFIER.key(:"), + Patch.new("Vitaminpl fix") + .imported?(nil) + .if? {|script| script.name == "Police"} + .if? {|script| script.source.include? "Lucida Sans Unicode"} + .if? {|script| script.source = "Font.default_size = 16"}, + Patch.new("Vitaminpl 2 fix") + .imported?(nil) + .include?("module Wora_NSS") + .sub!("SCREENSHOT_IMAGE = true", "SCREENSHOT_IMAGE = false") + .sub!("PREMADE_IMAGE = true", "PREMADE_IMAGE = false"), + Patch.new("Dark Hero Party") + .imported?(nil) + .include?('text.push(self.to_s.scan(/#<(\S+):/)[0][0].to_s)') + .remove!, + Patch.new("Flirt quest") + .imported?(nil) + .include?('class Spriteset_BattleUnit') + .include?('Spriteset_Kiseki') + .sub!(/\bsuper\b(?!\s*\()/, 'super()'), + + ] +end diff --git a/mkxp-z/Kawariki-patches/ports/BasicMouse.rb b/mkxp-z/Kawariki-patches/ports/BasicMouse.rb new file mode 100644 index 0000000..1d3093f --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/BasicMouse.rb @@ -0,0 +1,158 @@ +module Input + class BasicMouse + include IBasicMouse + + # コンストラクタ + def initialize() + # 関連付けられたウィンドウのハンドル + @hWnd = 0 + + # 前回のマウスの状態 + @prev = nil + + # 現在のマウスの状態 + @curr = nil + + # ボタンの配列 + @buttons = Array.new(self.NumButtons()){ |i| i = ButtonInfo.new() } + + # 何かボタンが押されているか + @isPressedAnyButton = false + + # ステータス情報文字列を更新すべきか + @needToUpdate = true + end + + # 初期化処理 + def Initialize(hWnd) + @hWnd = hWnd + @curr = DIMOUSESTATE2.new(hWnd) + end + + # 更新処理 + def Update + # マウスの更新処理 + @prev = @curr + @curr.Update() + + @isPressedAnyButton = false + for i in 0..NumButtons() - 1 + @buttons[i].Update((@curr.rgbButtons[i] & 0x80) == 128 ? ButtonStatus::Pressed : ButtonStatus::Released) + + if @buttons[i].Pressed() + @isPressedAnyButton = true; + end + end + + @needToUpdate = true + end + + # IBasicMouse::GetStatusString() オーバーライド + def GetStatusString + if @needToUpdate + def Static + leftButton = Keys::Key.new(0, "LeftButton", "左ボタン") + rightButton = Keys::Key.new(1, "RightButton", "右ボタン") + middleButton = Keys::Key.new(2, "MiddleButton", "中央ボタン") + xButton1 = Keys::Key.new(3, "XButton1", "Xボタン1") + xButton2 = Keys::Key.new(4, "XButton2", "Xボタン2") + xButton3 = Keys::Key.new(5, "XButton3", "Xボタン3") + xButton4 = Keys::Key.new(6, "XButton4", "Xボタン4") + xButton5 = Keys::Key.new(7, "XButton5", "Xボタン5") + @keyTable = + [ + leftButton, + rightButton, + middleButton, + xButton1, + xButton2, + xButton3, + xButton4, + xButton5 + ] + end + self.Static() + + puts sprintf("Position : (%5d,%5d)", self.X(), self.Y()) + puts sprintf("Velocity : (%5d,%5d,%5d)", self.VX(), self.VY(), self.VZ()) + puts "[ButtonName] [Pressed] [Released] [Repeated]" + + for i in 0..(NumButtons() - 1) + puts sprintf( + "%-20s %4s%4s%6d %4s%4s %4s%6d", + @keyTable[i].GetName(), + @buttons[i].Pressed() ? "ON" : "OFF", + @buttons[i].JustPressed() ? "ON" : "OFF", + @buttons[i].GetContinuousCount(), + @buttons[i].Released() ? "ON" : "OFF", + @buttons[i].JustReleased() ? "ON" : "OFF", + @buttons[i].Repeated() ? "ON" : "OFF", + @buttons[i].GetRepeatCount() + ) + end + print("\n") + end + end + + # マウスの最大ボタン数 + def NumButtons; return 8; end + + # IBasicMouse::GetPosition() オーバーライド + def GetPosition + pos = System::Math::Vector2.Zero() + pos.x = @curr.lX; pos.y = @curr.lY; + return pos; + end + + # IBasicMouse::SetPosition() オーバーライド + def SetPosition(x, y); Win32RGSS::Cursor::SetCursorPos(x, y); end + + # IBasicMouse::GetVelocity() オーバーライド + def GetVelocity + pos = System::Math::Vector2.Zero() + pos.x = @curr.lX; pos.y = @curr.lY; + return pos + end + + # IBasicMouse::X() オーバーライド + def X; return @curr.lX; end + + # IBasicMouse::Y() オーバーライド + def Y; return @curr.lY; end + + # IBasicMouse::VX() オーバーライド + def VX; return @curr.lX - @prev.lX; end + + # IBasicMouse::VY() オーバーライド + def VY; return @curr.lY - @prev.lY; end + + # IBasicMouse::VZ() オーバーライド + # def VZ; return @curr.lZ / 120; end + def VZ + # Check if @curr and @curr.lZ are valid (not nil) + return 0 if @curr.nil? || @curr.lZ.nil? + return @curr.lZ / 120.0 # Ensure division results in a float + end + + # IBasicMouse::LeftButton() オーバーライド + def LeftButton; return @buttons[0]; end + + # IBasicMouse::RightButton() オーバーライド + def RightButton; return @buttons[1]; end + + # IBasicMouse::MiddleButton() オーバーライド + def MiddleButton; return @buttons[2]; end + + # IBasicMouse::XButton1() オーバーライド + def XButton1; return @buttons[3]; end + + # IBasicMouse::XButton2() オーバーライド + def XButton2; return @buttons[4]; end + + # IBasicMouse::GetButtonState() オーバーライド + def GetButtonState(index); return @buttons[index]; end + + # 1つ以上のボタンが押し下げられている時にはtrueを返します。 + def IsPressedAnyButton; return @isPressedAnyButton; end + end + end diff --git a/mkxp-z/Kawariki-patches/ports/CRDJ_Input.rb b/mkxp-z/Kawariki-patches/ports/CRDJ_Input.rb new file mode 100644 index 0000000..faa7384 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/CRDJ_Input.rb @@ -0,0 +1,452 @@ +# CRDJ_Input.rb Kawariki MKXP-Z port +# Date: 2023-06-01 +# TODO: - handle modified configuration +# - Properly deal with the layout stuff? +#============================================================================== +# ** Input +#------------------------------------------------------------------------------- +# Created By Cidiomar R. Dias Junior +# Originally posted at +# +# Terms of Use: Credit "Cidiomar R. Dias Junior" +# +# Maintained on Hime Works +#------------------------------------------------------------------------------- +# ** Description +# +# A module that handles input data from a gamepad or keyboard. +# Managed by symbols rather than button numbers in RGSS3. +#------------------------------------------------------------------------------- +# ** Usage +# +# Scroll down to the configuration section. It is around line 185. +# You can set up your key mappings there. Use the reference to get the names +# of the keys. +# +#=============================================================================== + +module CRDJ_Input + #-------------------------------------------------------------------------- + # * Keymap in symbols + # To get a key from the keymap, you can use Input.key(sym) or Input:KEYMAP[sym] + # Or if you want to use a symbol in a input function, just pass the symbol + # as argument. + #-------------------------------------------------------------------------- + KEYMAP = { + LBUTTON: 0x01, RBUTTON: 0x02, + CANCEL: 0x03, MBUTTON: 0x04, + XBUTTON1: 0x05, XBUTTON2: 0x06, + BACK: 0x08, TAB: 0x09, + CLEAR: 0x0c, RETURN: 0x0d, + SHIFT: 0x10, CONTROL: 0x11, + MENU: 0x12, PAUSE: 0x13, + CAPITAL: 0x14, KANA: 0x15, + JUNJA: 0x17, FINAL: 0x18, + HANJA: 0x19, + ESCAPE: 0x1b, CONVERT: 0x1c, + NONCONVERT: 0x1d, ACCEPT: 0x1e, + MODECHANGE: 0x1f, SPACE: 0x20, + PRIOR: 0x21, NEXT: 0x22, + END: 0x23, HOME: 0x24, + LEFT: 0x25, UP: 0x26, + RIGHT: 0x27, DOWN: 0x28, + SELECT: 0x29, PRINT: 0x2a, + EXECUTE: 0x2b, SNAPSHOT: 0x2c, + INSERT: 0x2d, DELETE: 0x2e, + HELP: 0x2f, N0: 0x30, + KEY_1: 0x31, KEY_2: 0x32, + KEY_3: 0x33, KEY_4: 0x34, + KEY_5: 0x35, KEY_6: 0x36, + KEY_7: 0x37, KEY_8: 0x38, + KEY_9: 0x39, colon: 0x3a, + semicolon: 0x3b, less: 0x3c, + equal: 0x3d, greater: 0x3e, + question: 0x3f, at: 0x40, + LETTER_A: 0x41, LETTER_B: 0x42, + LETTER_C: 0x43, LETTER_D: 0x44, + LETTER_E: 0x45, LETTER_F: 0x46, + LETTER_G: 0x47, LETTER_H: 0x48, + LETTER_I: 0x49, LETTER_J: 0x4a, + LETTER_K: 0x4b, LETTER_L: 0x4c, + LETTER_M: 0x4d, LETTER_N: 0x4e, + LETTER_O: 0x4f, LETTER_P: 0x50, + LETTER_Q: 0x51, LETTER_R: 0x52, + LETTER_S: 0x53, LETTER_T: 0x54, + LETTER_U: 0x55, LETTER_V: 0x56, + LETTER_W: 0x57, LETTER_X: 0x58, + LETTER_Y: 0x59, LETTER_Z: 0x5a, + LWIN: 0x5b, RWIN: 0x5c, + APPS: 0x5d, asciicircum: 0x5e, + SLEEP: 0x5f, NUMPAD0: 0x60, + NUMPAD1: 0x61, NUMPAD2: 0x62, + NUMPAD3: 0x63, NUMPAD4: 0x64, + NUMPAD5: 0x65, NUMPAD6: 0x66, + NUMPAD7: 0x67, NUMPAD8: 0x68, + NUMPAD9: 0x69, MULTIPLY: 0x6a, + ADD: 0x6b, SEPARATOR: 0x6c, + SUBTRACT: 0x6d, DECIMAL: 0x6e, + DIVIDE: 0x6f, F1: 0x70, + F2: 0x71, F3: 0x72, + F4: 0x73, F5: 0x74, + F6: 0x75, F7: 0x76, + F8: 0x77, F9: 0x78, + F10: 0x79, F11: 0x7a, + F12: 0x7b, F13: 0x7c, + F14: 0x7d, F15: 0x7e, + F16: 0x7f, F17: 0x80, + F18: 0x81, F19: 0x82, + F20: 0x83, F21: 0x84, + F22: 0x85, F23: 0x86, + F24: 0x87, NUMLOCK: 0x90, + SCROLL: 0x91, LSHIFT: 0xa0, + RSHIFT: 0xa1, LCONTROL: 0xa2, + RCONTROL: 0xa3, LMENU: 0xa4, + RMENU: 0xa5, BROWSER_BACK: 0xa6, + BROWSER_FORWARD: 0xa7, BROWSER_REFRESH: 0xa8, + BROWSER_STOP: 0xa9, BROWSER_SEARCH: 0xaa, + BROWSER_FAVORITES: 0xab, BROWSER_HOME: 0xac, + VOLUME_MUTE: 0xad, VOLUME_DOWN: 0xae, + VOLUME_UP: 0xaf, MEDIA_NEXT_TRACK: 0xb0, + MEDIA_PREV_TRACK: 0xb1, MEDIA_STOP: 0xb2, + MEDIA_PLAY_PAUSE: 0xb3, LAUNCH_MAIL: 0xb4, + LAUNCH_MEDIA_SELECT: 0xb5, LAUNCH_APP1: 0xb6, + LAUNCH_APP2: 0xb7, cedilla: 0xb8, + onesuperior: 0xb9, masculine: 0xba, + guillemotright: 0xbb, onequarter: 0xbc, + onehalf: 0xbd, threequarters: 0xbe, + questiondown: 0xbf, Agrave: 0xc0, + Aacute: 0xc1, Acircumflex: 0xc2, + Atilde: 0xc3, Adiaeresis: 0xc4, + Aring: 0xc5, AE: 0xc6, + Ccedilla: 0xc7, Egrave: 0xc8, + Eacute: 0xc9, Ecircumflex: 0xca, + Ediaeresis: 0xcb, Igrave: 0xcc, + Iacute: 0xcd, Icircumflex: 0xce, + Idiaeresis: 0xcf, ETH: 0xd0, + Ntilde: 0xd1, Ograve: 0xd2, + Oacute: 0xd3, Ocircumflex: 0xd4, + Otilde: 0xd5, Odiaeresis: 0xd6, + multiply: 0xd7, Oslash: 0xd8, + Ugrave: 0xd9, Uacute: 0xda, + Ucircumflex: 0xdb, Udiaeresis: 0xdc, + Yacute: 0xdd, THORN: 0xde, + ssharp: 0xdf, agrave: 0xe0, + aacute: 0xe1, acircumflex: 0xe2, + atilde: 0xe3, adiaeresis: 0xe4, + PROCESSKEY: 0xe5, ae: 0xe6, + PACKET: 0xe7, egrave: 0xe8, + eacute: 0xe9, ecircumflex: 0xea, + ediaeresis: 0xeb, igrave: 0xec, + iacute: 0xed, icircumflex: 0xee, + idiaeresis: 0xef, eth: 0xf0, + ntilde: 0xf1, ograve: 0xf2, + oacute: 0xf3, ocircumflex: 0xf4, + otilde: 0xf5, ATTN: 0xf6, + CRSEL: 0xf7, EXSEL: 0xf8, + EREOF: 0xf9, PLAY: 0xfa, + ZOOM: 0xfb, NONAME: 0xfc, + PA1: 0xfd, thorn: 0xfe, + ydiaeresis: 0xff + } + + KEYMAP[:WIN] = [KEYMAP[:LWIN], KEYMAP[:RWIN]] + + #=============================================================================== + # Configuration + #=============================================================================== + #-------------------------------------------------------------------------- + # * Default Keys, you can configure here instead of by pressing F1. + #-------------------------------------------------------------------------- + UP = [KEYMAP[:UP]] + DOWN = [KEYMAP[:DOWN]] + LEFT = [KEYMAP[:LEFT]] + RIGHT = [KEYMAP[:RIGHT]] + A = [KEYMAP[:SHIFT]] + B = [KEYMAP[:ESCAPE], KEYMAP[:LETTER_X]] + C = [KEYMAP[:RETURN], KEYMAP[:LETTER_Z]] + X = [] + Y = [] + Z = [] + L = [KEYMAP[:PRIOR]] + R = [KEYMAP[:NEXT]] + F5 = [KEYMAP[:F5]] + F6 = [KEYMAP[:F6]] + F7 = [KEYMAP[:F7]] + F8 = [KEYMAP[:F8]] + F9 = [KEYMAP[:F9]] + SHIFT = [KEYMAP[:SHIFT]] + CTRL = [KEYMAP[:CONTROL]] + ALT = [KEYMAP[:MENU]] + + #=============================================================================== + # Rest of script + #=============================================================================== + #-------------------------------------------------------------------------- + # * Symbol version of default keys. + #-------------------------------------------------------------------------- + SYM_KEYS = { + :UP => UP, + :LEFT => LEFT, + :DOWN => DOWN, + :RIGHT => RIGHT, + :A => A, + :B => B, + :C => C, + :X => X, + :Y => Y, + :Z => Z, + :L => L, + :R => R, + :F5 => F5, + :F6 => F6, + :F7 => F7, + :F8 => F8, + :F9 => F9, + :SHIFT => SHIFT, + :CTRL => CTRL, + :ALT => ALT + } + + # Letters that can have accent + PssbLetters = "AEIOUCNYaeioucny" + # Accents, in ASCII, configured at runtime to avoid encoding troubles + Accents = [96.chr, 180.chr, 94.chr, 126.chr, 168.chr].join + NonCompatChars = [180, 168] + + module InputExtension + #-------------------------------------------------------------------------- + # * Determines whether the button corresponding to the symbol sym is + # currently being pressed. + # + # If the button is being pressed, returns TRUE. If not, returns FALSE. + # + # if Input.press?(:C) + # do_something + # end + #-------------------------------------------------------------------------- + def press?(keys) + if keys.is_a?(Numeric) + k = keys.to_i + return (self.pressex? k) + elsif keys.is_a?(Array) + return keys.any? {|key| self.pressex?(key) } + elsif keys.is_a?(Symbol) + if SYM_KEYS.key?(keys) + return super(keys) + return SYM_KEYS[keys].any? {|key| self.pressex?(key) } + elsif KEYMAP.key?(keys) + k = KEYMAP[keys] + return self.pressex?(k) + else + return false + end + end + end + + #-------------------------------------------------------------------------- + # * Determines whether the button corresponding to the symbol sym is + # currently being pressed again. + # "Pressed again" is seen as time having passed between the button being + # not pressed and being pressed. + # + # If the button is being pressed, returns TRUE. If not, returns FALSE. + #-------------------------------------------------------------------------- + def trigger?(keys) + if keys.is_a?(Numeric) + return Input.triggerex?(keys.to_i) + elsif keys.is_a?(Array) + return keys.any? {|key| self.triggerex?(key)} + elsif keys.is_a?(Symbol) + if SYM_KEYS.key?(keys) + return super(keys) + Preload.print "Trigger #{keys} #{SYM_KEYS[keys]} #{x}" + return SYM_KEYS[keys].any? {|key| self.triggerex?(key)} + elsif KEYMAP.key?(keys) + return self.triggerex?(KEYMAP[keys]) + else + return false + end + end + end + + #-------------------------------------------------------------------------- + # * Determines whether the button corresponding to the symbol sym is + # currently being pressed again. + # Unlike trigger?, takes into account the repeated input of a button being + # held down continuously. + # + # If the button is being pressed, returns TRUE. If not, returns FALSE. + #-------------------------------------------------------------------------- + def repeat?(keys) + if keys.is_a?(Numeric) + key = keys.to_i + return self.repeatex?(key) + elsif keys.is_a?(Array) + return keys.any? {|key| self.repeatex?(key)} + elsif keys.is_a?(Symbol) + if SYM_KEYS.key?(keys) + return super(keys) + return SYM_KEYS[keys].any? {|key| self.repeatex?(key)} + elsif KEYMAP.key?(keys) + return self.repeatex?(KEYMAP[keys]) + else + return false + end + end + end + + #-------------------------------------------------------------------------- + # * Determines whether the button corresponding to the symbol sym + # was released. + # + # If the button was released, returns TRUE. If not, returns FALSE. + #-------------------------------------------------------------------------- + def release?(keys) + if keys.is_a?(Numeric) + return self.releaseex? keys.to_i + elsif keys.is_a?(Array) + return keys.any? {|key| self.release?(key)} + elsif keys.is_a?(Symbol) + if SYM_KEYS.key?(keys) + return super(keys) + return SYM_KEYS[keys].any? {|key| self.releaseex?(key)} + elsif KEYMAP.key?(keys) + return self.releaseex?(KEYMAP[keys]) + else + return false + end + end + end + + #-------------------------------------------------------------------------- + # * Checks the status of the directional buttons, translates the data into + # a specialized 4-direction input format, and returns the number pad + # equivalent (2, 4, 6, 8). + # + # If no directional buttons are being pressed (or the equivalent), returns 0. + #-------------------------------------------------------------------------- + # def dir4 + # return 2 if self.press?(DOWN) + # return 4 if self.press?(LEFT) + # return 6 if self.press?(RIGHT) + # return 8 if self.press?(UP) + # return 0 + # end + + #-------------------------------------------------------------------------- + # * Checks the status of the directional buttons, translates the data into + # a specialized 8-direction input format, and returns the number pad + # equivalent (1, 2, 3, 4, 6, 7, 8, 9). + # + #If no directional buttons are being pressed (or the equivalent), returns 0. + #-------------------------------------------------------------------------- + # def dir8 + # down = self.press?(DOWN) + # left = self.press?(LEFT) + # return 1 if down and left + # right = self.press?(RIGHT) + # return 3 if down and right + # up = self.press?(UP) + # return 7 if up and left + # return 9 if up and right + # return 2 if down + # return 4 if left + # return 6 if right + # return 8 if up + # return 0 + # end + + #-------------------------------------------------------------------------- + # * Gets the character that correspond to vk using the keyboard layout + #-------------------------------------------------------------------------- + def get_character(vk) + # FIXME: do something smarter + return vk + end + + #-------------------------------------------------------------------------- + # * Accents Table, to bo used in conversion from ASCII to UTF8 + #-------------------------------------------------------------------------- + AccentsCharsConv = { + "A" => ["A", "A", "A", "A", "A"], + "E" => ["E", "E", "E", 0, "E"], + "I" => ["I", "I", "I", 0, "I"], + "O" => ["O", "O", "O", "O", "O"], + "U" => ["U", "U", "U", 0, "U"], + "C" => [ 0 , "C", 0 , 0, 0 ], + "N" => [ 0 , 0, 0 , "N", 0 ], + "Y" => [ 0 , "Y", 0 , 0, "?"], + "a" => ["a", "a", "a", "a", "a"], + "e" => ["e", "e", "e", 0 , "e"], + "i" => ["i", "i", "i", 0 , "i"], + "o" => ["o", "o", "o", "o", "o"], + "u" => ["u", "u", "u", 0 , "u"], + "c" => [ 0 , "c", 0 , 0 , 0 ], + "n" => [ 0 , 0 , 0 , "n", 0 ], + "y" => [ 0 , "y", 0 , 0 , "y"], + } + + @last_accent = nil + + #-------------------------------------------------------------------------- + # * Get inputed string transcoded to UTF8 + #-------------------------------------------------------------------------- + def UTF8String + result = "" + 31.upto(255) {|key| + if self.repeat?(key) + c = self.get_character(key) + if (cc = c.unpack("C")[0]) and NonCompatChars.include?(cc) + result += cc.chr + else + result += UNICODE_TO_UTF8.convertc if c != "" + end + end + } + return "" if result == "" + if @last_accent + result = @last_accent + result + @last_accent = nil + end + f_result = "" + jump = false + for i in 0 ... result.length + c = result[i] + if jump + jump = false + next + end + if Accents.include?c + if (nc = result[i+1]) != nil + if PssbLetters.include?(nc) + if (ac = AccentsCharsConv[nc][Accents.indexc]) != 0 + f_result << ac + jump = true + else + f_result << c + f_result << nc + jump = true + end + elsif Accents.include?(nc) + f_result << c + f_result << nc + jump = true + else + f_result << c + f_result << nc + jump = true + end + else + @last_accent = c + end + else + f_result << c + end + end + return f_result + end + end + + Input.singleton_class.prepend InputExtension + Input.const_set :KEYMAP, KEYMAP +end diff --git a/mkxp-z/Kawariki-patches/ports/Glitchfinder_Keyboard_Stub.rb b/mkxp-z/Kawariki-patches/ports/Glitchfinder_Keyboard_Stub.rb new file mode 100644 index 0000000..9130b2c --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/Glitchfinder_Keyboard_Stub.rb @@ -0,0 +1,254 @@ +# Glitchfinder's Key Input MKXP stub +# Only press? trigger? release? repeat? implemented +# Direct forwarding to respective MKXP Input.*ex? methods +# Key constants copied from 1.30 source +# ANYKEY is not implemented by MKXP +# +# Authors: Glitchfinder, Taeyeon Mori +# Date: 2022-01-31 + +module Keys + # ****************************** Key names ******************************** + #-------------------------------------------------------------------------- + # * Miscellaneous Keys + #-------------------------------------------------------------------------- + CANCEL = 0x03 # Control-Break Processing + BACKSPACE = 0x08 # Backspace Key + TAB = 0x09 # Tab Key + CLEAR = 0x0C # Clear Key + RETURN = 0x0D # Enter Key + SHIFT = 0x10 # Shift Key + CONTROL = 0x11 # Ctrl Key + MENU = 0x12 # Alt Key + PAUSE = 0x13 # Pause Key + ESCAPE = 0x1B # Esc Key + CONVERT = 0x1C # IME Convert Key + NONCONVERT = 0x1D # IME Nonconvert Key + ACCEPT = 0x1E # IME Accept Key + SPACE = 0x20 # Space Bar Key (Space, usually blank) + PRIOR = 0x21 # Page Up Key + NEXT = 0x22 # Page Down Key + ENDS = 0x23 # End Key + HOME = 0x24 # Home Key + LEFT = 0x25 # Left Arrow Key + UP = 0x26 # Up Arrow Key + RIGHT = 0x27 # Right Arrow Key + DOWN = 0x28 # Down Arrow Key + SELECT = 0x29 # Select Key + PRINT = 0x2A # Print Key + EXECUTE = 0x2B # Execute Key + SNAPSHOT = 0x2C # Print Screen Key + DELETE = 0x2E # Delete Key + HELP = 0x2F # Help Key + LSHIFT = 0xA0 # Left Shift Key + RSHIFT = 0xA1 # Right Shift Key + LCONTROL = 0xA2 # Left Control Key (Ctrl) + RCONTROL = 0xA3 # Right Control Key (Ctrl) + LMENU = 0xA4 # Left Menu Key (Alt) + RMENU = 0xA5 # Right Menu Key (Alt) + PACKET = 0xE7 # Used to Pass Unicode Characters as Keystrokes + #-------------------------------------------------------------------------- + # * Number Keys + #-------------------------------------------------------------------------- + N0 = 0x30 # 0 Key + N1 = 0x31 # 1 Key + N2 = 0x32 # 2 Key + N3 = 0x33 # 3 Key + N4 = 0x34 # 4 Key + N5 = 0x35 # 5 Key + N6 = 0x36 # 6 Key + N7 = 0x37 # 7 Key + N8 = 0x38 # 8 Key + N9 = 0x39 # 9 Key + #-------------------------------------------------------------------------- + # * Letter Keys + #-------------------------------------------------------------------------- + A = 0x41 # A Key + B = 0x42 # B Key + C = 0x43 # C Key + D = 0x44 # D Key + E = 0x45 # E Key + F = 0x46 # F Key + G = 0x47 # G Key + H = 0x48 # H Key + I = 0x49 # I Key + J = 0x4A # J Key + K = 0x4B # K Key + L = 0x4C # L Key + M = 0x4D # M Key + N = 0x4E # N Key + O = 0x4F # O Key + P = 0x50 # P Key + Q = 0x51 # Q Key + R = 0x52 # R Key + S = 0x53 # S Key + T = 0x54 # T Key + U = 0x55 # U Key + V = 0x56 # V Key + W = 0x57 # W Key + X = 0x58 # X Key + Y = 0x59 # Y Key + Z = 0x5A # Z Key + #-------------------------------------------------------------------------- + # * Windows Keys + #-------------------------------------------------------------------------- + LWIN = 0x5B # Left Windows Key (Natural keyboard) + RWIN = 0x5C # Right Windows Key (Natural Keyboard) + APPS = 0x5D # Applications Key (Natural keyboard) + SLEEP = 0x5F # Computer Sleep Key + BROWSER_BACK = 0xA6 # Browser Back Key + BROWSER_FORWARD = 0xA7 # Browser Forward Key + BROWSER_REFRESH = 0xA8 # Browser Refresh Key + BROWSER_STOP = 0xA9 # Browser Stop Key + BROWSER_SEARCH = 0xAA # Browser Search Key + BROWSER_FAVORITES = 0xAB # Browser Favorites Key + BROWSER_HOME = 0xAC # Browser Start and Home Key + VOLUME_MUTE = 0xAD # Volume Mute Key + VOLUME_DOWN = 0xAE # Volume Down Key + VOLUME_UP = 0xAF # Volume Up Key + MEDIA_NEXT_TRACK = 0xB0 # Next Track Key + MEDIA_PREV_TRACK = 0xB1 # Previous Track Key + MEDIA_STOP = 0xB2 # Stop Media Key + MEDIA_PLAY_PAUSE = 0xB3 # Play/Pause Media Key + LAUNCH_MAIL = 0xB4 # Start Mail Key + LAUNCH_MEDIA_SELECT = 0xB5 # Select Media Key + LAUNCH_APP1 = 0xB6 # Start Application 1 Key + LAUNCH_APP2 = 0xB7 # Start Application 2 Key + PROCESSKEY = 0xE5 # IME Process Key + ATTN = 0xF6 # Attn Key + CRSEL = 0xF7 # CrSel Key + EXSEL = 0xF8 # ExSel Key + EREOF = 0xF9 # Erase EOF Key + PLAY = 0xFA # Play Key + ZOOM = 0xFB # Zoom Key + PA1 = 0xFD # PA1 Key + #-------------------------------------------------------------------------- + # * Number Pad Keys + #-------------------------------------------------------------------------- + NUMPAD0 = 0x60 # Numeric Keypad 0 Key + NUMPAD1 = 0x61 # Numeric Keypad 1 Key + NUMPAD2 = 0x62 # Numeric Keypad 2 Key + NUMPAD3 = 0x63 # Numeric Keypad 3 Key + NUMPAD4 = 0x64 # Numeric Keypad 4 Key + NUMPAD5 = 0x65 # Numeric Keypad 5 Key + NUMPAD6 = 0x66 # Numeric Keypad 6 Key + NUMPAD7 = 0x67 # Numeric Keypad 7 Key + NUMPAD8 = 0x68 # Numeric Keypad 8 Key + NUMPAD9 = 0x69 # Numeric Keypad 9 Key + MULTIPLY = 0x6A # Multiply Key (*) + ADD = 0x6B # Add Key (+) + SEPARATOR = 0x6C # Separator Key + SUBTRACT = 0x6D # Subtract Key (-) + DECIMAL = 0x6E # Decimal Key (.) + DIVIDE = 0x6F # Divide Key (/) + #-------------------------------------------------------------------------- + # * Function Keys + #-------------------------------------------------------------------------- + F1 = 0x70 # F1 Key + F2 = 0x71 # F2 Key + F3 = 0x72 # F3 Key + F4 = 0x73 # F4 Key + F5 = 0x74 # F5 Key + F6 = 0x75 # F6 Key + F7 = 0x76 # F7 Key + F8 = 0x77 # F8 Key + F9 = 0x78 # F9 Key + F10 = 0x79 # F10 Key + F11 = 0x7A # F11 Key + F12 = 0x7B # F12 Key + F13 = 0x7C # F13 Key + F14 = 0x7D # F14 Key + F15 = 0x7E # F15 Key + F16 = 0x7F # F16 Key + F17 = 0x80 # F17 Key + F18 = 0x81 # F18 Key + F19 = 0x82 # F19 Key + F20 = 0x83 # F20 Key + F21 = 0x84 # F21 Key + F22 = 0x85 # F22 Key + F23 = 0x86 # F23 Key + F24 = 0x87 # F24 Key + #-------------------------------------------------------------------------- + # * Toggle Keys + #-------------------------------------------------------------------------- + CAPITAL = 0x14 # Caps Lock Key + KANA = 0x15 # IME Kana Mode Key + HANGUL = 0x15 # IME Hangul Mode Key + JUNJA = 0x17 # IME Junja Mode Key + FINAL = 0x18 # IME Final Mode Key + HANJA = 0x19 # IME Hanja Mode Key + KANJI = 0x19 # IME Kanji Mode Key + MODECHANGE = 0x1F # IME Mode Change Request Key + INSERT = 0x2D # Insert Key + NUMLOCK = 0x90 # Num Lock Key + SCROLL = 0x91 # Scroll Lock Key + #-------------------------------------------------------------------------- + # * OEM Keys (Vary by keyboard) + #-------------------------------------------------------------------------- + OEM_1 = 0xBA # Misc Characters (; : in USA 101/102 Keyboards) + OEM_SEMICOLON = 0xBA + OEM_PLUS = 0xBB # + = Key + OEM_COMMA = 0xBC # , < Key + OEM_MINUS = 0xBD # - _ Key + OEM_PERIOD = 0xBE # . > Key + OEM_2 = 0xBF # Misc Characters (/ ? in USA 101/102 Keyboards) + OEM_SLASH = 0xBF + OEM_3 = 0xC0 # Misc Characters (` ~ in USA 101/102 Keyboards) + OEM_GRAVE = 0xC0 + OEM_4 = 0xDB # Misc Characters ([ { in USA 101/102 Keyboards) + OEM_OPENBRACKET = 0xDB + OEM_5 = 0xDC # Misc Characters (\ | in USA 101/102 Keyboards) + OEM_BACKSLASH = 0xDC + OEM_6 = 0xDD # Misc Characters (] } in USA 101/102 Keyboards) + OEM_CLOSEBRACKET = 0xDD + OEM_7 = 0xDE # Misc Characters (' " in USA 101/102 Keyboards) + OEM_APOSTROPHE = 0xDE + OEM_8 = 0xDF # Misc Characters (Varies by Keyboard) + OEM_9 = 0xE1 # OEM Specific + OEM_10 = 0x92 # OEM Specific + OEM_11 = 0x93 # OEM Specific + OEM_12 = 0x94 # OEM Specific + OEM_13 = 0x95 # OEM Specific + OEM_14 = 0x96 # OEM Specific + OEM_15 = 0xE3 # OEM Specific + OEM_16 = 0xE4 # OEM Specific + OEM_17 = 0xE6 # OEM Specific + OEM_18 = 0xE9 # OEM Specific + OEM_19 = 0xEA # OEM Specific + OEM_20 = 0xEB # OEM Specific + OEM_21 = 0xEC # OEM Specific + OEM_22 = 0xED # OEM Specific + OEM_23 = 0xEE # OEM Specific + OEM_24 = 0xEF # OEM Specific + OEM_25 = 0xF1 # OEM Specific + OEM_26 = 0xF2 # OEM Specific + OEM_27 = 0xF3 # OEM Specific + OEM_28 = 0xF4 # OEM Specific + OEM_29 = 0xF5 # OEM Specific + OEM_102 = 0xE2 # Angle Bracket or Backslash on RT-102 Keyboards + OEM_CLEAR = 0xFE # Clear Key + #-------------------------------------------------------------------------- + # * Special Keys + #-------------------------------------------------------------------------- + #ANYKEY = 0x100 # Any Key + + # ********************************* Stub ********************************** + def self.update + end + + def self.press?(key) + return Input.pressex? key + end + + def self.trigger?(key) + return Input.triggerex? key + end + + def self.repeat?(key) + return Input.repeatex? key + end + + def self.release?(key) + return Input.releaseex? key + end +end \ No newline at end of file diff --git a/mkxp-z/Kawariki-patches/ports/Mouse.rb b/mkxp-z/Kawariki-patches/ports/Mouse.rb new file mode 100644 index 0000000..03bbe79 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/Mouse.rb @@ -0,0 +1,1052 @@ +#============================================================================ +# SUPER SIMPLE MOUSE SCRIPT +# v1.10 by Shaz, mkxp port by Taeyeon Mori +#---------------------------------------------------------------------------- +# This is a conversion of the XP Mouse script by Near Fantastica and +# SephirothSpawn modified by Amaranth Games, to run under VX Ace. +#---------------------------------------------------------------------------- +# To Install: +# Copy and paste into a new slot in materials, below all other scripts +#---------------------------------------------------------------------------- +# To Customize: +# Add keyword icon index pairs to the ICON hash (below this documentation). +# Each of the keywords can be used in an event comment to make the mouse +# cursor change into that icon when hovering over the event. +#---------------------------------------------------------------------------- +# To Use: +# Add the following comment to an event page: +# +# where icon is the keyword from the ICON hash below +# x and y are the offsets to override player movement (optional) +# name is the text to display next to the icon when hovering over the event (optional) +# +# Examples: +# +# will change the cursor into the 'fight' icon when over the event +# +# will change the cursor into the 'touch' icon when over the event, and +# make the player walk to the tile below the event when the mouse button is +# clicked +# +# will change the cursor into the 'talk' icon and display the name Gloria +# +# will change the cursor into the 'talk' icon and display the name Henry Smith, +# and when the mouse button is clicked, the player will walk to the tile +# two below the event (good to use for shops where there's a counter in between) +# +# To force pathfinding on the player or an event, simply add a move route with +# the player or event as the subject, with a Script command, and call +# find_path(x, y) where x and y are the coordinates of the tile you want to move to +# Examples: +# Set Move Route (Player): Script: find_path(5, 8) +# will make the player find a path to tile 5, 8 +# Set Move Route (This Event): Script: find_path(10, 5) +# will make the event find a path to tile 10, 5 +# +# NOTE: The path will be ATTEMPTED. If there is no path TO that exact tile, +# a path to an adjacent tile will be attempted. If no path is found there +# either, no movement will occur. +# If a route is found, the player or event will begin moving towards it. But +# if their path is blocked while they are moving, movement will be cancelled. +#---------------------------------------------------------------------------- +# Author's Notes: +# This script should work with any RTP script. +# I do not guarantee that it will work with ANY other script (especially anything +# that overrides player or event movement, such as pixel movement scripts, or +# custom window scripts). +# +# Script OVERWRITES the following methods: +# Game_Map.setup_starting_map_event +# Game_Map.setup_autorun_common_event +# +# If you have other scripts that ALIAS these methods, this mouse script should +# be placed above them. +#---------------------------------------------------------------------------- +# Terms: +# Use in free and commercial games +# Credit: Near Fantastica, SephirothSpawn, Amaranth Games, Shaz +#---------------------------------------------------------------------------- +# Versions: +# 1.0 - 6 Sept 2013 - initial release +# 1.02 - 7 Sept 2013 - fixed crash when loading games saved prior to adding script +# - fixed player gets stuck on impassable area on world map +# when clicking while leaving air ship +# 1.03 - 8 Sept 2013 - fixed actor moving to diagonal tile instead of adjacent +# 1.04 - 10 Sept 2013 - fixed vehicle pathfinding on world map +# - fixed event trigger when no path found +# 1.05 - 14 Sept 2013 - tweaked accessing of tilemap offset +# 1.06 - 3 Nov 2013 - disabled mouse movement when waiting for NPC move route +# - fixed events not triggering after player finishes walking +# 1.07 - 6 Nov 2013 - slow down mouse scrolling, and don't loop save files +# 1.08 - 24 Nov 2013 - cater for YEA Core large resolution with too-small maps +# - fixed early event activation bug introduced in 1.06 +# - replaced calc of Windows_Selectable boundaries with item_rect +# - added ability to completely disable mouse +# 1.09 - 21 Dec 2013 - fixed mouse re-enable when calling common events +# 1.10 - 6 Apr 2014 - add interaction for top part of > 32pixel high event +# - activate an event without walking up to it +# (add comment at top of event page) +# - arrow keys override mouse movement when pathfinding +# - ignore mouse in menus when using keyboard +# - make player walk to counter opposite shopkeepers +# kk1 - 5 Jan 2022 - port to mkxp-z +#============================================================================ + + + + + + +#============================================================================ +# SUPER SIMPLE MOUSE SCRIPT +# Mouse Sprite +#============================================================================ + + +# Add/remove/change icon names here. The icon name is what will be used in the +# event command to show a different mouse icon when hovering over +# the event. These MUST be in lower case here! +ICON = {'arrow' => 528, 'talk' => 4, 'look' => 3, 'fight' => 116, + 'touch' => 491, 'exit' => 121, 'work' => 560, 'workshowel' => 576} +DEFAULT_ICON = 'arrow' + +class Sprite_Mouse < Sprite + #-------------------------------------------------------------------------- + # * Initialization + #-------------------------------------------------------------------------- + def initialize + super + self.z = 10100 + self.ox = 4 + update + @dummy = Bitmap.new(32, 32) + self.bitmap = Bitmap.new(32, 32) + @enabled = true + @ignored = false + end + #-------------------------------------------------------------------------- + # * Frame Update + #-------------------------------------------------------------------------- + def update + return if !@enabled + super + if !SceneManager.scene.nil? + if !Mouse.position.nil? + mx, my = *Mouse.position + if @cursor == DEFAULT_ICON + self.x = mx unless mx.nil? + else + self.x = [mx, Graphics.width - self.bitmap.width].min unless mx.nil? + end + self.y = my unless my.nil? + end + if @scene != SceneManager.scene.class || Mouse.trigger? + @scene = SceneManager.scene.class + set_bitmap + end + end + end + #-------------------------------------------------------------------------- + # * Set Bitmap + #-------------------------------------------------------------------------- + def set_bitmap(cursor = DEFAULT_ICON, text = nil) + if @ignored + cursor = DEFAULT_ICON + text = nil + end + + if @cursor != cursor || @text != text + @cursor = cursor + @text = text + item_cursor = ICON[cursor] + rect = Rect.new(item_cursor % 16 * 24, item_cursor / 16 * 24, 24, 24) + if @text.nil? + self.bitmap = Bitmap.new(24, 32) + self.bitmap.blt(0, 0, Cache.system('Iconset'), rect) + else + w = @dummy.text_size(@text).width + h = @dummy.font.size + bitmap = Bitmap.new(26 + w, [32, h+2].max) + bitmap.font.size = @dummy.font.size + bitmap.font.shadow = true + if self.x + 26 + w > Graphics.width + bitmap.draw_text(0, 0, w, h, @text) + bitmap.blt(w, 0, Cache.system('Iconset'), rect) + else + bitmap.blt(0, 0, Cache.system('Iconset'), rect) + bitmap.draw_text(26, 0, w, h, @text) + end + self.bitmap = bitmap + end + end + end + #-------------------------------------------------------------------------- + # * Update Event Cursors + #-------------------------------------------------------------------------- + def update_event_cursors + # Remove mouse icon and text if we're off the grid + if Mouse.grid.nil? + set_bitmap + return + end + # Set cursor and text according to event + x, y = *Mouse.grid + event = $game_map.lowest_mouse_event_xy(x, y) + unless event.nil? && y < 410 + if !event.mouse_icon.nil? || !event.mouse_text.nil? + set_bitmap(event.mouse_icon, event.mouse_text) + return + end + end + # default bitmap if not over an event + set_bitmap + end + #-------------------------------------------------------------------------- + # * Enable Mouse + #-------------------------------------------------------------------------- + def enabled=(value) + @enabled = value + self.visible = value + end + #-------------------------------------------------------------------------- + # * Mouse Enabled? + #-------------------------------------------------------------------------- + def enabled? + @enabled + end + #-------------------------------------------------------------------------- + # * Ignore Mouse + #-------------------------------------------------------------------------- + def ignored=(value) + @ignored = value + end + #-------------------------------------------------------------------------- + # * Mouse Ignored? + #-------------------------------------------------------------------------- + def ignored? + @ignored + end +end + +$mouse = Sprite_Mouse.new + + + + + + +#============================================================================ +# SUPER SIMPLE MOUSE SCRIPT +# Mouse Module +#============================================================================ + + +#============================================================================== +# ** Mouse Module +#------------------------------------------------------------------------------ +# by Near Fantastica and SephirothSpawn +# adapted and converted to VX Ace by Shaz +#============================================================================== +module Mouse + #-------------------------------------------------------------------------- + # * Mouse to Input Triggers + # key => Input::KeyCONSTANT (key: 0 - left, 1 - middle, 2 - right) + #-------------------------------------------------------------------------- + Mouse_to_Input_Triggers = {0 => Input::C, 1 => Input::B, 2 => Input::A} + #-------------------------------------------------------------------------- + # * Module Variables + #-------------------------------------------------------------------------- + @old_pos = 0 + @sys_cursor_visible = true + #-------------------------------------------------------------------------- + # * Mouse Grid Position + #-------------------------------------------------------------------------- + def self.grid + return nil if @pos.nil? + return nil if !SceneManager.scene_is?(Scene_Map) + mx, my = SceneManager.scene.instance_variable_get(:@spriteset).tilemap_offset + x = (@pos[0] + mx) / 32 + y = (@pos[1] + my) / 32 + return [x, y] + end + #-------------------------------------------------------------------------- + # * Mouse Position + #-------------------------------------------------------------------------- + def self.position + return @pos.nil? ? [0, 0] : @pos + end + #-------------------------------------------------------------------------- + # * Mouse Position + #-------------------------------------------------------------------------- + def self.pos + return Input.mouse_x, Input.mouse_y + end + #-------------------------------------------------------------------------- + # * Update Mouse Position + #-------------------------------------------------------------------------- + def self.update + old_pos = @pos + @pos = self.pos + + # Has mouse been moved? + if old_pos != @pos + Input.method = :mouse + end + + # Which mouse to show - custom, or system? + if $mouse.enabled? == @sys_cursor_visible + @sys_cursor_visible = !@sys_cursor_visible + Graphics.show_cursor = @sys_cursor_visible + end + end + #-------------------------------------------------------------------------- + # * Trigger? + # id : 0:Left, 1:Right, 2:Center + #-------------------------------------------------------------------------- + def self.trigger?(id = 0) + return Input.trigger? [:MOUSELEFT, :MOUSERIGHT, :MOUSEMIDDLE][id] + end + #-------------------------------------------------------------------------- + # * Repeat? + # id : 0:Left, 1:Right, 2:Center + #-------------------------------------------------------------------------- + def self.repeat?(id = 0) + return Input.repeat? [:MOUSELEFT, :MOUSERIGHT, :MOUSEMIDDLE][id] + end + #-------------------------------------------------------------------------- + # * Client Size + #-------------------------------------------------------------------------- + def self.client_size + return Graphics.width, Graphics.height + end +end + + + + + + +#============================================================================ +# SUPER SIMPLE MOUSE SCRIPT +# Input +#============================================================================ + + +class << Input + #-------------------------------------------------------------------------- + # * Public Instance Variables + #-------------------------------------------------------------------------- + attr_accessor :method + #-------------------------------------------------------------------------- + # * Alias Listings + #-------------------------------------------------------------------------- + alias :seph_mouse_input_update :update + alias :seph_mouse_input_trigger? :trigger? + alias :seph_mouse_input_repeat? :repeat? + #-------------------------------------------------------------------------- + # * Frame Update + #-------------------------------------------------------------------------- + def update + $mouse.update + Mouse.update + seph_mouse_input_update + # Are we using the mouse or the keyboard? + @method = :keyboard if dir4 != 0 || dir8 != 0 + end + #-------------------------------------------------------------------------- + # * Trigger? Test + #-------------------------------------------------------------------------- + def trigger?(constant) + return true if seph_mouse_input_trigger?(constant) + if $mouse.enabled? && !Mouse.pos.nil? + if Mouse::Mouse_to_Input_Triggers.has_value?(constant) + return true if Mouse.trigger?(Mouse::Mouse_to_Input_Triggers.key(constant)) + end + end + return false + end + #-------------------------------------------------------------------------- + # * Repeat? Test + #-------------------------------------------------------------------------- + def repeat?(constant) + return true if seph_mouse_input_repeat?(constant) + if $mouse.enabled? && !Mouse.pos.nil? + if Mouse::Mouse_to_Input_Triggers.has_value?(constant) + return true if Mouse.repeat?(Mouse::Mouse_to_Input_Triggers.key(constant)) + end + end + return false + end +end + + + + + + +#============================================================================ +# SUPER SIMPLE MOUSE SCRIPT +# Map +#============================================================================ + + +class Spriteset_Map + #-------------------------------------------------------------------------- + # * Tilemap Offset + #-------------------------------------------------------------------------- + def tilemap_offset + if $imported && $imported["YEA-CoreEngine"] + [@tilemap.ox - @viewport1.rect.x, @tilemap.oy - @viewport1.rect.y] + else + [@tilemap.ox, @tilemap.oy] + end + end +end + +class Game_Map + #-------------------------------------------------------------------------- + # * Detect/Set Up Starting Map Event + #-------------------------------------------------------------------------- + def setup_starting_map_event + event = @events.values.find {|event| event.starting } + event.clear_starting_flag if event + @interpreter.setup(event.list, event.id, event.trigger_in?([0,1,2,3])) if event + event + end + #-------------------------------------------------------------------------- + # * Detect/Set Up Autorun Common Event + #-------------------------------------------------------------------------- + def setup_autorun_common_event + event = $data_common_events.find do |event| + event && event.autorun? && $game_switches[event.switch_id] + end + @interpreter.setup(event.list, 0, true) if event + event + end + #-------------------------------------------------------------------------- + # * Get ID of Lowest Mouse-enabled Event at Designated Coordinates + #-------------------------------------------------------------------------- + def lowest_mouse_event_xy(x, y) + list = events_xy(x, y) + events_xy(x, y+1) + list.sort! {|a, b| b.y - a.y} + evt = nil + list.each do |event| + if (event.pos?(x, y) || (event.pos?(x, y+1) && event.height > 32)) && + (evt.nil? || event.y > evt.y) + evt = event + break + end + end + return evt + end +end + +class Scene_Map + #-------------------------------------------------------------------------- + # * Frame Update + #-------------------------------------------------------------------------- + alias shaz_mouse_scene_map_update update + def update + $mouse.update_event_cursors + shaz_mouse_scene_map_update + end +end + + + + + + +#============================================================================ +# SUPER SIMPLE MOUSE SCRIPT +# Event +#============================================================================ + + +module RPG + class Event + class Page + #-------------------------------------------------------------------- + # * Public Instance Variables + #-------------------------------------------------------------------- + attr_reader :mouse_icon + attr_reader :mouse_text + attr_reader :mouse_position + attr_reader :mouse_autoactivate + #-------------------------------------------------------------------- + # * Build Stats + #-------------------------------------------------------------------- + def build_stats + # Mouse icons (icon mandatory, others optional) + # + @mouse_icon = nil + @mouse_text = nil + @mouse_position = [0, 0] + @mouse_autoactivate = false + # look for mouse instructions + list.each do |command| + if [108, 408].include?(command.code) + comment = command.parameters[0] + case comment + when //i.match(comment)[1].split(' ') + @mouse_icon = params.shift + if params.size > 1 && params[0] =~ /\d+/ && params[1] =~ /\d+/ + @mouse_position = [params.shift.to_i, params.shift.to_i] + end + if params.size > 0 + @mouse_text = params.join(' ') + end + when // + @mouse_autoactivate = true + end + end #if + end #do + end #def + end + end +end + +class Game_Event < Game_Character + #-------------------------------------------------------------------------- + # * Public Instance Variables + #-------------------------------------------------------------------------- + attr_reader :mouse_icon + attr_reader :mouse_text + attr_reader :mouse_position + attr_reader :mouse_autoactivate + #-------------------------------------------------------------------------- + # * Start Event + #-------------------------------------------------------------------------- + alias shaz_mouse_game_event_start start + def start + $game_player.start_event(@id) if !empty? + shaz_mouse_game_event_start + end + #-------------------------------------------------------------------------- + # * Clear Event Page Settings + #-------------------------------------------------------------------------- + alias shaz_mouse_game_event_clear_page_settings clear_page_settings + def clear_page_settings + shaz_mouse_game_event_clear_page_settings + @mouse_icon = nil + @mouse_text = nil + @mouse_position = [0, 0] + @mouse_autoactivate = false + @height = 0 + end + #-------------------------------------------------------------------------- + # * Set Up Event Page Settings + #-------------------------------------------------------------------------- + alias shaz_mouse_game_event_setup_page_settings setup_page_settings + def setup_page_settings + shaz_mouse_game_event_setup_page_settings + @page.build_stats + @mouse_icon = @page.mouse_icon + @mouse_text = @page.mouse_text + @mouse_position = @page.mouse_position + @mouse_autoactivate = @page.mouse_autoactivate + set_size + end +end + + + + + + +#============================================================================ +# SUPER SIMPLE MOUSE SCRIPT +# Character +#============================================================================ + + +class Game_CharacterBase + attr_reader :height # Height of character bitmap + #-------------------------------------------------------------------------- + # * Initialize Public Member Variables + #-------------------------------------------------------------------------- + alias shaz_mouse_game_characterbase_init_public_members init_public_members + def init_public_members + shaz_mouse_game_characterbase_init_public_members + @height = 0 + end + #-------------------------------------------------------------------------- + # * Change Graphics + # character_name : new character graphic filename + # character_index : new character graphic index + #-------------------------------------------------------------------------- + alias shaz_mouse_game_characterbase_set_graphic set_graphic + def set_graphic(character_name, character_index) + shaz_mouse_game_characterbase_set_graphic(character_name, character_index) + set_size + end + #-------------------------------------------------------------------------- + # * Set character width/height size + #-------------------------------------------------------------------------- + def set_size + bw = Cache.character(@character_name).width + bh = Cache.character(@character_name).height + sign = @character_name[/^[\!\$]./] + if sign && sign.include?('$') + @width = bw / 3 + @height = bh / 4 + else + @width = bw / 12 + @height = bh / 8 + end + end + #-------------------------------------------------------------------------- + # * Detect Collision with Event + #-------------------------------------------------------------------------- + def collide_with_events?(x, y) + $game_map.events_xy_nt(x, y).any? do |event| + self != event && (event.normal_priority? || self.is_a?(Game_Event)) + end + end + #-------------------------------------------------------------------------- + # * Detect Collision with Vehicle + #-------------------------------------------------------------------------- + def collide_with_vehicles?(x, y) + !self.is_a?(Game_Player) && ($game_map.boat.pos_nt?(x, y) || $game_map.ship.pos_nt?(x, y)) + end + #-------------------------------------------------------------------------- + # * Frame Update + #-------------------------------------------------------------------------- + alias shaz_mouse_game_characterbase_update update + def update + run_path if @runpath + shaz_mouse_game_characterbase_update + end + #-------------------------------------------------------------------------- + # * Run Path + #-------------------------------------------------------------------------- + def run_path + return if moving? + @step = @map.nil? || @map[@x, @y].nil? ? 0 : @map[@x, @y] - 1 + if @step < 1 + clear_path + else + x, y = @x, @y + dirs = [] + dirs.push(6) if @map[@x+1, @y] == @step && passable?(@x, @y, 6) + dirs.push(2) if @map[@x, @y+1] == @step && passable?(@x, @y, 2) + dirs.push(4) if @map[@x-1, @y] == @step && passable?(@x, @y, 4) + dirs.push(8) if @map[@x, @y-1] == @step && passable?(@x, @y, 8) + while dirs.size > 0 + dir = dirs.delete_at(rand(dirs.size)) + move_straight(dir) + break if x != @x || y != @y + end + # clear the path if we couldn't move + clear_path if x == @x && y == @y + end + end + #-------------------------------------------------------------------------- + # * Find Path + #-------------------------------------------------------------------------- + def find_path(x, y) + sx, sy = @x, @y + @tx, @ty = x, y + result = setup_map(sx, sy) + @runpath = result[0] + @map = result[1] + @map[sx, sy] = result[2] if result[2] != nil + end + #-------------------------------------------------------------------------- + # * Clear Path + #-------------------------------------------------------------------------- + def clear_path + @map = nil + @runpath = false + end + #-------------------------------------------------------------------------- + # * Setup Map + #-------------------------------------------------------------------------- + def setup_map(sx, sy) + map = Table.new($game_map.width, $game_map.height) + update_counter = 0 + map[@tx, @ty] = 1 + old_positions = [[@tx, @ty]] + new_positions = [] + + # if tile is impassable, but CAN move to adjacent tiles, use the adjacent tiles instead + if (!passable?(@tx, @ty, 2) && !passable?(@tx, @ty, 4) && + !passable?(@tx, @ty, 6) && !passable?(@tx, @ty, 8)) || + $game_map.events_xy_nt(@tx, @ty).any? { |evt| evt.normal_priority? && evt != self } + old_positions = [] + + # Can we move from the destination tile in any direction? + if map_passable?(@tx, @ty, 2) + map[@tx, @ty+1] = 1 + old_positions.push([@tx, @ty+1]) + end + if map_passable?(@tx, @ty, 8) + map[@tx, @ty-1] = 1 + old_positions.push([@tx, @ty-1]) + end + if map_passable?(@tx, @ty, 4) + map[@tx-1, @ty] = 1 + old_positions.push([@tx-1, @ty]) + end + if map_passable?(@tx, @ty, 6) + map[@tx+1, @ty] = 1 + old_positions.push([@tx+1, @ty]) + end + + # If not, can we at least move up to the destination tile? + if old_positions.size == 0 + if map_passable?(@tx-1,@ty,6) + map[@tx-1,@ty] = 1 + old_positions.push([@tx-1,@ty]) + end + if map_passable?(@tx+1,@ty,4) + map[@tx+1,@ty] = 1 + old_positions.push([@tx+1,@ty]) + end + if map_passable?(@tx,@ty-1,2) + map[@tx,@ty-1] = 1 + old_positions.push([@tx,@ty-1]) + end + if map_passable?(@tx,@ty+1,8) + map[@tx,@ty+1] = 1 + old_positions.push([@tx,@ty+1]) + end + end + end + + # If there are any counters, can we move to the tile on the other side? + if map_passable?(@tx-2,@ty,6) && $game_map.counter?(@tx-1,@ty) + map[@tx-2,@ty] = 1 + old_positions.push([@tx-2,@ty]) + end + if map_passable?(@tx+2,@ty,4) && $game_map.counter?(@tx+1,@ty) + map[@tx+2,@ty] = 1 + old_positions.push([@tx+2,@ty]) + end + if map_passable?(@tx,@ty-2,2) && $game_map.counter?(@tx,@ty-1) + map[@tx,@ty-2] = 1 + old_positions.push([@tx,@ty-2]) + end + if map_passable?(@tx,@ty+2,2) && $game_map.counter?(@tx,@ty+1) + map[@tx,@ty+2] = 1 + old_positions.push([@tx,@ty+2]) + end + + + depth = 2 + depth.upto(100) { |step| + break if old_positions[0].nil? + @step = step + loop do + break if old_positions[0].nil? + x, y = old_positions.shift + return [true, map, @step-1] if x == sx && y == sy + if map[x, y + 1] == 0 && passable?(x, y, 2) + map[x, y + 1] = @step + new_positions.push([x, y + 1]) + end + if map[x - 1, y] == 0 && passable?(x, y, 4) + map[x - 1, y] = @step + new_positions.push([x - 1, y]) + end + if map[x + 1, y] == 0 && passable?(x, y, 6) + map[x + 1, y] = @step + new_positions.push([x + 1, y]) + end + if map[x, y - 1] == 0 && passable?(x, y, 8) + map[x, y - 1] = @step + new_positions.push([x, y - 1]) + end + # Update graphics? (to reduce lag) + update_counter += 1 + if update_counter > 50 + Graphics.update + update_counter = 0 + end + end + old_positions = new_positions + new_positions = [] + } + return [false, nil, nil] + end +end + +class Game_Character < Game_CharacterBase + #-------------------------------------------------------------------------- + # * Force Move Route + #-------------------------------------------------------------------------- + alias shaz_mouse_game_character_force_move_route force_move_route + def force_move_route(move_route) + clear_path + shaz_mouse_game_character_force_move_route(move_route) + end +end + + + + + + +#============================================================================ +# SUPER SIMPLE MOUSE SCRIPT +# Player +#============================================================================ + + +class Game_Player < Game_Character + #-------------------------------------------------------------------------- + # * Trigger Map Event + # triggers : Trigger array + # normal : Is priority set to [Same as Characters] ? + #-------------------------------------------------------------------------- + alias shaz_mouse_game_player_start_map_event start_map_event + def start_map_event(x, y, triggers, normal) + @started_events = [] + shaz_mouse_game_player_start_map_event(x, y, triggers, normal) + end + #-------------------------------------------------------------------------- + # * Start Event + #-------------------------------------------------------------------------- + def start_event(event_id) + @started_events = [] if @started_events.nil? + @started_events.push(event_id) + end + #-------------------------------------------------------------------------- + # * Processing of Movement via Input from Directional Buttons + #-------------------------------------------------------------------------- + alias shaz_mouse_game_player_move_by_input move_by_input + def move_by_input + if Input.dir4 > 0 + clear_path + shaz_mouse_game_player_move_by_input + else + # Move by mouse input + if !$game_message.busy? && !$game_message.visible && !@move_route_forcing && + !@vehicle_getting_on && !@vehicle_getting_off && + Mouse.trigger?(0) && !Mouse.grid.nil? && !$mouse.ignored? + mx, my = *Mouse.grid + # turn in direction + if (@x - mx).abs >= (@y - my).abs + set_direction(@x > mx ? 4 : 6) + else + set_direction(@y > my ? 8 : 2) + end + # find path + @event = $game_map.lowest_mouse_event_xy(mx, my) + if @event.nil? + find_path(mx, my) + elsif @event.mouse_autoactivate + @event.start + @started_events = [] + clear_path + else + find_path(@event.x + @event.mouse_position[0], + @event.y + @event.mouse_position[1]) + end + end + end + end + #-------------------------------------------------------------------------- + # * Frame Update + #-------------------------------------------------------------------------- + alias shaz_mouse_game_player_update update + def update + shaz_mouse_game_player_update + update_pathfinding if !@event.nil? && !moving? + end + #-------------------------------------------------------------------------- + # * Check event after pathfinding + #-------------------------------------------------------------------------- + def update_pathfinding + if @map.nil? || @map[@x, @y] <= 1 + dir = @x < @event.x ? 6 : @x > @event.x ? 4 : @y < @event.y ? 2 : @y > @event.y ? 8 : 0 + # Face event and trigger it (only if not triggered by start_map_event) + turn_toward_character(@event) if !@event.pos?(@x, @y) + if !@started_events.include?(@event.id) && !@map.nil? && !in_airship? + @event.start + @started_events = [] + end + clear_path + end + end + #-------------------------------------------------------------------------- + # * Clear Path + #-------------------------------------------------------------------------- + def clear_path + @event = nil + super + end +end + + + + + + +#============================================================================ +# SUPER SIMPLE MOUSE SCRIPT +# Interpreter +#============================================================================ + + +class Game_Interpreter + #-------------------------------------------------------------------------- + # * Event Setup + #-------------------------------------------------------------------------- + alias shaz_mouse_game_interpreter_setup setup + def setup(list, event_id = 0, lock_player = false) + shaz_mouse_game_interpreter_setup(list, event_id) + @lock_player = lock_player + end + #-------------------------------------------------------------------------- + # * Execute + #-------------------------------------------------------------------------- + alias shaz_mouse_game_interpreter_run run + def run + $mouse.ignored = true if @lock_player + shaz_mouse_game_interpreter_run + $mouse.ignored = false if @lock_player + end +end + + + + + + +#============================================================================ +# SUPER SIMPLE MOUSE SCRIPT +# Windows +#============================================================================ + + +class Window_Selectable < Window_Base + #-------------------------------------------------------------------------- + # * Frame Update + #-------------------------------------------------------------------------- + alias shaz_mouse_window_selectable_update update + def update + shaz_mouse_window_selectable_update + process_mouse_handling if Input.method == :mouse + end + #-------------------------------------------------------------------------- + # * Mouse Movement Processing + #-------------------------------------------------------------------------- + def process_mouse_handling + return unless $mouse.enabled? && cursor_movable? + # Add a delay to prevent too-fast scrolling + @delay = @delay ? @delay + 1 : 0 + return if @delay % 3 > 0 + + mx, my = *Mouse.position + vx = self.viewport ? self.x - self.viewport.ox + self.viewport.rect.x : self.x + vy = self.viewport ? self.y - self.viewport.oy + self.viewport.rect.y : self.y + if mx.between?(vx, vx + self.width) && + my.between?(vy, vy + self.height) + mx -= vx + mx -= padding + my -= vy + my -= padding + my += oy + for i in 0 ... item_max + rect = item_rect(i) + if mx.between?(rect.x, rect.x + rect.width) && + my.between?(rect.y, rect.y + rect.height) + last_index = @index + select(i) + if @index != last_index + Sound.play_cursor + end + break + end + end + end + end +end + +class Window_NameInput < Window_Selectable + #-------------------------------------------------------------------------- + # * Mouse Movement Processing + #-------------------------------------------------------------------------- + def process_mouse_handling + return unless $mouse.enabled? + # Add a delay to prevent too-fast scrolling + @delay = @delay ? @delay + 1 : 0 + return if @delay % 3 > 0 + + mx, my = *Mouse.position + vx = (self.viewport ? self.x - self.viewport.ox + self.viewport.rect.x : self.x) + padding + vy = (self.viewport ? self.y - self.viewport.oy + self.viewport.rect.y : self.y) + padding + if mx.between?(vx, vx + self.width - padding * 2) && + my.between?(vy, vy + self.height - padding * 2) + mx -= vx + my -= vy + x = (mx > 5*32+16 ? mx-16 : mx) / 32 + y = my / line_height + last_index = @index + @index = y * 10 + x + Sound.play_cursor if @index != last_index + end + end +end + +class Scene_File < Scene_MenuBase + #-------------------------------------------------------------------------- + # * Update Cursor + #-------------------------------------------------------------------------- + alias shaz_mouse_scene_file_update_cursor update_cursor + def update_cursor + shaz_mouse_scene_file_update_cursor + process_mouse_handling if Input.method == :mouse + end + #-------------------------------------------------------------------------- + # * Mouse Movement Processing + #-------------------------------------------------------------------------- + def process_mouse_handling + return unless $mouse.enabled? + # Add a delay to prevent too-fast scrolling + @delay = @delay ? @delay + 1 : 0 + return if @delay % 3 > 0 + + mx, my = *Mouse.position + vx = @savefile_viewport.ox + mx + vy = @savefile_viewport.oy + my + last_index = @index + new_index = vy / savefile_height + if @index != new_index + if new_index > @index + cursor_down(false) + else + cursor_up(false) + end + Sound.play_cursor + @savefile_windows[last_index].selected = false + @savefile_windows[@index].selected = true + end + end +end + +#============================================================================== +# ** Game_Player +#------------------------------------------------------------------------------ +# This class handles the player. It includes event starting determinants and +# map scrolling functions. The instance of this class is referenced by +# $game_player. +#============================================================================== + +class Game_Player < Game_Character + #-------------------------------------------------------------------------- + # * Frame Update + #-------------------------------------------------------------------------- + alias amn_shazmouse_gameplayer_mousesupport_update update + def update + amn_shazmouse_gameplayer_mousesupport_update + stop_movement_if_message + end + + def stop_movement_if_message + clear_path if $game_message.busy? || $game_message.visible + end + +end \ No newline at end of file diff --git a/mkxp-z/Kawariki-patches/ports/TH_EventTriggerLabels.rb b/mkxp-z/Kawariki-patches/ports/TH_EventTriggerLabels.rb new file mode 100644 index 0000000..c260408 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/TH_EventTriggerLabels.rb @@ -0,0 +1,315 @@ +=begin +#============================================================================== + Title: Event Trigger Labels + Author: Hime + Date: Dec 22, 2014 +------------------------------------------------------------------------------ + ** Change log + Sep 09, 2022 + - Add mkxp-z 2.4 support + Feb 13, 2022 + - MKXP(-Z) port by Taeyeon Mori + - Replace any_key_pressed? implementation based on Win32API with + one using System.raw_key_states + Dec 22, 2014 + - fixed bug where page list is not properly reset after execution + - added a flag for post-event processing for whether an event was triggered + Nov 6, 2014 + - completely refactored code + - implemented "any key pressed?" check + - moved key item triggers and touch triggers into separate scripts + Jan 30, 2014 + -refactored code + -added compatibility with instance items + Dec 27, 2013 + -optimized performance by not clearing out the key item variable on update + Nov 19, 2013 + -fixed bug where stepping on event with no active page crashed the game + Sep 26, 2013 + -added support for "player touch" trigger label + Sep 6, 2013 + -fixed bug where trigger labels as the first command doesn't register properly + Mar 22, 2013 + -fixed bug where you can still trigger events after they have been erased + Dec 5, 2012 + -fixed issue where event was checked before page was setup + Oct 20, 2012 + -fixed issue where using key item in succession crashes game + -added support for key item triggers + Aug 22, 2012 + -Support input names greater than one character (eg: F5 vs C) + Aug 18, 2012 + -fixed label parsing to store all buttons + Jun 13, 2012 + -initial release +------------------------------------------------------------------------------ + ** Terms of Use + * Free to use in non-commercial projects + * Contact me for commercial use + * No real support. The script is provided as-is + * Will do bug fixes, but no compatibility patches + * Features may be requested but no guarantees, especially if it is non-trivial + * Credits to Hime Works in your project + * Preserve this header +------------------------------------------------------------------------------ + ** Description: + + This script allows you to assign multiple action triggers to an event. + Every page can have its own set of action triggers. + + The action button, by default, is the C button (on keyboards, it is by default + the Z key, Enter, or Space). So when you press the action button when you're + standing beside an event, you will execute its action trigger and the event + will begin running. + + Multiple action triggers allow you to set up your event to run different + sets of commands depending on which button you pressed. For example, you can + press the C button to interact with the event normally, or you can press the + X button (default A key) to initiate a mini-game. + +------------------------------------------------------------------------------ + ** Installation + + Place this script below Materials and above Main + +------------------------------------------------------------------------------ + ** Usage + + Instead of treating your page as one list of commands, you should instead + treat it as different sections of commands. Each section will have its own + label, specified in a specific format. + + To create a section, add a Label command and then write + + button?(:C) + + This means that any commands under this label will be executed when you press + the C button (remember that the C button is the Z key on your keyboard). + + You can create as many sections as you want, each with their own buttons. + Press F1 in-game and then go to the keyboard tab to see which buttons are + available. + +#============================================================================== +=end +$imported = {} if $imported.nil? +$imported[:TH_EventTriggerLabels] = true +#============================================================================== +# ** Configuration +#============================================================================== +module TH + module Event_Trigger_Labels + + # this is what you need to type for all your labels if you want to use + # the input branching + Button_Format = "button?" + +#============================================================================== +# ** Rest of the script +#============================================================================== + Button_Regex = /#{Regexp.escape(Button_Format)}\(\:(.*)\)/ + end +end + +module Input + class << self + alias :th_any_key_pressed_check_update :update + end + + # Not every key should be checked + Keys_To_Check = 4.upto(99) # SDL scancodes, most standard keys, no modifiers + + if Input.respond_to?(:raw_key_states) then # MKXP-Z 2.4 + + def self.update + th_any_key_pressed_check_update + state = Input.raw_key_states + @any_key_pressed = Keys_To_Check.any?{|key| state[key]} + end + else + def self.update + th_any_key_pressed_check_update + state = System.raw_key_states + @any_key_pressed = Keys_To_Check.any?{|key| state[key] != 0} + end + end + + def self.any_key_pressed? + @any_key_pressed + end +end + +module RPG + class Event::Page + + attr_accessor :button_labels + + def button_labels + return @button_labels ||= [] + end + + alias :th_event_trigger_labels_list :list + def list + setup_trigger_labels unless @trigger_labels_set + th_event_trigger_labels_list + end + + def setup_trigger_labels + @trigger_labels_set = true + nulls = [] + if @trigger < 3 + @list.each_with_index do |cmd, index| + if cmd.code == 118 + label = cmd.parameters[0] + # Check for extra buttons + if trigger_label?(label) + nulls << index + end + end + end + end + + # insert "exit event processing" before each "event branch" + nulls.reverse.each {|index| + @list.insert(index, RPG::EventCommand.new(115)) + } + end + + def trigger_label?(label) + if label =~ TH::Event_Trigger_Labels::Button_Regex + self.button_labels << $1.to_sym + return true + end + return false + end + end +end + +class Game_Player < Game_Character + + #----------------------------------------------------------------------------- + # Alias. Try to avoid hardcoding it + #----------------------------------------------------------------------------- + alias :th_trigger_labels_nonmoving :update_nonmoving + def update_nonmoving(last_moving) + return if $game_map.interpreter.running? + if trigger_conditions_met? + pre_trigger_event_processing + triggered = check_event_label_trigger + post_trigger_event_processing(triggered) + end + th_trigger_labels_nonmoving(last_moving) + end + + def trigger_conditions_met? + movable? && Input.any_key_pressed? + end + + #----------------------------------------------------------------------------- + # + #----------------------------------------------------------------------------- + def pre_trigger_event_processing + end + + #----------------------------------------------------------------------------- + # New. Clean up. + #----------------------------------------------------------------------------- + def post_trigger_event_processing(triggered) + end + + #----------------------------------------------------------------------------- + # New. Check for any valid events in the area + #----------------------------------------------------------------------------- + def check_event_label_trigger + positions_to_check_for_event.each do |x, y| + $game_map.events_xy(x, y).each do |event| + label = event.check_trigger_label + + # If no label was found, check next event + next unless label + + # If the event can run, insert a jump to label command at + # the beginning before running it + if check_action_event + text = [label] + command = RPG::EventCommand.new(119, 0, text) + event.list.insert(0, command) + return true + end + end + end + return false + end + + #----------------------------------------------------------------------------- + # New. Positions to check events + #----------------------------------------------------------------------------- + def positions_to_check_for_event + positions = [[@x, @y]] + x2 = $game_map.round_x_with_direction(@x, @direction) + y2 = $game_map.round_y_with_direction(@y, @direction) + positions << [x2, y2] + return positions unless $game_map.counter?(x2, y2) + x3 = $game_map.round_x_with_direction(x2, @direction) + y3 = $game_map.round_y_with_direction(y2, @direction) + positions << [x3, y3] + return positions + end +end + +class Game_Event + + attr_reader :page + attr_reader :erased + attr_accessor :list + + alias :th_event_trigger_labels_setup_page_settings :setup_page_settings + def setup_page_settings(*args) + th_event_trigger_labels_setup_page_settings + @list = Marshal.load(Marshal.dump(@page.list.clone)) + end + + def check_trigger_label + return nil if @erased + label = get_trigger_label + if label + # Reset the commands (Since we maybe have inserted a jump command before) + @needs_reset = true + end + return label + end + + def get_trigger_label + label = check_button_trigger + return label if label + end + + #----------------------------------------------------------------------------- + # New. Check whether the button triggers the event + #----------------------------------------------------------------------------- + def check_button_trigger + return unless button_trigger_met? + @page.button_labels.each do |button| + if Input.trigger?(button) + return "#{TH::Event_Trigger_Labels::Button_Format}(:#{button})" + end + end + return nil + end + + def button_trigger_met? + return false unless @page + return false if @page.button_labels.empty? + return true + end + + alias :th_event_trigger_labels_update :update + def update + th_event_trigger_labels_update + reset_page if @needs_reset + end + + def reset_page + @needs_reset = false + @list = Marshal.load(Marshal.dump(@page.list.clone)) + end +end \ No newline at end of file diff --git a/mkxp-z/Kawariki-patches/ports/TH_SimpleAudioEncryption.rb b/mkxp-z/Kawariki-patches/ports/TH_SimpleAudioEncryption.rb new file mode 100644 index 0000000..a481dd2 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/TH_SimpleAudioEncryption.rb @@ -0,0 +1,110 @@ +=begin +#=============================================================================== +Title: Simple Audio Encryption MKXP-Z +Author: Taeyeon Mori +Date: Feb 23, 2022 +Original Title: Simple Audio Encryption +Original Author: Hime +Original Date: Mar 22, 2014 +Original URL: http://www.himeworks.com/2014/03/21/simple-audio-encryption/ +-------------------------------------------------------------------------------- + ** Original Terms of Use + * Free to use in non-commercial projects + * Contact me for commercial use + * No real support. The script is provided as-is + * Will do bug fixes, but no compatibility patches + * Features may be requested but no guarantees, especially if it is non-trivial + * Credits to Hime Works in your project + * Preserve this header +#=============================================================================== +=end + +$imported = {} if $imported.nil? +$imported[:TH_SimpleAudioEncryption] = true + +module TH + module Crypt + @@video_extensions = [".ogv"] + @@audio_extensions = ["", ".ogg", ".mp3", ".mid", ".wav"] + @@cache = {} + + def self.find_real_path(path, exts) + # return unmodified if we can verify that it exists outside archive + return path if exts.any? {|ext| File.exist? (path + ext)} + exts.each do|ext| + # There is no way to check if a file exists in the archive from Ruby + # So we try to load it to a string and see if it fails. + # This is expensive so make sure to cache the result of this method + candidate = "Data/" + path + ext + begin + # MKXP-Z extension + load_data candidate, true + rescue + next + end + Preload.print "TH_SAE: Found #{path} at #{candidate}" + return candidate + end + # return nil if not found + return nil + end + + def self.real_path(path, exts) + # Check cache + real_path = @@cache[path] + return real_path unless real_path.nil? + # Try to find + real_path = self.find_real_path path, exts + # Just fall back to the original path if not found + # Otherwise, we'll repeat the expensive lookup + # whenever this path comes up + real_path = path if real_path.nil? + # Save to cache + @@cache[path] = real_path + return real_path + end + + def self.real_audio_path(path) + self.real_path path, @@audio_extensions + end + + def self.real_video_path(path) + self.real_path path, @@video_extensions + end + end +end + +module Audio + class << self + alias :th_simple_audio_decryption_bgm_play :bgm_play + alias :th_simple_audio_decryption_bgs_play :bgs_play + alias :th_simple_audio_decryption_me_play :me_play + alias :th_simple_audio_decryption_se_play :se_play + end + + def self.bgm_play(name, *args) + th_simple_audio_decryption_bgm_play(TH::Crypt::real_audio_path(name), *args) + end + + def self.bgs_play(name, *args) + th_simple_audio_decryption_bgs_play(TH::Crypt::real_audio_path(name), *args) + end + + def self.me_play(name, *args) + th_simple_audio_decryption_me_play(TH::Crypt::real_audio_path(name), *args) + end + + def self.se_play(name, *args) + th_simple_audio_decryption_se_play(TH::Crypt::real_audio_path(name), *args) + end +end + +module Graphics + class << self + alias :th_simple_audio_encryption_play_movie :play_movie + end + + def self.play_movie(name, *args) + th_simple_audio_encryption_play_movie(TH::Crypt::real_video_path(name), *args) + end +end diff --git a/mkxp-z/Kawariki-patches/ports/XP_CustomResolution.rb b/mkxp-z/Kawariki-patches/ports/XP_CustomResolution.rb new file mode 100644 index 0000000..01c5170 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/XP_CustomResolution.rb @@ -0,0 +1,815 @@ +#=============================================================================== +# Custom Resolution +# Authors: ForeverZer0, KK20 +# Version: 0.96b +# Date: 11.15.2013 +#=============================================================================== +# MKXP Port +# Authors: Taeyeon Mori +# Date: 01/30/2022 +# +# Includes tilemap texture size workaround +Preload.require 'XP_TilemapOverrideLib.rb' +#=============================================================================== +# KK20's Notes +#=============================================================================== +# Introduction: +# +# This script is intended to create screen resolutions other than 640 x 480. +# The script comes with its own Tilemap rewrite in order to combat larger +# screen resolutions (because anything beyond 640 x 480 is not drawn). +# +# Instructions: +# +# Place script above 'Main'. Probably best to put it below all your other +# custom scripts. +# You will also need 'screenshot.dll' included in your project. You can find +# that in Fantasist's Transitions Pack linked below. +# +# Things to Consider: +# +# - Fullscreen will change the resolution back to 640 x 480. A solution is in +# the works. +# - Transitions do not work properly on larger resolutions. You can use a +# Transitions Add-Ons script if you want better transitions (otherwise, all +# you will get is the default fade in/out). Links listed below. +# - Custom scripts that draw windows to the screen will most likely need edits. +# - Larger resolutions = more processing power = more lag +# +# *************************************************************************** +# * THIS IS STILL A WORK IN PROGRESS; IF YOU FIND ANYTHING PLEASE REPORT IT * +# *************************************************************************** +# +# Links: +# - Fantasist's Transitions Pack (w/ screenshot.dll) +# http://forum.chaos-project.com/index.php/topic,1390.0.html +# - ForeverZer0's Add-ons +# http://forum.chaos-project.com/index.php/topic,7862.0.html +# - ThallionDarkshine's Add-ons +# http://forum.chaos-project.com/index.php/topic,12655.0.html +# - Drago Transition Pack +# http://forum.chaos-project.com/index.php/topic,13488.0.html +# +#=============================================================================== +# ForeverZer0's Notes from v0.93 (outdated information) +#=============================================================================== +# Introduction: +# +# My goal in creating this script was to create a system that allowed the user +# to set the screen size to something other than 640 x 480, but not have make +# huge sacrifices in compatibility and performance. Although the script is +# not simply Plug-and-Play, it is about as close as one can achieve with a +# script of this nature. +# +# Instructions: +# +# - Place the "screenshot.dll" from Fantasist's Transition Pack script, which +# can be found here: http://www.sendspace.com/file/yjd54h in your game folder +# - Place this script above main, below default scripts. +# - In my experience, unchecking "Reduce Screen Flickering" actually helps the +# screen not to flicker. Open menu with F1 while playing and set this to what +# you get the best results with. +# +# Features: +# +# - Totally re-written Tilemap and Plane class. Both classes were written to +# display the map across any screen size automatically. The Tilemap class +# is probably even more efficient than the original, which will help offset +# any increased lag due to using a larger screen size with more sprites +# being displayed. +# - Every possible autotile graphic (48 per autotile) will be cached for the +# next time that tile is used. +# - Autotile animation has been made as efficient as possible, with a system +# that stores their coodinates, but only if they change. This greatly reduces +# the number of iterations at each update. +# - System creates an external file to save pre-cached data priorities and +# autotiles. This will decrease any loading times even more, and only takes a +# second, depending on the number of maps you have. +# - User defined autotile animation speed. Can change with script calls. +# - Automatic re-sizing of Bitmaps and Viewports that are 640 x 480 to the +# defined resolution, unless explicitely over-ridden in the method call. +# The graphics themselves will not be resized, but any existing scripts that +# use the normal screen size will already be configured to display different +# sizes of graphics for transitions, battlebacks, pictures, fogs, etc. +# - Option to have a log file ouput each time the game is ran, which can alert +# you to possible errors with map sizes, etc. +# +# Issues/Bugs/Possible Bugs: +# +# - Graphic related scripts and your graphics will need to be customized to +# fit the new screen size, so this script is not for everyone. +# - The Z-axis for the Plane class, which is used for Fogs and Panoramas has +# been altered. It is now multiplied by 1000. This will likely be a minor +# issue for most, since this class is very rarely used except for Fogs and +# Panoramas, which are already far above and below respectfully. +# - Normal transitions using graphics cannot be used. With the exception of +# a standard fade, like that used when no graphic is defined will be used. +# Aside from that, only special transitions from Transition Pack can be +# used. +#=============================================================================== +# Credits/Thanks: +# - ForeverZer0, for script. +# - Creators of the Transition Pack and Screenshot.dll +# - Selwyn, for base resolution script +# - KK20, for Version 0.94 and above and the Tilemap class +#=============================================================================== +# CONFIGURATION +#=============================================================================== +# FIXME: need to exfiltrate this in the preload patch process + +SCREEN = [1024, 576] +# Define the resolution of the game screen. These values can be anything +# within reason. Centering, viewports, etc. will all be taken care of, but it +# is recommended that you use values divisible by 32 for best results. + +UPDATE_COUNT = 8 +# Define the number of frames between autotile updates. The lower the number, +# the faster the animations cycle. This can be changed in-game with the +# following script call: $game_map.autotile_speed = SPEED + +RESOLUTION_LOG = true +# This will create a log in the Game directory each time the game is ran in +# DEBUG mode, which will list possible errors with map sizes, etc. + +#=============================================================================== +# ** Resolution +#=============================================================================== + +class Resolution + + attr_reader :version + + def initialize + # Define version. + @version = 0.96 + # Resize screen + Graphics.resize_screen(SCREEN[0], SCREEN[1]) + end + #-------------------------------------------------------------------------- + def size + # Returns the screen size of the machine. + # FIXME: available in MKXP? + return [1920, 1080] + end + #-------------------------------------------------------------------------- + def snapshot(filename = 'Data/snap', quality = 0) + # FILENAME = Filename that the picture will be saved as. + # FILETYPE = 0 = High Quality 1 = Low Quality (ignored) + Graphics.screenshot(filename) + end + #-------------------------------------------------------------------------- +end + +#=============================================================================== +# ** RPG::Cache +#=============================================================================== + +module RPG::Cache + + AUTO_INDEX = [ + [27,28,33,34], [5,28,33,34], [27,6,33,34], [5,6,33,34], + [27,28,33,12], [5,28,33,12], [27,6,33,12], [5,6,33,12], + [27,28,11,34], [5,28,11,34], [27,6,11,34], [5,6,11,34], + [27,28,11,12], [5,28,11,12], [27,6,11,12], [5,6,11,12], + [25,26,31,32], [25,6,31,32], [25,26,31,12], [25,6,31,12], + [15,16,21,22], [15,16,21,12], [15,16,11,22], [15,16,11,12], + [29,30,35,36], [29,30,11,36], [5,30,35,36], [5,30,11,36], + [39,40,45,46], [5,40,45,46], [39,6,45,46], [5,6,45,46], + [25,30,31,36], [15,16,45,46], [13,14,19,20], [13,14,19,12], + [17,18,23,24], [17,18,11,24], [41,42,47,48], [5,42,47,48], + [37,38,43,44], [37,6,43,44], [13,18,19,24], [13,14,43,44], + [37,42,43,48], [17,18,47,48], [13,18,43,48], [1,2,7,8] + ] + + def self.autotile(filename) + key = "Graphics/Autotiles/#{filename}" + if !@cache.include?(key) || @cache[key].disposed? + # Cache the autotile graphic. + @cache[key] = (filename == '') ? Bitmap.new(128, 96) : Bitmap.new(key) + # Cache each configuration of this autotile. + new_bm = self.format_autotiles(@cache[key], filename) + @cache[key].dispose + @cache[key] = new_bm + end + return @cache[key] + end + + def self.format_autotiles(bitmap, filename) + if bitmap.height > 32 + frames = bitmap.width / 96 + template = Bitmap.new(256*frames,192) + # Create a bitmap to use as a template for creation. + (0..frames-1).each{|frame| + (0...6).each {|i| (0...8).each {|j| AUTO_INDEX[8*i+j].each {|number| + number -= 1 + x, y = 16 * (number % 6), 16 * (number / 6) + rect = Rect.new(x + (frame * 96), y, 16, 16) + template.blt((32 * j + x % 32) + (frame * 256), 32 * i + y % 32, bitmap, rect) + }}}} + return template + else + return bitmap + end + end +end + +#=============================================================================== +# ** Tilemap_DataTable +#=============================================================================== +class Tilemap_DataTable + attr_accessor :updates + attr_accessor :table + def initialize(table) + @table = table + @updates = [] + end + + def updated + return @updates.size >= 1 + end + + def [](x,y=nil,z=nil) + return @table[x,y,z] unless z.nil? + return @table[x,y] unless y.nil? + return @table[x] + end + + def []=(x,y,z=nil,t_id=nil) + @updates.push([x,y,z,t_id]) unless t_id.nil? + t_id.nil? ? (z.nil? ? @table[x] = y : @table[x,y] = z) : @table[x,y,z] = t_id + end + + def xsize; return @table.xsize; end + def ysize; return @table.ysize; end + def zsize; return @table.zsize; end + + def resize(x,y=nil,z=nil); @table.resize(x,y,z); end +end + +#=============================================================================== +# ** Tilemap +#=============================================================================== + +class Tilemap + + attr_reader :tileset, :map_data, :ox, :oy, :viewport + attr_accessor :autotiles, :priorities + + # +++ MKXP +++ + def tileset=(value) + # Need to wrap tilesets that don't fit into texture + if value.mega? + @tileset = TileWrap::wrapTileset(value) + value.dispose + else + @tileset = value + end + end + + def initialize(viewport) + # Initialize instance variables to store required data. + @viewport, @autotiles, @tile_sprites, @ox, @oy = viewport, [], [], 0, 0 + @current_frame, @total_frames = [], [] + @tilemap_drawn = false + @ox_oy_set = [false, false] + # Get priority data for this tileset from instance of Game_Map. + @priorities = $game_map.priorities + # Holds all the Sprite instances of animating tiles (keys based on tile's ID) + @animating_tiles = {} + # Game map's x/y location of the top left corner tile + @corner_tile_loc = [-1,-1] + end + + #----------------------------------------------------------------------------- + # Initialize all tile sprites. Draws three sprites per (x,y). + #----------------------------------------------------------------------------- + def init_tiles + # Determine how many frames of animation this autotile has + for i in 0..6 + bm = @autotiles[i] + if bm.nil? + @total_frames = 1 + elsif bm.height > 32 + @total_frames[i] = bm.width / 256 + else + @total_frames[i] = bm.width / 32 + end + @current_frame[i] = 0 + end + # Turn on flag that the tilemap sprites have been initialized + @tilemap_drawn = true + + @animating_tiles.clear + # Create a sprite and viewport to use for each priority level. + (0...((SCREEN[0]/32+2) * (SCREEN[1]/32+2))*3).each{|i| + @tile_sprites[i/3] = [] if @tile_sprites[i/3].nil? + @tile_sprites[i/3][i%3] = Sprite.new(@viewport) unless @tile_sprites[i/3][i%3].is_a?(Sprite) + # Rename to something shorter and easier to work with for below + tile = @tile_sprites[i/3][i%3] + # Assign tile's respective ID value + tile.tile_sprite_id = i + # Draw sprite at index location (ex. ID 0 should always be the top-left sprite) + tile.x = (i % ((SCREEN[0]/32+2)*3) / 3 * 32) - 32 + (@ox % 32) + tile.y = (i / ((SCREEN[0]/32+2)*3) * 32) - 32 + (@oy % 32) + + map_x, map_y = (tile.x+@ox)/32, (tile.y+@oy)/32 + @corner_tile_loc = [map_x, map_y] if i == 0 + # If the tile happens to be drawn along the outside borders of the map + if map_x < 0 || map_x >= $game_map.width || map_y < 0 || map_y >= $game_map.height + update_tile_id tile, 0 + else # Tile is actually on the map + update_tile_id tile, @map_data[map_x, map_y, i%3] + end + } + # Sprite ID located at top left corner (ranges from 0..map_width * map_height + @corner_index = 0 + end + + # Common code for setting the tile by id + def update_tile_id(tile, tile_id) + if tile_id == 0 # empty tile + tile.z = 0 + tile.bitmap = RPG::Cache.picture('')#@tileset + tile.src_rect.set(0,0,0,0) + return + end + if @priorities[tile_id] == 0 + tile.z = 0 + else + tile.z = tile.y + @priorities[tile_id] * 32 + 32 + end + if tile_id >= 384 # non-autotile + tile.bitmap = @tileset + tile.src_rect.set(((tile_id - 384) % 8) * 32, + ((tile_id - 384) / 8) * 32, + 32, 32) + # Fix rect for possibly wrapped tileset + TileWrap.wrapRect! tile.src_rect + else # autotile + auto_id = tile_id/48-1 + tile.bitmap = @autotiles[auto_id] + tile.src_rect.set(((tile_id % 48) % 8) * 32 + @current_frame[auto_id] * 256, + ((tile_id % 48) / 8) * 32, + 32, 32) + @animating_tiles[tile.tile_sprite_id] = tile if @total_frames[auto_id] > 1 + end + end + + #----------------------------------------------------------------------------- + # Makes update to ox and oy. Sprites out of range will be moved based on these + # two values. + #----------------------------------------------------------------------------- + def ox=(ox) + # + unless @tilemap_drawn + @ox = ox + @ox_oy_set[0] = true + return + end + + return if @ox == ox + # Shift all tiles left or right by the difference + shift = @ox - ox + + @tile_sprites.each {|set| set.each{|tile| tile.x += shift }} + @ox = ox + # Determine if columns need to be shifted + col_num = @corner_index + #return unless @tile_sprites[col_num][0].x <= -49 || @tile_sprites[col_num][0].x >= -17 + while @tile_sprites[col_num][0].x <= -49 || @tile_sprites[col_num][0].x >= -17 + + @corner_tile_loc[0] += (shift < 0 ? 1 : -1) + modTileId = ((SCREEN[0]+64)*(SCREEN[1]+64))/1024 + # If new ox is greater than old ox + if shift < 0 + # Move all sprites in left column to the right side and change bitmaps + # and z-values + (0...(SCREEN[1]/32+2)).each{|n| + j = ((SCREEN[0]/32+2) * n + col_num) % modTileId + @tile_sprites[j].each_index{|i| + tile = @tile_sprites[j][i] + @animating_tiles.delete(tile.tile_sprite_id) + tile.x += 64 + SCREEN[0] + + map_x, map_y = (tile.x+@ox)/32, (tile.y+@oy)/32 + tile_id = @map_data[map_x,map_y,i] + + if tile_id.nil? + tile.z = [map_y * 32, 0].max + tile.bitmap = RPG::Cache.picture('') + tile.src_rect.set(0,0,0,0) + next + end + + update_tile_id tile, tile_id + } + } + # New corner should be the tile immediately right of the previous tile + col_num /= SCREEN[0]/32+2 + col_num *= SCREEN[0]/32+2 + @corner_index = (@corner_index + 1) % (SCREEN[0]/32+2) + col_num + else + # Shift right column to the left + # Gets the right column + row_index = col_num / (SCREEN[0]/32+2) + row_index *= (SCREEN[0]/32+2) + col_num = (@corner_index - 1) % (SCREEN[0]/32+2) + row_index + + (0...(SCREEN[1]/32+2)).each{|n| + j = ((SCREEN[0]/32+2) * n + col_num) % modTileId + @tile_sprites[j].each_index{|i| + tile = @tile_sprites[j][i] + @animating_tiles.delete(tile.tile_sprite_id) + tile.x -= 64 + SCREEN[0] + + map_x, map_y = (tile.x+@ox)/32, (tile.y+@oy)/32 + tile_id = @map_data[map_x,map_y,i] + if tile_id.nil? + tile.z = [map_y * 32, 0].max + tile.bitmap = @tileset + tile.src_rect.set(0,0,0,0) + next + end + + update_tile_id tile, tile_id + } + } + col_num /= SCREEN[0]/32+2 + col_num *= SCREEN[0]/32+2 + @corner_index = (@corner_index - 1) % (SCREEN[0]/32+2) + col_num + end + col_num = @corner_index + end #end of while + end + +#----------------------------------------------------------------------------- + + def oy=(oy) + # + unless @tilemap_drawn + @oy = oy + @ox_oy_set[1] = true + return + end + + return if @oy == oy + # Shift all tiles up or down by the difference, and change z-value + shift = @oy - oy + + @tile_sprites.each {|set| set.each{|tile| tile.y += shift; tile.z += shift unless tile.z == 0 }} + @oy = oy + # Determine if rows need to be shifted + row_num = @corner_index + #return unless @tile_sprites[row_num][0].y <= -49 || @tile_sprites[row_num][0].y >= -17 + while @tile_sprites[row_num][0].y <= -49 || @tile_sprites[row_num][0].y >= -17 + + + # Needed for resetting the new corner index much later. + modTileId = ((SCREEN[0]+64)*(SCREEN[1]+64))/1024 + @corner_tile_loc[1] += (shift < 0 ? 1 : -1) + # If new oy is greater than old oy + if shift < 0 + row_num /= SCREEN[0]/32+2 + row_num *= SCREEN[0]/32+2 + # Move all sprites in top row to the bottom side and change bitmaps + # and z-values + (0...(SCREEN[0]/32+2)).each{|n| + # Run through each triad of sprites from left to right + j = n + row_num + @tile_sprites[j].each_index{|i| + # Get each individual tile on each layer + tile = @tile_sprites[j][i] + @animating_tiles.delete(tile.tile_sprite_id) + tile.y += 64 + SCREEN[1] + # Determine what map coordinate this tile now resides at... + map_x, map_y = (tile.x+@ox)/32, (tile.y+@oy)/32 + # ...and get its tile_id + tile_id = @map_data[map_x,map_y,i] + # If no tile exists here (effectively out of array bounds) + if tile_id.nil? + tile.z = [map_y * 32, 0].max + tile.bitmap = RPG::Cache.picture('') + tile.src_rect.set(0,0,0,0) + next + end + + update_tile_id tile, tile_id + } + } + + @corner_index = (@corner_index + (SCREEN[0]/32+2)) % modTileId + else + row_num = (@corner_index - (SCREEN[0]/32+2)) % modTileId + row_num /= SCREEN[0]/32+2 + row_num *= SCREEN[0]/32+2 + (0...(SCREEN[0]/32+2)).each{|n| + # Run through each triad of sprites from left to right + j = n + row_num + @tile_sprites[j].each_index{|i| + # Get each individual tile on each layer + tile = @tile_sprites[j][i] + @animating_tiles.delete(tile.tile_sprite_id) + tile.y -= 64 + SCREEN[1] + # Determine what map coordinate this tile now resides at... + map_x, map_y = (tile.x+@ox)/32, (tile.y+@oy)/32 + # ...and get its tile_id + tile_id = @map_data[map_x,map_y,i] + # If no tile exists here (effectively out of array bounds) + if tile_id.nil? + tile.z = [map_y * 32, 0].max + tile.bitmap = RPG::Cache.picture('') + tile.src_rect.set(0,0,0,0) + next + end + + update_tile_id tile, tile_id + } + } + @corner_index = (@corner_index - (SCREEN[0]/32+2)) % modTileId + end + row_num = @corner_index + end # end of while + end + #----------------------------------------------------------------------------- + # Dispose all the tile sprites + #----------------------------------------------------------------------------- + def dispose + # Dispose all of the sprites + @tile_sprites.each {|set| set.each{|tile| tile.dispose }} + @tile_sprites.clear + @animating_tiles.clear + end + #----------------------------------------------------------------------------- + # Set map data + #----------------------------------------------------------------------------- + def map_data=(data) + # Set the map data to new class + if data.is_a?(Tilemap_DataTable) + @map_data = data + else + @map_data = Tilemap_DataTable.new(data) + end + @map_data.table = @map_data.table.clone + @map_data.updates = [] + + @animating_tiles.clear + @tilemap_drawn = false + end + #----------------------------------------------------------------------------- + # Update the tile sprites; make changes to the map_data and update autotiles + #----------------------------------------------------------------------------- + def update + # Can't update anything if the ox and oy have not yet been set + return if @ox_oy_set != [true, true] + # If the tilemap sprites have not been initialized, GO DO IT + if !@tilemap_drawn + init_tiles + end + + # If made any changes to $game_map.data, the proper graphics will be drawn + if @map_data.updated + @map_data.updates.each{|item| + x,y,z,tile_id = item + # If this changed tile is visible on screen + if x.between?(@corner_tile_loc[0], @corner_tile_loc[0]+(SCREEN[0]/32 + 1)) and + y.between?(@corner_tile_loc[1], @corner_tile_loc[1]+(SCREEN[1]/32 + 1)) + + x_dif = x - @corner_tile_loc[0] + y_dif = y - @corner_tile_loc[1] + + id = @corner_index + x_dif + id -= SCREEN[0]/32+2 if id/(SCREEN[0]/32+2) > @corner_index/(SCREEN[0]/32+2) + + id += y_dif * (SCREEN[0]/32+2) + id -= (SCREEN[0]/32+2)*(SCREEN[1]/32+2) if id >= (SCREEN[0]/32+2)*(SCREEN[1]/32+2) + + tile = @tile_sprites[id][z] + @animating_tiles.delete(tile.tile_sprite_id) + + update_tile_id tile, tile_id + end + } + @map_data.updates = [] + end + + # Update the sprites. + if Graphics.frame_count % $game_map.autotile_speed == 0 + # Increase current frame of tile by one, looping by width. + for i in 0..6 + @current_frame[i] = (@current_frame[i] + 1) % @total_frames[i] + end + @animating_tiles.each_value{|tile| + frames = tile.bitmap.width + tile.src_rect.set((tile.src_rect.x + 256) % frames, tile.src_rect.y, 32, 32) + } + end + end +end + +#=============================================================================== +# Game_Map +#=============================================================================== + +class Game_Map + + attr_reader :tile_size, :autotile_speed, :autotile_data, :priority_data + + alias zer0_load_autotile_data_init initialize + def initialize + # Call original method. + zer0_load_autotile_data_init + # Store the screen dimensions in tiles to save on calculations later. + @tile_size = [SCREEN[0], SCREEN[1]].collect {|n| (n / 32.0).ceil } + @autotile_speed = UPDATE_COUNT + end + + alias zer0_map_edge_setup setup + def setup(map_id) + # Call original method. + zer0_map_edge_setup(map_id) + # Change Map's data into a special Table class + @map.data = Tilemap_DataTable.new(@map.data) + # Find the displayed area of the map in tiles. No calcualting every step. + @map_edge = [self.width - @tile_size[0], self.height - @tile_size[1]] + @map_edge.collect! {|size| size * 128 } + end + + def scroll_down(distance) + # Find point that the map edge meets the screen edge, using custom size. + @display_y = [@display_y + distance, @map_edge[1]].min + end + + def scroll_right(distance) + # Find point that the map edge meets the screen edge, using custom size. + @display_x = [@display_x + distance, @map_edge[0]].min + end + + def autotile_speed=(speed) + # Keep the speed above 0 to prevent the ZeroDivision Error. + @autotile_speed = speed + @autotile_speed = 1 if @autotile_speed < 1 + end + +end + +#=============================================================================== +# ** Game_Player +#=============================================================================== + +class Game_Player + + CENTER_X = ((SCREEN[0] / 2) - 16) * 4 # Center screen x-coordinate * 4 + CENTER_Y = ((SCREEN[1] / 2) - 16) * 4 # Center screen y-coordinate * 4 + + def center(x, y) + # Recalculate the screen center based on the new resolution. + max_x = ($game_map.width - $game_map.tile_size[0]) * 128 + max_y = ($game_map.height - $game_map.tile_size[1]) * 128 + $game_map.display_x = [0, [x * 128 - CENTER_X, max_x].min].max + $game_map.display_y = [0, [y * 128 - CENTER_Y, max_y].min].max + end +end + +#=============================================================================== +# ** Sprite +#=============================================================================== +class Sprite + attr_accessor :tile_sprite_id + alias tile_sprite_id_init initialize + def initialize(view = nil) + # No defined ID + @tile_sprite_id = nil + # Call original method. + tile_sprite_id_init(view) + end +end + +#=============================================================================== +# ** Viewport +#=============================================================================== +class Viewport + + alias zer0_viewport_resize_init initialize + def initialize(x=0, y=0, width=SCREEN[0], height=SCREEN[1], override=false) + if x.is_a?(Rect) + # If first argument is a Rectangle, just use it as the argument. + zer0_viewport_resize_init(x) + elsif [x, y, width, height] == [0, 0, 640, 480] && !override + # Resize fullscreen viewport, unless explicitly overridden. + zer0_viewport_resize_init(Rect.new(0, 0, SCREEN[0], SCREEN[1])) + else + # Call method normally. + zer0_viewport_resize_init(Rect.new(x, y, width, height)) + end + end + + def resize(*args) + # Resize the viewport. Can call with (X, Y, WIDTH, HEIGHT) or (RECT). + self.rect = args[0].is_a?(Rect) ? args[0] : Rect.new(*args) + end +end + +#=============================================================================== +# ** Plane +#=============================================================================== + +# class Plane < Sprite + +# def z=(z) +# # Change the Z value of the viewport, not the sprite. +# super(z * 1000) +# end + +# def ox=(ox) +# return if @bitmap == nil +# # Have viewport stay in loop on X-axis. +# super(ox % @bitmap.width) +# end + +# def oy=(oy) +# return if @bitmap == nil +# # Have viewport stay in loop on Y-axis. +# super(oy % @bitmap.height) +# end + +# def bitmap +# # Return the single bitmap, before it was tiled. +# return @bitmap +# end + +# def bitmap=(tile) +# @bitmap = tile +# # Calculate the number of tiles it takes to span screen in both directions. +# xx = 1 + (SCREEN[0].to_f / tile.width).ceil +# yy = 1 + (SCREEN[1].to_f / tile.height).ceil +# # Create appropriately sized bitmap, then tile across it with source image. +# plane = Bitmap.new(@bitmap.width * xx, @bitmap.height * yy) +# (0..xx).each {|x| (0..yy).each {|y| +# plane.blt(x * @bitmap.width, y * @bitmap.height, @bitmap, @bitmap.rect) +# }} +# # Set the bitmap to the sprite through its super class (Sprite). +# super(plane) +# end + +# # Redefine methods dealing with coordinates (defined in super) to do nothing. +# def x; end +# def y; end +# def x=(x); end +# def y=(y); end +# end +#=============================================================================== +# ** Integer +#=============================================================================== + +class Integer + + def gcd(num) + # Returns the greatest common denominator of self and num. + min, max = self.abs, num.abs + while min > 0 + tmp = min + min = max % min + max = tmp + end + return max + end + + def lcm(num) + # Returns the lowest common multiple of self and num. + return [self, num].include?(0) ? 0 : (self / self.gcd(num) * num).abs + end +end + +#=============================================================================== +# ** Resolution Log +#=============================================================================== +if RESOLUTION_LOG + undersize, mapinfo = [], load_data('Data/MapInfos.rxdata') + # Create a text file and write the header. + file = File.open('Resolution Log.txt', 'wb') + file.write("[RESOLUTION LOG]\r\n\r\n") + time = Time.now.strftime("%x at %I:%M:%S %p") + file.write(" Logged on #{time}\r\n\r\n") + lcm = SCREEN[0].lcm(SCREEN[1]).to_f + aspect = [(lcm / SCREEN[1]), (lcm / SCREEN[0])].collect {|num| num.round } + file.write("RESOLUTION:\r\n #{SCREEN[0].to_i} x #{SCREEN[1].to_i}\r\n") + file.write("ASPECT RATIO:\r\n #{aspect[0]}:#{aspect[1]}\r\n") + file.write("MINIMUM MAP SIZE:\r\n #{(SCREEN[0] / 32).ceil} x #{(SCREEN[1] / 32).ceil}\r\n\r\n") + file.write("UNDERSIZED MAPS:\r\n") + mapinfo.keys.each {|key| + map = load_data(sprintf("Data/Map%03d.rxdata", key)) + next if map.width*32 >= SCREEN[0] && map.height*32 >= SCREEN[1] + undersize.push(key) + } + unless undersize.empty? + file.write("The following maps are too small for the defined resolution. They should be adjusted to prevent graphical errors.\r\n\r\n") + undersize.sort.each {|id| file.write(" MAP[#{id}]: #{mapinfo[id].name}\r\n") } + file.write("\r\n") + else + file.write(' All maps are sized correctly.') + end + file.close +end + +# Call the resolution, setting it to a global variable for plug-ins. +$resolution = Resolution.new \ No newline at end of file diff --git a/mkxp-z/Kawariki-patches/ports/Zeus_Fullscreen++.rb b/mkxp-z/Kawariki-patches/ports/Zeus_Fullscreen++.rb new file mode 100644 index 0000000..ac59e7b --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/Zeus_Fullscreen++.rb @@ -0,0 +1,61 @@ +# Zeus81 Fullscreen++ MKXP API Shim +# Authors: Taeyeon Mori + +Preload.require 'PreloadIni.rb' + +# Fullscreen++ v2.2 for VX and VXace by Zeus81 +# Free for non commercial and commercial use +# Licence : http://creativecommons.org/licenses/by-sa/3.0/ +# Contact : zeusex81@gmail.com +# (fr) Manuel d'utilisation : http://pastebin.com/raw.php?i=1TQfMnVJ +# (en) User Guide : http://pastebin.com/raw.php?i=EgnWt9ur + +$imported ||= {} +$imported[:Zeus_Fullscreen] = __FILE__ + +class << Graphics + Disable_VX_Fullscreen = true + + unless method_defined?(:zeus_fullscreen_update) + alias zeus_fullscreen_update update + end + + def load_fullscreen_settings + fullscreen = (Preload::Ini.readIniString('./Game.ini', 'Fullscreen++', 'Fullscreen') || '0') == '1' + end + def save_fullscreen_settings + Preload::Ini.writeIniString('./Game.ini', 'Fullscreen++', 'Fullscreen', fullscreen ? '1' : '0') + end + + def fullscreen? + fullscreen + end + def vx_fullscreen? + false + end + def toggle_fullscreen + fullscreen = !fullscreen + end + def toggle_vx_fullscreen + end + def vx_fullscreen_mode + end + def fullscreen_mode + fullscreen = true + end + def windowed_mode + fullscreen = false + end + def toggle_ratio + end + def ratio + 1 + end + def ratio=(r) + end + def update + zeus_fullscreen_update + toggle_fullscreen if Input.trigger?(Input::F5) + end +end +Graphics.load_fullscreen_settings \ No newline at end of file diff --git a/mkxp-z/Kawariki-patches/ports/Zeus_Map_Effects.rb b/mkxp-z/Kawariki-patches/ports/Zeus_Map_Effects.rb new file mode 100644 index 0000000..96707f8 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/Zeus_Map_Effects.rb @@ -0,0 +1,530 @@ +# Map Effects v1.4.1 for VX and VXace by Zeus81 +# €30 for commercial use +# Licence : http://creativecommons.org/licenses/by-nc-nd/4.0/ +# Contact : zeusex81@gmail.com +# (fr) Manuel d'utilisation : https://www.dropbox.com/s/lb1d3q9jmx53taf/Map%20Effects%20Doc%20Fr.txt +# (en) User Guide : https://www.dropbox.com/s/sk3uwq2bleoxr7s/Map%20Effects%20Doc%20En.txt +# Demo : https://www.dropbox.com/s/2ex6906dyehl7an/Map%20Effects.zip + +$imported ||= {} +$imported[:Zeus_Map_Effects] = __FILE__ + +def xp?() false end ; def vx?() false end ; def vxace?() false end + RUBY_VERSION == '1.8.1' ? defined?(Hangup) ? + def xp?() true end : def vx?() true end : def vxace?() true end + + class << Graphics + def snap_elements_to_bitmap(*elements) + if !@snap_elements_back or @snap_elements_back.disposed? + @snap_elements_back = Sprite.new + @snap_elements_back.bitmap = Bitmap.new(1, 1) + @snap_elements_back.bitmap.set_pixel(0, 0, Color.new(0, 0, 0)) + @snap_elements_back.z = 0x0FFF_FFFF + end + @snap_elements_back.zoom_x = width + @snap_elements_back.zoom_y = height + @snap_elements_back.visible = true + elements.each {|element| element.z += 0x1FFF_FFFF} + bmp = snap_to_bitmap rescue retry + @snap_elements_back.visible = false + elements.each {|element| element.z -= 0x1FFF_FFFF} + return bmp + end + end + + module Math + module_function + def min(x, y) x < y ? x : y end + def max(x, y) x < y ? y : x end + def middle(min, x, max) x < max ? x < min ? min : x : max end + end + + module Zeus + module Animation + def animate(variable, target_value, duration=0, ext=nil) + @za_animations ||= {} + base_value = Marshal.load(Marshal.dump(instance_variable_get(variable))) + if duration < 1 + update_animation_value(variable, base_value, target_value, 1, 1, ext) + @za_animations.delete(variable) + else + @za_animations[variable] = [base_value, target_value, 0, duration.to_i, ext] + end + end + def animating? + @za_animations and !@za_animations.empty? + end + def clear_animations + @za_animations and @za_animations.clear + end + def memorize_animations(variables = instance_variables) + data = {} + variables.each {|var| data[var.to_sym] = instance_variable_get(var)} + data.delete(:@za_memorize) + @za_memorize = Marshal.dump(data) + end + def restore_animations + return unless @za_memorize + Marshal.load(@za_memorize).each {|var,value| instance_variable_set(var,value)} + end + def update_animations + return unless @za_animations + @za_animations.delete_if do |variable, data| + data[2] += 1 + update_animation_value(variable, *data) + data[2] == data[3] + end + end + private + def calculate_next_value(base_value, target_value, duration, duration_total) + base_value + (target_value - base_value) * duration / duration_total + end + def update_animation_value(variable, base_value, target_value, duration, duration_total, ext) + method_name = "update_animation_variable_#{variable.to_s[1..-1]}" + method_name = "update_animation_#{base_value.class}" unless respond_to?(method_name) + send(method_name, variable, base_value, target_value, duration, duration_total, ext) + end + def update_animation_Color(variable, base_value, target_value, duration, duration_total, ext) + value = instance_variable_get(variable) + value.red = calculate_next_value(base_value.red , target_value.red , duration, duration_total) + value.green = calculate_next_value(base_value.green, target_value.green, duration, duration_total) + value.blue = calculate_next_value(base_value.blue , target_value.blue , duration, duration_total) + value.alpha = calculate_next_value(base_value.alpha, target_value.alpha, duration, duration_total) + end + def update_animation_Tone(variable, base_value, target_value, duration, duration_total, ext) + value = instance_variable_get(variable) + value.red = calculate_next_value(base_value.red , target_value.red , duration, duration_total) + value.green = calculate_next_value(base_value.green, target_value.green, duration, duration_total) + value.blue = calculate_next_value(base_value.blue , target_value.blue , duration, duration_total) + value.gray = calculate_next_value(base_value.gray , target_value.gray , duration, duration_total) + end + def update_animation_Float(variable, base_value, target_value, duration, duration_total, ext) + value = calculate_next_value(base_value, target_value, duration, duration_total) + instance_variable_set(variable, value) + end + alias update_animation_Fixnum update_animation_Float + alias update_animation_Bignum update_animation_Float + end + end + + class Game_Map_Effects + include Zeus::Animation + attr_accessor :active, :refresh_rate, :back, :x, :y, :ox, :oy, :angle, + :zoom_x, :zoom_y, :mirror, :opacity, :blend_type, :color, :tone, + :hue, :wave_amp, :wave_length, :wave_speed, :wave_phase, + :pixelize, :blur_division, :blur_fade, :blur_animation, + :gaussian_blur_length, :linear_blur_angle, :linear_blur_length, + :radial_blur_angle, :zoom_blur_length, :motion_blur_rate + def initialize + @active = true + @refresh_rate = 30.0 + clear + end + def clear + @back = false + @x = @ox = Graphics.width / 2 + @y = @oy = Graphics.height / 2 + @zoom_x = 1.0 + @zoom_y = 1.0 + @zoom2 = Math.sqrt(100.0) + @angle = 0.0 + @wave_amp = 0.0 + @wave_length = 180 + @wave_speed = 360 + @wave_phase = 0.0 + @mirror = false + @opacity = 255 + @blend_type = 0 + @color ||= Color.new(0, 0, 0, 0) + @color.set(0, 0, 0, 0) + @tone ||= Tone.new(0, 0, 0, 0) + @tone.set(0, 0, 0, 0) + @hue = 0 + @pixelize = 1.0 + @pixelize2 = Math.sqrt(100.0) + @blur_division = 4.0 + @blur_fade = 1.0 + @blur_animation = 0.0 + @gaussian_blur_length = 0.0 + @linear_blur_angle = 0.0 + @linear_blur_length = 0.0 + @radial_blur_angle = 0.0 + @zoom_blur_length = 0.0 + @motion_blur_rate = 0.0 + clear_animations + end + alias memorize memorize_animations + alias restore restore_animations + alias update update_animations + def active? + return false unless @active + animating? or blur? or @mirror or @blend_type != 0 or + @zoom_x != 1 or @zoom_y != 1 or @pixelize > 1 or + @angle % 360 != 0 or @hue.to_i % 360 != 0 or @color.alpha != 0 or + @tone.red != 0 or @tone.green != 0 or @tone.blue != 0 or @tone.gray != 0 or + (@wave_amp * @zoom_x >= 1 and @wave_length * @zoom_y >= 1) + end + def blur? + return false if @blur_division < 1 + @gaussian_blur_length != 0 or @linear_blur_length != 0 or + @radial_blur_angle != 0 or @zoom_blur_length != 0 or @motion_blur_rate != 0 + end + def refresh_bitmap? + @refresh_rate > 0 and + Graphics.frame_count % (Graphics.frame_rate / @refresh_rate.to_f) < 1 + end + def tilemap_wave_sync(tilemap_oy) + return 0 if @wave_length == 0 + tilemap_oy * @wave_speed / @wave_length.to_f + end + def blur_animation_offset + return 0 if @blur_animation == 0 + 1 - @blur_animation * Graphics.frame_count / Graphics.frame_rate.to_f % 1 + end + def refresh_motion_blur? + @blur_division >= 1 and @motion_blur_rate > 0 and + Graphics.frame_count % @motion_blur_rate < 1 + end + def set_origin(x, y, duration=0) + x = x * Graphics.width / 100 + y = y * Graphics.height / 100 + animate(:@x , x, duration) + animate(:@y , y, duration) + animate(:@ox, x, duration) + animate(:@oy, y, duration) + end + def set_zoom(zoom, duration=0, center_on_player=true) + zoom = Math.sqrt(Math.max(1, zoom)) + animate(:@zoom2, zoom, duration, center_on_player) + end + def update_animation_variable_zoom2(variable, base_value, target_value, duration, duration_total, center_on_player) + update_animation_Float(variable, base_value, target_value, duration, duration_total, nil) + @zoom_y = @zoom_x = @zoom2 ** 2 / 100.0 + display_ratio = Game_Map::DisplayRatio.to_f + if center_on_player + x = $game_player.real_x / display_ratio + y = $game_player.real_y / display_ratio + else + x = $game_map.display_x / display_ratio + $game_map.screen_tile_x / 2 + y = $game_map.display_y / display_ratio + $game_map.screen_tile_y / 2 + end + $game_player.center(x, y) + end + def set_angle(angle, duration=0) + animate(:@angle, angle, duration) + end + def set_opacity(opacity, duration=0) + opacity = opacity * 255 / 100 + animate(:@opacity, opacity, duration) + end + def set_color(red, green, blue, alpha, duration=0) + animate(:@color, Color.new(red, green, blue, alpha), duration) + end + def set_tone(red, green, blue, gray, duration=0) + animate(:@tone, Tone.new(red, green, blue, gray), duration) + end + def set_hue(hue, duration=0) + animate(:@hue, hue, duration) + end + def set_wave(amp, length, speed, duration=0) + animate(:@wave_amp , amp , duration) + animate(:@wave_length, length, duration) + animate(:@wave_speed , speed , duration) + end + def set_pixelize(pixelize, duration=0) + pixelize = Math.sqrt(Math.max(100, pixelize)) + animate(:@pixelize2, pixelize, duration) + end + def update_animation_variable_pixelize2(variable, base_value, target_value, duration, duration_total, ext) + update_animation_Float(variable, base_value, target_value, duration, duration_total, ext) + @pixelize = @pixelize2 ** 2 / 100.0 + end + def setup_blur(division, fade, animation, duration=0) + division = Math.middle(0, division, 16) + animate(:@blur_division , division , duration) + animate(:@blur_fade , fade , duration) + animate(:@blur_animation, animation, duration) + end + def set_gaussian_blur(length, duration=0) + animate(:@gaussian_blur_length, length, duration) + end + def set_linear_blur(angle, length, duration=0) + animate(:@linear_blur_angle , angle , duration) + animate(:@linear_blur_length, length, duration) + end + def set_radial_blur(angle, duration=0) + animate(:@radial_blur_angle, angle, duration) + end + def set_zoom_blur(zoom, duration=0) + length = Math.max(1, zoom) / 100.0 - 1 + animate(:@zoom_blur_length, length, duration) + end + def set_motion_blur(rate, duration=0) + animate(:@motion_blur_rate, rate, duration) + end + end + + class Spriteset_Map_Effects + Blur_Offset = [[0.7,0.7], [-0.7,-0.7], [-0.7,0.7], [0.7,-0.7], + [0,1], [0,-1], [1,0], [-1,0]] + def initialize(*viewports) + @map_viewports = viewports + @viewport = Viewport.new(viewports[0].rect) + @viewport.z = viewports[0].z + @viewport.visible = false + @effects_sprites = [] + @effects_bitmaps = [] + @data = $game_map.effects + end + def dispose(dispose_viewport=true) + @effects_sprites.each {|sprite| sprite.dispose} + @effects_sprites.clear + @effects_bitmaps.each {|bitmap| bitmap.dispose if bitmap} + @effects_bitmaps.clear + @pixelize_bitmap.dispose if @pixelize_bitmap + @pixelize_bitmap = nil + @back_sprite.dispose if @back_sprite + @back_sprite = nil + if dispose_viewport + @viewport.dispose + else + @viewport.visible = false + @map_viewports.each {|viewport| viewport.visible = true} + end + end + def update(tilemap_oy = 0) + unless @data.active? + dispose(false) if @viewport.visible + return + end + @viewport.visible = true + @motion_blur_refresh ||= @data.refresh_motion_blur? + refresh_sprites + if !@effects_bitmaps[0] or @data.refresh_bitmap? + refresh_bitmaps + refresh_pixelize + end + refresh_back + wave_sync = @data.tilemap_wave_sync(tilemap_oy) + blur_offset = @data.blur_animation_offset + @effects_sprites.each_with_index do |sprite, id| + update_effects(sprite, id, wave_sync) + update_pixelize(sprite) if @pixelize_bitmap + update_blur(sprite, id, blur_offset) if id > 0 + end + @data.wave_phase = @effects_sprites[0].wave_phase - wave_sync + end + def refresh_sprites + n = (@data.blur? ? @data.blur_division.to_i+1 : 1) - @effects_sprites.size + n.times {@effects_sprites << Sprite.new(@viewport)} + (-n).times {@effects_sprites.pop.dispose} + end + def refresh_bitmaps + n = (@data.motion_blur_rate == 0 ? 1 : @effects_sprites.size) - @effects_bitmaps.size + n.times {@effects_bitmaps << nil} + (-n).times {bmp = @effects_bitmaps.pop and bmp.dispose} + @map_viewports.each {|viewport| viewport.visible = true} + @effects_bitmaps.unshift(@effects_bitmaps.pop) if @motion_blur_refresh + @effects_bitmaps[0].dispose if @effects_bitmaps[0] + @effects_bitmaps[0] = Graphics.snap_elements_to_bitmap(*@map_viewports) + @effects_bitmaps[0].hue_change(@data.hue % 360) if @data.hue.to_i % 360 != 0 + @map_viewports.each {|viewport| viewport.visible = false} + @motion_blur_refresh = false + end + def refresh_pixelize + if @data.pixelize > 1 + bmp = @effects_bitmaps[0] + @pixelize_rect ||= Rect.new(0, 0, 0, 0) + @pixelize_rect.width = Math.max(1, bmp.width / @data.pixelize) + @pixelize_rect.height = Math.max(1, bmp.height / @data.pixelize) + @pixelize_bitmap ||= Bitmap.new(bmp.width, bmp.height) + @pixelize_bitmap.clear + @pixelize_bitmap.stretch_blt(@pixelize_rect, bmp, bmp.rect) + elsif @pixelize_bitmap + @pixelize_bitmap.dispose + @pixelize_bitmap = nil + end + end + def refresh_back + if @data.back + @back_sprite ||= Sprite.new(@viewport) + @back_sprite.bitmap = @effects_bitmaps[0] + elsif @back_sprite + @back_sprite.dispose + @back_sprite = nil + end + end + def update_effects(sprite, id, wave_sync) + sprite.bitmap = @effects_bitmaps[id] || @effects_bitmaps[0] + sprite.x = @data.x + sprite.y = @data.y + sprite.z = id + 1 + sprite.ox = @data.ox + sprite.oy = @data.oy + sprite.zoom_x = @data.zoom_x + sprite.zoom_y = @data.zoom_y + sprite.angle = @data.angle % 360 + sprite.wave_amp = @data.wave_amp * @data.zoom_x + sprite.wave_length = @data.wave_length * @data.zoom_y + sprite.wave_speed = @data.wave_speed * @data.zoom_y + sprite.wave_phase = @data.wave_phase + wave_sync + sprite.mirror = @data.mirror + sprite.opacity = @data.opacity + sprite.blend_type = @data.blend_type + sprite.color = @data.color + sprite.tone = @data.tone + sprite.update + end + def update_pixelize(sprite) + pzx = @pixelize_bitmap.width / @pixelize_rect.width.to_f + pzy = @pixelize_bitmap.height / @pixelize_rect.height.to_f + sprite.bitmap = @pixelize_bitmap + sprite.src_rect = @pixelize_rect + sprite.x -= sprite.ox - (sprite.ox /= pzx).to_i * pzx + sprite.y -= sprite.oy - (sprite.oy /= pzy).to_i * pzy + sprite.zoom_x *= pzx + sprite.zoom_y *= pzy + end + def update_blur(sprite, id, blur_offset) + update_blur_opacity(sprite, id-blur_offset) + update_gaussian_blur(sprite, id) if @data.gaussian_blur_length != 0 + update_linear_blur(sprite, id-blur_offset) if @data.linear_blur_length != 0 + update_radial_blur(sprite, id-blur_offset) if @data.radial_blur_angle != 0 + update_zoom_blur(sprite, id-blur_offset) if @data.zoom_blur_length != 0 + end + def update_blur_opacity(sprite, id) + sprite.opacity /= (id < 1 ? 2 : id+1) ** + (1 + @data.blur_fade / (@data.blur_division*20.0)) + end + def update_gaussian_blur(sprite, id) + box, boy = *Blur_Offset[(id-1)%8] + offset = ((id+3)/4) / ((@data.blur_division.to_i+3)/4).to_f * + @data.gaussian_blur_length + sprite.x += (offset.ceil * box).round + sprite.y += (offset.ceil * boy).round + end + def update_linear_blur(sprite, id) + radian = @data.linear_blur_angle * Math::PI / 180 + offset = id * @data.linear_blur_length / @data.blur_division.to_f + sprite.x += offset * Math.cos( radian) + sprite.y += offset * Math.sin(-radian) + end + def update_zoom_blur(sprite, id) + zoom = 1 + id * @data.zoom_blur_length / @data.blur_division.to_f + sprite.zoom_x *= zoom + sprite.zoom_y *= zoom + end + def update_radial_blur(sprite, id) + sprite.angle += id * @data.radial_blur_angle / @data.blur_division.to_f + sprite.angle %= 360 + end + end + + class Game_Map + if vx? + def screen_tile_x() Graphics.width / 32 end + def screen_tile_y() Graphics.height / 32 end + DisplayRatio = 256 + else + DisplayRatio = 1 + end + def zoom_ox + return 0 unless effects.active and effects.zoom_x > 1 + (1 - 1 / effects.zoom_x) * screen_tile_x / 2 + end + def zoom_oy + return 0 unless effects.active and effects.zoom_y > 1 + (1 - 1 / effects.zoom_y) * screen_tile_y / 2 + end + def limit_x(x) + ox = zoom_ox + min = DisplayRatio * -ox + max = DisplayRatio * (width - screen_tile_x + ox) + x < max ? x < min ? min : x : max + end + def limit_y(y) + oy = zoom_oy + min = DisplayRatio * -oy + max = DisplayRatio * (height - screen_tile_y + oy) + y < max ? y < min ? min : y : max + end + def set_display_x(x) + x = loop_horizontal? ? x % (width * DisplayRatio) : limit_x(x) + @parallax_x += x - @display_x if @parallax_loop_x or !loop_horizontal? + @display_x = x + end + def set_display_y(y) + y = loop_vertical? ? y % (height * DisplayRatio) : limit_y(y) + @parallax_y += y - @display_y if @parallax_loop_y or !loop_vertical? + @display_y = y + end + def set_display_pos(x, y) set_display_x(x); set_display_y(y) end + def scroll_down(distance) set_display_y(@display_y + distance) end + def scroll_left(distance) set_display_x(@display_x - distance) end + def scroll_right(distance) set_display_x(@display_x + distance) end + def scroll_up(distance) set_display_y(@display_y - distance) end + def effects() @effects ||= Game_Map_Effects.new end + alias zeus_map_effects_update update + def update(*args) + zeus_map_effects_update(*args) + effects.update + end + end + + class Game_Interpreter + def map_effects + $game_map.effects + end + end + + class Game_Player + def center(x, y) + $game_map.set_display_pos(x*256-CENTER_X, y*256-CENTER_Y) + end + end if vx? + + class Spriteset_Map + alias zeus_map_effects_update update + def update + zeus_map_effects_update + @map_effects ||= Spriteset_Map_Effects.new(@viewport1) + @map_effects.update(@tilemap.oy) + end + alias zeus_map_effects_dispose dispose + def dispose + zeus_map_effects_dispose + @map_effects.dispose + end + end + + $imported[:Zeus_Weather_Viewport] ||= __FILE__ + if $imported[:Zeus_Weather_Viewport] == __FILE__ + + class Spriteset_Map + alias zeus_weather_viewport_create_weather create_weather + def create_weather + zeus_weather_viewport_create_weather + @weather.weather_viewport = @viewport1 + end + end + + class Spriteset_Weather + if vx? + def weather_viewport=(viewport) + for sprite in @sprites + sprite.viewport = viewport + sprite.z = 0x8000 + end + end + else + attr_accessor :weather_viewport + alias zeus_weather_viewport_add_sprite add_sprite + def add_sprite + zeus_weather_viewport_add_sprite + @sprites[-1].viewport = @weather_viewport + @sprites[-1].z = 0x8000 + end + end + end + + end diff --git a/mkxp-z/Kawariki-patches/ports/achievements.rb b/mkxp-z/Kawariki-patches/ports/achievements.rb new file mode 100644 index 0000000..b7e9f89 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/achievements.rb @@ -0,0 +1,386 @@ +# [122] 29075340: achievements +# cyanic's Quick and Easy Steamworks Achievements Integration for Ruby +# https://github.com/GMMan/RGSS_SteamUserStatsLite +# r4 06/16/16 +# +# Drop steam_api.dll into the root of your project. Requires Steamworks SDK version >= 1.37. +# +# "Miller complained about how hard achievements were to implement in C++, so this was born." +# + +$imported ||= {} +$imported['cyanic-SteamUserStatsLite'] = 4 # Slightly unorthodox, it's a version number. + +# A context class to get Steamworks pointers to interfaces. +# +# @author cyanic +class SteamAPIContext + STEAMCLIENT_INTERFACE_VERSION = 'SteamClient017' + STEAMUSERSTATS_INTERFACE_VERSION = 'STEAMUSERSTATS_INTERFACE_VERSION011' + STEAMAPPS_INTERFACE_VERSION = 'STEAMAPPS_INTERFACE_VERSION008' + + # Instantiates a new instance of +SteamAPIContext+. + def initialize + # @initted = false + # @h_steam_user = # @@dll_SteamAPI_GetHSteamUser.call + # if (@h_steam_pipe = # @@dll_SteamAPI_GetHSteamPipe.call) != 0 + # return if (@steam_client = # @@dll_SteamInternal_CreateInterface.call(STEAMCLIENT_INTERFACE_VERSION)) == 0 + # return if (@steam_user_stats = # @@dll_SteamAPI_ISteamClient_GetISteamUserStats.call(@steam_client, @h_steam_user, @h_steam_pipe, STEAMUSERSTATS_INTERFACE_VERSION)) == 0 + # return if (@steam_apps = # @@dll_SteamAPI_ISteamClient_GetISteamApps.call(@steam_client, @h_steam_user, @h_steam_pipe, STEAMAPPS_INTERFACE_VERSION)) == 0 + # + # @initted = true + # end + end + + # Checks if context is initialized. + # + # @return [true, false] Whether context is initialized. + def initted? + @initted + end + + # Gets the ISteamClient pointer + # + # @return [Fixnum, nil] The ISteamClient pointer if context is initialized, otherwise +nil+. + def steam_client + @steam_client if initted? + end + + # Gets the ISteamUserStats pointer + # + # @return [Fixnum, nil] The ISteamUserStats pointer if context is initialized, otherwise +nil+. + def steam_user_stats + @steam_user_stats if initted? + end + + # Gets the ISteamApps pointer + # + # @return [Fixnum, nil] The ISteamUserStats pointer if context is initialized, otherwise +nil+. + def steam_apps + @steam_apps if initted? + end + + private + def self.is_64bit? + # Probably very bad detection of whether current runtime is 64-bit + (/x64/ =~ RUBY_PLATFORM) != nil + end + + def self.steam_dll_name + # @@dll_name ||= self.is_64bit? ? 'steam_api64' : 'steam_api' + end + + # @@dll_SteamAPI_GetHSteamUser = Win32API.new(self.steam_dll_name, 'SteamAPI_GetHSteamUser', '', 'I') + # @@dll_SteamAPI_GetHSteamPipe = Win32API.new(self.steam_dll_name, 'SteamAPI_GetHSteamPipe', '', 'I') + # @@dll_SteamInternal_CreateInterface = Win32API.new(self.steam_dll_name, 'SteamInternal_CreateInterface', 'P', 'I') + # @@dll_SteamAPI_ISteamClient_GetISteamUserStats = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamClient_GetISteamUserStats', 'IIIP', 'I') + # @@dll_SteamAPI_ISteamClient_GetISteamApps = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamClient_GetISteamApps', 'IIIP', 'I') + +end + +# A simple class for Steamworks UserStats integration. +# +# @author cyanic +class SteamUserStatsLite + + # Instantiates a new instance of +SteamUserStatsLite+. + def initialize + @initted = false + api_initted = # @@dll_SteamAPI_Init.call % 256 != 0 + if api_initted + @context = SteamAPIContext.new + if @context.initted? + @i_apps = @context.steam_apps + @i_user_stats = @context.steam_user_stats + @initted = true + end + end + end + + # Shuts down Steamworks. + # + # @return [void] + def shutdown + if @initted + @i_apps = nil + @i_user_stats = nil + # @@dll_SteamAPI_Shutdown.call + @initted = false + end + end + + # Checks if Steamworks is initialized. + # + # @return [true, false] Whether Steamworks is initialized. + def initted? + @initted + end + + # Restarts the app if Steamworks is not availble. + # + # @param app_id [Integer] The app ID to relaunch as. + # @return [true, false] +true+ if current instance should exit, +false+ if not. + def self.restart_app_if_necessary(app_id) + # @@dll_SteamAPI_RestartAppIfNecessary.call(app_id) % 256 != 0 + end + + # Runs Steam callbacks. + # + # @return [void] + def update + # @@dll_SteamAPI_RunCallbacks.call if initted? + end + + # Checks if current app is owned. + # + # @return [true, false, nil] Whether the current user has a license for the current app. +nil+ is returned if ownership status can't be retrieved. + def is_subscribed + if initted? + # @@dll_SteamAPI_ISteamApps_BIsSubscribed.call(@i_apps) % 256 != 0 + else + nil + end + end + + # Checks if a DLC is installed. + # + # @param app_id [Integer] The app ID of the DLC to check. + # @return [true, false, nil] Whether the DLC is installed. +nil+ is returned if the installation status can't be retrieved. + def is_dlc_installed(app_id) + if initted? + # @@dll_SteamAPI_ISteamApps_BIsDlcInstalled.call(@i_apps, app_id) % 256 != 0 + else + nil + end + end + + # Pulls current user's stats from Steam. + # + # @return [true, false] Whether the stats have been successfully pulled. + def request_current_stats + if initted? + # @@dll_SteamAPI_ISteamUserStats_RequestCurrentStats.call(@i_user_stats) % 256 != 0 + else + false + end + end + + # Gets the value of an INT stat. + # + # @param name [String] The name of the stat. + # @return [Integer, nil] The value of the stat, or +nil+ if the stat cannot be retrieved. + def get_stat_int(name) + if initted? + val = ' ' * 4 + ok = # @@dll_SteamAPI_ISteamUserStats_GetStat.call(@i_user_stats, name, val) % 256 != 0 + ok ? val.unpack('I')[0] : nil + else + nil + end + end + + # Gets the value of an FLOAT stat. + # + # @param name [String] The name of the stat. + # @return [Float, nil] The value of the stat, or +nil+ if the stat cannot be retrieved. + def get_stat_float(name) + if initted? + val = ' ' * 4 + ok = # @@dll_SteamAPI_ISteamUserStats_GetStat0.call(@i_user_stats, name, val) % 256 != 0 + ok ? val.unpack('f')[0] : nil + else + nil + end + end + + # Sets the value of a stat. + # + # @param name [String] The name of the stat. + # @param val [Integer, Float] The value of the stat. + # @return [true, false] Whether the stat was successfully updated. + # @example + # steam = SteamUserStatsLite.instance + # steam.set_stat 'YOUR_STAT_ID_HERE', 100 + # steam.update + def set_stat(name, val) + if initted? + + # @@dll_SteamAPI_ISteamUserStats_StoreStats.call(@i_user_stats) % 256 != 0 && ok + end + end + + # Updates an AVGRATE stat. + # + # @param name [String] The name of the stat. + # @param count_this_session [Float] The value during this session. + # @param session_length [Float] The length of this session. + # @return [true, false] Whether the stat was successfully updated. + def update_avg_rate_stat(name, count_this_session, session_length) + if initted? + packed = self.class.pack_double session_length + end + end + + # Gets an achievement's state. + # + # @param name [String] The name of the achievement. + # @return [true, false, nil] Whether the achievement has unlocked, or +nil+ if the achievement cannot be retrieved. + def get_achievement(name) + if initted? + val = ' ' + ok = # @@dll_SteamAPI_ISteamUserStats_GetAchievement.call(@i_user_stats, name, val) % 256 != 0 + ok ? val.unpack('C')[0] != 0 : nil + else + nil + end + end + + # Sets an achievement as unlocked. + # + # @param name [String] The name of the achievement. + # @return [true, false] Whether the achievement was set successfully. + # @example + # steam = SteamUserStatsLite.instance + # steam.set_achievement 'YOUR_ACH_ID_HERE' + # steam.update + def set_achievement(name) + # if initted? + # ok = # @@dll_SteamAPI_ISteamUserStats_SetAchievement.call(@i_user_stats, name) % 256 != 0 + # # @@dll_SteamAPI_ISteamUserStats_StoreStats.call(@i_user_stats) % 256 != 0 && ok + # # else + # # false + # end + end + + # Sets an achievement as locked. + # + # @param name [String] The name of the achievement. + # @return [true, false] Whether the achievement was cleared successfully. + def clear_achievement(name) + # if initted? + # ok = # @@dll_SteamAPI_ISteamUserStats_ClearAchievement.call(@i_user_stats, name) % 256 != 0 + # # @@dll_SteamAPI_ISteamUserStats_StoreStats.call(@i_user_stats) % 256 != 0 && ok + # else + # false + # end + end + + # Gets an achievement's state and unlock time. + # + # @param name [String] The name of the achievement. + # @return [] The achievement's state (+true+ or +false+) and the time it was unlocked. + def get_achievement_and_unlock_time(name) + if initted? + achieved = ' ' + unlock_time = ' ' * 4 + ok = # @@dll_SteamAPI_ISteamUserStats_GetAchievementAndUnlockTime.call(@i_user_stats, name, achieved, unlock_time) % 256 != 0 + ok ? [achieved.unpack('C')[0] != 0, Time.at(unlock_time.unpack('L')[0])] : nil + else + nil + end + end + + # Gets the value of an achievement's display attribute. + # + # @param name [String] The name of the achievement. + # @param key [String] The key of the display attribute. + # @return [String] The value of the display attribute. + def get_achievement_display_attribute(name, key) + if initted? + # @@dll_SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute.call @i_user_stats, name, key + else + nil + end + end + + # Gets the number of achievements. + # + # @return [Integer, nil] The number of achievements, or +nil+ if the number cannot be retrieved. + def get_num_achievements + if initted? + # @@dll_SteamAPI_ISteamUserStats_GetNumAchievements.call @i_user_stats + else + nil + end + end + + # Gets the name of an achievement by its index. + # + # @param achievement [Integer] The index of the achievement. + # @return [String] The name of the achievement. + def get_achievement_name(achievement) + if initted? + # @@dll_SteamAPI_ISteamUserStats_GetAchievementName.call @i_user_stats, achievement + else + nil + end + end + + # Resets all stats. + # + # @param achievements_too [true, false] Whether to reset achievements as well. + # @return [true, false] Whether achievements have been reset. + def reset_all_stats(achievements_too) + # if initted? + # ok = # @@dll_SteamAPI_ISteamUserStats_ResetAllStats.call(@i_user_stats, achievements_too ? 1 : 0) % 256 != 0 + # # @@dll_SteamAPI_ISteamUserStats_StoreStats.call(@i_user_stats) % 256 != 0 && ok + # else + # false + # end + end + + # Gets the global instance of SteamUserStatsLite. + # + # @return [SteamUserStatsLite] The global instance of the class. + def self.instance + @@instance + end + + private + def self.is_64bit? + # Probably very bad detection of whether current runtime is 64-bit + (/x64/ =~ RUBY_PLATFORM) != nil + end + + def self.steam_dll_name + # @@dll_name ||= self.is_64bit? ? 'steam_api64' : 'steam_api' + end + + # Function imports + # @@dll_SteamAPI_RestartAppIfNecessary = Win32API.new(self.steam_dll_name, 'SteamAPI_RestartAppIfNecessary', 'I', 'I') + # @@dll_SteamAPI_Init = Win32API.new(self.steam_dll_name, 'SteamAPI_Init', '', 'I') + # @@dll_SteamAPI_Shutdown = Win32API.new(self.steam_dll_name, 'SteamAPI_Shutdown', '', 'V') + # @@dll_SteamAPI_RunCallbacks = Win32API.new(self.steam_dll_name, 'SteamAPI_RunCallbacks', '', 'V') + # @@dll_SteamAPI_ISteamUserStats_RequestCurrentStats = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_RequestCurrentStats', 'P', 'I') + # @@dll_SteamAPI_ISteamUserStats_GetStat = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_GetStat', 'PPP', 'I') + # @@dll_SteamAPI_ISteamUserStats_GetStat0 = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_GetStat0', 'PPP', 'I') + # @@dll_SteamAPI_ISteamUserStats_SetStat = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_SetStat', 'PPL', 'I') + # @@dll_SteamAPI_ISteamUserStats_SetStat0 = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_SetStat0', 'PPI', 'I') + # @@dll_SteamAPI_ISteamUserStats_UpdateAvgRateStat = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_UpdateAvgRateStat', 'PPIII', 'I') + # @@dll_SteamAPI_ISteamUserStats_GetAchievement = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_GetAchievement', 'PPP', 'I') + # @@dll_SteamAPI_ISteamUserStats_SetAchievement = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_SetAchievement', 'PP', 'I') + # @@dll_SteamAPI_ISteamUserStats_ClearAchievement = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_ClearAchievement', 'PP', 'I') + # @@dll_SteamAPI_ISteamUserStats_GetAchievementAndUnlockTime = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_GetAchievementAndUnlockTime', 'PPPP', 'I') + # @@dll_SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute', 'PPP', 'P') + # @@dll_SteamAPI_ISteamUserStats_GetNumAchievements = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_GetNumAchievements', 'P', 'I') + # @@dll_SteamAPI_ISteamUserStats_GetAchievementName = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_GetAchievementName', 'PI', 'P') + # @@dll_SteamAPI_ISteamUserStats_StoreStats = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_StoreStats', 'P', 'I') + # @@dll_SteamAPI_ISteamUserStats_ResetAllStats = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamUserStats_ResetAllStats', 'PI', 'I') + # @@dll_SteamAPI_ISteamApps_BIsSubscribed = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamApps_BIsSubscribed', 'P', 'I') + # @@dll_SteamAPI_ISteamApps_BIsDlcInstalled = Win32API.new(self.steam_dll_name, 'SteamAPI_ISteamApps_BIsDlcInstalled', 'PI', 'I') + + @@instance = self.new + + def self.pack_float(val) + # Packs number to a string, then unpack to an int + inter = [val].pack 'e' + inter.unpack('I')[0] + end + + def self.pack_double(val) + # Packs number to a string, then unpack to an array of two ints + inter = [val].pack 'd' + inter.unpack 'II' + end + +end diff --git a/mkxp-z/Kawariki-patches/ports/debug/basewt.rb b/mkxp-z/Kawariki-patches/ports/debug/basewt.rb new file mode 100644 index 0000000..293e236 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/debug/basewt.rb @@ -0,0 +1,613 @@ +#============================================================================== +# ■ Window_Base +#------------------------------------------------------------------------------ +#  ゲーム中の全てのウィンドウのスーパークラスです。 +#============================================================================== + +class Window_Base < Window + #-------------------------------------------------------------------------- + # ● オブジェクト初期化 + #-------------------------------------------------------------------------- + def initialize(x, y, width, height) + super + self.windowskin = Cache.system("Window") + update_padding + update_tone + create_contents + @opening = @closing = false + end + #-------------------------------------------------------------------------- + # ● 解放 + #-------------------------------------------------------------------------- + def dispose + contents.dispose unless disposed? + super + end + #-------------------------------------------------------------------------- + # ● 行の高さを取得 + #-------------------------------------------------------------------------- + def line_height + return 24 + end + #-------------------------------------------------------------------------- + # ● 標準パディングサイズの取得 + #-------------------------------------------------------------------------- + def standard_padding + return 12 + end + #-------------------------------------------------------------------------- + # ● パディングの更新 + #-------------------------------------------------------------------------- + def update_padding + self.padding = standard_padding + end + #-------------------------------------------------------------------------- + # ● ウィンドウ内容の幅を計算 + #-------------------------------------------------------------------------- + def contents_width + width - standard_padding * 2 + end + #-------------------------------------------------------------------------- + # ● ウィンドウ内容の高さを計算 + #-------------------------------------------------------------------------- + def contents_height + height - standard_padding * 2 + end + #-------------------------------------------------------------------------- + # ● 指定行数に適合するウィンドウの高さを計算 + #-------------------------------------------------------------------------- + def fitting_height(line_number) + line_number * line_height + standard_padding * 2 + end + #-------------------------------------------------------------------------- + # ● 色調の更新 + #-------------------------------------------------------------------------- + def update_tone + self.tone.set($game_system.window_tone) + end + + #-------------------------------------------------------------------------- + # ● ウィンドウ内容の作成 + #-------------------------------------------------------------------------- + def create_contents + test5 = contents_width + testf = contents_height + # Debugging: Log the current types and values of contents_width and contents_height + puts "Before disposing, contents width: #{contents_width}, height: #{contents_height}" + puts "Before disposing, contents width type: #{contents_width.class}, height type: #{contents_height.class}" + + contents.dispose + + # Check for nil and ensure that contents_width and contents_height are integers + if contents_width.nil? || contents_height.nil? + puts "sadada disposing, contents width: #{contents_width}, height: #{contents_height}" + puts "Warning: contents_width or contents_height is nil, setting default values." + contents_width = 190 # Default width (you can adjust this to your needs) + contents_height = 120 # Default height (you can adjust this to your needs) + end + + # Ensure contents_width and contents_height are valid integers before proceeding + # if contents_width.is_a?(Integer) && contents_height.is_a?(Integer) + if contents_width > 0 && contents_height > 0 + # Limit height to a maximum of 2048 pixels + # contents_height = 2048 if contents_height > 2048 + self.contents = Bitmap.new(contents_width, contents_height) + puts "Created new bitmap with dimensions: #{contents_width} x #{contents_height}" + else + puts "Invalid dimensions: creating fallback 1x1 bitmap" + self.contents = Bitmap.new(1, 1) + end + # else + # # If contents_width or contents_height are not valid integers, create a fallback 1x1 bitmap + # puts "Warning: Invalid types for contents width or height. Creating fallback 1x1 bitmap." + # self.contents = Bitmap.new(1, 1) + # end + end + def create_contents + # puts "vv Before disposing, contents width: #{contents_width}, height: #{contents_height}" + # puts "vv Before disposing, contents width type: #{contents_width.class}, height type: #{contents_height.class}" + contents.dispose + # puts "Before disposing, contents width: #{contents_width}, height: #{contents_height}" + # puts "Before disposing, contents width type: #{contents_width.class}, height type: #{contents_height.class}" + + # end + if contents_height > 10000 + self.contents = Bitmap.new(1, 1) + elsif contents_width > 0 && contents_height > 0 + self.contents = Bitmap.new(contents_width, contents_height) + puts "Created new bitmap with dimensions: #{contents_width} x #{contents_height}" + else + self.contents = Bitmap.new(1, 1) + end + end + #-------------------------------------------------------------------------- + # ● フレーム更新 + #-------------------------------------------------------------------------- + def update + super + update_tone + update_open if @opening + update_close if @closing + end + #-------------------------------------------------------------------------- + # ● 開く処理の更新 + #-------------------------------------------------------------------------- + def update_open + self.openness += 48 + @opening = false if open? + end + #-------------------------------------------------------------------------- + # ● 閉じる処理の更新 + #-------------------------------------------------------------------------- + def update_close + self.openness -= 48 + @closing = false if close? + end + #-------------------------------------------------------------------------- + # ● ウィンドウを開く + #-------------------------------------------------------------------------- + def open + @opening = true unless open? + @closing = false + self + end + #-------------------------------------------------------------------------- + # ● ウィンドウを閉じる + #-------------------------------------------------------------------------- + def close + @closing = true unless close? + @opening = false + self + end + #-------------------------------------------------------------------------- + # ● ウィンドウの表示 + #-------------------------------------------------------------------------- + def show + self.visible = true + self + end + #-------------------------------------------------------------------------- + # ● ウィンドウの非表示 + #-------------------------------------------------------------------------- + def hide + self.visible = false + self + end + #-------------------------------------------------------------------------- + # ● ウィンドウのアクティブ化 + #-------------------------------------------------------------------------- + def activate + self.active = true + self + end + #-------------------------------------------------------------------------- + # ● ウィンドウの非アクティブ化 + #-------------------------------------------------------------------------- + def deactivate + self.active = false + self + end + #-------------------------------------------------------------------------- + # ● 文字色取得 + # n : 文字色番号(0..31) + #-------------------------------------------------------------------------- + def text_color(n) + windowskin.get_pixel(64 + (n % 8) * 8, 96 + (n / 8) * 8) + end + #-------------------------------------------------------------------------- + # ● 各種文字色の取得 + #-------------------------------------------------------------------------- + def normal_color; text_color(0); end; # 通常 + def system_color; text_color(16); end; # システム + def crisis_color; text_color(17); end; # ピンチ + def knockout_color; text_color(18); end; # 戦闘不能 + def gauge_back_color; text_color(19); end; # ゲージ背景 + def hp_gauge_color1; text_color(20); end; # HP ゲージ 1 + def hp_gauge_color2; text_color(21); end; # HP ゲージ 2 + def mp_gauge_color1; text_color(22); end; # MP ゲージ 1 + def mp_gauge_color2; text_color(23); end; # MP ゲージ 2 + def mp_cost_color; text_color(23); end; # 消費 TP + def power_up_color; text_color(24); end; # 装備 パワーアップ + def power_down_color; text_color(25); end; # 装備 パワーダウン + def tp_gauge_color1; text_color(28); end; # TP ゲージ 1 + def tp_gauge_color2; text_color(29); end; # TP ゲージ 2 + def tp_cost_color; text_color(29); end; # 消費 TP + #-------------------------------------------------------------------------- + # ● 保留項目の背景色を取得 + #-------------------------------------------------------------------------- + def pending_color + windowskin.get_pixel(80, 80) + end + #-------------------------------------------------------------------------- + # ● 半透明描画用のアルファ値を取得 + #-------------------------------------------------------------------------- + def translucent_alpha + return 160 + end + #-------------------------------------------------------------------------- + # ● テキスト描画色の変更 + # enabled : 有効フラグ。false のとき半透明で描画 + #-------------------------------------------------------------------------- + def change_color(color, enabled = true) + contents.font.color.set(color) + contents.font.color.alpha = translucent_alpha unless enabled + end + #-------------------------------------------------------------------------- + # ● テキストの描画 + # args : Bitmap#draw_text と同じ + #-------------------------------------------------------------------------- + def draw_text(*args) + contents.draw_text(*args) + end + #-------------------------------------------------------------------------- + # ● テキストサイズの取得 + #-------------------------------------------------------------------------- + def text_size(str) + contents.text_size(str) + end + #-------------------------------------------------------------------------- + # ● 制御文字つきテキストの描画 + #-------------------------------------------------------------------------- + def draw_text_ex(x, y, text) + reset_font_settings + text = convert_escape_characters(text) + pos = {:x => x, :y => y, :new_x => x, :height => calc_line_height(text)} + process_character(text.slice!(0, 1), text, pos) until text.empty? + end + #-------------------------------------------------------------------------- + # ● フォント設定のリセット + #-------------------------------------------------------------------------- + def reset_font_settings + change_color(normal_color) + contents.font.size = Font.default_size + contents.font.bold = false + contents.font.italic = false + end + #-------------------------------------------------------------------------- + # ● 制御文字の事前変換 + # 実際の描画を始める前に、原則として文字列に変わるものだけを置き換える。 + # 文字「\」はエスケープ文字(\e)に変換。 + #-------------------------------------------------------------------------- + def convert_escape_characters(text) + result = text.to_s.clone + result.gsub!(/\\/) { "\e" } + result.gsub!(/\e\e/) { "\\" } + result.gsub!(/\eV\[(\d+)\]/i) { $game_variables[$1.to_i] } + result.gsub!(/\eV\[(\d+)\]/i) { $game_variables[$1.to_i] } + result.gsub!(/\eN\[(\d+)\]/i) { actor_name($1.to_i) } + result.gsub!(/\eP\[(\d+)\]/i) { party_member_name($1.to_i) } + result.gsub!(/\eG/i) { Vocab::currency_unit } + result + end + #-------------------------------------------------------------------------- + # ● アクター n 番の名前を取得 + #-------------------------------------------------------------------------- + def actor_name(n) + actor = n >= 1 ? $game_actors[n] : nil + actor ? actor.name : "" + end + #-------------------------------------------------------------------------- + # ● パーティメンバー n 番の名前を取得 + #-------------------------------------------------------------------------- + def party_member_name(n) + actor = n >= 1 ? $game_party.members[n - 1] : nil + actor ? actor.name : "" + end + #-------------------------------------------------------------------------- + # ● 文字の処理 + # c : 文字 + # text : 描画処理中の文字列バッファ(必要なら破壊的に変更) + # pos : 描画位置 {:x, :y, :new_x, :height} + #-------------------------------------------------------------------------- + def process_character(c, text, pos) + case c + when "\n" # 改行 + process_new_line(text, pos) + when "\f" # 改ページ + process_new_page(text, pos) + when "\e" # 制御文字 + process_escape_character(obtain_escape_code(text), text, pos) + else # 普通の文字 + process_normal_character(c, pos) + end + end + #-------------------------------------------------------------------------- + # ● 通常文字の処理 + #-------------------------------------------------------------------------- + def process_normal_character(c, pos) + text_width = text_size(c).width + draw_text(pos[:x], pos[:y], text_width * 2, pos[:height], c) + pos[:x] += text_width + end + #-------------------------------------------------------------------------- + # ● 改行文字の処理 + #-------------------------------------------------------------------------- + def process_new_line(text, pos) + pos[:x] = pos[:new_x] + pos[:y] += pos[:height] + pos[:height] = calc_line_height(text) + end + #-------------------------------------------------------------------------- + # ● 改ページ文字の処理 + #-------------------------------------------------------------------------- + def process_new_page(text, pos) + end + #-------------------------------------------------------------------------- + # ● 制御文字の本体を破壊的に取得 + #-------------------------------------------------------------------------- + def obtain_escape_code(text) + text.slice!(/^[\$\.\|\^!><\{\}\\]|^[A-Z]+/i) + end + #-------------------------------------------------------------------------- + # ● 制御文字の引数を破壊的に取得 + #-------------------------------------------------------------------------- + def obtain_escape_param(text) + text.slice!(/^\[\d+\]/)[/\d+/].to_i rescue 0 + end + #-------------------------------------------------------------------------- + # ● 制御文字の処理 + # code : 制御文字の本体部分(「\C[1]」なら「C」) + #-------------------------------------------------------------------------- + def process_escape_character(code, text, pos) + case code.upcase + when 'C' + change_color(text_color(obtain_escape_param(text))) + when 'I' + process_draw_icon(obtain_escape_param(text), pos) + when '{' + make_font_bigger + when '}' + make_font_smaller + end + end + #-------------------------------------------------------------------------- + # ● 制御文字によるアイコン描画の処理 + #-------------------------------------------------------------------------- + def process_draw_icon(icon_index, pos) + draw_icon(icon_index, pos[:x], pos[:y]) + pos[:x] += 24 + end + #-------------------------------------------------------------------------- + # ● フォントを大きくする + #-------------------------------------------------------------------------- + def make_font_bigger + contents.font.size += 8 if contents.font.size <= 64 + end + #-------------------------------------------------------------------------- + # ● フォントを小さくする + #-------------------------------------------------------------------------- + def make_font_smaller + contents.font.size -= 8 if contents.font.size >= 16 + end + #-------------------------------------------------------------------------- + # ● 行の高さを計算 + # restore_font_size : 計算後にフォントサイズを元に戻す + #-------------------------------------------------------------------------- + def calc_line_height(text, restore_font_size = true) + result = [line_height, contents.font.size].max + last_font_size = contents.font.size + text.slice(/^.*$/).scan(/\e[\{\}]/).each do |esc| + make_font_bigger if esc == "\e{" + make_font_smaller if esc == "\e}" + result = [result, contents.font.size].max + end + contents.font.size = last_font_size if restore_font_size + result + end + #-------------------------------------------------------------------------- + # ● ゲージの描画 + # rate : 割合(1.0 で満タン) + # color1 : グラデーション 左端 + # color2 : グラデーション 右端 + #-------------------------------------------------------------------------- + def draw_gauge(x, y, width, rate, color1, color2) + fill_w = (width * rate).to_i + gauge_y = y + line_height - 8 + contents.fill_rect(x, gauge_y, width, 6, gauge_back_color) + contents.gradient_fill_rect(x, gauge_y, fill_w, 6, color1, color2) + end + #-------------------------------------------------------------------------- + # ● アイコンの描画 + # enabled : 有効フラグ。false のとき半透明で描画 + #-------------------------------------------------------------------------- + def draw_icon(icon_index, x, y, enabled = true) + bitmap = Cache.system("Iconset") + rect = Rect.new(icon_index % 16 * 24, icon_index / 16 * 24, 24, 24) + contents.blt(x, y, bitmap, rect, enabled ? 255 : translucent_alpha) + end + #-------------------------------------------------------------------------- + # ● 顔グラフィックの描画 + # enabled : 有効フラグ。false のとき半透明で描画 + #-------------------------------------------------------------------------- + def draw_face(face_name, face_index, x, y, enabled = true) + bitmap = Cache.face(face_name) + rect = Rect.new(face_index % 4 * 96, face_index / 4 * 96, 96, 96) + contents.blt(x, y, bitmap, rect, enabled ? 255 : translucent_alpha) + bitmap.dispose + end + #-------------------------------------------------------------------------- + # ● 歩行グラフィックの描画 + #-------------------------------------------------------------------------- + def draw_character(character_name, character_index, x, y) + return unless character_name + bitmap = Cache.character(character_name) + sign = character_name[/^[\!\$]./] + if sign && sign.include?('$') + cw = bitmap.width / 3 + ch = bitmap.height / 4 + else + cw = bitmap.width / 12 + ch = bitmap.height / 8 + end + n = character_index + src_rect = Rect.new((n%4*3+1)*cw, (n/4*4)*ch, cw, ch) + contents.blt(x - cw / 2, y - ch, bitmap, src_rect) + end + #-------------------------------------------------------------------------- + # ● HP の文字色を取得 + #-------------------------------------------------------------------------- + def hp_color(actor) + return knockout_color if actor.hp == 0 + return crisis_color if actor.hp < actor.mhp / 4 + return normal_color + end + #-------------------------------------------------------------------------- + # ● MP の文字色を取得 + #-------------------------------------------------------------------------- + def mp_color(actor) + return crisis_color if actor.mp < actor.mmp / 4 + return normal_color + end + #-------------------------------------------------------------------------- + # ● TP の文字色を取得 + #-------------------------------------------------------------------------- + def tp_color(actor) + return normal_color + end + #-------------------------------------------------------------------------- + # ● アクターの歩行グラフィック描画 + #-------------------------------------------------------------------------- + def draw_actor_graphic(actor, x, y) + draw_character(actor.character_name, actor.character_index, x, y) + end + #-------------------------------------------------------------------------- + # ● アクターの顔グラフィック描画 + #-------------------------------------------------------------------------- + def draw_actor_face(actor, x, y, enabled = true) + draw_face(actor.face_name, actor.face_index, x, y, enabled) + end + #-------------------------------------------------------------------------- + # ● 名前の描画 + #-------------------------------------------------------------------------- + def draw_actor_name(actor, x, y, width = 112) + change_color(hp_color(actor)) + draw_text(x, y, width, line_height, actor.name) + end + #-------------------------------------------------------------------------- + # ● 職業の描画 + #-------------------------------------------------------------------------- + def draw_actor_class(actor, x, y, width = 112) + change_color(normal_color) + draw_text(x, y, width, line_height, actor.class.name) + end + #-------------------------------------------------------------------------- + # ● 二つ名の描画 + #-------------------------------------------------------------------------- + def draw_actor_nickname(actor, x, y, width = 180) + change_color(normal_color) + draw_text(x, y, width, line_height, actor.nickname) + end + #-------------------------------------------------------------------------- + # ● レベルの描画 + #-------------------------------------------------------------------------- + def draw_actor_level(actor, x, y) + change_color(system_color) + draw_text(x, y, 32, line_height, Vocab::level_a) + change_color(normal_color) + draw_text(x + 32, y, 24, line_height, actor.level, 2) + end + #-------------------------------------------------------------------------- + # ● ステートおよび強化/弱体のアイコンを描画 + #-------------------------------------------------------------------------- + def draw_actor_icons(actor, x, y, width = 96) + icons = (actor.state_icons + actor.buff_icons)[0, width / 24] + icons.each_with_index {|n, i| draw_icon(n, x + 24 * i, y) } + end + #-------------------------------------------------------------------------- + # ● 現在値/最大値を分数形式で描画 + # current : 現在値 + # max : 最大値 + # color1 : 現在値の色 + # color2 : 最大値の色 + #-------------------------------------------------------------------------- + def draw_current_and_max_values(x, y, width, current, max, color1, color2) + change_color(color1) + xr = x + width + if width < 96 + draw_text(xr - 40, y, 42, line_height, current, 2) + else + draw_text(xr - 92, y, 42, line_height, current, 2) + change_color(color2) + draw_text(xr - 52, y, 12, line_height, "/", 2) + draw_text(xr - 42, y, 42, line_height, max, 2) + end + end + #-------------------------------------------------------------------------- + # ● HP の描画 + #-------------------------------------------------------------------------- + def draw_actor_hp(actor, x, y, width = 124) + draw_gauge(x, y, width, actor.hp_rate, hp_gauge_color1, hp_gauge_color2) + change_color(system_color) + draw_text(x, y, 30, line_height, Vocab::hp_a) + draw_current_and_max_values(x, y, width, actor.hp, actor.mhp, + hp_color(actor), normal_color) + end + #-------------------------------------------------------------------------- + # ● MP の描画 + #-------------------------------------------------------------------------- + def draw_actor_mp(actor, x, y, width = 124) + draw_gauge(x, y, width, actor.mp_rate, mp_gauge_color1, mp_gauge_color2) + change_color(system_color) + draw_text(x, y, 30, line_height, Vocab::mp_a) + draw_current_and_max_values(x, y, width, actor.mp, actor.mmp, + mp_color(actor), normal_color) + end + #-------------------------------------------------------------------------- + # ● TP の描画 + #-------------------------------------------------------------------------- + def draw_actor_tp(actor, x, y, width = 124) + draw_gauge(x, y, width, actor.tp_rate, tp_gauge_color1, tp_gauge_color2) + change_color(system_color) + draw_text(x, y, 30, line_height, Vocab::tp_a) + change_color(tp_color(actor)) + draw_text(x + width - 42, y, 42, line_height, actor.tp.to_i, 2) + end + #-------------------------------------------------------------------------- + # ● シンプルなステータスの描画 + #-------------------------------------------------------------------------- + def draw_actor_simple_status(actor, x, y) + draw_actor_name(actor, x, y) + draw_actor_level(actor, x, y + line_height * 1) + draw_actor_icons(actor, x, y + line_height * 2) + draw_actor_class(actor, x + 120, y) + draw_actor_hp(actor, x + 120, y + line_height * 1) + draw_actor_mp(actor, x + 120, y + line_height * 2) + end + #-------------------------------------------------------------------------- + # ● 能力値の描画 + #-------------------------------------------------------------------------- + def draw_actor_param(actor, x, y, param_id) + change_color(system_color) + draw_text(x, y, 120, line_height, Vocab::param(param_id)) + change_color(normal_color) + draw_text(x + 120, y, 36, line_height, actor.param(param_id), 2) + end + #-------------------------------------------------------------------------- + # ● アイテム名の描画 + # enabled : 有効フラグ。false のとき半透明で描画 + #-------------------------------------------------------------------------- + def draw_item_name(item, x, y, enabled = true, width = 172) + return unless item + draw_icon(item.icon_index, x, y, enabled) + change_color(normal_color, enabled) + draw_text(x + 24, y, width, line_height, item.name) + end + #-------------------------------------------------------------------------- + # ● 通貨単位つき数値(所持金など)の描画 + #-------------------------------------------------------------------------- + def draw_currency_value(value, unit, x, y, width) + cx = text_size(unit).width + change_color(normal_color) + draw_text(x, y, width - cx - 2, line_height, value, 2) + change_color(system_color) + draw_text(x, y, width, line_height, unit, 2) + end + #-------------------------------------------------------------------------- + # ● 能力値変化の描画色取得 + #-------------------------------------------------------------------------- + def param_change_color(change) + return power_up_color if change > 0 + return power_down_color if change < 0 + return normal_color + end + end diff --git a/mkxp-z/Kawariki-patches/ports/debug/savebitmanwin32api.rb b/mkxp-z/Kawariki-patches/ports/debug/savebitmanwin32api.rb new file mode 100644 index 0000000..63cfd83 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/debug/savebitmanwin32api.rb @@ -0,0 +1,2723 @@ +#============================================================================== +# ■ Audio +#============================================================================== +module Audio + class << self + #-------------------------------------------------------------------------- + # ● 基本初期化処理 + #-------------------------------------------------------------------------- + def init_basic + bgm_stop + bgs_stop + @fiber_bgm = nil + @fiber_bgs = nil + end + + #-------------------------------------------------------------------------- + # ● 更新処理 + #-------------------------------------------------------------------------- + def update; end + + def alias_file_name(file_name) + NWConst::Audio::AUDIO_ALIAS[file_name] || file_name + end + + alias hima_alias_audio_bgm_play bgm_play + def bgm_play(*args) + args[0] = alias_file_name(args[0]) + hima_alias_audio_bgm_play(*args) + end + + alias hima_alias_audio_me_play me_play + def me_play(*args) + args[0] = alias_file_name(args[0]) + hima_alias_audio_me_play(*args) + end + + alias hima_alias_audio_se_play se_play + def se_play(*args) + args[0] = alias_file_name(args[0]) + hima_alias_audio_se_play(*args) + end + + alias hima_alias_audio_bgs_play bgs_play + def bgs_play(*args) + args[0] = alias_file_name(args[0]) + hima_alias_audio_bgs_play(*args) + end + end + end + + module BattleManager + class << self + #-------------------------------------------------------------------------- + # ○ セットアップ + #-------------------------------------------------------------------------- + def setup(troop_id, can_escape = true, can_lose = false) + init_members + $game_troop.setup(troop_id) + @can_escape = can_escape + @can_lose = can_lose + @troop_id = troop_id + make_escape_ratio + setup_terrain + $game_temp.battle_init + end + + def init_members + @phase = :init # 戦闘進行フェーズ + @can_escape = false # 逃走可能フラグ + @can_lose = false # 敗北可能フラグ + @event_proc = nil # イベント用コールバック + @preemptive = false # 先制攻撃フラグ + @surprise = false # 不意打ちフラグ + @actor_index = -1 # コマンド入力中のアクター + @action_forced = nil # 戦闘行動の強制 + @map_bgm = nil # 戦闘前の BGM 記憶用 + @map_bgs = nil # 戦闘前の BGS 記憶用 + @action_battlers = [] # 行動順序リスト + @action_game_masters = [] # 行動順序リスト(GM専用) + @giveup = false # 降参フラグ + @giveup_count = 0 # 降参カウント + @bind_count = 0 # 拘束カウント + @terrain = :未定義 # 地形 + @troop_id = nil + @fastest_action = nil + @mimic_action_history = [] + @retry_data = nil + end + + #-------------------------------------------------------------------------- + # ○ エンカウント時の処理 + #-------------------------------------------------------------------------- + def on_encounter + @preemptive = (rand < rate_preemptive) + @surprise = (rand < rate_surprise && !@preemptive) + print "先制攻撃発動率#{(rate_preemptive * 100.0).to_i}%.先制#{@preemptive ? '成功' : '失敗'}。\n" + print "不意打ち発動率#{(rate_surprise * 100.0).to_i}%.不意打ち#{@surprise ? '成功' : '失敗'}。\n" + end + + #-------------------------------------------------------------------------- + # ○ 戦闘 BGM の演奏 + #-------------------------------------------------------------------------- + def play_battle_bgm + $game_troop.battle_bgm.play unless bgm_same?($game_troop.battle_bgm) + RPG::BGS.stop + end + + #-------------------------------------------------------------------------- + # ● 初期フェイズ + #-------------------------------------------------------------------------- + def init_phase + @phase = :init + end + + #-------------------------------------------------------------------------- + # ● 仲間入れ替えフェイズ + #-------------------------------------------------------------------------- + def shift_change + @phase = :shift_change + $game_party.clear_actions + end + + #-------------------------------------------------------------------------- + # ● 仲間入れ替えフェイズ? + #-------------------------------------------------------------------------- + def shift_change? + @phase == :shift_change + end + + def battle_end? + @phase.nil? + end + + #-------------------------------------------------------------------------- + # ● 降参 + #-------------------------------------------------------------------------- + def giveup + @giveup = true + @giveup_count = 10 + $game_party.clear_actions + $game_party.all_members.reject { |m| m.luca? }.each { |m| m.hide } + ($game_party.all_members + $game_troop.alive_members).each { |m| m.clear_states } + luca_index = 0 + $game_party.all_members.each_with_index do |actor, i| + luca_index = (actor.luca? ? i : luca_index) + end + $game_party.swap_order(0, luca_index) + end + + #-------------------------------------------------------------------------- + # ● 降参中? + #-------------------------------------------------------------------------- + def giveup? + @giveup + end + + #-------------------------------------------------------------------------- + # ● 降参カウントダウン + #-------------------------------------------------------------------------- + def giveup_count_down + @giveup_count -= 1 + @giveup_count == 0 + end + + #-------------------------------------------------------------------------- + # ● 拘束セット + #-------------------------------------------------------------------------- + def bind_set(count) + bind_reset + @bind_count = count + @bind_start_turn = $game_troop.turn_count + end + + #-------------------------------------------------------------------------- + # ● 拘束ターン + #-------------------------------------------------------------------------- + def binding_turn + $game_troop.turn_count - @bind_start_turn + end + + #-------------------------------------------------------------------------- + # ● 拘束カウントダウン + #-------------------------------------------------------------------------- + def bind_count_down + @bind_count -= 1 + bind_refresh + end + + #-------------------------------------------------------------------------- + # ● 拘束リフレッシュ + #-------------------------------------------------------------------------- + def bind_refresh + bind_reset unless bind? + end + + #-------------------------------------------------------------------------- + # ● 拘束発生中? + #-------------------------------------------------------------------------- + def bind? + 0 != @bind_count && bind_user_exist? && bind_target_exist? + end + + #-------------------------------------------------------------------------- + # ● 拘束状態のリセット + #-------------------------------------------------------------------------- + def bind_reset + $game_party.members.each { |m| m.clear_actions if m.bind_target? } + $game_troop.members.each { |m| m.clear_actions if m.bind_user? } + @bind_count = 0 + $game_troop.members.each do |m| + m.remove_state(NWConst::State::UBIND) + m.remove_state(NWConst::State::EUBIND) + end + $game_party.members.each do |m| + m.remove_state(NWConst::State::TBIND) + m.remove_state(NWConst::State::ETBIND) + end + end + + #-------------------------------------------------------------------------- + # ● 拘束技使用者は存在する? + #-------------------------------------------------------------------------- + def bind_user_exist? + $game_troop.members.any? { |m| m.bind_user? } + end + + #-------------------------------------------------------------------------- + # ● 拘束技対象は存在する? + #-------------------------------------------------------------------------- + def bind_target_exist? + $game_party.members.any? { |m| m.bind_target? } + end + + #-------------------------------------------------------------------------- + # ● 拘束技使用者のインデックス + #-------------------------------------------------------------------------- + def bind_user_index + $game_troop.members.each_with_index do |member, i| + return i if member.bind_user? + end + -1 + end + + #-------------------------------------------------------------------------- + # ○ 逃走成功率の作成 + #-------------------------------------------------------------------------- + def make_escape_ratio + lv = 0 + lv = if $game_party.in_member_luca? + $game_actors[NWConst::Actor::LUCA].base_level + else + $game_party.all_members.map(&:base_level).max + end + escape_level = $game_troop.escape_level_max + @escape_ratio = lv >= escape_level ? 1.0 : (1.0 / (escape_level - lv + 1)) + end + + #-------------------------------------------------------------------------- + # ● 戦闘地形の設定 + #-------------------------------------------------------------------------- + def setup_terrain + NWConst::Field::TERRAIN.each do |key, value| + next if value[:tag] != $game_player.terrain_tag && !value[:map_id].include?($game_map.map_id) + + @terrain = key + break + end + end + + #-------------------------------------------------------------------------- + # ● 戦闘地形の取得 + #-------------------------------------------------------------------------- + attr_reader :terrain + + #-------------------------------------------------------------------------- + # ○ 逃走許可の取得 + #-------------------------------------------------------------------------- + def can_escape? + @can_escape && !bind? && !$game_troop.challenge_battle? + end + + #-------------------------------------------------------------------------- + # ○ 戦闘開始 + #-------------------------------------------------------------------------- + def battle_start + @retry_data = Marshal.dump(DataManager.make_save_contents) + $game_variables[NWConst::Var::BATTLE_END_TURN] = 0 + $game_temp.reserve_common_event(NWConst::Common::BATTLE_START) + $game_party.on_battle_start + $game_troop.on_battle_start + $game_troop.enemy_names.each do |name| + $game_message.add(format(Vocab::Emerge, name)) + end + tmp = [] + $game_troop.members.each { |enemy| tmp.push(enemy.id) if enemy } + $game_library.enemy.set_discovery(tmp) + if @preemptive + $game_message.add(format(Vocab::Preemptive, $game_party.name)) + elsif @surprise + $game_message.add(format(Vocab::Surprise, $game_party.name)) + end + wait_for_message + end + + #-------------------------------------------------------------------------- + # ● 好感度上昇 + #-------------------------------------------------------------------------- + def gain_love + $game_party.battle_members.select do |member| + !member.luca? + end.each do |member| + member.love += $game_variables[NWConst::Var::BATTLE_END_GAIN_LOVE] + end + end + + #-------------------------------------------------------------------------- + # ○ 逃走の処理 + #-------------------------------------------------------------------------- + def process_escape + $game_message.add(format(Vocab::EscapeStart, $game_party.name)) + success = @preemptive ? true : (rand < @escape_ratio) + Sound.play_escape + if success + process_abort + else + $game_message.add('\.' + Vocab::EscapeFailure) + $game_party.clear_actions + end + wait_for_message + success + end + + #-------------------------------------------------------------------------- + # ● 強制逃走の処理 + #-------------------------------------------------------------------------- + def process_forced_escape + $game_message.add(format(Vocab::EscapeStart, $game_party.name)) + Sound.play_escape + if can_forced_escape? + process_abort + else + $game_message.add('\.' + Vocab::EscapeFailure) + end + wait_for_message + end + + #-------------------------------------------------------------------------- + # ● 強制逃走が可能か + #-------------------------------------------------------------------------- + def can_forced_escape? + can_escape? and !($game_switches[NWConst::Sw::STRICT_ENCOUNT]) + end + + #-------------------------------------------------------------------------- + # ● プレイヤのリセット + #-------------------------------------------------------------------------- + def reset_player + $game_player.transparent = true + $game_player.followers.visible = false + $game_player.moveto(0, 0) + $game_player.refresh + end + + #-------------------------------------------------------------------------- + # ● スキップ不能か + #-------------------------------------------------------------------------- + def no_lose_skip? + enemy_id = $game_troop.lose_event_id - NWConst::Common::LOSE_EVENT_BASE + return true if enemy_id == 0 # LOSE_EVENT_BASEを直接実行=混沌の迷宮エネミー + return true if $data_enemies[enemy_id].no_lose_skip? + + false + end + + #-------------------------------------------------------------------------- + # ○ 戦闘終了 + # result : 結果(0:勝利 1:逃走 2:敗北) + #-------------------------------------------------------------------------- + def battle_end(result) + @phase = nil + @giveup = false + @event_proc.call(result) if @event_proc + $game_temp.reserve_common_event(NWConst::Common::BATTLE_END) + $game_party.on_battle_end + $game_troop.on_battle_end + SceneManager.exit if $BTEST + $game_temp.battle_init + @retry_data = nil unless result == 2 + end + + #-------------------------------------------------------------------------- + # ○ 獲得した経験値の表示 + #-------------------------------------------------------------------------- + def display_exp + if $game_troop.exp_total > 0 + text = format(Vocab::ObtainExp, $game_troop.exp_total) + $game_message.add('\.' + text) + end + if $game_troop.class_exp_total > 0 + text2 = format(Vocab::ObtainJobExp, $game_troop.class_exp_total) + $game_message.add('\.' + text2) + end + end + + #-------------------------------------------------------------------------- + # ○ 経験値の獲得とレベルアップの表示 + #-------------------------------------------------------------------------- + def gain_exp + $game_party.all_members.each do |actor| + actor.gain_exp($game_troop.exp_total, $game_troop.class_exp_total) + end + wait_for_message + end + + #-------------------------------------------------------------------------- + # ● ドロップアイテムの獲得と表示 【再定義】 + #-------------------------------------------------------------------------- + def gain_drop_items + $game_temp.clear_get_item + + $game_troop.make_drop_items.each do |item| + $game_party.gain_item(item, 1) + end + + $game_temp.get_item_data.each do |item, value| + value.times do + $game_message.add(Vocab.item_get_message(item, 1)) + end + end + + wait_for_message + end + + def turn_start + @phase = :turn + @mimic_action_history = [] + clear_actor + $game_party.set_action_history + $game_party.check_change_action + $game_troop.increase_turn + make_action_orders + end + + def setup_auto_skill? + !giveup? || $game_party.in_battle || !$game_troop.interpreter.running? + end + + def set_battle_start_skill + set_auto_skill(&:battle_start_skill) + end + + def set_turn_start_skill + set_auto_skill(&:turn_start_skill) + end + + def set_turn_end_skill + set_auto_skill(&:turn_end_skill) + set_turn_end_revive + end + + def counter_skill(battler) + auto_skill_per(battler.counter_skill, battler) + end + + def set_auto_skill + @action_game_masters = [] + return unless setup_auto_skill? + + members = ($game_troop.alive_members + $game_party.alive_members).select(&:enable_action?).reject(&:cant_move?) + members.each do |member| + skills = yield member + auto_skills_per(skills, member).reverse_each do |skill| + act = skill_interrupt(member, skill) + act.target_index = member.index if act && act.object && act.object.scope == 7 + end + end + end + + def auto_skills_per(skills, battler) + r = _auto_skill_per(skills, battler).group_by do |skill| + skill.fetch(:priority, 99) + end + r.sort_by { |k, _| k }.map do |_, ss| + ss.sample.fetch(:id) + end + end + + def auto_skill_per(skills, battler) + _auto_skill_per(skills, battler).map { |s| s.fetch(:id) }.sample + end + + def _auto_skill_per(skills, battler) + skills.select do |obj| + if obj[:condition_type] + next false unless battler.skill_race_ok?($data_skills[obj[:id]]) + + result = case obj[:condition_type] + when 1 + members = $game_party.battle_members_id + obj[:condition_ids].any? { |id| members.include?($game_actors.original_id(id)) } + when 2 + members = $game_troop.members.map(&:id) + obj[:condition_ids].any? { |id| members.include?(id) } + when 3 + members = $game_party.battle_members + obj[:condition_ids].any? { |id| members.any? { |m| m.state?(id) } } + when 4 + members = $game_troop.members + obj[:condition_ids].any? { |id| members.any? { |m| m.state?(id) } } + when 5 + obj[:condition_ids].any? { |id| battler.state?(id) } + else + true + end + next false unless result + end + rand < obj[:per] + end + end + + def set_dead_skill(battler) + return unless setup_auto_skill? + + battler.dead_skill.each do |skill| + skill_interrupt(battler, skill, :dead_skill) + end + end + + def set_final_invoke(battler) + return unless setup_auto_skill? + + battler.final_invoke.each do |skill| + skill_interrupt(battler, skill, :final_invoke) + end + end + + #-------------------------------------------------------------------------- + # ○ 戦闘回想かどうか + #-------------------------------------------------------------------------- + def memory_battle? + $game_temp.in_memory_battle + end + + #-------------------------------------------------------------------------- + # ○ 経験値を入手可能か + #-------------------------------------------------------------------------- + def enable_get_exp? + return false if memory_battle? + return false if $game_switches[NWConst::Sw::GET_DISABLE_EXP_DROP] + return false if $game_switches[NWConst::Sw::GET_DISABLE_EXP] + + true + end + + #-------------------------------------------------------------------------- + # ○ ゴールドを入手可能か + #-------------------------------------------------------------------------- + def enable_get_gold? + return false if memory_battle? + return false if $game_switches[NWConst::Sw::GET_DISABLE_GOLD] + + true + end + + #-------------------------------------------------------------------------- + # ○ ドロップアイテムを入手可能か + #-------------------------------------------------------------------------- + def enable_get_drop? + return false if memory_battle? + return false if $game_switches[NWConst::Sw::GET_DISABLE_EXP_DROP] + return false if $game_switches[NWConst::Sw::GET_DISABLE_DROP] + + true + end + + #-------------------------------------------------------------------------- + # ○ 敗北カウントを行うか + #-------------------------------------------------------------------------- + def enable_party_lose_count? + $game_troop.members.each do |enemy| + return false if enemy.enemy.no_lose_skip? + end + true + end + + def play_battle_end_me + $game_system.battle_end_me.play unless bgm_same?($game_troop.battle_bgm) + end + + #-------------------------------------------------------------------------- + # ○ 勝利の処理 + #-------------------------------------------------------------------------- + def process_victory + @phase = nil + $game_variables[NWConst::Var::BATTLE_END_TURN] = $game_troop.turn_count + play_battle_end_me + replay_bgm_and_bgs + $game_temp.in_victory_message = true + $game_message.add(format(Vocab::Victory, $game_party.name)) + display_exp if enable_get_exp? + gain_gold if enable_get_gold? + gain_drop_items if enable_get_drop? + gain_exp if enable_get_exp? + gain_love + process_follow unless memory_battle? + wait_for_message if memory_battle? + $game_temp.in_victory_message = false + SceneManager.return + battle_end(0) + DataManager.auto_save_game if !memory_battle? && !@event_proc + true + end + + #-------------------------------------------------------------------------- + # ○ 敗北の処理 ベース/Module + #-------------------------------------------------------------------------- + def process_defeat + @phase = nil + SceneManager.scene.process_common_event_on_defeat if $game_temp.common_event_reserved? + $game_message.add(format(Vocab::Defeat, $game_party.name)) + wait_for_message + if @can_lose + revive_battle_members + replay_bgm_and_bgs + SceneManager.return + else + # 通常のゲームオーバーは完全排除 + Audio.bgm_stop + Audio.bgs_stop + revive_battle_members + unless memory_battle? + $game_map.interpreter.clear + reset_player + end + change_novel_scene + end + battle_end(2) + true + end + + #-------------------------------------------------------------------------- + # ● ノベルパートへの移行 + #-------------------------------------------------------------------------- + def change_novel_scene + unless memory_battle? + SceneManager.clear + SceneManager.push(Scene_Map) + end + $game_novel.setup($game_troop.lose_event_id) + SceneManager.goto(Scene_Novel) + + skip_flag = $game_system.conf[:ls_skip] == 1 + skip_flag &&= $game_library.lose_event_view?($game_novel.event_id) + check_flag = $game_system.conf[:ls_skip] == 2 + choice = -1 + if no_lose_skip? + skip_flag = false + check_flag = false + end + if check_flag + $game_message.add("Skip defeat scene?") + ["Yes", "No"].each { |s| $game_message.choices.push(s) } + $game_message.choice_cancel_type = 2 + $game_message.choice_proc = proc { |n| choice = n } + wait_for_message + end + if no_lose_skip? and memory_battle? + $game_novel.interpreter.memory_interruption + elsif skip_flag || (choice == 0) + $game_novel.interpreter.goto_ilias + end + end + + def follower_disable? + $game_switches[NWConst::Sw::FOLLOWER_DISABLE_1] || $game_switches[NWConst::Sw::FOLLOWER_DISABLE_2] || ($game_party.temp_actors_use? && !$game_party.multi_party?) + end + + #-------------------------------------------------------------------------- + # ● 仲間化の処理 + #-------------------------------------------------------------------------- + def process_follow + return if follower_disable? + + $game_troop.check_getup + return unless $game_troop.follower_enemy + + if NWConst::Follow::SPECIAL.include?($game_troop.follower_enemy.id) + send("process_follow_enemy#{$game_troop.follower_enemy.id}".to_sym) + else + process_follow_normal + end + end + + #-------------------------------------------------------------------------- + # ● 仲間加入時の質問処理 + #-------------------------------------------------------------------------- + def process_follow_question + e = $game_troop.follower_enemy + e.follow_question_word.execute + wait_for_message + end + + #-------------------------------------------------------------------------- + # ● 仲間加入時の選択肢処理 + #-------------------------------------------------------------------------- + def process_follow_choice(follower_name = nil) + e = $game_troop.follower_enemy + follower_name ||= e.original_name + $game_message.add("#{follower_name} manages to rise back up.") + $game_message.add("She looks like she wants to join your party!\f") + $game_message.add("Add her as a companion?") + choice = 0 + ["Yes", "No"].each { |s| $game_message.choices.push(s) } + $game_message.choice_cancel_type = 2 + $game_message.choice_proc = proc { |n| choice = n } + wait_for_message + + choice == 0 + end + + #-------------------------------------------------------------------------- + # ● 仲間加入時の承諾処理 + #-------------------------------------------------------------------------- + def process_follow_ok(follower_name = nil) + e = $game_troop.follower_enemy + follower_name ||= e.original_name + e.follow_yes_word.execute + wait_for_message + $game_message.add("#{follower_name} has joined the party!") + if $game_party.multi_party? + $game_message.add("#{wait_member_name} heads back to the pocket castle!") + $game_party.add_stand_actor(e.follower_actor_id) + $game_temp.getup_enemy = e.follower_actor_id + wait_for_message + return + end + wait_for_message + process_follow_ok_member_full(e.follower_actor_id, follower_name) if $game_party.party_member_full? + # 仲間になったエネミーを保存 + $game_actors[e.follower_actor_id].setup(e.follower_actor_id) + $game_party.add_actor(e.follower_actor_id) + $game_temp.getup_enemy = e.follower_actor_id + end + + #-------------------------------------------------------------------------- + # ● パーティ満員時の選択 + #-------------------------------------------------------------------------- + def process_follow_ok_member_full(follower_id, follower_name = nil) + $game_message.add("The party is full.") + $game_message.add("Please choose a member to remove from the party.") + wait_for_message + stand_actor = $game_party.choice_stand_actor_on_member_full(follower_id, follower_name) + $game_party.move_stand_actor(stand_actor.id) if stand_actor + wait_member_name = stand_actor ? stand_actor.name : follower_name + $game_message.add("#{wait_member_name} heads back to the pocket castle!") + wait_for_message + end + + #-------------------------------------------------------------------------- + # ● 仲間加入時の拒否処理 + #-------------------------------------------------------------------------- + def process_follow_no + e = $game_troop.follower_enemy + e.follow_no_word.execute + wait_for_message + end + + #-------------------------------------------------------------------------- + # ● 仲間加入時の去る処理 + #-------------------------------------------------------------------------- + def process_follow_bye(follower_name = nil) + e = $game_troop.follower_enemy + follower_name ||= e.original_name + $game_troop.follower_enemy = nil + $game_message.add("#{follower_name} sulks away...") + wait_for_message + end + + #-------------------------------------------------------------------------- + # ● 仲間加入時演出(通常パターン) + #-------------------------------------------------------------------------- + def process_follow_normal + process_follow_question + if process_follow_choice + process_follow_ok + else + process_follow_no + process_follow_bye + end + end + + #-------------------------------------------------------------------------- + # ● ピクチャーの簡易操作 + #-------------------------------------------------------------------------- + def pic_easy_setup(num, name, x, y) + name = "../Battlers/#{name}" + $game_troop.screen.pictures[num].easy_setup(name, x, y) + end + + def pic_easy_appear_move(num, x, y) + $game_troop.screen.pictures[num].easy_appear_move(x, y) + end + + def pic_easy_appear(num) + $game_troop.screen.pictures[num].easy_appear + end + + def pic_easy_erase(num) + $game_troop.screen.pictures[num].easy_erase + end + + #-------------------------------------------------------------------------- + # ● ピクチャーの表示 + #-------------------------------------------------------------------------- + def pic_show(num, name, ori, x, y, zx, zy, op, bl) + name = "../Battlers/#{name}" + $game_troop.screen.pictures[num].show(name, ori, x, y, zx, zy, op, bl) + end + + #-------------------------------------------------------------------------- + # ● ピクチャーの移動 + #-------------------------------------------------------------------------- + def pic_move(num, ori, x, y, zx, zy, op, bl, dur) + $game_troop.screen.pictures[num].move(ori, x, y, zx, zy, op, bl, dur) + end + + #-------------------------------------------------------------------------- + # ● ピクチャーのクリア + #-------------------------------------------------------------------------- + def pic_clear + $game_troop.screen.clear_pictures + end + + def skill_combo(battler, action, base_action) + action.set_symbol(:skill_combo) + return unless base_action.skill_combo_enable?(action) + + action.combo_setting(base_action) + battler.actions << action + @action_battlers.unshift(action) + end + + #-------------------------------------------------------------------------- + # ○ 次の行動主体の取得 + # 行動順序リストの先頭からバトラーを取得する。 + # 現在パーティにいないアクターを取得した場合(index が nil, バトルイベ + # ントでの離脱直後などに発生)は、それをスキップする。 + #-------------------------------------------------------------------------- + def next_subject + if gm_exist? + subject = @action_game_masters.shift + return battler(subject) + end + + loop do + subject = @action_battlers.shift + return nil unless subject + + battler = battler(subject) + next unless battler.index + + return battler + end + end + + #-------------------------------------------------------------------------- + # ○ 勝敗判定 + #-------------------------------------------------------------------------- + def judge_win_loss + if @phase && !gm_exist? + return process_abort if $game_party.members.empty? + return process_defeat if $game_party.all_dead? + return process_victory if $game_troop.all_dead? + return process_abort if aborting? + end + false + end + + #-------------------------------------------------------------------------- + # ● GMアクションが存在する? + #-------------------------------------------------------------------------- + def gm_exist? + !@action_game_masters.empty? + end + + #-------------------------------------------------------------------------- + # ○ BGM と BGS の再開 + #-------------------------------------------------------------------------- + def replay_bgm_and_bgs + @map_bgm.replay unless $BTEST || bgm_same?(RPG::BGM.last) + @map_bgs.replay unless $BTEST + $game_map.autoplay_field if $game_map.auto_bgm? + end + + def skill_interrupt(battler, skill_id, symbol = :interrupt) + return unless battler.skill_race_ok?($data_skills[skill_id]) + + battler = Game_Master.new(battler) + action = battler.skill_interrupt(skill_id, symbol) + @action_game_masters.unshift(action) + action + end + + def make_action_orders + ab = [] + ab += $game_troop.members unless @preemptive + ab += $game_party.members unless @surprise + ab.each(&:make_speed) + i = 0 + + @action_battlers = ab.map { |b| b.actions }.flatten.compact + @action_battlers.sort_by! { |v| [v, i += 1] } + @action_battlers += ab.select { |m| m.actions.empty? }.shuffle + @fastest_action = @action_battlers.first + end + + def delete_action(subject) + @action_battlers.reject! { |ab| battler(ab) == subject } + end + + def battler(obj) + return nil unless obj + + if obj.action? + obj.subject.current_action_index = obj + obj.subject + else + obj.current_action_index = nil + obj + end + end + + def force_action(battler) + @action_forced = battler + delete_action(battler) + end + + def bgm_same?(bgm) + return false unless @map_bgm + + @map_bgm.name == bgm.name && @map_bgm.volume == bgm.volume && @map_bgm.pitch == bgm.pitch + end + + def can_giveup? + return false if $game_switches[NWConst::Sw::INVALID_GIVEUP] + return false if $game_troop.challenge_battle? + + $game_party.all_members.any? { |member| member.luca? } && $game_actors[NWConst::Actor::LUCA].exist? + end + + def set_turn_end_revive + return if giveup? || !$game_party.in_battle || $game_troop.interpreter.running? + + members = ($game_troop.dead_members + $game_party.dead_members).select(&:turn_end_revive?) + members.each do |member| + skill_interrupt(member, TURN_END_REVIVE_SKILL.id) + end + end + + def skill_chain(battler, base_action) + action = base_action.chain_action + return if action.nil? + + battler.actions << action + @action_battlers.unshift(action) + end + + def add_mimic_history(action) + return if action.nil? || action.symbol != :count + + @mimic_action_history << action + end + + def interrupt_mimic(batller) + mimic_skills(battler).reverse_each do |skill_id| + skill_interrupt(batller, skill_id, :mimic) + end + end + + def interrupt_super_mimic(battler) + super_mimic_skills(battler).reverse_each do |skill_id| + skill_interrupt(battler, skill_id, :mimic) + end + end + + def mimic_skills(action) + m = mimic_items.last + return [] if m.nil? || !action.subject.mimic_usable?(m) + + [m.id] + end + + def super_mimic_skills(battler) + items = mimic_items.select do |a| + battler.mimic_usable?(a) + end + items.map(&:id) + end + + def mimic_items + @mimic_action_history.map(&:object).compact + end + + def evasion_skill(battler) + auto_skill_per(battler.evasion_skill, battler) + end + + def interrupt_evasion_skill + members = ($game_troop.alive_members + $game_party.alive_members).select(&:enable_action?).reject(&:cant_move?) + members.select(&:evasion_action).each do |member| + act = member.evasion_action + bact = skill_interrupt(member, act.object.id) + bact.target_index = act.target_index if bact + member.clear_evasion_action + end + end + + def fastest_action?(action) + return false unless $game_party.in_battle + + @fastest_action == action + end + + def latest? + return false unless $game_party.in_battle + + @action_battlers.select(&:action?).select { |a| a.symbol == :count }.empty? + end + + def retry_battle + return if $game_party.in_battle || @retry_data.nil? + + SceneManager.push(Scene_Battle) + bgm = @map_bgm + bgs = @map_bgs + DataManager.extract_save_contents(Marshal.load(@retry_data)) + setup(@troop_id, @can_escape, @can_lose) + play_battle_bgm + @map_bgm = bgm + @map_bgs = bgs + true + end +end +end + +module Cache + class << self + alias h_normal_bitmap normal_bitmap + def normal_bitmap(path) + check_filesize + h_normal_bitmap(path) + end + + alias h_hue_changed_bitmap hue_changed_bitmap + def hue_changed_bitmap(path, hue) + check_filesize + h_hue_changed_bitmap(path, hue) + end + + def check_filesize + clear if @cache.size > 600 + end + end +end + +#============================================================================== +# ■ DataManager +#============================================================================== +module DataManager + class << self + #-------------------------------------------------------------------------- + # ○ データベースのロード + #-------------------------------------------------------------------------- + def load_database + if $BTEST + load_battle_test_database + else + load_normal_database + check_player_location + end + $data_library = Data_Library.new + end + + #-------------------------------------------------------------------------- + # ○ 各種ゲームオブジェクトの作成 + #-------------------------------------------------------------------------- + def create_game_objects + $game_temp = Game_Temp.new + $game_system = Game_System.new + $game_timer = Game_Timer.new + $game_message = Game_Message.new + $game_switches = Game_Switches.new + $game_variables = Game_Variables.new + $game_self_switches = Game_SelfSwitches.new + $game_actors = Game_Actors.new + $game_party = Game_Party.new + $game_troop = Game_Troop.new + $game_map = Game_Map.new + $game_player = Game_Player.new + $game_novel = Game_Novel.new + $game_poker = Game_Poker.new + $game_slot = Game_Slot.new + end + + #-------------------------------------------------------------------------- + # ○ 戦闘テストのセットアップ + #-------------------------------------------------------------------------- + def setup_battle_test + $game_map.setup($data_system.start_map_id) + $game_party.setup_battle_test + BattleManager.setup($data_system.test_troop_id) + BattleManager.play_battle_bgm + end + + #-------------------------------------------------------------------------- + # ○ セーブ内容の作成 + #-------------------------------------------------------------------------- + def make_save_contents + contents = {} + contents[:system] = $game_system + contents[:timer] = $game_timer + contents[:message] = $game_message + contents[:switches] = $game_switches + contents[:variables] = $game_variables + contents[:self_switches] = $game_self_switches + contents[:actors] = $game_actors + contents[:party] = $game_party + contents[:troop] = $game_troop + contents[:map] = $game_map + contents[:player] = $game_player + contents[:novel] = $game_novel + contents + end + + #-------------------------------------------------------------------------- + # ○ セーブ内容の展開 + #-------------------------------------------------------------------------- + def extract_save_contents(contents) + $game_system = contents[:system] + $game_timer = contents[:timer] + $game_message = contents[:message] + $game_switches = contents[:switches] + $game_variables = contents[:variables] + $game_self_switches = contents[:self_switches] + $game_actors = contents[:actors] + $game_party = contents[:party] + $game_troop = contents[:troop] + $game_map = contents[:map] + $game_player = contents[:player] + $game_novel = contents[:novel] + end + + #-------------------------------------------------------------------------- + # ● ロードの実行(例外処理なし) + #-------------------------------------------------------------------------- + def load_game_without_rescue(index) + File.open(make_filename(index), "rb") do |file| + Marshal.load(file) + extract_save_contents(Marshal.load(file)) + update + reload_map_if_updated + @last_savefile_index = index.is_a?(Integer) ? index : 0 + end + true + end + + #-------------------------------------------------------------------------- + # ○ モジュール初期化 + #-------------------------------------------------------------------------- + def init + @last_savefile_index = 0 + create_game_objects + setup_battle_test if $BTEST + end + + #-------------------------------------------------------------------------- + # ● システムバックアップファイル名の取得 + #-------------------------------------------------------------------------- + def system_backup_filename + "Save/SystemSaveBackup.rvdata2" + end + + #-------------------------------------------------------------------------- + # ● システムデータの初期化 + #-------------------------------------------------------------------------- + def setup_system + return if system_object_exist? + + if system_file_exist? + load_system + fix_system + else + create_system_objects + save_system + end + $game_library.make_preparation_list + end + + def fix_system + $game_global_system.fix + end + + #-------------------------------------------------------------------------- + # ● システムオブジェクトが存在する? + #-------------------------------------------------------------------------- + def system_object_exist? + ($game_library && $game_system_switches) + end + + #-------------------------------------------------------------------------- + # ● システムバックアップセーブの実行(例外処理なし) + #-------------------------------------------------------------------------- + def save_system_backup_without_rescue + File.open(system_backup_filename, "wb") do |file| + Marshal.dump(make_system_save_contents, file) + end + true + end + + #-------------------------------------------------------------------------- + # ● システムセーブ内容の作成 + #-------------------------------------------------------------------------- + def make_system_save_contents + contents = {} + contents[:system_save_count] = @system_save_count + contents[:library] = $game_library + contents[:system_switches] = $game_system_switches + contents[:global_system] = $game_global_system + contents + end + + #-------------------------------------------------------------------------- + # ● システムセーブ内容の展開 + #-------------------------------------------------------------------------- + def extract_system_save_contents(contents) + @system_save_count = contents[:system_save_count].nil? ? 0 : contents[:system_save_count] + $game_library = contents[:library].nil? ? Game_Library.new : contents[:library] + $game_system_switches = contents[:system_switches].nil? ? Game_SystemSwitches.new : contents[:system_switches] + $game_global_system = contents[:global_system] + if $game_global_system.nil? + $game_global_system = Global::Game_System.new + save_system + end + end + + #-------------------------------------------------------------------------- + # ● 各種システムオブジェクトの作成 + #-------------------------------------------------------------------------- + def create_system_objects + p "システムオブジェクトの生成" if $TEST + @system_save_count = 0 + $game_library = Game_Library.new + $game_system_switches = Game_SystemSwitches.new + $game_global_system = Global::Game_System.new + end + + #-------------------------------------------------------------------------- + # ○ セーブファイルの存在判定 + #-------------------------------------------------------------------------- + def save_file_exists? + !Dir.glob("Save/Save*.rvdata2").empty? + end + + #-------------------------------------------------------------------------- + # ○ セーブファイルの最大数 + #-------------------------------------------------------------------------- + def savefile_max + 99 + end + + #-------------------------------------------------------------------------- + # ● ファイル名の作成 + # index : ファイルインデックス + #-------------------------------------------------------------------------- + def make_filename(index) + if index.is_a?(Integer) + format("Save/Save%02d.rvdata2", index + 1) + else + format("Save/AutoSave%s.rvdata2", index) + end + end + + #-------------------------------------------------------------------------- + # ○ セーブヘッダの作成 + #-------------------------------------------------------------------------- + def make_save_header + header = {} + header[:characters] = $game_party.characters_for_savefile + header[:playtime_s] = $game_system.playtime_s + header[:realtime_s] = $game_system.realtime_s + header[:luca_level] = $game_actors[NWConst::Actor::LUCA].base_level + header + end + + #-------------------------------------------------------------------------- + # ● サムネイル画像の解放 + #-------------------------------------------------------------------------- + def dispose_thumbnail + @current_thumbnail.dispose + @dummy_thumbnail.dispose + @thumbnails.each_value { |thumbnail| thumbnail.dispose } + @current_thumbnail = nil + @dummy_thumbnail = nil + @thumbnails = {} + end + + #-------------------------------------------------------------------------- + # ● 現在サムネイル画像の取得 + #-------------------------------------------------------------------------- + def get_current_thumbnail + @current_thumbnail + end + + #-------------------------------------------------------------------------- + # ● ダミーサムネイル画像の取得 + #-------------------------------------------------------------------------- + def get_dummy_thumbnail + @dummy_thumbnail + end + + #-------------------------------------------------------------------------- + # ● サムネイル画像の取得 + #-------------------------------------------------------------------------- + def get_thumbnail(index) + @thumbnails[index] + end + + #-------------------------------------------------------------------------- + # ● サムネイル画像の変更 + #-------------------------------------------------------------------------- + def change_thumbnail(index) + @thumbnails[index].dispose if @thumbnails[index] + bitmap = Bitmap.new(160, 120) + bitmap.blt(0, 0, @current_thumbnail, bitmap.rect) + @thumbnails[index] = bitmap + end + + #-------------------------------------------------------------------------- + # ● サムネイル画像の保存 + #-------------------------------------------------------------------------- + def save_thumbnail(file_name) + @current_thumbnail.save(file_name, :PNG) + end + + def update_database? + script_update? || diff_version_id? || data_update? + end + + def diff_version_id? + load_data("Data/ExVersionID.rvdata2") != load_data("Data/System.rvdata2").version_id + rescue Errno::ENOENT + true + end + + def latest_updatetime + return 0 if rgss_encrypt? + + files = ["Data/Actors", "Data/Classes", "Data/Skills", "Data/Items", "Data/Weapons", "Data/Armors", + "Data/Enemies", "Data/Troops", "Data/States", "Data/Animations", "Data/Tilesets", "Data/CommonEvents", "Data/System", "Data/MapInfos", "#{over_map_dir}/Data/MapInfos"] + times = files.map do |f| + next 0 if File.exist?("#{f}.rvdata2") + + File.mtime("#{f}.rvdata2") + end + times.max + end + + def data_update? + return false if rgss_encrypt? + + load_data("Data/ExDataUpdate.rvdata2") < latest_updatetime + rescue Errno::ENOENT + true + end + + def script_update? + SCRIPT_UPDATE != load_data("Data/ExScriptUpdate.rvdata2") + rescue Errno::ENOENT + true + end + + def rgss_encrypt? + @rgss_encrypt ||= FileTest.file?("Game.rgss3a") + end + + def setting_features_data + $data_actors.compact.each(&:setting_feature_data) + $data_classes.compact.each(&:setting_feature_data) + $data_weapons.compact.each(&:setting_feature_data) + $data_armors.compact.each(&:setting_feature_data) + $data_enemies.compact.each(&:setting_feature_data) + $data_states.compact.each(&:setting_feature_data) + $data_items.compact.each(&:setting_feature_data) + $data_skills.compact.each(&:setting_feature_data) + end + + #-------------------------------------------------------------------------- + # ● 通常のデータベースEXをロード + #-------------------------------------------------------------------------- + def load_normal_database_ex + ex_data = load_data("Data/DataEx.rvdata2") + $data_actors = ex_data[:actors] + $data_classes = ex_data[:classes] + $data_skills = ex_data[:skills] + $data_items = ex_data[:items] + $data_weapons = DataBaseEquip.new(ex_data[:weapons], :weapons) + $data_armors = DataBaseEquip.new(ex_data[:armors], :armors) + $data_enemies = ex_data[:enemies] + $data_troops = ex_data[:troops] + $data_states = ex_data[:states] + $data_animations = ex_data[:animations] + $data_tilesets = ex_data[:tilesets] + $data_common_events = ex_data[:common_events] + $data_system = ex_data[:system] + $data_mapinfos = ex_data[:mapinfos] + end + + #-------------------------------------------------------------------------- + # ● 戦闘テスト用のデータベースEXをロード + #-------------------------------------------------------------------------- + def load_battle_test_database_ex + ex_data = load_data("Data/DataEx.rvdata2") + $data_actors = ex_data[:actors] + $data_classes = ex_data[:classes] + $data_skills = ex_data[:skills] + $data_items = ex_data[:items] + $data_weapons = DataBaseEquip.new(ex_data[:weapons], :weapons) + $data_armors = DataBaseEquip.new(ex_data[:armors], :armors) + $data_enemies = ex_data[:enemies] + $data_troops = load_data("Data/BT_Troops.rvdata2") + $data_states = ex_data[:states] + $data_animations = load_data("Data/BT_Animations.rvdata2") + $data_tilesets = ex_data[:tilesets] + $data_common_events = ex_data[:common_events] + $data_system = load_data("Data/BT_System.rvdata2") + end + + #-------------------------------------------------------------------------- + # ● 解析済みデータベースEXをセーブ + #-------------------------------------------------------------------------- + def save_analyzed_database_ex + ex_data = {} + ex_data[:actors] = $data_actors + ex_data[:classes] = $data_classes + ex_data[:skills] = $data_skills + ex_data[:items] = $data_items + ex_data[:weapons] = $data_weapons + ex_data[:armors] = $data_armors + ex_data[:enemies] = $data_enemies + ex_data[:troops] = $data_troops + ex_data[:states] = $data_states + ex_data[:tilesets] = $data_tilesets + ex_data[:animations] = $data_animations + ex_data[:common_events] = $data_common_events + ex_data[:system] = $data_system + ex_data[:mapinfos] = $data_mapinfos + save_data(ex_data, "Data/DataEx.rvdata2") + save_data($data_system.version_id, "Data/ExVersionID.rvdata2") + save_data(SCRIPT_UPDATE, "Data/ExScriptUpdate.rvdata2") + save_data(latest_updatetime, "Data/ExDataUpdate.rvdata2") + + p "DataEx.rvdata2をセーブしました" + end + + #-------------------------------------------------------------------------- + # ○ 通常のデータベースをロード + #-------------------------------------------------------------------------- + alias nw_base_load_normal_database load_normal_database + def load_normal_database + bm = Benchmark.new + bm.start + if update_database? + nw_base_load_normal_database + nw_analyze_database + setting_features_data + save_analyzed_database_ex unless rgss_encrypt? + else + load_normal_database_ex + end + bm.stop + p "データベース読み込み時間は#{bm.elapsed}です" + $data_skills << TURN_END_REVIVE_SKILL + $data_skills << REVIVE_SKILL + [TURN_END_REVIVE_SKILL, REVIVE_SKILL].each do |skill| + skill.id = $data_skills.index(skill) + skill.note_analyze + skill.setting_feature_data + skill.data_ex[:visible?] = true + skill.data_ex[:lib_exclude?] = true + end + end + + #-------------------------------------------------------------------------- + # ○ 戦闘テスト用のデータベースをロード + #-------------------------------------------------------------------------- + alias nw_base_load_battle_test_database load_battle_test_database + def load_battle_test_database + bm = Benchmark.new + bm.start + if update_database? + nw_base_load_normal_database # Data_Ex作成には通常データベースを使う + nw_analyze_database + setting_features_data + save_analyzed_database_ex + end + load_battle_test_database_ex + bm.stop + p "データベース読み込み時間は#{bm.elapsed}です" if $test_battlers + end + + #-------------------------------------------------------------------------- + # ● セーブファイルの存在判定 + #-------------------------------------------------------------------------- + def auto_save_file_exists? + return false if Dir.glob("Save/AutoSave*.rvdata2").empty? + return false unless load_header("01")[:save_index].is_a?(Integer) + + true + end + + #-------------------------------------------------------------------------- + # ● セーブ試行ファイル名の作成 + #-------------------------------------------------------------------------- + def make_temp_filename + sprintf("Save/SaveTemp.rvdata2") + end + + #-------------------------------------------------------------------------- + # ● セーブ試行ファイルの削除 + #-------------------------------------------------------------------------- + def delete_temp_file + File.delete(make_temp_filename) if File.exist?(make_temp_filename) + end + + #-------------------------------------------------------------------------- + # ○ オートセーブヘッダの作成 + #-------------------------------------------------------------------------- + def make_auto_save_header + header = make_save_header + header[:save_index] = DataManager.last_savefile_index + header + end + + #-------------------------------------------------------------------------- + # ○ セーブの実行 + #-------------------------------------------------------------------------- + def save_game(index) + Dir.mkdir("Save") unless Dir.exist?("Save") + save_game_without_rescue(index) + begin; save_thumbnail(format("Save/Save%02d", index + 1)) + rescue StandardError; process_save_failure2("通常") + end + if $game_system.save_count % SaveData::BackUpNum == 0 + begin + save_game_backup_without_rescue + begin; save_thumbnail("Save/SaveBackup") + rescue StandardError; process_save_failure2("バックアップ") + end + rescue StandardError; process_save_failure1("バックアップ") + end + end + delete_temp_file + true + rescue StandardError + process_save_failure1("通常") + delete_temp_file + false + end + + #-------------------------------------------------------------------------- + # ● セーブの実行(例外処理なし) + #-------------------------------------------------------------------------- + def save_game_without_rescue(index) + File.open(make_temp_filename, "wb") do |file| + $game_system.on_before_save + Marshal.dump(make_save_header, file) + Marshal.dump(make_save_contents, file) + @last_savefile_index = index + end + File.rename(make_temp_filename, make_filename(index)) + true + end + + #-------------------------------------------------------------------------- + # ● バックアップセーブの実行(例外処理なし) + #-------------------------------------------------------------------------- + def save_game_backup_without_rescue + File.open(make_temp_filename, "wb") do |file| + Marshal.dump(make_save_header, file) + Marshal.dump(make_save_contents, file) + end + File.rename(make_temp_filename, "Save/SaveBackup.rvdata2") + true + end + + #-------------------------------------------------------------------------- + # ● セーブ失敗時の表示名の作成 + #-------------------------------------------------------------------------- + def save_failure_word(text) + NWPatch.ver_name(" - セーブ失敗メッセージ\n\n") + "【" + text + "】" + end + + #-------------------------------------------------------------------------- + # ● セーブ失敗時の処理 + #-------------------------------------------------------------------------- + def process_save_failure1(text) + text = save_failure_word(text) + m1 = text + SaveData::FailureMes1.inject("") { |r, text| r += text + "\n" } + m2 = "不明" + begin + File.open(make_temp_filename, "wb") do |file| + make_save_header.each do |key, value| + m2 = "header " + key.to_s + Marshal.dump(value, file) + end + make_save_contents.each do |key, value| + m2 = "contents " + key.to_s + Marshal.dump(value, file) + end + end + m2 = "不明" + rescue StandardError + end + backup = text.include?("バックアップ") + m1.gsub!("<1>") { backup ? "\n" + SaveData::FailureMesBackUp : "" } + m1.gsub!("<2>") { format("セーブ失敗原因: %s", m2) } + msgbox m1 + end + + #-------------------------------------------------------------------------- + # ● サムネイル失敗時の処理 + #-------------------------------------------------------------------------- + def process_save_failure2(text) + text = save_failure_word(text) + m1 = text + SaveData::FailureMes2.inject("") { |r, text| r += text + "\n" } + backup = text.include?("バックアップ") + m1.gsub!("<1>") { backup ? "\n" + SaveData::FailureMesBackUp : "" } + end + + #-------------------------------------------------------------------------- + # ○ オートセーブの実行 + #-------------------------------------------------------------------------- + def auto_save_game + return if $game_switches[NWConst::Sw::LIBRARY_H_MEMORY] || $BTEST + + if $game_system.save_disabled + puts "★オートセーブを実行しようとしたが、セーブ不能のため無効" + return + end + puts "★オートセーブを実行" + index = "01" + begin + Dir.mkdir("Save") unless Dir.exist?("Save") + auto_save_game_without_rescue(index) + delete_temp_file + true + rescue StandardError + process_save_failure1("オート") + File.rename(make_temp_filename, make_filename(index)) if File.exist?(make_temp_filename) + delete_temp_file + false + end + end + + #-------------------------------------------------------------------------- + # ○ オートセーブの実行(例外処理なし) + #-------------------------------------------------------------------------- + def auto_save_game_without_rescue(index) + fname = make_filename(index) + File.rename(fname, make_temp_filename) if File.exist?(fname) + File.open(fname, "wb") do |file| + $game_system.on_before_save + $game_system.on_before_auto_save + Marshal.dump(make_auto_save_header, file) + Marshal.dump(make_save_contents, file) + end + + true + end + + #-------------------------------------------------------------------------- + # ● システムファイルが存在する? + #-------------------------------------------------------------------------- + def system_file_exist? + SaveSystemData.filename_set.any? { |name| File.exist?(name) } + end + + #-------------------------------------------------------------------------- + # ● システムセーブの実行 + #-------------------------------------------------------------------------- + def save_system + return false unless system_object_exist? + + begin + Dir.mkdir("Save") unless Dir.exist?("Save") + @system_save_count += 1 + save_system_without_rescue + save_system_backup_without_rescue if @system_save_count % SaveSystemData::BackUpNum == 0 + true + rescue StandardError + msgbox save_failure_word("システム") + + SaveSystemData::FailureMes1.inject("") { |r, text| r += text + "\n" } + false + end + end + + #-------------------------------------------------------------------------- + # ● システムロードの実行 + #-------------------------------------------------------------------------- + def load_system + load_system_without_rescue + rescue StandardError + msgbox save_failure_word("システム") + + SaveSystemData::FailureMes2.inject("") { |r, text| r += text + "\n" } + Kernel.exit + end + + #-------------------------------------------------------------------------- + # ● システムセーブの実行(例外処理なし) + #-------------------------------------------------------------------------- + def save_system_without_rescue + File.open(SaveSystemData.save_filename, "wb") do |file| + Marshal.dump(make_system_save_contents, file) + end + true + end + + #-------------------------------------------------------------------------- + # ● システムロードの実行(例外処理なし) + #-------------------------------------------------------------------------- + def load_system_without_rescue + File.open(SaveSystemData.load_filename, "rb") do |file| + extract_system_save_contents(Marshal.load(file)) + update_system + end + true + end + + def nw_analyze_database + [$data_actors, $data_classes, $data_skills, $data_items, $data_weapons, $data_armors, $data_enemies, + $data_states].each do |data| + data.each_with_index do |obj, i| + next unless obj + + obj.id = i + obj.note_analyze + end + end + $data_classes.compact.each do |c| + c.learnings.each { |l| l.nw_note_analyze } + end + $data_tilesets.compact.each { |t| t.nw_note_analyze } + # スクリプト素材の関係でnote消しはNG + end + def make_thumbnail + @thumbnails = {} # Hash to store thumbnails + @thumbnails.each_with_index do |(index, bitmap), i| + if bitmap.nil? + puts "Thumbnail at index #{i} is nil." + else + puts "Thumbnail at index #{i}:" + puts " Class: #{bitmap.class}" + puts " Dimensions: #{bitmap.rect.width}x#{bitmap.rect.height}" + end + end + @current_thumbnail = nil + @dummy_thumbnail = nil + + # Create current and dummy thumbnails + src_bitmap = SceneManager.background_bitmap + @current_thumbnail = Bitmap.new(160, 120) + @current_thumbnail.stretch_blt(@current_thumbnail.rect, src_bitmap, src_bitmap.rect) + + @dummy_thumbnail = Bitmap.new(160, 120) + @dummy_thumbnail.fill_rect(@dummy_thumbnail.rect, Color.new(0, 0, 0)) + + puts "Skipping thumbnail creation because the height is greater than 2000: #{@current_thumbnail.height}" + + # Ensure the Save directory exists + Dir.mkdir("Save") unless Dir.exist?("Save") + + # Process each file in the Save directory + Dir.entries("Save").each do |file_name| + puts "Checking file: #{file_name}" # Debug log + next unless file_name =~ /^Save(\d+)\.rvdata2/i # Match save file pattern + + save_number = Regexp.last_match(1) # Extract save number + thumbnail_file = "Save/Save#{save_number}.png" # Corresponding thumbnail path + + puts "Processing save file: #{file_name}, expected thumbnail: #{thumbnail_file}" + + index = save_number.to_i - 1 + + # Check if the thumbnail exists + if File.exist?(thumbnail_file) + puts "Thumbnail exists: #{thumbnail_file}" + + # Check size before indexing + # temp_bitmap = Bitmap.new(thumbnail_file) + temp_bitmap = Bitmap.new(thumbnail_file) : @dummy_thumbnail + puts "Temp Bitmap size: #{temp_bitmap.rect.width}x#{temp_bitmap.rect.height} size" + + # Add the bitmap to @thumbnails only if the size is acceptable + if temp_bitmap.rect.width > 0 && temp_bitmap.rect.height > 0 + @thumbnails[index] = temp_bitmap + puts "Successfully added thumbnail at index #{index}." + else + temp_bitmap = @dummy_thumbnail + @thumbnails[index] = temp_bitmap + + end + elsif File.exist?("Graphics/System/NotThumbnailFile.png") + puts "Thumbnail missing, using fallback: Graphics/System/NotThumbnailFile.png" + + # Check size of fallback image before indexing + fallback_bitmap = Bitmap.new("Graphics/System/NotThumbnailFile.png") + puts "Fallback Bitmap size: #{fallback_bitmap.rect.width}x#{fallback_bitmap.rect.height}" + + # Add the fallback bitmap to @thumbnails + @thumbnails[index] = fallback_bitmap + puts "Using fallback thumbnail at index #{index}." + else + temp_bitmap = @dummy_thumbnail + @thumbnails[index] = temp_bitmap + puts "Thumbnail for Save#{save_number} not found, and fallback image is missing." + end + + # Check if the thumbnail is nil or has the correct class + if @thumbnails[index].nil? + puts "Warning: Thumbnail at index #{index} is nil!" + else + puts "Thumbnail at index #{index} is of class: #{@thumbnails[index].class}" + puts "Dimensions: #{@thumbnails[index].rect.width}x#{@thumbnails[index].rect.height}" + end + end + end + + def update + $game_actors.bugfix_ignore_flag = true + $game_system.update_config + $game_party.pre_update + $game_actors.version_update + $game_party.update + $game_actors.bugfix_ignore_flag = false + end + + def update_system + $game_library.refresh_skill_update + end + + def over_map_dir + "Data/Map" + end + + def map_file_name(map_id) + div, mod = map_id.divmod(1000) + return format("Data/Map%03d.rvdata2", mod) if div == 0 + + format("#{over_map_dir}/Data/Map%03d.rvdata2", mod) + end + end +end + +module EquipInfoScene + #-------------------------------------------------------------------------- + # ● 開始処理 + #-------------------------------------------------------------------------- + def start + super + @deactivate_windows = [] + @equip_info_windows = [] + @hide_windows = [] + create_equip_info_itemname_window + create_equip_info_page_window + create_equip_info_window + @equip_info_current_window_index = 0 + @equip_info_page = 0 + @equip_info_isshow = false + end + + def update + super + equip_info_change_visible if Input.trigger?(:X) + end + + def update_all_windows + if @equip_info_isshow + if Input.trigger?(:B) + equip_info_hide + Input.update + elsif Input.trigger?(:L) || Input.trigger?(:LEFT) + equip_info_before_page + Input.update + elsif Input.trigger?(:R) || Input.trigger?(:RIGHT) + equip_info_next_page + Input.update + end + end + super + end + + #-------------------------------------------------------------------------- + # ○ 性能差比較ウィンドウの表示状態を切り替え + #-------------------------------------------------------------------------- + def equip_info_change_visible + equip_info_next_page if @equip_info_isshow + equip_info_show if !@equip_info_isshow && !equip_info_hide? + end + + #-------------------------------------------------------------------------- + # ○ 性能差比較ウィンドウの非表示 + #-------------------------------------------------------------------------- + def equip_info_hide + @equip_info_itemname_window.hide + @equip_info_page_window.hide + @equip_info_page = 0 + @equip_info_current_window_index = 0 + equip_info_windows.each { |window| window.hide } + @deactivate_windows.each(&:activate) + @hide_windows.each(&:show) + @equip_info_isshow = false + end + + #-------------------------------------------------------------------------- + # ● 性能差比較ウィンドウを非表示にするか + #-------------------------------------------------------------------------- + def equip_info_hide? + return true unless equip_info_select_item.is_a?(RPG::EquipItem) + end + + #-------------------------------------------------------------------------- + # ● 性能差比較アイテム名の文字色を明るくするか + #-------------------------------------------------------------------------- + def equip_info_enable + true + end + + def equip_info_init + @equip_info_windows = __equip_info_windows + equip_info_set_item + equip_info_set_actor + equip_info_windows.each do |window| + window.page_index = 0 + window.refresh + end + equip_info_set_page + end + + def equip_info_set_item + select_item = equip_info_select_item + @equip_info_itemname_window.item = select_item + @equip_info_windows.each { |window| window.item = select_item } + end + + def equip_info_set_page + @equip_info_page_window.page_index = equip_info_get_all_page_index[@equip_info_current_window_index] + @equip_info_page + @equip_info_page_window.page_max = equip_info_all_page_max + equip_info_window.page_index = @equip_info_page + equip_info_window.refresh if @equip_info_isshow + end + + def equip_info_set_actor + @equip_info_windows.each { |window| window.actor = equip_info_select_actor } + end + + def equip_info_select_item + nil + end + + def equip_info_select_actor + nil + end + + def create_equip_info_window + @compare_ex_window = Window_ShopCompareEx.new + @equip_info_window = Window_ShopStatusEx.new(0, 56, 640, 368) + @enchant_stone_window = Window_Enchant_Stones.new(0, 56, 640, 368) + @set_effect_window = Window_SetEffect.new(0, 56, 640, 368) + @compare_ex_window.z = 100 + @equip_info_window.z = 100 + @enchant_stone_window.z = 100 + @set_effect_window.z = 100 + @compare_ex_window.viewport = @viewport + @equip_info_window.viewport = @viewport + @enchant_stone_window.viewport = @viewport + @enchant_stone_window.viewport = @viewport + end + + def __equip_info_windows + @equip_info_windows = [@equip_info_window, @set_effect_window, @enchant_stone_window, @compare_ex_window] + end + + def create_equip_info_itemname_window + @equip_info_itemname_window = Window_ItemName.new + @equip_info_itemname_window.viewport = @viewport + end + + def create_equip_info_page_window + @equip_info_page_window = Window_Page.new + @equip_info_page_window.viewport = @viewport + end + + #-------------------------------------------------------------------------- + # ● 簡易操作説明テキスト + #-------------------------------------------------------------------------- + def show_key_text + if @equip_info_isshow + [Help.equip_info_change, + Help.equip_info_next, + Help.equip_info_hide] + else + [] + end + end + + #-------------------------------------------------------------------------- + # ● 全ページ + #-------------------------------------------------------------------------- + def equip_info_all_page_max + equip_info_windows.map(&:page_max).inject(:+) + end + + #-------------------------------------------------------------------------- + # ● ウィンドウ毎の全ページから見た初期ページ数 + #-------------------------------------------------------------------------- + def equip_info_get_all_page_index + equip_info_windows.map(&:page_max).map.with_index do |_p, index| + (equip_info_windows.map(&:page_max)[0, index] || []).inject(1) { |i, v| i + v } + end + end + + #-------------------------------------------------------------------------- + # ● 全ウィンドウ取得 + #-------------------------------------------------------------------------- + def equip_info_windows + @equip_info_windows.select { |window| window.page_max > 0 } + end + + #-------------------------------------------------------------------------- + # ● 現在の表示状態取得 + #-------------------------------------------------------------------------- + def equip_info_window_visible + equip_info_windows.any?(&:visible) + end + + #-------------------------------------------------------------------------- + # ● 次のページへ + #-------------------------------------------------------------------------- + def equip_info_next_page + @equip_info_page += 1 + if @equip_info_page >= equip_info_window.page_max + equip_info_window.hide + if @equip_info_current_window_index >= equip_info_windows.size - 1 + @equip_info_current_window_index = 0 + else + @equip_info_current_window_index += 1 + end + @equip_info_page = 0 + equip_info_window.show + + end + equip_info_set_page + end + + #-------------------------------------------------------------------------- + # ● 前のページへ + #-------------------------------------------------------------------------- + def equip_info_before_page + @equip_info_page -= 1 + if @equip_info_page == -1 + equip_info_window.hide + if @equip_info_current_window_index == 0 + @equip_info_current_window_index = equip_info_windows.size - 1 + else + @equip_info_current_window_index -= 1 + end + @equip_info_page = (equip_info_window.page_max - 1) + equip_info_window.show + end + equip_info_set_page + end + + #-------------------------------------------------------------------------- + # ● 現在起動中のウィンドウ取得 + #-------------------------------------------------------------------------- + def equip_info_window + equip_info_windows[@equip_info_current_window_index] + end + + #-------------------------------------------------------------------------- + # ○ 性能差比較ウィンドウの表示 + #-------------------------------------------------------------------------- + def equip_info_show + equip_info_init + @equip_info_isshow = true + @deactivate_windows = [] + @hide_windows = [] + instance_variables.each do |varname| + ivar = instance_variable_get(varname) + if ivar.is_a?(Window) + @deactivate_windows << ivar if ivar.active + @hide_windows << ivar if ivar.visible + end + end + @deactivate_windows.each(&:deactivate) + @hide_windows.each(&:hide) + @equip_info_page = 0 + @equip_info_current_window_index = 0 + equip_info_window.show + @equip_info_itemname_window.show + @equip_info_itemname_window.refresh + @equip_info_page_window.show + equip_info_window.refresh + end + + #-------------------------------------------------------------------------- + # ● 性能差比較ウィンドウを変更するか + #-------------------------------------------------------------------------- + def equip_info_change? + if (equip_info_select_item.nil? || !equip_info_select_item.enchant_item?) && @enchant_stone_window.visible + return true + end + return true if equip_info_select_item.nil? && @equip_info_window.visible + end +end + +module EquipInfoWindow + attr_accessor :page_index + attr_writer :item + attr_writer :actor + + def page_max + 1 + end +end + +module FavoriteItem + #-------------------------------------------------------------------------- + # ○ 選択中のアイテムのお気に入り状態を変更 + #-------------------------------------------------------------------------- + def process_set_favorite_item + Input.update + return Sound.play_buzzer if @item_window.item.nil? + + Sound.play_ok + @item_window.set_favorite_item + @item_window.refresh + @item_window.select([@item_window.index - 1, 0].max) if @item_window.favorite_mode + + @item_window.refresh + end + + #-------------------------------------------------------------------------- + # ○ お気に入り表示モードを変更 + #-------------------------------------------------------------------------- + def process_change_favorite_mode + Input.update + Sound.play_ok + last_item = @item_window.item + @item_window.change_favorite_mode + @item_window.refresh + if @item_window.active + @item_window.select_item(last_item) + else + @item_window.select(-1) + end + end + + #-------------------------------------------------------------------------- + # ○ フレーム更新 + #-------------------------------------------------------------------------- + def update + return process_set_favorite_item if enable_set_favorite_item? && Input.trigger?(:Y) + return process_change_favorite_mode if enable_change_favorite_mode? && Input.trigger?(:Z) + + @item_window.change_favorite_mode if off_favorite_mode? and @item_window.favorite_mode + super + end + + #-------------------------------------------------------------------------- + # ○ 可否 選択中のアイテムのお気に入り状態を変更 + #-------------------------------------------------------------------------- + def enable_set_favorite_item? + false + end + + #-------------------------------------------------------------------------- + # ○ 可否 お気に入り表示モードを変更 + #-------------------------------------------------------------------------- + def enable_change_favorite_mode? + false + end + + #-------------------------------------------------------------------------- + # ○ 有無 お気に入り表示モードをオフ + #-------------------------------------------------------------------------- + def off_favorite_mode? + !@item_window.visible + end +end + +module Graphics + class << self + alias hima_set_frame_count frame_count= + def frame_count=(other) + @frame_count = other.abs + hima_set_frame_count(0) + end + + alias hima_frame_count frame_count + def frame_count + hima_frame_count + @frame_count + end + + alias hima_update update + def update + hima_update + Sprite_Base.clear_checker + end +end +end +module Help + class << self + #-------------------------------------------------------------------------- + # ● アビリティ画面の上部ヘルプメッセージ + #-------------------------------------------------------------------------- + def ability + "Please choose an ability type" + end + + #-------------------------------------------------------------------------- + # ● アビリティ画面の下部ヘルプメッセージ + #-------------------------------------------------------------------------- + def ability_key + "#{Vocab.key_c}:Select #{Vocab.key_b}:Cancel #{Vocab.key_a}:Remove" + end + + #-------------------------------------------------------------------------- + # ● スキル画面の下部ヘルプメッセージ + #-------------------------------------------------------------------------- + def skill_type_key + t = "" + t += "\\C[0]" + t += "#{Vocab.key_a}:Hide in battle + Disable in auto mode" + t += "(Disabled)" unless $game_system.conf[:bt_stype] + t += "\n" + t += "\\C[16]" if Input.press?(:X) + t += "#{Vocab.key_x}+↑/↓:Sort by skill type + t += "#{Vocab.key_x}+#{Vocab.key_b}:Reset skill order" + t + end + + #-------------------------------------------------------------------------- + # ● コンフィグ-色調のヘルプメッセージ + #-------------------------------------------------------------------------- + def config_tone + [ + "Set red value.\r\n←/→:-/+#{Vocab.key_l}/#{Vocab.key_r}:Big -/+", + "Set green value.\r\n←/→:-/+#{Vocab.key_l}/#{Vocab.key_r}:Big -/+", + "Set blue value.\r\n←/→:-/+#{Vocab.key_l}/#{Vocab.key_r}:Big -/+", + "Restore defaults.", + "Return" + ] + end + + #-------------------------------------------------------------------------- + # ● コンフィグ-音量のヘルプメッセージ + #-------------------------------------------------------------------------- + def config_sound + [ + "BGM Volume\r\n←/→:-/+#{Vocab.key_l}/#{Vocab.key_r}:Big -/+", + "BGS Volume\r\n←/→:-/+#{Vocab.key_l}/#{Vocab.key_r}:Big -/+", + "ME Volume\r\n←/→:-/+#{Vocab.key_l}/#{Vocab.key_r}:Big -/+", + "SE Volume\r\n←/→:-/+#{Vocab.key_l}/#{Vocab.key_r}:Big -/+", + "Restore defaults.", + "Return." + ] + end + + #-------------------------------------------------------------------------- + # ● パーティ編成画面のヘルプメッセージ + #-------------------------------------------------------------------------- + def party_edit + [ + "#{Vocab.key_a}:Remove", + "#{Vocab.key_x}:Status", + "#{Vocab.key_y}:Sort", + "#{Vocab.key_z}:Warp To", + ] + end + + #-------------------------------------------------------------------------- + # ● 転職画面のヘルプメッセージ + #-------------------------------------------------------------------------- + def job_change + ["#{Vocab.key_y}/#{Vocab.key_z}:Sort"] + end + + #-------------------------------------------------------------------------- + # ● スロット画面の操作説明テキスト + #-------------------------------------------------------------------------- + def slot_description + { + :stand => "→:Increase Wager #{Vocab.key_c}:Spin Slots\n←:Decrease Wager #{Vocab.key_b}:Quit", + :play => "#{Vocab.key_c}:Stop Reel" + } + end + + #-------------------------------------------------------------------------- + # ● ショップ画面の装備品情報変更テキスト + #-------------------------------------------------------------------------- + def shop_equip_change + "←/→:Info Change" + end + + #-------------------------------------------------------------------------- + # ● ショップ画面の装備品性能比較テキスト + #-------------------------------------------------------------------------- + def shop_param_compare + "#{Vocab.key_x}:Stat Change" + end + + #-------------------------------------------------------------------------- + # ● 性能差比較 装備情報ウインドウのボタン説明1 + #-------------------------------------------------------------------------- + def item_info_change + "#{Vocab.key_x}:Info Change" + end + + #-------------------------------------------------------------------------- + # ● 性能差比較 装備情報ウインドウのボタン説明2 + #-------------------------------------------------------------------------- + def item_info_exit + "#{Vocab.key_x}:Close" + end + + #-------------------------------------------------------------------------- + # ● 図鑑画面のヘルプメッセージ + #-------------------------------------------------------------------------- + def library + { + :blank => "This entry is blank.", + :discovery => "This entry's details are unknown.", + :return_top => "Return to top.", + :close_lib => "Close and return to last screen.", + :btn_detail => "#{Vocab.key_c}:View Details", + :btn_column => "↑/↓:Select", + :btn_jump => "#{Vocab.key_l}/#{Vocab.key_r}:Jump", + :btn_page => "←/→:Page", + :btn_scroll => "#{Vocab.key_y}/#{Vocab.key_z}:Scroll Text", + :btn_equip => "#{Vocab.key_x}:Equip Info" + } + end + + #-------------------------------------------------------------------------- + # ● 装備品情報変更テキスト + #-------------------------------------------------------------------------- + def equip_info_change + "←/→,#{Vocab.key_l}/#{Vocab.key_r}:Switch info page" + end + + #-------------------------------------------------------------------------- + # ● 装備品情報変更テキスト + #-------------------------------------------------------------------------- + def equip_info_next + "#{Vocab.key_x}:Next page" + end + + #-------------------------------------------------------------------------- + # ● 装備品情報変更テキスト + #-------------------------------------------------------------------------- + def equip_info_hide + "#{Vocab.key_b}:Close info page" + end + end +end + +#============================================================================== +# ■ SceneManager +#============================================================================== +module SceneManager + class << self + attr_writer :background_bitmap + attr_accessor :stack + + #-------------------------------------------------------------------------- + # ○ 実行 + #-------------------------------------------------------------------------- + def run + Audio.setup_midi if use_midi? + # ~ Audio.init_basic + Audio.reset_sound + DataManager.init + LibraryH::Manager.init + @scene = first_scene_class.new + @scene.main while @scene + end + + #-------------------------------------------------------------------------- + # ● シーンスタックへのプッシュ + #-------------------------------------------------------------------------- + def push(scene) + @stack.push(scene.new) + end + + #-------------------------------------------------------------------------- + # ○ 背景として使うためのスナップショット作成 + #-------------------------------------------------------------------------- + def snapshot_for_background + @background_bitmap.dispose if @background_bitmap + @background_bitmap = Graphics.snap_to_bitmap + # @background_bitmap.blur + end + end +end + +#============================================================================== +# ■ ShowKey_Help +#============================================================================== +module ShowKey_Help + class << self + #-------------------------------------------------------------------------- + # ● 各画面 + #-------------------------------------------------------------------------- + def lr_scroll + "#{Vocab.key_l}/#{Vocab.key_r}:Page Scroll" + end + + #-------------------------------------------------------------------------- + # ● アイテムを表示する画面 + #-------------------------------------------------------------------------- + def favorite_item(state) + "#{Vocab.key_y}:#{state ? "Remove" : "Save"} favorite" + end + + #-------------------------------------------------------------------------- + # ● アイテムを表示する画面 + #-------------------------------------------------------------------------- + def favorite_mode(mode) + "#{Vocab.key_z}:#{mode ? "Display all but" : "Display only"} favorites" + end + + #-------------------------------------------------------------------------- + # ● 装備を表示する画面 + #-------------------------------------------------------------------------- + def equip_info + "#{Vocab.key_x}:Equip Info" + end + + #-------------------------------------------------------------------------- + # ● キャラごとの画面 + #-------------------------------------------------------------------------- + def lr_actor + "#{Vocab.key_l}/#{Vocab.key_r}:Switch Character" + end + + #-------------------------------------------------------------------------- + # ● スキル画面 + #-------------------------------------------------------------------------- + def stype_disable + "#{Vocab.key_a}:Hide in Battle + Disable in Auto Mode" + end + + #-------------------------------------------------------------------------- + # ● スキル画面 + #-------------------------------------------------------------------------- + def stype_move + "#{Vocab.key_x}+↑↓:Sort" + end + + #-------------------------------------------------------------------------- + # ● 装備画面 + #-------------------------------------------------------------------------- + def equip_shift + "#{Vocab.key_y}:Remove Equip" + end + + def equip_stone + "#{Vocab.key_a}:Switch Gem" + end + #-------------------------------------------------------------------------- + # ● アビリティ画面 + #-------------------------------------------------------------------------- + def ability_shift_all + "#{Vocab.key_a}:Remove all Abilities" + end + + #-------------------------------------------------------------------------- + # ● アビリティ画面 + #-------------------------------------------------------------------------- + def ability_shift + "#{Vocab.key_a}:Remove Ability" + end + + #-------------------------------------------------------------------------- + # ● ショップ画面の装備品情報変更テキスト + #-------------------------------------------------------------------------- + def shop_equip_change + Help.shop_equip_change + end + + #-------------------------------------------------------------------------- + # ● ショップ画面の装備品性能比較テキスト + #-------------------------------------------------------------------------- + def shop_param_compare + Help.shop_param_compare + end + + #-------------------------------------------------------------------------- + # ● 秘石画面 + #-------------------------------------------------------------------------- + def stone_shift_all + "#{Vocab.key_a}:Remove All Gems" + end + + #-------------------------------------------------------------------------- + # ● 秘石画面 + #-------------------------------------------------------------------------- + def stone_shift + "#{Vocab.key_a}:Remove Gem" + end + + def stone_change_scene + "←/→:Gem Equip Screen" + end + end +end + +module ShowKey_HelpWindow + def post_start + create_show_key_sprite + super + end + + #-------------------------------------------------------------------------- + # ● 簡易操作説明スプライトの作成 + #-------------------------------------------------------------------------- + def create_show_key_sprite + @show_key_sprite = Sprite_ShowKey.new(show_key_sprite_window) + update_show_key_sprite + end + + #-------------------------------------------------------------------------- + # ● 簡易操作説明の更新 + #-------------------------------------------------------------------------- + def set_text_show_key_sprite(text) + @show_key_sprite.set_text(text) + end + + #-------------------------------------------------------------------------- + # ● 簡易操作説明ウィンドウ + #-------------------------------------------------------------------------- + def show_key_sprite_window + @help_window + end + + #-------------------------------------------------------------------------- + # ● フレーム更新 + #-------------------------------------------------------------------------- + def update + super + update_show_key_sprite + end + + #-------------------------------------------------------------------------- + # ● 簡易操作説明の更新 + #-------------------------------------------------------------------------- + def update_show_key_sprite + set_text_show_key_sprite(show_key_text) + end + + #-------------------------------------------------------------------------- + # ● 終了処理 + #-------------------------------------------------------------------------- + def terminate + @show_key_sprite.dispose + super + end +end + +#============================================================================== +# ■ Vocab +#============================================================================== +module Vocab + #-------------------------------------------------------------------------- + # ○ 基本挿替 + #-------------------------------------------------------------------------- + # Evasion = "しかし%sは素早くかわした!" + ActorNoHit = "But %s quickly dodged!" + EnemyNoHit = "But %s quickly dodged!" + ActorNoDamage = "%s took no damage!" + EnemyNoDamage = "%s took no damage!" + Block = "%s blocked the attack with their shield!" + #-------------------------------------------------------------------------- + # ● ユーザ定義 + #-------------------------------------------------------------------------- + Giveup = [ + "Luka yields to temptation and stops fighting!", + "His companions desert and leave him to his fate..." + ] + BindingStart = [ + "%s is bound!", + "%s is being raped!", + "%s is being raped!", + "But %s is already bound!" + ] + TemptationActionFailure = "But Luka has already been defeated!" + + Ability = "Ability" + Shortage = "But there isn't enough %s!" + SkillSealedFailure = "But that skill is sealed!" + ObtainJobExp = "%s job XP gained." + Stealed = "Stole %s from %s!" + StealFailure = "Couldn't steal anything from %s!" + StealedItemEmpty = "%s has nothing to steal!" + Stand = "%s refused to admit defeat!" + Invalidate = "It had no effect on %s!" + DefenseWall = "%s was defended by a wall!" + PayLife = "%s was spent!" + PayLifeFailure = "%s was debilitated!" + OverDriveSuccess = "%s stopped time!" + OverDriveFailure = "But time was already stopped!" + BindResistSuccess = "...and escaped from %s's restraint!" + BindResistFailure = "...but couldn't escape from %s's restraint!" + EternalBindResist = "...but is still being held down by %s!" + PleasureFinished = " came!" + Predation = "%s was devoured!" + ReStoration = "%s absorbed %s!" + ThrowItem = "%s threw a %s!" + PhysicalReflection = "%s reflects the attack!" + NotObtainItem = "%s obtained but you can't carry any more..." + ACTOR_DAMAGE = "%s takes %s %s damage!" + ENEMY_DAMAGE = "%s takes %s %s damage!" + STATE_BOOST = "Condition bonus!!" + EX_CATEGORY_BOOST = "Slayer bonus!!" + + AUTOBATTLE = "Auto" + BATTLE_STATES = "Status" + ENCHANT_STONE = "Gem" + ClassLevel = "Job Level" + TribeLevel = "Race Level" + + module AutoBattle + NORMAL = "Random" + NOT_MP_SKILL = "Conserve MP" + REPEAT = "Repeat" + ATTACK_ONLY = "Normal Attack" + end + + PartySaveMessage = "Which slot do you want to register a party?" + PartyLoadMessage = "Which party do you want to call?" + + class << self + # 能力値 (短) + def params_a(param_id) + ["ATK", "DEF", "MAG", "WIL", "AGI", "DEX"][param_id] + end + + # 仮想キー + def key_a + { :gamepad => "1", :keyboard => "Shift" }[$game_system.conf[:key_text]] + end + + def key_b + { :gamepad => "2", :keyboard => "X" }[$game_system.conf[:key_text]] + end + + def key_c + { :gamepad => "3", :keyboard => "Z" }[$game_system.conf[:key_text]] + end + + def key_x + { :gamepad => "4", :keyboard => "A" }[$game_system.conf[:key_text]] + end + + def key_y + { :gamepad => "5", :keyboard => "S" }[$game_system.conf[:key_text]] + end + + def key_z + { :gamepad => "6", :keyboard => "D" }[$game_system.conf[:key_text]] + end + + def key_l + { :gamepad => "7", :keyboard => "Q" }[$game_system.conf[:key_text]] + end + + def key_r + { :gamepad => "8", :keyboard => "W" }[$game_system.conf[:key_text]] + end + + # パーティコマンド + def giveup + "Give Up" + end + + def shift_change + "Party" + end + + def library + "Library" + end + + def config + "Config" + end + + def all_attack + "Auto Atk" + end + + def item_get_message(item, value, is_display_value = false) + message = item.window_name + if value == 0 + format(NotObtainItem, message) + else + #Below was reordered so the number is leading(before the item that is obtained) + format(ObtainItem,(item.enchant_item? || !is_display_value ? "":" #{value}") + message ) + end + end + end +end diff --git a/mkxp-z/Kawariki-patches/ports/testencode.rb b/mkxp-z/Kawariki-patches/ports/testencode.rb new file mode 100644 index 0000000..f4f2db2 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/testencode.rb @@ -0,0 +1,1078 @@ +# =========================================================================== +# ★ WF-RGSS Scripts ★ +# 共通スクリプト(XP/VX/VXAce両対応版) +# バージョン : rev-29 (2013-3-26) +# 作者 : A Crying Minister (WHITE-FLUTE) +# サポート先URI: http://www.whiteflute.org/wfrgss/ +# --------------------------------------------------------------------------- +# 機能: +# ・このセクションより後ろのスクリプトでSyntaxError が発生しても +# 別の例外にすりかえられるため、詳細情報が削除されなくなります。 +# (VXAceでは標準で削除されなくなりました。) +# ・SyntaxErrorの情報をファイルに書き出します。 +# ・不用意なデバッグモードへの遷移を防止します。 +# (元からデバッグモードの場合は問題ありません。) +# ・不正なデバッグモードへの遷移を阻害します。 +# ・デバッグコンソールを表示しデバッグしやすくします。 +# --------------------------------------------------------------------------- +# 影響: +# ・外部コマンド実行など一部の組み込み関数が未定義になります。 +# ・例外 SyntaxError は作成できません。(XP/VX) +# (※ ScriptSyntaxError にすりかえられます。) +# --------------------------------------------------------------------------- +# 設置場所:一番最初のセクション(Game_Temp(XP)/モジュール(VX/VXAce)よりも前) +# 必要スクリプト: +# ・共通実行スクリプト(VXAce は、Exit-EXで置き換える事ができます。) +# 注意事項: +# 共通実行スクリプトをMainセクションに上書きして利用することを推奨します。 +# =========================================================================== +BEGIN { + + module WFRGSS +# ------------------------------------------------------------------------- +# ● 調整ポイント +# ------------------------------------------------------------------------- +# デバッグモード遷移を阻害するかを設定します。 +# 確実に保護できるわけではありませんので過信は禁物です。 + +DEBUG_CHECK = true + +# ------------------------------------------------------------------------- +# デバッグモード時、デバッグコンソールを表示します。 +# (VXAceでは設定に関わらず標準装備されています。) + +DEBUG_CONSOLE = false + +# ------------------------------------------------------------------------- +# リリースモード時、デバッグメッセージを消去します。 + +DELETE_DEBUG_MESSAGE = false + +# ------------------------------------------------------------------------- +# XPのみ。 デバッグフラグをVX/VXAce準拠に修正するか? +# (!)スクリプト対応が必要となります。 + +DEBUG_FLAG_CHANGE = false + + +# ------------------------------------------------------------------------- +# 調整ポイント終わり +# ------------------------------------------------------------------------- +end + + + +# --------------------------------------------------------------------------- +# ● 例外定義 +# --------------------------------------------------------------------------- + +# 継承例外 +class InheritanceError < Exception;end + +# Mix-in 例外 +class IncludeError < Exception;end + +# 仮想クラス、メソッドの使用 +class AbstractError < NotImplementedError;end + +# ◆ バグ検出 +# バグ以外、絶対にありえない状態になったときに発生させてください。 +# ※ バグ報告を行うためのものなので途中で破棄してはいけません。 +class BugDetected < Exception;end + +# ◆ 内部バグ報告用 +# これは、WFRGSS でバグしかありえない状態に陥ったときに発生します。 +# ※ バグ報告を行うためのものなので途中で破棄してはいけません。 +class InternalBugDetected < BugDetected;end + +# イベント階層が深すぎる場合 +class EventDeepError < SystemStackError;end + +# 初期化失敗 +class InitializeError < TypeError;end + +# アサーション失敗 +class AssertionError < Exception;end + +# ◆ 例外 Hangup +# ハングアップしたとみなされたときに投げられる例外。 +# 深い階層からでは捉えられないことがあるため、注意が必要 +# RPGVX/VXAceにはありませんが、XPとの互換性を保つため、設定しています。 +class Hangup < Exception;end + +# ◆ 例外 Reset +# Graphics.update の呼び出し時にF12が押されていると投げられる例外。(XP) +# F12 が押されていると投げられる例外(VX) +# これがセクション外に出ると +# 最初のセクションから再び実行をやり直そうとします。 +# この操作は、組み込みクラスのメソッドにて、 +# alias や undef が使われていると +# 不具合の元になります。 +class Reset < Exception;end + +# セキュリティ違反 +class SecurityHazard < Exception;end + +# RPGXP互換性のため +if WFRGSS::DEBUG_FLAG_CHANGE +$TEST = $DEBUG +$DEBUG = false +else +$TEST = false unless $TEST +end + +module WFRGSS +@@debug = ($TEST or $DEBUG) +def self.check +debug = ($TEST or $DEBUG) +if debug != @@debug or (@@debug and + (FileTest.file?("Game.rgssad") or FileTest.file?("Game.rgss2a") or + FileTest.file?("Game.rgss3a"))) +raise(SecurityHazard ,"Insecure - debug flag can't be change.") +end +end +end + +# --------------------------------------------------------------------------- +# ◆◆ デバッグモード阻止対策 +# --------------------------------------------------------------------------- +# デバッグモード防止策 +if defined? untrace_var +undef untrace_var +# デバッグモードでない場合、デバッグモードへの遷移を禁止する +proc = Proc.new { + if $DEBUG + $DEBUG = false + $TEST = false + $BTEST = false + raise(SecurityHazard ,"Insecure - debug flag can't be change.", caller(1)) + end + } +trace_var( :$DEBUG , proc ) +trace_var( :$-d , proc ) # $DEBUG の別名 +proc = nil + +# デバッグモードでない場合、デバッグモードへの遷移を禁止する +proc = Proc.new { + if $TEST + $DEBUG = false + $TEST = false + $BTEST = false + raise(SecurityHazard ,"Insecure - debug flag can't be change.", caller(1)) + end + } +trace_var( :$TEST , proc ) # RPGVX デバッグフラグ +proc = nil + +# 戦闘テストで無い場合、戦闘テストへの遷移を禁止する +proc = Proc.new { + if $BTEST + $DEBUG = false + $TEST = false + $BTEST = false + raise(SecurityHazard ,"Insecure - battle test flag can't be change.", + caller(1)) + end + } +trace_var( :$BTEST , proc ) +proc = nil +end + +unless defined? ScriptSyntaxError +# RGSSでは、本来持っているSyntaxErrorの詳細情報をすべて削除してしまい、 +# デバッグが非常に困難になってしまいます。 +# そのために用意されている例外です。 +# ※特にイベントスクリプト上でSyntaxErrorを出すととても悲惨なことに… +# 「スクリプト実行中に SyntaxError が発生しました。」だけで +# すぐに修正できるのでしょうか? +class ScriptSyntaxError < Exception;end +# ------------------------------------------------------------------------- +# ◆ 組み込み例外クラス SyntaxError +# ------------------------------------------------------------------------- +# RGSSでは、本来持っているSyntaxErrorの詳細情報をすべて削除してしまうため、 +# SyntaxError のコンストラクタを乗っ取って別の例外にすりかえています。 +# ※ SyntaxError のインスタンスは作成できません。 +class SyntaxError < ScriptError +ERROR = Hash.new +end +class << SyntaxError +#-------------------------------------------------------------------------- +# ◆ (!※継承禁止※!) +#-------------------------------------------------------------------------- +def SyntaxError.inherited( subclass ) +raise( InheritanceError , + "can't inherit class #{subclass} from #{self}" , caller(1) ) +end +private_class_method(:inherited) +unless defined? BasicObject +undef allocate +undef new +#------------------------------------------------------------------------ +# ◆ 強制的に別の例外にすりかえる +#------------------------------------------------------------------------ +def new( errors , *args ) +error_message = make_error_message(errors) +error_message_dump(error_message) +ScriptSyntaxError.new(error_message) +end +else +alias __wfrgss__new__ new unless $! +def new( errors , *args ) +error_message = make_error_message(errors) +error_message_dump(error_message) +__wfrgss__new__(errors) +end +end +private +#---------------------------------------------------------------------------- +# ◆(内部専用)◆ エラーメッセージ作成 +#---------------------------------------------------------------------------- +def make_error_message( error_message ) +extra = "" +unless (error_message[/^(?:Section)?{?(\d+)}?:(\d+):/]).nil? +nums = $1.to_i # セクションを拾い出した場合 +extra = $RGSS_SCRIPTS.at(nums).at(1) +unless $2.nil? +lines = $2.to_i +unless ($RGSS_SCRIPTS.at(nums).at(3)).nil? +details = show_details_source_code( nums , lines ) +unless details.empty? +error_message += "\n** script source code:\n#{details}" +else +error_message += +"\n** script source code:#{lines} unknown data.\n" +end +end +end +end +error_message.gsub!(/^(?:Section)?{?(\d+)}?:/){"( #{extra} ):#{$1}:lines "} +error_message +end +#-------------------------------------------------------------------------- +# ◆(内部専用)◆ 情報を書きこみ +#-------------------------------------------------------------------------- +def error_message_dump(error_message) +e = Zlib::crc32(error_message) +return if SyntaxError::ERROR[e] +SyntaxError::ERROR[e] = true +File.open("syntaxerror.txt","a") do | file | +file.write("*SyntaxError - " + + (Time.now).strftime("%Y-%m-%d %H:%M:%S (%A)") + "\n") +file.write( "#{error_message}\n" ) +end +end +#-------------------------------------------------------------------------- +# ◆(内部専用)◆ ソースコード位置読み込み +#-------------------------------------------------------------------------- +def show_details_source_code( nums , lines ) +details = "" +splitstr = ($RGSS_SCRIPTS.at(nums).at(3)).split(/\r\n/) +if lines < 4 +ranges = ( 0...( lines + 3 )) +else +ranges = (( lines - 4 )...( lines + 3 )) +end +for i in ranges +unless (splitstr.at(i)).nil? +if i != (lines - 1) +details += "|" + splitstr.at(i) + "\n" +else +details += ">" + splitstr.at(i) + "\n" +end +else +details += "[End of File]\n" +break +end +end +splitstr = nil +rpgvxace? ? details.force_encoding("UTF-8") : details +end +end + + +# ------------------------------------------------------------------------- +# ◆ RGSS 組み込みモジュール Graphics +# ------------------------------------------------------------------------- + +class << Graphics +#------------------------------------------------------------------------ +# ◆ リセットを無視する Graphics.update +#------------------------------------------------------------------------ +def Graphics.safe_update +begin +update +rescue Reset +# リセット操作を無視する。 +# ただしカーソルアニメーションなどが巻き戻される。 +nil +end +end + +if WFRGSS::DEBUG_CHECK +#------------------------------------------------------------------------ +# ◆ Graphics.update にチェックを加える +#------------------------------------------------------------------------ +alias ___original__update___ update +def Graphics.update +WFRGSS.check +___original__update___ +end +private(:___original__update___) +end +end + +module Graphics +# ----------------------------------------------------------------------- +# ◆ クラス変数 +# ----------------------------------------------------------------------- +@@rate = 0 +@@count = 0 +# ----------------------------------------------------------------------- +# ◆ フレームレートを一時的に変更 +# ----------------------------------------------------------------------- +def self.frame_workset( frame_rates ) +@@rate = Graphics.frame_rate +@@count = Graphics.frame_count +Graphics.frame_rate = frame_rates +end +# ----------------------------------------------------------------------- +# ◆ フレームレートを元に戻す +# ----------------------------------------------------------------------- +def self.frame_restore +cnt = Graphics.frame_count - @@count +cnt *= @@rate +cnt /= Graphics.frame_rate +Graphics.frame_count = @@count + cnt +Graphics.frame_rate = @@rate +end +# ----------------------------------------------------------------------- +# ◆ 解像度横の幅(VX/VXAceでは無視される) +# ----------------------------------------------------------------------- +unless defined? Graphics.width +def self.width +640 +end +end +# ----------------------------------------------------------------------- +# ◆ 解像度縦の幅(VX/VXAceでは無視される) +# ----------------------------------------------------------------------- +unless defined? Graphics.height +def self.height +480 +end +end +end +end + +# --------------------------------------------------------------------------- +# ◆ 組み込みモジュール GC +# --------------------------------------------------------------------------- +module GC +module_function +#-------------------------------------------------------------------------- +# ◆ 安全にガーベージコレクションを行う +#-------------------------------------------------------------------------- +def update_start +Graphics.safe_update +start +Graphics.safe_update +end +end + +} + +unless defined? Abstract + # ----------------------------------------------------------------------------- + # ◆◆ 組み込みクラス Class + # ----------------------------------------------------------------------------- + class Class + # --------------------------------------------------------------------------- + # ◆ クラス変数 + # --------------------------------------------------------------------------- + @@_abstract_class = Hash.new + # --------------------------------------------------------------------------- + # ◆ 抽象クラス追加 + # --------------------------------------------------------------------------- + def self.abstract_class( klass ) + __failure_type_call__( klass ) unless klass.is_a?(Class) + unless @@_abstract_class[klass.__id__] + @@_abstract_class[klass.__id__] = klass + end + end + # --------------------------------------------------------------------------- + # ◆ 抽象クラス判定 + # --------------------------------------------------------------------------- + def self.abstract?( klass ) + @@_abstract_class[klass.__id__] + end + end + + module Abstract + # --------------------------------------------------------------------------- + # ◆ 抽象化 + # --------------------------------------------------------------------------- + def self.included( klass ) + if klass.include?(FinalClass) + # FinalClass モジュールが既にインクルードされている + raise( IncludeError , + "can't include #{klass} from #{self}" , caller(1) ) + end + Class.abstract_class( klass ) + klass.extend(Abstract_Class__) + end + private_class_method(:included) + end + + # --------------------------------------------------------------------------- + # ◆ 抽象化 ※ 特異メソッド専用 ※ + # --------------------------------------------------------------------------- + module Abstract_Class__ + # ------------------------------------------------------------------------- + # ◆ ※ 抽象クラス + # ------------------------------------------------------------------------- + def allocate + return super unless Class.abstract?( self ) + raise( AbstractError , + "class #{self} cannot use it because of the abstraction class.", + caller(1) ) + end + # ------------------------------------------------------------------------- + # ◆ ※ 抽象クラス + # ------------------------------------------------------------------------- + def new( *args ) + return super unless Class.abstract?( self ) + raise( AbstractError , + "class #{self} cannot use it because of the abstraction class.", + caller(1) ) + end + # ------------------------------------------------------------------------- + # ◆ ※ Mix-in 制限 + # ------------------------------------------------------------------------- + def self.included( klass ) + unless self.is_a?(Class) + raise( IncludeError , + "can't include #{klass} from #{self}" , caller(1) ) + end + end + end + + module FinalClass + # --------------------------------------------------------------------------- + # ◆ 継承できないクラス + # --------------------------------------------------------------------------- + def self.included( klass ) + if Class.abstract?( klass ) + # Abstract モジュールが既にインクルードされている + raise( IncludeError , + "can't include #{klass} from #{self}" , caller(1) ) + end + klass.module_eval(<<-End) + def self.inherited( subclass ) + raise( InheritanceError , + sprintf(\"can't inherit class %s from %s\",subclass.inspect, + self.inspect ) , caller(1) ) + end + private_class_method(:inherited) + End + end + private_class_method(:included) + end + + # --------------------------------------------------------------------------- + # ◆ 組み込みモジュール Kernel + # --------------------------------------------------------------------------- + + module Kernel + # --------------------------------------------------------------------------- + # ◆ 危険な関数を未定義に設定 + # --------------------------------------------------------------------------- + # 外部コマンド読み込みなど危険な操作は明示的に禁止にします。 + # ※ これらのコマンドの必要性が(通常は)ないうえ、 + # 危険(システム破壊をもたらすことがある)なため取り外しています。 + # ※ もし、これらを使用する場合は、効果と危険性を考慮してください。 + # --------------------------------------------------------------------------- + # 外部コマンドを実行する。 + undef ` #` # 外部コマンドを実行する。 + undef exec # 外部コマンドを実行する 制御を返さない。 + undef fork # サブシェルを生成する。 + undef system # 外部コマンドを実行する。 + undef abort # 後処理を行わずに強制終了する。後処理が必要な時に困る。 + undef exit! # abort と同じ。 + undef open # コマンドが実行できる。(ファイルを開くならIO.openを用いる) + #-------------------------------------------------------------------------- + private + #-------------------------------------------------------------------------- + # ◆ RPGVX/VXAce かどうか + #-------------------------------------------------------------------------- + def rpgvx? + defined? Graphics.resize_screen + end + #-------------------------------------------------------------------------- + # ◆ RPGVXAce かどうか + #-------------------------------------------------------------------------- + def rpgvxace? + defined? BasicObject + end + #-------------------------------------------------------------------------- + # ◆ RGSSのバージョン + #-------------------------------------------------------------------------- + def rgss_version + rpgvx? ? (rpgvxace? ? 3 : 2 ) : 1 + end + #-------------------------------------------------------------------------- + # ◆ テストモードかどうか + #-------------------------------------------------------------------------- + def debug? + $DEBUG or $TEST + end + #-------------------------------------------------------------------------- + # ◆ デバッグコンソール + #-------------------------------------------------------------------------- + if WFRGSS::DEBUG_CONSOLE and debug? and (not rpgvxace?) + unless $@ + $VERBOSE = true + alias wfrgss_p p + Win32API.new("kernel32","AllocConsole",'v','l').call + $stdout.reopen("conout$","w") + def p(*a) + a.each{|v|$stdout.puts(String.utf82ansi(v.inspect))} + end + end + elsif WFRGSS::DELETE_DEBUG_MESSAGE + unless $@ + alias display_p p + def p(*a) + # 何も出力しない + end + end + end + #-------------------------------------------------------------------------- + # ◆ 例外セット + # 例外を発生させます。 + #-------------------------------------------------------------------------- + # ◆ method_missing と同様 + #-------------------------------------------------------------------------- + def __failure_method_call__( method_name , *args ) + raise( NoMethodError , + "undefined method `#{method_name}' for #{self}" , caller(2) ) + end + #-------------------------------------------------------------------------- + # ◆ オーバーライドが必要 + #-------------------------------------------------------------------------- + def __override_required_call__( method_name , *args ) + raise( AbstractError , + "It is necessary to do override to use method `#{method_name}'" + + " of this class #{self}. " + + "(or, it tried to call the method of a super-class.)" , caller(2)) + end + #-------------------------------------------------------------------------- + # ◆ 例外 TypeErrorを発生させる + #-------------------------------------------------------------------------- + def __failure_type_call__( object_type ) + e = ( object_type.class ).to_s + raise( TypeError , "no implicit conversion from #{e}" , caller(2) ) + end + #-------------------------------------------------------------------------- + # ◆ 例外 RangeErrorを発生させる ( num が Numeric でない場合はBugDetected ) + #-------------------------------------------------------------------------- + def __outof_range_call__( num , str , call_value = 2 ) + if num.is_a?(Numeric) + if str.is_a?(String) + raise( RangeError , "#{num} out of range of #{str}",caller(call_value)) + else + e = ( str.class ).to_s + raise(TypeError,"no implicit conversion from #{e}",caller(call_value)) + end + else + e = ( num.class ).to_s + raise( BugDetected, "[BUG] no implicit conversion from #{e} (TypeError)", + caller(1)) + end + end + #-------------------------------------------------------------------------- + # ◆ 例外 ArgumentErrorを発生させる ( str が String でない場合はBugDetected ) + #-------------------------------------------------------------------------- + def __invalid_value_call__( str , value , call_value = 2 ) + if str.is_a?(String) + raise( ArgumentError , "invalid #{str} for #{value.to_s}") + else + e = ( str.class ).to_s + raise( BugDetected, "[BUG] no implicit conversion from #{e} (TypeError)", + caller(1)) + end + end + #-------------------------------------------------------------------------- + # ◆ 例外 SecurityErrorを発生させる ( str が String でない場合はBugDetected ) + #-------------------------------------------------------------------------- + def __insecure_call__( str ) + if str.is_a?(String) + raise( SecurityError , "Insecure operation - #{str}" , caller(2) ) + else + e = ( str.class ).to_s + raise( BugDetected, "[BUG] no implicit conversion from #{e} (TypeError)", + caller(1)) + end + end + #-------------------------------------------------------------------------- + # ◆ バグを報告する。 ( str が String でない場合も BugDetected ) + #-------------------------------------------------------------------------- + def __report_bug( str ) + if str.is_a?(String) + raise( BugDetected , str , caller(1)) + else + e = ( str.class ).to_s + raise( BugDetected , + "[BUG] no implicit conversion from #{e} (TypeError)" , caller(1) ) + end + end + #-------------------------------------------------------------------------- + # ◆ バグを報告する。(異常な値を検出) + #-------------------------------------------------------------------------- + def __report_bug_invalid_value( value ) + raise( BugDetected , "[BUG] invalid value for #{value}", caller(1)) + end + #-------------------------------------------------------------------------- + # ◆※!※◆ WF-RGSS の バグを報告する。 + # ※ このメソッドは WF-RGSS 内部バグを報告するためのものです。 + # ※ カスタマイズ時にバグを報告したい場合は、 + # __report_bug( str ) を使用してください。 + #-------------------------------------------------------------------------- + def __report_internal_bug( str ) + if str.is_a?(String) + raise( InternalBugDetected , str , caller(1)) + else + e = ( str.class ).to_s + raise( InternalBugDetected , + "[BUG] no implicit conversion from #{e} (TypeError)" , caller(1) ) + end + end + #-------------------------------------------------------------------------- + # ◆ チェック関連メソッド + # 型や範囲をチェックします。 + #-------------------------------------------------------------------------- + # ◆ 型が合わない場合、例外 TypeErrorを発生させる + # (※ klass が異常な場合はBugDetected ) + #-------------------------------------------------------------------------- + def _type_check_( object_type , klass , call_value = 2 ) + begin + return if object_type.is_a?(klass) + e = ( object_type.class ).to_s + raise( TypeError,"no implicit conversion from #{e}") + rescue TypeError => errobj + # 例外の報告位置をすりかえる + if errobj.message[/^no/].nil? + raise( BugDetected , "[BUG] #{errobj.message} (TypeError)",caller(1)) + else + raise( TypeError , errobj.message , caller(call_value)) + end + end + end + #-------------------------------------------------------------------------- + # ◆ Fixnum の範囲を検証する ※範囲が異常なら例外を発生させる + #-------------------------------------------------------------------------- + def _fixnum_range_check( value , minimum , maximum ) + __failure_type_call__( value , 3 ) unless value.is_a?(Fixnum) + unless value >= minimum and value <= maximum + # 範囲外のため、例外を発生させる + call_ary = caller(1) + if call_ary.size > 0 + sections = call_ary.at(0) + str = sections + " ( range #{minimum} .. #{maximum} )" + else + str = "caller method (unknown) ( range #{minimum} .. #{maximum} )" + end + __outof_range_call__( value , str , 3 ) + end + end + #-------------------------------------------------------------------------- + # ◆ Integer の範囲にまとめる (※処理速度が求められる箇所では用いない) + # ※異常範囲が認められていない場合は、↑のメソッドを用いること + #-------------------------------------------------------------------------- + def _integer_range_value( value , minimum , maximum ) + __failure_type_call__( value , 3 ) unless value.is_a?(Integer) + if value < minimum + return minimum + elsif value > maximum + return maximum + else + return value + end + end + # --------------------------------------------------------------------------- + # 可視性を変更 + public + # これ以降はモジュール関数として定義 + module_function + # --------------------------------------------------------------------------- + # ◆ 真 を検証 + # --------------------------------------------------------------------------- + def assert_if( value ) + unless value + raise(AssertionError,"Assertion Failed (false for true)",caller(1)) + end + end + # --------------------------------------------------------------------------- + # ◆ 偽 を検証 + # --------------------------------------------------------------------------- + def assert_unless( value ) + if value + raise(AssertionError,"Assertion Failed (true for false)",caller(1)) + end + end + end + + # --------------------------------------------------------------------------- + # ◆ 組み込みクラス Object + # --------------------------------------------------------------------------- + class Object + # 可視性を変更 + private + #-------------------------------------------------------------------------- + # ◆ Mix-in禁止を実装 + #-------------------------------------------------------------------------- + def self.__cannot_mixin( klass ) + raise( IncludeError , + "can't include #{klass} from #{self}" , caller(2) ) + end + #-------------------------------------------------------------------------- + # ◆ 継承禁止を実装 + #-------------------------------------------------------------------------- + def self.__cannot_inherited( subclass ) + raise( InheritanceError , + "can't inherit class #{subclass} from #{self}" , caller(2) ) + end + #-------------------------------------------------------------------------- + # ◆ 抽象クラス (かならず継承が必要となる) + #-------------------------------------------------------------------------- + def __abstract_class( target ) + klass = self.class + unless target.is_a?(Class) or target.is_a?(Module) + __report_bug("[BUG] class or modules required (TypeError)") + end + until ( klass = klass.superclass ) == Object + return if klass == target + end + raise( AbstractError , + "class #{target} cannot use it because of the abstraction class.", + caller(2) ) + end + end + + # --------------------------------------------------------------------------- + # ◆ 組み込みクラス String + # --------------------------------------------------------------------------- + class String + # --------------------------------------------------------------------------- + # ◆ 定数 + # --------------------------------------------------------------------------- + CP_ACP = 0 # ANSI コード + CP_UTF7 = 65000 # UTF-7 コード + CP_UTF8 = 65001 # UTF-8 コード + EMPTY_STR = "".freeze + #-------------------------------------------------------------------------- + # ◆ クラス変数 + #-------------------------------------------------------------------------- + @@rm = Win32API.new('kernel32','RtlZeroMemory',%w(p l),'l').freeze + # 文字列をワイド文字列(Unicode)にマップするAPI + @@mb2wc = Win32API.new('kernel32', + 'MultiByteToWideChar', %w(i l p i p i), 'l').freeze + # ワイド文字列を新しい文字列にマップするAPI + @@wc2mb = Win32API.new('kernel32', + 'WideCharToMultiByte', %w(i l p i p i p p), 'l').freeze + #-------------------------------------------------------------------------- + # ◆ クリア + #-------------------------------------------------------------------------- + unless rpgvxace? + def clear + @@rm.call( self , self.size ) + self.replace("") + end + end + #-------------------------------------------------------------------------- + # ◆ エンコード + #-------------------------------------------------------------------------- + def encode_by_ + for i in 0...self.size + self[i] = 256 - self[i] + end + end + #-------------------------------------------------------------------------- + # ◆ UTF-8 → ANSI 変換 + #-------------------------------------------------------------------------- + unless rpgvxace? + def self.utf82ansi(str) + to_multibyte(to_widechar(str, CP_UTF8), CP_ACP) + end + else + def self.utf82ansi(str) + result = to_multibyte(to_widechar(str, CP_UTF8), CP_ACP) + result.empty? ? result : result.force_encoding("ASCII-8BIT") + end + end + #-------------------------------------------------------------------------- + # ◆ ANSI → UTF-8 変換 + #-------------------------------------------------------------------------- + unless rpgvxace? + def self.ansi2utf8( str ) + to_multibyte( to_widechar( str , CP_ACP ) , CP_UTF8 ) + end + else + def self.ansi2utf8( str ) + to_multibyte(to_widechar(str,CP_ACP),CP_UTF8).force_encoding("UTF-8") + end + end + private # 可視性を private に変更 + #-------------------------------------------------------------------------- + # ◆(内部専用)◆ ワイド文字列( Unicode ) へ変換 + #-------------------------------------------------------------------------- + def self.to_widechar(str, codepage) + # Convert the string from the specified encoding to UTF-16 (which is Ruby's default wide character encoding) + return "" + end + private_class_method(:to_widechar) + #-------------------------------------------------------------------------- + # ◆(内部専用)◆ ワイド文字列( Unicode ) から 文字列を取得する + #-------------------------------------------------------------------------- + def self.to_multibyte(str, codepage) + # Convert a wide character string (UTF-16) to a multi-byte encoding (e.g., UTF-8 or Windows-1252) + return "" + end + private_class_method(:to_multibyte) + end + + # --------------------------------------------------------------------------- + # ◆ モジュール ErrorLogWriter + # --------------------------------------------------------------------------- + + module ErrorLogWriter + #-------------------------------------------------------------------------- + # ◆(内部専用)◆ Mix-in 禁止 + #-------------------------------------------------------------------------- + def self.included( klass ) + __cannot_mixin( klass ) + end + private_class_method(:included) + + ERROR_SECTION_NUM = (/^(?:Section)?{?(\d+)}?:/).freeze + ERROR_SECTION = (/^(?:Section)?{?\d+}?:/).freeze + DOUBLE_CRLF = (/\n\n/).freeze + # ------------------------------------------------------------------------- + # ◆ エラー情報を記録 ( DEBUG のみ ) + # ------------------------------------------------------------------------- + def self.write( errobj ) + return unless debug? + begin + begin + Graphics.safe_update + rescue SecurityHazard + end + sleep(0.1) + File.open("errors.txt","a") do | file | + file.write("*Error - " + + (Time.now).strftime("%Y-%m-%d %H:%M:%S (%A)") + "\n") + file.write( "Exception : #{errobj.class}\n" ) + file.write( errobj.message ) + unless $@.nil? and $@.empty? + backtrace = "" + for str in $@.dup + unless (str[ERROR_SECTION_NUM]).nil? + extra = $RGSS_SCRIPTS.at($1.to_i).at(1) + str.gsub!(ERROR_SECTION) { "( " + extra + " ):" } + end + backtrace += str + end + file.write( "\ntrace:\n" + $@.inspect + "\n" ) + end + end + rescue Exception => errs + raise( errs , + errs.message + "\n (" + (errobj.class).to_s + " )\n" + errobj.message ) + end + end + end + +end +def hook_exit(arg,arg2,arg3,arg4) + # puts arg,arg2,arg3,arg4 + puts "Exporting [#{arg}] ##{arg2}: #{arg3} #{arg4}" + # Simulate what the hookExit function might do + # puts "Simulated hookExit called with parameter:" + # You can add more simulated behavior here, like logging, altering global variables, etc. +end +module MessageBox + #---------------------------------------------------------------------------- + # ◆(内部専用)◆ Mix-in 禁止 + #---------------------------------------------------------------------------- + def self.included( klass ) + raise( IncludeError , "can't include #{klass} from #{self}" , caller(1) ) + end + private_class_method(:included) + # --------------------------------------------------------------------------- + # ◆ 定数 + # --------------------------------------------------------------------------- + MB_OK = 0x0 # + MB_OKCANCEL = 0x1 # + MB_ABORTRETRYIGNORE = 0x2 # + MB_YESNOCANCEL = 0x3 # + MB_YESNO = 0x4 # + MB_RETRYCANCEL = 0x5 # + + MB_ICONERROR = 0x10 # エラー + MB_ICONQUESTION = 0x20 # 問い合わせ + MB_ICONWARNING = 0x30 # 警告 + MB_ICONINFORMATION = 0x40 # 情報 + MB_SYSTEMMODAL = 0x1000 # システムモーダル + MB_TASKMODAL = 0x2000 # タスクモーダル + MB_TOPMOST = 0x040000 # 最前面 + MB_FATAL = 0x042010 # 致命的エラー用 + MB_WARN = 0x042030 # 警告 + + IDOK = 1 + IDCANCEL = 2 + IDABORT = 3 + IDRETRY = 4 + IDIGNORE = 5 + IDYES = 6 + IDNO = 7 + # --------------------------------------------------------------------------- + # ◆ 内部使用定数 + # --------------------------------------------------------------------------- + ERROR_SECTION_NUM = (/^(?:Section)?{?(\d+)}?:/).freeze + ERROR_SECTION = (/^(?:Section)?{?\d+}?:/).freeze + DOUBLE_CRLF = (/\n\n/).freeze + + begin + # メッセージボックス + @@mb = method(:hook_exit) + rescue Exception + # 取得失敗 + Script_Deleter.finalizer + raise( LoadError , "cannot read modules.(user32.dll)",caller(0)) + end + begin + # ini ファイルから、セクションのキーを取得するAPI + @@gpps = Win32API.new('kernel32', + 'GetPrivateProfileStringA',%w(p p p p l p),'l').freeze + rescue Exception + # 取得失敗 + Script_Deleter.finalizer + raise( LoadError , "cannot read modules.(kernel32.dll)",caller(0)) + end + #-------------------------------------------------------------------------- + # ◆(!要注意メソッド!)◆ メッセージボックスを表示 + # ※警告 :例外 Hangup 対策をかならず行うこと! + #-------------------------------------------------------------------------- + def self.messagebox( message , title , type ) + begin + # 停止対策用 + begin + Graphics.safe_update + rescue SecurityHazard + end + @@mb.call(0, String.utf82ansi( message ), String.utf82ansi( title ),type) + rescue Hangup + nil # !※例外時にここを通過せずに外部に投げられる恐れあり + ensure + # !※例外時にここを通過せずに外部に投げられる恐れあり + begin + Graphics.safe_update + rescue SecurityHazard + end + end + end + #-------------------------------------------------------------------------- + # ◆(!特定外使用禁止!)◆ 致命的エラーを通知 + # ※警告 :例外 Hangup 対策をかならず行うこと! + #-------------------------------------------------------------------------- + def self.fatalerror( errobj ) + begin + ErrorLogWriter.write( errobj ) + error_message , tracer = error_message_setup( errobj ) + # 瞬間消去 + Graphics.transition(0) + Audio.bgm_stop + Audio.bgs_stop + Audio.me_stop + Audio.se_stop + if debug? and ( not (tracer[/^(?:Section)?{?(\d+)}?:(\d+)/]).nil? ) + # セクションを拾い出した場合 + nums = $1.to_i + extra = $RGSS_SCRIPTS.at(nums).at(1) + lines = $2.to_i + tracer = "#{extra} の #{lines} 行目で、\n" + error_message = tracer + error_message + end + title = "\x00" * 256 + @@gpps.call( 'Game' , 'Title' , '' , title , 255 , '.\\Game.ini' ) + title = String.ansi2utf8(title) + title.tr("\x00","") + # マウスカーソルを表示 + Input.mouse_show if Input.method_defined?(:mouse_show) + # メッセージボックス + messagebox( error_message , title , MB_FATAL ) + rescue Hangup + nil # !※例外時にここを通過せずに外部に投げられる恐れあり + end + rescue Hangup + nil + end + # --------------------------------------------------------------------------- + # ◆ 異常終了のメッセージを整形 + # --------------------------------------------------------------------------- + def self.error_message_setup( errobj ) + Graphics.freeze + begin + Graphics.safe_update + rescue SecurityHazard + end + _message = "" + # バックトレースを記憶する + unless $@.nil? or ($@.at(0)).nil? + tracer = ($@.at(0)).dup + # バックトレースを解析する + backtrace = "" + i = 0 + for str in $@.dup + unless (str[ERROR_SECTION_NUM]).nil? + extra = $RGSS_SCRIPTS.at($1.to_i).at(1) + str.gsub!(ERROR_SECTION) { "( " + extra + " ):" } + end + backtrace += str + end + unless errobj.is_a?(SystemStackError) + if rpgvxace? + _message = errobj.message.force_encoding("UTF-8") + + "\n** backtrace:\n" + backtrace + else + _message = errobj.message + "\n** backtrace:\n" + backtrace + end + end + else + tracer = "" # バックトレース取得失敗 + if rpgvxace? + _message = errobj.message.force_encoding("UTF-8") + else + _message = errobj.message + end + end + until (_message[DOUBLE_CRLF]).nil? + _message.gsub!(DOUBLE_CRLF){ "\n" } + end + if debug? + _message = "解決できない例外 " + (errobj.class).inspect + + " が発生したため、処理を継続できません。\n" + + _message + else + _message = "解決できない例外 " + (errobj.class).inspect + + " が発生したため、処理を継続できませんでした。\n" + + "お手数をおかけして申し訳ありません。\n\n詳細情報:\n" + + _message + end + return _message, tracer + end +end + +# RGSS2 環境だと、リセットは阻止できない。 +if $@ and WFRGSS::DEBUG_CONSOLE and debug? + p "例外 Reset が外部へ投げられました。" +end diff --git a/mkxp-z/Kawariki-patches/ports/wxexittest.rb b/mkxp-z/Kawariki-patches/ports/wxexittest.rb new file mode 100644 index 0000000..ed672e7 --- /dev/null +++ b/mkxp-z/Kawariki-patches/ports/wxexittest.rb @@ -0,0 +1,191 @@ +# =========================================================================== +# ★ WF-RGSS Scripts ★ +# Exit-EX 終了処理スクリプト(共通実行スクリプト VXAce版) +# バージョン : rev-2.1 (2012-1-24) +# 作者 : A Crying Minister (WHITE-FLUTE) +# サポート先URI: http://www.whiteflute.org/wfrgss/ +# --------------------------------------------------------------------------- +# 機能: +# ・VXAceの終了処理をXP/VX形式(SystemExit送出)に設定します。 +# ・VXAceに限り、共通実行スクリプトと同様に使えます。 +# --------------------------------------------------------------------------- +# 設置場所 :Mainセクション(一番最後)に上書き +# または、Mainセクションの直前 +# 必要スクリプト: +# ・共通スクリプト +# 必要DLL +# ・WFExit.dll +# 注意事項: +# ▽ 共通スクリプトが必要です。 +# 改造して使用することを推奨しますが、そのまま使ってもOKです。 +# ▽ デバッグモードでエラーを記録する場合、 +# 現在のユーザで書き込みを行えることが必要になります。 +# ▽ スクリプトが実行されます。これ以降のセクションは実行されません。 +#============================================================================== +# ◆ Main ( Execute ) +#------------------------------------------------------------------------------ +#  各クラスの定義が終わった後、ここから実際の処理が始まります。 +#============================================================================== +#module Exit +# # --------------------------------------------------------------------------- +# # ◆ カスタマイズポイント セットアップ処理を記述します。 +# # --------------------------------------------------------------------------- +# def self.setup +# end +# # --------------------------------------------------------------------------- +# # ◆ カスタマイズポイント 解放処理を記述します。 +# # --------------------------------------------------------------------------- +# def self.dispose +# DataManager.save_system +# end +#end +# --------------------------------------------------------------------------- +# ◆ 以下の内容は変更する必要はありません。 +# --------------------------------------------------------------------------- + +#============================================================================== +# ◆ Exit モジュール +#------------------------------------------------------------------------------ +def hook_exit() + # Simulate what the hookExit function might do + # puts "Simulated hookExit called with parameter:" + # You can add more simulated behavior here, like logging, altering global variables, etc. +end +module Exit + # --------------------------------------------------------------------------- + # ◆ 処理実行 + # --------------------------------------------------------------------------- + begin + @@hook = method(:hook_exit) + @@exit = method(:hook_exit) + @@quit = method(:hook_exit) + @@reset = method(:hook_exit) + @@clear = method(:hook_exit) + + #@@hook = Win32API.new('System/WFExit','hookExit','v','l') + #@@exit = Win32API.new('System/WFExit','getToExit','v','l') + #@@quit = Win32API.new('System/WFExit','Quit','v','v') + #@@reset = Win32API.new('System/WFExit','getToReset','v','l') + #@@clear = Win32API.new('System/WFExit','clearReset','v','v') + rescue Exception + raise if $TEST + raise( LoadError , "cannot read modules.(WFExit.dll)") + end + @@hook.call() + # --------------------------------------------------------------------------- + # ◆ 終了を監視する + # --------------------------------------------------------------------------- + def self.toexit + raise SystemExit.new(0) if @@exit.call() == 1 + end + # --------------------------------------------------------------------------- + # ◆ F12リセットを監視する + # --------------------------------------------------------------------------- + def self.toreset + raise RGSSReset if @duration && @duration <= 0 if @@reset.call() == 1 + if @duration && @duration > 0 + @duration -= 1 + @@clear.call() + end + end + # --------------------------------------------------------------------------- + # ◆ 本当の終了処理 + # --------------------------------------------------------------------------- + def self.quit + @@quit.call() + end + # --------------------------------------------------------------------------- + # ◆ リセットカウントをクリア + # --------------------------------------------------------------------------- + def self.clearreset(wait = false) + @@clear.call() + @duration = wait ? 120 : 0 + end +end +# --------------------------------------------------------------------------- +# ◆ 終了監視をセット +# --------------------------------------------------------------------------- +class << Graphics + alias __wfexit_uodate__ update + def Graphics.update + __wfexit_uodate__ + Exit.toexit + Exit.toreset + end +end + +# --------------------------------------------------------------------------- +# ◆ セットアップをセット +# --------------------------------------------------------------------------- +class << SceneManager + alias __wfexit_run__ run + #-------------------------------------------------------------------------- + # ● 実行 + #-------------------------------------------------------------------------- + def SceneManager.run + Exit.setup + Exit.clearreset(true) + __wfexit_run__ + end +end + +# --------------------------------------------------------------------------- +# ◆ 処理実行 +# --------------------------------------------------------------------------- + +begin + rgss_main { SceneManager.run } + # 以下、例外処理 +rescue BugDetected, InternalBugDetected => errobj + begin + MessageBox.fatalerror( errobj ) + raise SystemExit.new(1) + rescue Hangup + nil + end + +rescue SyntaxError => errobj + # ------------------------------------------------------------------------- + # ◆ 例外 SyntaxError + # ------------------------------------------------------------------------- + # この例外はバグかセットアップが適切にされていない状況で無い限り、 + # 補足されることはない + begin + raise( BugDetected, + "[FATAL] The invalidated exception was detected. \n\n" + + "Exception:\n#{errobj}") + rescue BugDetected => errobj + begin + MessageBox.fatalerror( errobj ) + raise SystemExit.new(1) + rescue Hangup + nil + end + end +rescue SystemExit + # 終了を補足する。エラーメッセージに書き加えない。 +rescue Exception => errobj + # ------------------------------------------------------------------------- + # ◆ 例外処理 + # 特に指定されていない例外を補足します。 + # ※ rev-2 より、Errno::ENOENT もここで補足します。 + # ------------------------------------------------------------------------- + begin + MessageBox.fatalerror( errobj ) + raise SystemExit.new(1) + rescue Hangup + nil + end + +ensure + # ------------------------------------------------------------------------- + # ● 後処理 + # ------------------------------------------------------------------------- + # 後処理を担当します。 + # スクリプト内容によってはここで解放処理が必要になることがあります。 + Exit.dispose + # ★ ---------------------------------------------------------------------- + Exit.quit # フックを解放する。実行しないと終了しない危険性大 +end + +exit # Mainセクションが後に控えている時に処理が渡らないようにする diff --git a/mkxp-z/Kawariki-patches/preload.rb b/mkxp-z/Kawariki-patches/preload.rb new file mode 100644 index 0000000..ceed237 --- /dev/null +++ b/mkxp-z/Kawariki-patches/preload.rb @@ -0,0 +1,602 @@ +# Kawariki MKXP preload infrastructure + +module Preload + # Kawariki mkxp resources location + Path = File.dirname __FILE__ + + # Require common libs + def self.require(name) + Kernel.require File.join(Path, "libs", name) + end + + module Common + # In RMXP mode, Kernel.print opens message boxes + def print(text) + STDOUT.puts("[preload] " + text.to_s) + end + end + + extend Common + + # ------------------------------------------------------------------------- + # Patches + # ------------------------------------------------------------------------- + class Context + include Common + + def initialize(scripts) + @scripts = scripts + @script_instances = {} + @options = {} + @blacklist = [] + @delay = {} + @script_id_digits = Math.log10(scripts.size).ceil + end + + attr_reader :script_id_digits, :script_loc_format + + # Scripts + def script(i) + @script_instances[i] ||= Script.new self, i, @scripts[i] + end + + def script_count + @scripts.size + end + + def each_script + (0...@scripts.size).each {|i| yield script i} + end + + def last_script + script (script_count - 1) + end + + def script_loc(scriptid) + return script(scriptid).loc + end + + def blacklisted?(script) + @blacklist.include? script.name + end + + def add_script(name, code) + @scripts.pop + @scripts.push [name, "", nil, code] + # TODO: Find an empty script to canibalize instead + end + + # Read options from environment + FalseEnvValues = [nil, "", "0", "no"] + def read_env(env=ENV) + env_bool = ->(name) {!FalseEnvValues.include?(env[name])} + env_str = ->(name) {e = env[name]; e unless e == ""} + env_list = ->(name, delim) {e = env[name]; e.nil? ? [] : e.split(delim)} + + set :dump_scripts_raw, env_str.("KAWARIKI_MKXP_DUMP_SCRIPTS") + set :dump_scripts_patched, env_str.("KAWARIKI_MKXP_DUMP_PATCHED_SCRIPTS") + mark :dont_run_game if env_bool.("KAWARIKI_MKXP_DRY_RUN") + mark :no_font_effects if env_bool.("KAWARIKI_MKXP_NO_FONT_EFFECTS") + @blacklist = env_list.("KAWARIKI_MKXP_FILTER_SCRIPTS", ",") + end + + def read_system(system=System) + # TODO: Non mkxp-z variants + set :mkxp_version, system::VERSION + set :mkxp_version_tuple, (system::VERSION.split ".").map{|d| d.to_i} + + if (self[:mkxp_version_tuple] <=> [2, 4]) >= 0 then + mark :mkxpz_24 + _config = CFG + else + _config = system::CONFIG + end + # set :rgss_version, _config["rgssVersion"].to_i + # puts "jj"+_config["gameFolder"].to_s + # Preload.require "PreloadIni.rb" + # puts "vvv"+ENV["rpgvers"] + set :rgss_version, ENV["rpgvers"].to_i + + # puts self[:zeusrpgver] + # puts vcode + if defined?(RGSS_VERSION) && RGSS_VERSION == "3.0.1" then + # See mkxp-z/mri-binding.cpp + # puts "vvv"+RGSS_VERSION + set :rgss_version, 3 + end + + # FIXME: can this be reliably retrieved from MKXP if set to 0 in config? + if self[:rgss_version] == 0 then + print "Warning: rgssVersion not set in MKXP config. Are you running mkxp directly?" + if RGSS_VERSION == "3.0.1" then + # See mkxp-z/mri-binding.cpp + set :rgss_version, 3 + else + print "Warning: Cannot guess RGSS version. Kawariki should automatically set it correctly." + end + end + if self[:mkxp_version] == "MKXPZ_VERSION" then + print "Note: Using mkxp-z with broken System::VERSION reporting. Cannot detect real mkxp-z version" + set :mkxp_version, "mkxp-z" + end + end + + # Options + def set(sym, value=true) + @options.store sym, value unless value.nil? + end + + def [](sym) + @options[sym] + end + + def mark(*flags) + flags.each{|flag| set flag, true} + end + + def flag?(flag) + @options.key? flag + end + + # Delay + DelaySlots = [:after_patches] + + def delay(slot, &p) + raise "Unknown delay slot #{slot}" unless DelaySlots.include? slot + @delay[slot] = [] unless @delay.key? slot + @delay[slot].push p + end + + def run_delay_slot(slot, *args) + raise "Unknown delay slot #{slot}" unless DelaySlots.include? slot + if @delay[slot] then + @delay[slot].each {|p| p.call(self, *args)} + @delay.delete slot + end + end + end + + class Script + + def initialize(context, i, script) + @context = context + @index = i + @script = script + @log = [] + end + + attr_reader :context + attr_reader :index + + def log(msg=nil) + @log.push msg unless msg.nil? || @log.last == msg + @log + end + + def loc + "##{index.to_s.rjust @context.script_id_digits} '#{name}'" + end + + def [](i) + @script[i] + end + + def name + @script[1] + end + + + def source + @script[3] + end + + def sub!(*p) + @script[3].gsub!(*p) + end + + def source=(code) + @script[3] = code + end + + def load_file(path) + log "replaced with #{File.basename path}" + @script[3] = File.read(path) + end + + def remove + log "removed" + @script[3] = "" + end + + # Extract $imported key only once + # $imported['Hello'] = 1 + # $imported[:Hello] = true + # ($imported ||= {})["Hello"] = true + # Type (String/Symbol) is preserved + ImportedKeyExpr = /^\s*(?:\$imported|\(\s*\$imported(?:\s*\|\|=\s*\{\s*\})?\s*\))\[(:\w+|'[^']+'|"[^"]+")\]\s*=\s*(.+)\s*$/ + + def _extract_imported + match = ImportedKeyExpr.match(source) + @imported_entry = !match.nil? + return unless @imported_entry + @imported_key = match[1][0] == ':' ? match[1][1..].to_sym : match[1][1...-1] + @imported_value = match[2] + end + + def imported_entry? + _extract_imported if @imported_entry.nil? + @imported_entry + end + + def imported_key + _extract_imported if @imported_entry.nil? + @imported_key + end + + def imported_value + _extract_imported if @imported_entry.nil? + @imported_value + end + end + + class Patch + include Common + + def initialize(desc=nil) + @description = desc + @conditions = [] + @actions = [] + @terminal = false + end + + def is_applicable(script) + return @conditions.all? {|cond| cond.call(script)} + end + + def apply(script) + print "Patch #{script.loc}: #{@description}" + @actions.each {|action| action.call script} + @terminal + end + + def eval(script) + apply script if is_applicable script + end + + # --- Conditions --- + # Arbitrary condition + def if?(&p) + @conditions.push p + self + end + + # Source code contains text + def include?(str) + # XXX: maybe should restrict this to the start of the script for performance? + if? {|script| script.source.include? str} + end + + # Source code matches (any) pattern + def match?(*ps) + pattern = Regexp.union(*ps) + if? {|script| script.source.match? pattern} + end + + # Script sets $imported[key] + def imported?(key) + if? {|script| script.imported_key == key} + end + + # Global flag set + def flag?(flag) + if? {|script| script.context.flag? flag} + end + + # --- Actions --- + # Arbitrary action + def then!(&p) + @actions.push p + self + end + + # Run arbitrary action later + def delay!(slot, &p) + @actions.push proc{|script|script.context.delay(slot, &p)} + self + end + + # Substitute text + def sub!(pattern, replacement) + @actions.push proc{|script| script.source.gsub! pattern, replacement} + self + end + + # Set a global flag for later reference + def flag!(*flags) + @actions.push proc{|script| script.context.mark *flags} + self + end + + # Remove the script (terminal) + def remove! + @actions.push proc{|script| script.remove } + @terminal = true + self + end + + # Replace the whole script with a file from ports/ (terminal) + def replace!(filename) + puts filename + @actions.push proc{|script| script.load_file File.join(Path, "ports", filename)} + @terminal = true + self + end + + # Stop processing this script if patch is applicable (terminal) + def next! + @terminal = true + self + end + end + + # ------------------------------------------------------------------------- + # Apply Patches + # ------------------------------------------------------------------------- + class ClassInfo + include Common + + def initialize(name, script, supername) + @name = name + @defs = [[script, supername]] + @superdef = 0 + end + + attr_reader :name + attr_reader :defs + + def first_script + return @defs[0][0] + end + + def super_name + return @defs[@superdef][1] + end + + def super_script + return @defs[@superdef][0] + end + + def first_loc + return first_script.loc + end + + def super_loc + return super_script.loc + end + + def add_definition(script, supername) + if !supername.nil? && super_name != supername then + print "Warning: Redefinition of class '#{name}' in #{script.loc} with inconsistent superclass '#{supername}'. Previous definition in #{super_loc} has superclass '#{super_name}'" + @superdef = @defs.size + end + @defs.push [script, supername] + end + + def inconsistent? + return @superdef > 0 + end + end + + def self.get_class_defs(ctx) + classes = {} + expr = /^class\s+(\w+)\s*(?:<\s*(\w+)\s*)?$/ + ctx.each_script do |script| + # Encoding is all kinds of messed up in RM + e = script.source.encoding + script.source.force_encoding Encoding.find("ASCII-8BIT") + script.source.scan(expr) do |groups| + name, supername = *groups + if !classes.include? name then + classes[name] = ClassInfo.new name, script, supername + else + classes[name].add_definition script, supername + end + end + script.source.force_encoding e + end + return classes + end + + def self.overwrite_redefinitions(ctx) + classes = get_class_defs ctx + classes.each_pair do |name, cls| + if cls.inconsistent? then + print "Eliminating definitions of class '#{name}' before #{cls.super_loc}. First in #{cls.first_loc}" + cls.super_script.sub!(Regexp.new("^(class\\s+#{name}\\s*<\\s*#{cls.super_name}\\s*)$"), + "Object.remove_const :#{name}\n\\1") + end + end + end + + def self.patch_scripts(ctx) + ctx.each_script do |script| + # Remove blacklisted scripts + if ctx.blacklisted? script then + print "Removed #{script.loc}: Blacklisted" + script.remove + next + end + # Encodings are a mess in RGSS. Can break Regexp matching + e = script.source.encoding + script.source.force_encoding "ASCII-8BIT" + # Apply patches + Patches.each do |patch| + break if patch.eval script + end + print "Patched #{script.loc}: #{script.log.join(', ')}" if script.log.size > 0 + # Warn if Win32API references in source + if script.source.include? "Win32API.new" then + print "Warning: Script #{script.loc} uses Win32API." + require "Win32API.rb" + end + # Restore encoding + script.source.force_encoding e + end + ctx.run_delay_slot :after_patches + end + + NoFilenameChars = "/$|*#=" + + def self.dump_scripts(ctx, opt) + # Dump all scripts to a folder specified by opt + if ctx.flag? opt then + dump = ctx[opt] + print "Dumping all scripts to %s" % dump + Dir.mkdir dump unless Dir.exist? dump + fn_format = "%0#{ctx.script_id_digits}d%s%s%s" + ctx.each_script do |script| + filename = fn_format % [script.index, + script.name.empty? ? "" : " ", + script.name.tr(NoFilenameChars, "_"), + script.source.empty? ? "" : ".rb"] + File.write File.join(dump, filename), script.source + end + end + end + + # ------------------------------------------------------------------------- + # Logic + # ------------------------------------------------------------------------- + RgssVersionNames = ["Unknown", "XP", "VX", "VX Ace"] + + + + @on_preload = [] + @on_load = [] + @on_boot = [] + @ctx = nil + + def self._run_preload + # Initialize + @ctx = ctx = Context.new $RGSS_SCRIPTS + ctx.read_system + ctx.read_env + + # Preload[:vcode] = vcode + # Preload.Context.set(:vscode, vcode) + # puts vcode + # set :zeusrpgver, vcode + # set :zeusrpgver, vcode + # puts vcode + # set :zeusrpgver, vcode + # print "#{ctx[:zeusrpgver]}" + print "MKXP mkxp-z #{ctx[:mkxp_version]} RGSS #{ctx[:rgss_version]} (#{RgssVersionNames[ctx[:rgss_version]]})\n" + # Run preload hooks + @on_preload.each{|p| p.call ctx} + ctx.each_script do |script| + print "Script ##{script.index}: #{script.name}#{"\t[#{script.imported_key}]" if script.imported_key}" + end + # Patch Scripts + dump_scripts ctx, :dump_scripts_raw + patch_scripts ctx + overwrite_redefinitions ctx if ctx.flag? :redefinitions_overwrite_class + dump_scripts ctx, :dump_scripts_patched + # Try to inject hook after most (plugin) scripts are loaded but before game starts + ctx.last_script.source= "Preload._run_boot\n\n" + ctx.last_script.source + # Done + if ctx.flag? :dont_run_game then + print "KAWARIKI_MKXP_DRY_RUN is set, not continuing to game code" + exit 123 + end + end + + def self._run_load + @on_load.each {|p| p.call @ctx} + end + + def self._run_boot + @on_boot.each {|p| p.call @ctx} + end + + # ------------------------------------------------------------------------- + # Callbacks for user-scripts + # ------------------------------------------------------------------------- + # Register block to be called with preload context + def self.on_preload(&p) + @on_preload.push p + end + + # Register block to be called after patches are applied + def self.on_load(&p) + @on_load.push p + end + + # Register block to be called on RGSS boot + def self.on_boot(&p) + @on_boot.push p + end +end +_config = CFG + +# puts "hhh" +def find_game_ini_in_directory(directory) + # Search for "game.ini" within the specified directory, case-insensitive + files = Dir.glob("#{directory}/Game.ini", File::FNM_CASEFOLD) + + # If the file exists, return the full path + if files.any? + return files.first # Return the full path of the first match + else + return nil # Return nil if no file is found + end +end +game_ini_path = find_game_ini_in_directory(_config["gameFolder"].to_s) +# puts Dir.pwd + + + +def checkini(file_path) + # Check if the file exists + if File.exist?(file_path) + # Read the content of the file + input_string = File.read(file_path, encoding: 'ASCII-8BIT') + + # Match the content of the file and return the appropriate value + # Match the pattern in the input string and return corresponding values + if input_string =~ /rvdata2/ + return 3 + elsif input_string =~ /rvdata/ + return 2 + elsif input_string =~ /rxdata/ + return 1 + else + return 0 # Return nil if none of the patterns match + end + else + puts "File does not exist!" + return nil + end +end +vers = checkini(game_ini_path) + +rgssversioncodes = ["Unknown", ":xp", ":vx", ":vxace"] + +# vcode = +# set :zeusrpgver, vcode +ENV["vcode"] = rgssversioncodes[vers] +puts ENV["vcode"] +ENV["rpgvers"] = vers.to_s +# puts "nbnn"+ENV["vcode"] +# Ensure Zlib is loaded +Kernel.require 'zlib' unless Kernel.const_defined? :Zlib +# Load patch definitions +Kernel.require File.join(Preload::Path, 'patches.rb') +# Inject user scripts +Dir['*.kawariki.rb'].each do |filename| + Preload.print "Loading user script #{filename}" + Kernel.require filename +end +# Apply patches to scripts +Preload._run_preload +# Run load hooks just before control returns to MKXP to run the scripts +Preload._run_load diff --git a/mkxp-z/Kawariki-patches/versions.json b/mkxp-z/Kawariki-patches/versions.json new file mode 100644 index 0000000..9a766af --- /dev/null +++ b/mkxp-z/Kawariki-patches/versions.json @@ -0,0 +1,31 @@ +{ + "$schema": "../versions.schema.json", + "format": 2, + "name": "mkxp", + "common": { + "slug": "{variant}-{version!v}-{platform}" + }, + "versions": [ + { + "variant": "mkxp-z", + "version": [2, 4, 0], + "platforms": ["linux-x86_64"], + "binary": "mkxp-z", + "url": "https://github.com/Orochimarufan/Kawariki/releases/download/mkxp-2.3.0-kk/mkxp-z-{version!v}-{platform}.tar.xz" + }, + { + "variant": "mkxp-z", + "version": [2, 3, 1], + "platforms": ["linux-x86_64"], + "binary": "mkxp-z.x86_64", + "url": "https://github.com/Orochimarufan/Kawariki/releases/download/mkxp-2.3.0-kk/mkxp-z_2.3.1_x64.tar.xz" + }, + { + "variant": "mkxp-z", + "version": [2, 3, 0], + "platforms": ["linux-x86_64"], + "binary": "mkxp-z.x86_64", + "url": "https://github.com/Orochimarufan/Kawariki/releases/download/mkxp-2.3.0-kk/mkxp-z_2.3.0_x64.tar.xz" + } + ] +} \ No newline at end of file diff --git a/mkxp-z/mkxp.json b/mkxp-z/mkxp.json new file mode 100644 index 0000000..c724767 --- /dev/null +++ b/mkxp-z/mkxp.json @@ -0,0 +1,529 @@ +{ + // Lines starting with '//' are comments. + // + // About filesystem paths specified in this config: + // to the directory containing the mkxp executable + // (the default behavior), or relative to the current + // working directory (when compiled with + // -DWORKDIR_CURRENT). All other paths are resolved + "gameFolder": "ggg", + // encrypted archives. Since this is JSON, any + // backslashes in paths need to be escaped (i.e. a + // single backslash becomes a double backslash). If + // that's too much hassle, you can use a single forward + // slash instead (even on Windows). + + // Some influential environment variables, set them to either "1" or "0": + // "MKXPZ_WINDOWS_CONSOLE" + // - Enables/disables the extra console window on Windows. It appears by + // default whenever mkxp-z is started in debug mode. + // "MKXPZ_MACOS_METAL" + // - Setting this will influence the rendering backend used. + // Probably not a good idea to mess with it unless you have some kind of issue. + // OpenGL is default on x86, and Metal is the default on Apple Silicon. + // This takes priority over the config option if it's set. + // "MKXPZ_FOLDER_SELECT" + // - Allows the manual selection of the game's folder at startup. + // Only works on macOS at the moment. + // "MTL_HUD_ENABLED" + // - On macOS 13 (Ventura) and over, provided the Metal backend is used, + // this will enable a full performance HUD containing more details than + // the built-in framerate display. + + + // Specify the RGSS version to run under. + // Possible values are 0, 1, 2, 3. If set to 0, + // mkxp will try to guess the required version + // If this fails, the version defaults to 1. + // (default: 0) + // + // "rgssVersion": 1, + + + // Continuously display average FPS in window title. + // This can always be toggled with F2 at runtime. + // (default: disabled) + // + // "displayFPS": false, + + + // Continuously print average FPS to console. + // This setting does not affect the window title + // FPS display toggled via F2 + // (default: disabled) + // + // "printFPS": false, + + + // Game window is resizable + // (default: enabled) + // + // "winResizable": true, + + + // Start game in fullscreen (this can + // always be toggled with Alt-Enter at runtime) + // (default: disabled) + // + // "fullscreen": false, + + + // Preserve game screen aspect ratio, + // as opposed to stretch-to-fill + // (default: enabled) + // + // "fixedAspectRatio": true, + + + // Apply smooth interpolation when game screen + // is upscaled + // 0: Nearest-Neighbor + // 1: Bilinear + // 2: Bicubic + // 3: Lanczos3 + // 4: xBRZ + // (default: 0) + // + // "smoothScaling": 0, + + + // Apply smooth interpolation when game screen + // is downscaled (same values as smoothScaling) + // (default: 0) + // + // "smoothScalingDown": 0, + + + // Apply smooth interpolation when bitmaps + // are upscaled (same values as smoothScaling) + // (default: 0) + // + // "bitmapSmoothScaling": 0, + + + // Apply smooth interpolation when bitmaps + // are downscaled (same values as smoothScaling) + // (default: 0) + // + // "bitmapSmoothScalingDown": 0, + + + // Apply mipmap interpolation when game screen + // or bitmaps are downscaled (requires + // "smoothScalingDown": 1 or "bitmapSmoothScalingDown": 1) + // (default: false) + // + // "smoothScalingMipmaps": false, + + + // Sharpness when using Bicubic scaling. + // A good starting range is 0 to 100, + // but you may wish to go outside that range in either direction. + // (default: 100) + // + // "bicubicSharpness": 100, + + + // Scaling factor for xBRZ interpolation + // (set to at least the ratio of your window size + // to the game's native resolution) + // (default: 1.0) + // + // "xbrzScalingFactor": 4.0, + + + // Replace the game's Bitmap files with external high-res files + // provided in the "Hires" directory. + // (You'll also need to set the below Scaling Factors.) + // (default: disabled) + // + // "enableHires": false, + + + // Scaling factor for textures (e.g. Bitmaps) + // (higher values will look better if you use high-res textures) + // (default: 1.0) + // + // "textureScalingFactor": 4.0, + + + // Scaling factor for screen framebuffer + // (higher values will look better if you use high-res textures) + // (default: 1.0) + // + // "framebufferScalingFactor": 4.0, + + + // Scaling factor for tileset atlas + // (higher values will look better if you use high-res textures) + // (default: 1.0) + // + // "atlasScalingFactor": 4.0, + + + // Sync screen redraws to the monitor refresh rate + // (default: disabled) + // + // "vsync": false, + + + // Specify the window width on startup. If set to 0, + // it will default to the default resolution width + // specific to the RGSS version (640 in RGSS1, 544 + // in RGSS2 or higher). + // (default: 0) + // + // "defScreenW": 640, + + + // Specify the window height on startup. If set to 0, + // it will default to the default resolution height + // specific to the RGSS version (480 in RGSS1, 416 + // in RGSS2 or higher). + // (default: 0) + // + // "defScreenH": 480, + + + // Override the game window title + // (default: none) + // + // "windowTitle": "Custom Title", + + + // Enforce a static frame rate + // (0 = disabled) + // + // "fixedFramerate": 0, + + + // Skip (don't draw) frames when behind. + // Can be changed at runtime, but this is the + // default value when the game starts. + // (default: disabled) + // + // "frameSkip": false, + + + // Use a fixed framerate that is approx. equal to the + // native screen refresh rate. This is different from + // "fixedFramerate" because the actual frame rate is + // reported back to the game, ensuring correct timers. + // If the screen refresh rate cannot be determined, + // this option is force-disabled. + // This option may be force-disabled at build time. + // (default: disabled) + // + // "syncToRefreshrate": false, + + + // A list of fonts to render without alpha blending. + // (default: none) + // + // "solidFonts": [ + // "Arial", + // "Times New Roman", + // ], + + + // Prefer the use of Metal over OpenGL on macOS. + // This defaults to false under Intel machines, + // and true under ARM/Apple Silicon ones (which + // merely emulate OpenGL anyway) + // + // Try changing this if you have graphics problems. + // Metal is far better, but ANGLE may not initialize correctly + // on some Intel machines. + // + // On Apple Silicon it's probably better to not touch it. + // Emulated OpenGL is buggy, and it will also break things like + // the Steam overlay. + // (default: true if Apple Silicon, false if Intel) + // + // "preferMetalRenderer": true, + + + // Work around buggy graphics drivers which don't + // properly synchronize texture access, most + // apparent when text doesn't show up or the map + // tileset doesn't render at all + // (default: disabled) + // + // "subImageFix": false, + + + // Enable framebuffer blitting if the driver is + // capable of it. Some drivers carry buggy + // implementations of this functionality, so + // disabling it can be used as a workaround. + // Does nothing on macOS. Force-disabled when + // smoothScaling or smoothScalingDown isn't + // Nearest-Neighbor or Bilinear. + // (default: disabled) + // + // "enableBlitting": false, + + + // Limit the maximum size (width, height) of + // most textures mkxp will create (exceptions are + // rendering backbuffers and similar). + // If set to 0, the hardware maximum is used. + // This is useful for recording traces that can + // be played back on machines with lower specs. + // (default: 0) + // + // "maxTextureSize": 0, + + // Scale up the game screen by an integer amount, + // as large as the current window size allows, before + // doing any last additional scalings to fill part or + // all of the remaining window space (or none at all + // if lastMileScaling is disabled). + // If fixedAspectRatio is disabled, the integer scale + // factors in horizontal and vertical direction can differ + // depending on how much space is available, otherwise + // they are forced to the smaller of the two. + // (default: disabled) + // + // "integerScalingActive": false, + + + // When integer scaling is enabled, this option controls + // whether the scaled game screen is further scaled + // (with linear interpolation when smoothScaling is enabled) + // to fill the rest of the game window. + // Note that this option still respects fixedAspectRatio. + // (default: enabled) + // + // "integerScalingLastMile": true, + + + // Set the base path of the game to '/path/to/game' + // (default: executable directory) + // + + + // Use either right or left Alt + Enter to toggle + // fullscreen + // (default: disabled) + // + // "anyAltToggleFS": false, + + + // Enable F12 game reset + // (default: enabled) + // + // "enableReset": true, + + // Enable F1/keybinding menu + // (default: enabled) + // + // "enableSettings": true, + + + // Allow symlinks for game assets to be followed + // (default: disabled) + // + // "allowSymlinks": false, + + + // Organisation / company and application / game + // name to build the directory path where mkxp + // will store game specific data (eg. key bindings). + // If not specified, mkxp-z will use a folder based + // on the name of the game, if possible, defaulting + // to "." for Org and "mkxp-z" for App otherwise. + // (default: none) + // + // "dataPathOrg": "mycompany", + // "dataPathApp": "mygame", + + + // Set the game window icon to 'path/to/icon.png' + // Only functions on Linux. + // (default: none) + // + // "iconPath": "/path/to/icon.png", + + + // Instead of playing an RPG Maker game, + // execute a single plain text script instead + // (default: none) + // + // "customScript": "/path/to/script.rb", + + + // Define raw scripts (e.g. compatibility wrappers) + // to be executed before the actual Scripts.rxdata + // execution starts + // (default: none) + // + // "preloadScript": [ + // "scripts/preload/ruby_classic_wrap.rb", + // "scripts/preload/mkxp_wrap.rb", + // "scripts/preload/win32_wrap.rb", + // ], + "preloadScript": [ + "Kawariki-patches/preload.rb", + ], + + + // Index all accesible assets via their lower case path + // (emulates windows case insensitivity) + // (default: enabled) + // + // "pathCache": true, + + // Add 'rtp1', 'rtp2.zip' and 'game.rgssad' to the asset search path + // (multiple allowed). You can use folders, RGSS archives, and any archive + // formats supported by PhysicsFS; see the compatibility list at: + // https://www.icculus.org/physfs/docs/html/ + // (default: none) + // + "RTP": [ + "RGSS3/RPGVXAce", + "RGSS2/RPGVX", + "RGSS/Standard"], + + // Similar to the RTP option, except items are loaded before + // the game archive and folder, for incremental game updates + // or modding. + // (default: none) + // + // "patches": [ + // "/path/to/patch1.zip", + // "/path/to/patch2", + // ], + + + // Use the script's name as filename in warnings and error messages + // (default: enabled) + // + // "useScriptNames": true, + + + // Font substitutions allow drop-in replacements of fonts + // to be used without changing the RGSS scripts, + // eg. providing 'Open Sans' when the game thinkgs it's + // using 'Arial'. Font family to be substituted and + // replacement family are separated by one sole '>'. + // Be careful not to include any spaces. + // This is not connected to the built-in font, which is + // always used when a non-existing font family is + // requested by RGSS. + // (default: none) + // + // "fontSub": [ + // "Arial>Open Sans", + // "Times New Roman>Liberation Serif", + // ], + + + // Because mkxp is usually distributed as a stand alone + // build, no predefined load paths are initialized + // ($:, $LOAD_PATH) in the MRI backend. With this option, + // they can be specified manually (eg. when using a system + // libruby.so). It is however recommended to statically + // link all required gems into libruby.so. + // (default: none) + // + // "rubyLoadpath": [ + // "/usr/lib64/ruby/", + // "/usr/local/share/ruby/site_ruby", + // ], + + // Determines whether MJIT is enabled. This probably + // won't work unless you also have the header file + // that it needs. Only works with Ruby 2.6 or higher. + // This Ruby feature is experimental. + // (default: false) + // + // "JITEnable": false, + + // Determines what level of verbosity to use when + // logging MJIT events. Starts at 0, which is next + // to nothing. Set it higher to see more. + // (default: 0) + // + // "JITVerboseLevel": 0, + + // Determines how many compiled methods that Ruby + // will keep in its MJIT cache. + // (default: 100) + // + // "JITMaxCache": 100, + + // Determines how many times a function has to be + // called before it is compiled by MJIT. + // (default: 10000) + // + // "JITMinCalls": 10000, + + // Determines whether YJIT is enabled. + // Only works with Ruby 3.1 or higher. + // This Ruby feature is experimental. + // (default: false) + // + // "YJITEnable": false, + + // SoundFont to use for midi playback (via fluidsynth) + // (default: none) + // + // "midiSoundFont": "Audio/BGM/GMGSx.sf2", + + + // Activate "chorus" effect for midi playback + // + // "midiChorus": false, + + + // Activate "reverb" effect for midi playback + // + // "midiReverb": false, + + + // Number of OpenAL sources to allocate for SE playback. + // If there are a lot of sounds playing at the same time + // and audibly cutting each other off, try increasing + // this number. Maximum: 64. + // + // "SESourceCount": 6, + + // Number of streams to open for BGM tracks. If the game + // needs multitrack audio, this should be set to as many + // available tracks as the game needs. Maximum: 16. + // + // "BGMTrackCount": 1, + + + // The Windows game executable name minus ".exe". By default + // this is "Game", but some developers manually rename it. + // mkxp needs this name because both the .ini (game + // configuration) and .rgssad (encrypted data archive) must + // carry the same name minus their extension, and we cannot + // guess the executable's name. + // You could just as well rename them both to "Game.ini" and + // "Game.rgssad", but specifying the executable name here + // is a tiny bit less intrusive. + // + // "execName": "Game", + + // You can define alternate terminology for the different + // inputs recognized by RPG Maker. A, B, C, X, Y, Z, L, and R + // can all be set using this dictionary, and will be displayed + // on the F1 menu. This is only a cosmetic effect, so it will + // have no effect on the game's scripts. + // + // "bindingNames": { + // "c": "Confirm", + // "b": "Cancel", + // "x": ..., + // }, + + + // Dump tile atlas (for debugging purposes) + // (default: false) + // + // "dumpAtlas": false, + +}