[XP] XP Ace Tilemap

Started by KK20, November 02, 2014, 05:53:24 am

Previous topic - Next topic

KK20

For the syntax error in transitions, if you're talking about "unexpected ':', expecting keyword_then" that has nothing to do with the Tilemap and moreso to do with Ruby 1.8 vs 1.9

#BAD
when 1: "First"
when 2: "Second"

#GOOD
when 1 then "First"
when 2 then "Second"

# ALSO GOOD
when 1
  "First"
when 2
  "Second"

Since TDS already has his whens separated with a newline, just delete the colon in front of them.

          when 0, 1, 2, 3, 4
            sprite[3].dispose
          when 5
            sprite[3].each { |el| el.dispose }
            sprite[3] = []


What about anti-lag is not working?

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Kise

July 06, 2017, 04:44:17 am #201 Last Edit: July 06, 2017, 06:45:14 am by Kise
With antilag I get superlass mismatch error.

KK20

Yeah, that would be the superclass mismatch helper script bundled in the XPA package. I'm still looking for a good solution to resolve that. Did you get a notification that a superclass mismatch was found and you should reopen the project? Do that.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Kise

No, i don't get that notification. Program has problem with this line ("TypeError") -

class Sprite_Character < Control_Sprite_Character
 
end

> superclass mismatch for class Sprite_Character.

It seems to work after deleting those two lines.

KK20

In the XPA Configuration, this should be enabled:

  # Checks your scripts for any superclass mismatches and fixes them. This
  # script will run automatically upon test-playing your game. It will make the
  # necessary changes to your scripts and save them directly to your
  # Scripts.rxdata file.
  # Use true or false to enable or disable this script.
  SUPERCLASS_MISMATCH_HELPER = true

Running the project now should fix the superclass mismatch error and the message will pop up saying to reopen your project to apply the fix. You will get a "stack level too deep" error if you try to start a new game.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Kise

July 08, 2017, 06:30:41 am #205 Last Edit: July 08, 2017, 10:08:08 am by Kise
Yeah, that fixes problem with antilag, but also gets rid of every foreign font character - like 'ą, ź' etc.

Btw. Would it be possible to use VX Ace features like blurring effect on XPA?

KK20

Can you explain the situation where those characters are removed/replaced? I tried simulating a superclass mismatch, had the script find and fix the issue, reopened project, and the two characters you provided were still present in another script.

If by blurring effects you mean RGSS3's built in Bitmap#blur method, then yes, it works.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Kise

July 09, 2017, 03:12:08 am #207 Last Edit: July 09, 2017, 03:36:23 am by Kise
That's the script which causes problems.

Spoiler: ShowHide

#==============================================================================
# Text Cache
# by atoa
#==============================================================================
# This script has the objective of storing text bitmap objects in cache.
# By deafult, the RPG Maker draw each character individually from
# scratch, even if the same character is repeated frequently.
# This make bitmap.draw_text really slow.
#
# This script store the character bitmap on cache at the first it's drawn,
# and then use an clone of that bitmap if it's needed again
#
# The main objective of this script is reduce the lag caused by windows
# that are updated constantly or windows with really long text.
#
# The script has one drawback: if the text is bigger than the width set
# for the draw_text, it won't be streched to fit the smaller space.
#==============================================================================

#==============================================================================
# ■ Bitmap
#------------------------------------------------------------------------------
# Classe that handles bitmaps
#==============================================================================

class Bitmap
  #------------------------------------------------------------------------------
  # Draw text
  #     x      : Text Coord X or text rect
  #     y      : Text Coord y or text sting
  #     width  : Text width or text align
  #     height : Text height
  #     str    : Text string
  #     align  : Text align
  #------------------------------------------------------------------------------
  alias text_cache_draw_text draw_text if !method_defined?(:text_cache_draw_text)
  def draw_text(x, y, width = 0, height = 0, str = '', align = 0)
    case x
    when Numeric
      adj = align == 0 ? 0 : align == 1 ? (width / 2) - (text_size(str).width / 2) : width  - text_size(str).width
      i = 0 
      for digit in str.to_s.split('')
        bitmap = check_bitmap(digit)
        blt(adj + x + i, y + 4, bitmap[0], bitmap[1])
        i += bitmap[0].width - 2
      end
    when Rect
      rect  = x
      str   = y
      align = width
      adj = align == 0 ? 0 : align == 1 ? (rect.width / 2) - (text_size(str).width / 2) : rect.width  - text_size(str).width
      i = 0 
      for digit in str.to_s.split('')
        bitmap = check_bitmap(digit)
        blt(adj + rect.x + i, rect.y + 4, bitmap[0], bitmap[1])
        i += bitmap[0].width - 2
      end
    end
  end
  #------------------------------------------------------------------------------
  # Check if bitmap is areay in cache
  #   digit : digit to be drawn
  #------------------------------------------------------------------------------
  def check_bitmap(digit)
    $text_cache ||= {}
    color = [font.color.red, font.color.green, font.color.blue, font.color.alpha]
    key = [digit, color, font.size, font.name]
    if $text_cache[key].nil? or $text_cache[key][0].disposed?
     width = text_size(digit).width + 2
      height = text_size(digit).height + 2
      b = Bitmap.new(width, height)
      $text_cache[key] = [b, b.rect]
      b.font.name = font.name
      b.font.size = font.size
      b.font.color.set(color[0], color[1], color[2], color[3])
      b.text_cache_draw_text(0, 0, width, height, digit)
    end
    return $text_cache[key]
  end
end

#==============================================================================
# ■ Object
#------------------------------------------------------------------------------
# All classes inherit methods from this class
#==============================================================================

class Object
  #------------------------------------------------------------------------------
  # clear text cache
  #------------------------------------------------------------------------------
  def clear_text_cache
    $text_cache ||= {}
    for key in $text_cache.key
      $text_cache[key].dispose
    end
    $text_cache.clear
  end
end


I would like to blur background in menus. I always loved this effect on newer makers. Could you share the code snippet that does that? I'd insert it myself.

LiTTleDRAgo

bitmap = Bitmap.new(32,32)
bitmap.blur

angle    = 180 # 0-360
division = 10  # 2-100
bitmap   = Bitmap.new(32,32)
bitmap.radial_blur(angle,division)

KK20

I made an update to the Superclass Mismatch Helper (SMH) so UTF-8 characters will be displayed properly. Ideally though, you should only run your game with SMH until all superclass errors are resolved.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

KK20

Updated to Version 0.35

Just some bug fixes that were reported earlier, like priority 1 tiles with opacity were drawing over each other, autotile lecture was wrong, and aliasing Game_Map scroll methods to make the script more compatible (namely with LiTTleDRAgo's pixel movement).

It's just the Ruby code that updated. No need to update the DLL if you were using 0.34 (or probably a couple earlier versions before it)

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Kise

Latest version causes invalid scrolling if player gets teleported or loads save with Drago Smooth Scrolling. This issue wasn't present on previous version.

https://littledrago.blogspot.com/2017/03/rgss-drago-smooth-scroller.html

KK20

Laughing and hating myself

While I was working on v0.35, I was also experimenting some map scrolling feature for Zexion. That somehow slipped into the official build and is causing the weird behavior.

Can you try this out?

=begin
================================================================================
XPA Tilemap                                                      Version 0.35
by KK20                                                         
________________________________________________________________________________

[ Version History ]

Ver.      Date            Notes
-----     -----------     ----------------------------------------------------
0.35  ... 01 Aug 2017 ... Bug fixes:
                            - Aliased Game_Map#scroll_down/right methods
                            - 48th autotile was being drawn incorrectly
                            - Custom autotile graphics were not functioning
                            - Translucent tiles with a priority of 1 were being
                              redrawn if screen scrolled up and down a lot
0.34  ... 04 May 2017 ... Bug fixes:
                            - CallBackController was not disposing its contents,
                              causing a memory leak whenever a new Tilemap was
                              created (and eventually crashing the game)
0.33  ... 30 Apr 2017 ... Bug fixes:
                            - Maps with empty autotiles would crash the game
                            - Tilesets without a graphic would crash the game
                            - The creation of empty autotiles would continually
                              spawn disposed Bitmap objects (related to above)
                            - 32x32 autotiles that animated would crash the game
0.32  ... 25 Feb 2017 ... Bug fixes:
                            - Child viewports were having display issues when
                              tone changes were involved
                            - Viewport#flash had missing parameters
                            - Added more compatibility for XP games (thanks
                              LiTTleDRAgo!)
0.31  ... 08 Feb 2017 ... Bug fixes:
                            - Bad logic with border sprites for non-wrapping map
                            - Problem with Plane objects not initialized with a
                              viewport, causing children-creation to crash
                            - Disable Graphics.resize_screen in Hime's script
                              for XP games as this method does not exist nor is
                              the code necessary to "break" the resolution limit
                            - RGSS does not support Array#cover?
                           Changes:
                            - The Resolution module is disabled for XPA
                            - If WEATHER_ADJUSTMENT is false, it will disable
                              all of the RPG::Weather and Game_Screen classes
                              from loading rather than only some methods
                            - Input module for supporting different game window
                              sizes is disabled for XP as this feature does not
                              work in RGSS
0.3   ... 04 Feb 2017 ... Bug fixes:
                            - A graphical bug involving priority tiles if the
                              MAX_PRIORITY_LAYERS is set to less than 5
                            - Loading autotiles in RPG::Cache has been revised
                              for better compatibility
                            - CallBackController added to replace the method
                              implemented in v0.2b which prevented saving games
                           Additions:
                            - Commented out a majority of the anonymous scripter
                              code that breaks the resolution limit for RGSS3;
                              its ramifications are unknown at this point
                            - A new Plane rewrite which is merely just creating
                              more Plane objects
                            - Maps that do not wrap have a high z-level black
                              sprite positioned around the map's borders
                            - New fullscreen and other window size options for
                              XPA games (will not work for XP)
                            - Tilesets that are not divisible by 32 will raise
                              an error rather than crash
                            - Maps with invalid tile ID's (caused when a tile
                              was placed with a taller tileset graphic, but then
                              the map uses a shorter tileset) no longer crash
                              the game
0.13b ... 23 Oct 2016 ... Bug fixes:
                            - Resolutions not divisible by 32 would not show the
                              last row/column of tiles entirely
                           ** Fix added on top of v0.12b, not v0.2b, but is
                           ** included in all versions above this one
0.2b  ... 07 Jun 2016 ... Bug fixes:
                            - Shaking still had issues
                           Additions:
                            - Maps can now wrap by putting [WRAP] in the map
                              name, due to a new way of drawing the map
                            - ...which also fixed the shaking above
                            - ...and can also allow vertical shaking
                            - Added Unlimited Resolution, an RMVXA script with
                              some changes
                            - Changing tileset and autotile bitmaps in-game will
                              not crash (assuming you know how to do that)
                            - Overall cleaning of code
0.12b ... 27 Feb 2016 ... Bug fixes:
                            - Tiles with some transparency and priority were
                              being redrawn continuously on each other
                            - Setting the Plane bitmap to nil would crash
0.11b ... 03 Nov 2014 ... Bug fixes:
                            - Table did not take in parameters upon initialize
                            - Centering of maps now shrinks Viewport sizes
                            - Fixed Tilemap#oy= logic
0.1b  ... 02 Nov 2014 ... Initial release
________________________________________________________________________________

[ Introduction ]

In light of recent discoveries regarding the usage of RGSS3 in RPG Maker XP
games, many users were left with a dilemma in choosing which Tilemap rewrite to
use due to the vast differences between RGSS1's and RGSS3's Tilemap classes
that would cause complications in this transition. I aimed to find the best
Tilemap rewrite and decided that I would have to make my own. Like every other
Tilemap rewrite before it, this implementation is in no ways perfect, boasting
PROs and CONs.

This script is intended to be used for RPG Maker XP games using the RGSS3
library (unofficially coined RPG Maker XP Ace); however, it is entirely
compatible with RPG Maker XP games in the RGSS1 library.
________________________________________________________________________________

[ License ]

This work is protected by the following license:
http://creativecommons.org/licenses/by-nc-sa/3.0/

********************************************************************************

You are free:

to Share - to copy, distribute and transmit the work
to Remix - to adapt the work

Under the following conditions:

Attribution:
You must attribute the work in the manner specified by the author or licensor,
but not in any way that suggests that they endorse you or your use of the work.

Noncommercial:
You may not use this work for commercial purposes.

Share alike:
If you alter, transform, or build upon this work, you may distribute the
resulting work only under the same or similar license to this one.

- For any reuse or distribution, you must make clear to others the license terms
  of this work. The best way to do this is with a link to this web page.

- Any of the above conditions can be waived if you get permission from the
  copyright holder.

- Nothing in this license impairs or restricts the author's moral rights.

********************************************************************************

[ Instructions ]

- Place this script below the default scripts but above Main.
- Move 'XPA_Tilemap.dll' into your project folder (same directory as 'Game.exe')
- Configure values at the start of the script
________________________________________________________________________________

[ Features ]

About the script:
- XP and XPA (RGSS1 and RGSS3) compatible, though designed for XPA
- Define your own custom resolution
- Adds new tilemap features
- Maps that are smaller than the game resolution are automatically centered
- Drawing methods written in C-language, which has faster pixel-by-pixel
   operations than Ruby

Add-ons:
- Customize frame rate animation and define unique patterns for your autotiles
- Remove unnecessary priority layers to boost frames-per-second (FPS)
- Extend the default RPG::Weather class to fit larger screens, or not
- Change the way fullscreen works (or disable it), including a double and half-
   size window option (only for XPA)
- more to add later...
________________________________________________________________________________

[ Compatibility ]

- There have been reports of Screen Flash not working for some users, though I
   have yet to receive any valid proof or ways to reproduce this issue
- Your tileset must have dimensions divisible by 32. The game will raise an
   error otherwise.
________________________________________________________________________________

[ Credits ]

KK20 - Author of this script and DLL
Blizzard - Tester and providing bug fixes
LiTTleDRAgo - Reusing code from his edits to Custom Resolution and bug fixes
Zexion - Tester and morale support
ForeverZer0 - Reusing code from his Custom Resolution script, found here:
                http://forum.chaos-project.com/index.php/topic,7814.0.html
________________________________________________________________________________

[ Contact ]

To contact the author of this script, please visit
                http://forum.chaos-project.com/index.php
               
or send an email to
                        tscrane20@gmail.com
                       
================================================================================
=end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#                                       B E G I N   C O N F I G U R A T I O N
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#-------------------------------------------------------------------------------
# The game window's screen resolution. RPG Maker XP's default is [640, 480].
# Do note that a larger resolution is prone to sprite lag.
# Anything larger than the default resolution will enable the custom Plane class.
#-------------------------------------------------------------------------------
SCREEN_RESOLUTION = [640,480]

#-------------------------------------------------------------------------------
# The largest level of priority your game uses. This value should be between
# 1 and 5. If using a large resolution, lowering the number of priority layers
# will help in reducing the lag.
#-------------------------------------------------------------------------------
MAX_PRIORITY_LAYERS = 5

#-------------------------------------------------------------------------------
# If using a larger resolution than 640x480, the default weather effects will
# not cover the entire map. It is recommended that you set this to true to
# compensate for that, unless you are using some custom weather script that can
# address this.
#-------------------------------------------------------------------------------
WEATHER_ADJUSTMENT = false

#-------------------------------------------------------------------------------
# When the map's display_x/y variables extend beyond its borders, the map wraps
# around to fill in the gaps. Setting this to true will prevent that, showing
# black borders instead.
# If you want some maps to wrap around, putting [WRAP] in a map's name will
# allow this.
# Note that the custom Plane class will be enabled in order to create this
# effect regardless of your resolution size.
#-------------------------------------------------------------------------------
DISABLE_WRAP = false

#-------------------------------------------------------------------------------
# Choose a form of fullscreen for your game. The available choices are:
#  0 = Default RPG Maker fullscreen (changes monitor resolution to 640x480)
#  1 = Stetches game window to player's monitor size (only for XPA)
#  2 = Disable the ability to go into fullscreen
# Please note that if you choose anything other than 0, it disables everything
# on the player's computer from being able to use ALT + ENTER.
#-------------------------------------------------------------------------------
FULLSCREEN_METHOD = 0

#-------------------------------------------------------------------------------
# (only for XPA)
# Button to trigger 2x window size. Disable by setting it to false
#-------------------------------------------------------------------------------
TWICE_SIZE_BUTTON = Input::F5

#-------------------------------------------------------------------------------
# (only for XPA)
# Button to trigger 0.5x window size. Disable by setting it to false
#-------------------------------------------------------------------------------
HALF_SIZE_BUTTON = Input::F6

#-------------------------------------------------------------------------------
# Set the animation frame rate for autotiles. By default, all autotiles will
# update on the 16th frame. You can change that by providing an array of numbers
# that represent how many frames that particular frame of animation will be
# visible for.
# Format:
#   when AUTOTILE_FILENAME then FRAME_DATA
# where FRAME_DATA is an array containing the number of frames that particular
# animation will play for before moving onto the next frame. Be sure to match
# the number of autotile animation frames with the number of elements you put
# into the array.
# Check the examples below.
#-------------------------------------------------------------------------------
def autotile_framerate(filename)
  case filename
  #------------------------------------------------------ START ------
  when '001-G_Water01' then [8, 8, 8, 8]      # Animates twice as fast
  when '009-G2_Water01' then [20, 20, 20, 20] # Animates a bit slower
  when '024-Ocean01' then [32, 16, 32, 16]    # Sine wave effect
  #------------------------------------------------------- END -------
  # Don't touch any of this below
  else
    return nil if filename == ''
    # Generates array of [16, 16, ...] based on autotile width
    # (or nil if not animating autotile)
    w = RPG::Cache.autotile(filename).width
    h = RPG::Cache.autotile(filename).height
    if (h == 32 && w / 32 == 1) || (h == 192 && w / 256 == 1)
      return nil
    else
      return h == 32 ? Array.new(w/32){|i| 16} : Array.new(w/256){|i| 16}
    end
  end
end

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#                                           E N D   C O N F I G U R A T I O N
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FULLSCREEN_METHOD = 0 unless FULLSCREEN_METHOD.between?(0,2)
if FULLSCREEN_METHOD != 0
  # Disable ALT+Enter
  reghotkey = Win32API.new('user32', 'RegisterHotKey', 'LIII', 'I')
  reghotkey.call(0, 1, 1, 0x0D)
end

XPACE = RUBY_VERSION == "1.9.2"

MAX_PRIORITY_LAYERS = 5 unless (1..5).include?(MAX_PRIORITY_LAYERS)

if XPACE
#===============================================================================
# ** Input
#===============================================================================
module Input

  GetActiveWindow = Win32API.new('user32', 'GetActiveWindow', '', 'L')
  SetWindowLong = Win32API.new('user32', 'SetWindowLong', 'LIL', 'L')
  SetWindowPos  = Win32API.new('user32', 'SetWindowPos', 'LLIIIII', 'I')
  GetSystemMetrics = Win32API.new('user32', 'GetSystemMetrics', 'I', 'I')
  GetAsyncKeyState = Win32API.new('user32', 'GetAsyncKeyState', 'I', 'I')
 
  @fullscreenKeysReleased = true
  @current_state = 0
  NORMAL_STATE     = 0
  FULLSCREEN_STATE = 1
  TWICESIZE_STATE  = 2
  HALFSIZE_STATE   = 3
 
  class << self
    alias get_fullscreen_keys update
   
    # Check for window resize buttons
    def update
      enterkey_state = GetAsyncKeyState.call(0x0D)
      # If ALT+ENTER was pressed, but only for modified fullscreen
      if FULLSCREEN_METHOD == 1 && @fullscreenKeysReleased && Input.press?(Input::ALT) && enterkey_state != 0
        @current_state = @current_state == FULLSCREEN_STATE ? NORMAL_STATE : FULLSCREEN_STATE
        @fullscreenKeysReleased = false
        # Changing game window to fullscreen
        if @current_state == FULLSCREEN_STATE
          full_screen_size
        else
          normal_screen_size
        end
      # If button to double the normal window size was pressed
      elsif TWICE_SIZE_BUTTON && Input.trigger?(TWICE_SIZE_BUTTON)
        # Get out of fullscreen if using default method
        simulate_alt_enter if fullscreen?
        # Check if to double or normalize the window
        @current_state = @current_state == TWICESIZE_STATE ? NORMAL_STATE : TWICESIZE_STATE
        if @current_state == TWICESIZE_STATE
          double_screen_size
        else
          normal_screen_size
        end
      # If button to halve the normal window size was pressed
      elsif HALF_SIZE_BUTTON && Input.trigger?(HALF_SIZE_BUTTON)
        # Get out of fullscreen if using default method
        simulate_alt_enter if fullscreen?
        # Check if to halve or normalize the window
        @current_state = @current_state == HALFSIZE_STATE ? NORMAL_STATE : HALFSIZE_STATE
        if @current_state == HALFSIZE_STATE
          half_screen_size
        else
          normal_screen_size
        end
      else
        # Check if ALT or ENTER is released
        @fullscreenKeysReleased = (!Input.press?(Input::ALT) || enterkey_state == 0)
      end
      # Call the alias
      get_fullscreen_keys
    end
   
    # Doubles the window size based on SCREEN_RESOLUTION.
    def double_screen_size
      rw = GetSystemMetrics.call(0)
      rh = GetSystemMetrics.call(1)
      nw = SCREEN_RESOLUTION[0] * 2
      nh = SCREEN_RESOLUTION[1] * 2
      x = (rw - nw) / 2
      y = (rh - nh) / 2
      w = nw + (GetSystemMetrics.call(5) + GetSystemMetrics.call(45)) * 2
      h = nh + (GetSystemMetrics.call(6) + GetSystemMetrics.call(45)) * 2 + GetSystemMetrics.call(4)
      SetWindowLong.call(GetActiveWindow.call, -16, 0x14CA0000)
      SetWindowPos.call(GetActiveWindow.call, 0, x, y, w, h, 0x0020)
    end
   
    # Halves the window size based on SCREEN_RESOLUTION.
    def half_screen_size
      rw = GetSystemMetrics.call(0)
      rh = GetSystemMetrics.call(1)
      nw = SCREEN_RESOLUTION[0] / 2
      nh = SCREEN_RESOLUTION[1] / 2
      x = (rw - nw) / 2
      y = (rh - nh) / 2
      w = nw + (GetSystemMetrics.call(5) + GetSystemMetrics.call(45)) * 2
      h = nh + (GetSystemMetrics.call(6) + GetSystemMetrics.call(45)) * 2 + GetSystemMetrics.call(4)
      SetWindowLong.call(GetActiveWindow.call, -16, 0x14CA0000)
      SetWindowPos.call(GetActiveWindow.call, 0, x, y, w, h, 0x0020)
    end
   
    # Makes game window as large as the monitor's resolution
    def full_screen_size
      rw = GetSystemMetrics.call(0)
      rh = GetSystemMetrics.call(1)
      SetWindowLong.call(GetActiveWindow.call, -16, 0x10000000)
      SetWindowPos.call(GetActiveWindow.call, 0, 0, 0, rw, rh, 0)
    end
   
    # Reverts the game window back to normal size
    def normal_screen_size
      rw = GetSystemMetrics.call(0)
      rh = GetSystemMetrics.call(1)
      x = (rw - SCREEN_RESOLUTION[0]) / 2
      y = (rh - SCREEN_RESOLUTION[1]) / 2
      w = SCREEN_RESOLUTION[0] + (GetSystemMetrics.call(5) + GetSystemMetrics.call(45)) * 2
      h = SCREEN_RESOLUTION[1] + (GetSystemMetrics.call(6) + GetSystemMetrics.call(45)) * 2 + GetSystemMetrics.call(4)
      SetWindowLong.call(GetActiveWindow.call, -16, 0x14CA0000)
      SetWindowPos.call(GetActiveWindow.call, 0, x, y, w, h, 0x0020)
    end
   
    # Simulates the key press of ALT+Enter; called if we need to get in or out
    # of fullscreen even though the player did not press these keys
    def simulate_alt_enter
      keybd = Win32API.new 'user32.dll', 'keybd_event', ['i', 'i', 'l', 'l'], 'v'
      keybd.call(0xA4, 0, 0, 0)
      keybd.call(13, 0, 0, 0)
      keybd.call(13, 0, 2, 0)
      keybd.call(0xA4, 0, 2, 0)
    end
   
    # Check if the game is in default fullscreen
    def fullscreen?
      # We're not using default fullscreen, so this should always be false
      return false if FULLSCREEN_METHOD != 0
      # If current monitor resolution is 640x480, then it is fullscreen
      if GetSystemMetrics.call(0) == 640 && GetSystemMetrics.call(1) == 480
        @current_state = FULLSCREEN_STATE
        return true
      end
    end
   
  end
end

end # if XPACE

if !XPACE
#===============================================================================
# ** Resolution
#===============================================================================
module Resolution
 
  def self.resize_game
    # Set instance variables for calling basic Win32 functions.
    ini = Win32API.new('kernel32', 'GetPrivateProfileStringA','PPPPLP', 'L')
    title = "\0" * 256
    ini.call('Game', 'Title', '', title, 256, '.\\Game.ini')
    title.delete!("\0")
    @window = Win32API.new('user32', 'FindWindow', 'PP', 'I').call('RGSS Player', title)
    set_window_long = Win32API.new('user32', 'SetWindowLong', 'LIL', 'L')
    set_window_pos  = Win32API.new('user32', 'SetWindowPos', 'LLIIIII', 'I')
    @metrics         = Win32API.new('user32', 'GetSystemMetrics', 'I', 'I')
    # Set default size, displaying error if size is larger than the hardware.
    default_size = Resolution.size
    # Apply resolution change.
    x = (@metrics.call(0) - SCREEN_RESOLUTION[0]) / 2
    y = (@metrics.call(1) - SCREEN_RESOLUTION[1]) / 2
    set_window_long.call(@window, -16, 0x14CA0000)
    set_window_pos.call(@window, 0, x, y, SCREEN_RESOLUTION[0] + 6, SCREEN_RESOLUTION[1] + 26, 0)
    @window = Win32API.new('user32', 'FindWindow', 'PP', 'I').call('RGSS Player', title)
  end
  #--------------------------------------------------------------------------
  def self.size
    # Returns the screen size of the machine.
    [@metrics.call(0), @metrics.call(1)]
  end
  #--------------------------------------------------------------------------
end

end # if !XPACE

#===============================================================================
# ** NilClass
#===============================================================================
class NilClass
  unless method_defined?(:dispose)
    def dispose; end
    def disposed?; end
  end
end
#===============================================================================
# ** Bitmap
#===============================================================================
class Bitmap
  attr_accessor :filename
  alias set_filename_of_bitmap initialize
  def initialize(*args)
    # Associate the bitmap with the filename of the graphic; empty string otherwise
    @filename = args.size == 1 ? File.basename(args[0], '.*') : ''
    set_filename_of_bitmap(*args)
  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?
      # Load the autotile graphic.
      orig_bm = self.load_bitmap('Graphics/Autotiles/', filename)
      # Cache each configuration of this autotile.
      new_bm = self.format_autotiles(orig_bm, filename)
      if new_bm != orig_bm
        @cache[key].dispose
        @cache[key] = new_bm
      end
    end
    @cache[key]
  end

  def self.format_autotiles(bitmap, filename)
    if bitmap.height > 32 && bitmap.height < 192
      frames = bitmap.width / 96
      template = Bitmap.new(256*frames,192)
      template.filename = filename
      # 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
#===============================================================================
# ** CallBackController
#===============================================================================
module CallBackController
  @@callback = {}
 
  def self.clear
    @@callback.clear
  end
 
  def self.setup_callback(obj, proc)
    @@callback[obj.object_id] = proc
  end
 
  def self.call(obj, *args)
    @@callback[obj.object_id].call(*args) if @@callback[obj.object_id]
    true
  end
 
  def self.delete(obj)
    @@callback.delete(obj.object_id)
  end
 
end

#===============================================================================
# ** Viewport
#===============================================================================
class Viewport
  attr_accessor :offset_x, :offset_y
 
  alias zer0_viewport_resize_init initialize
  def initialize(x=0, y=0, width=SCREEN_RESOLUTION[0], height=SCREEN_RESOLUTION[1], override=false)
    # Variables needed for Viewport children (for the Plane rewrite); ignore if
    # your game resolution is not larger than 640x480
    @offset_x = @offset_y = 0
    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_RESOLUTION[0], SCREEN_RESOLUTION[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).
    if args[0].is_a?(Rect)
      args[0].x += @offset_x
      args[0].y += @offset_y
      self.rect = args[0]
    else
      args[0] += @offset_x
      args[1] += @offset_y
      self.rect = Rect.new(*args)
    end
  end
end

#===============================================================================
# ** Tilemap
#===============================================================================
class Tilemap
 
  attr_accessor :tileset, :autotiles, :map_data, :priorities, :ground_sprite
  attr_reader :wrapping
  #---------------------------------------------------------------------------
  # Initialize
  #---------------------------------------------------------------------------
  def initialize(viewport = nil)
    # Ensure that all callbacks are removed to prevent memory leaks
    CallBackController.clear
   
    @viewport = viewport
    @layer_sprites = []
    @autotile_frame = []      #[[ANIMATION_DRAW_INDEX, CURRENT_LOGICAL_FRAME], ... ]
    @autotile_framedata = []  #[[DATA_FROM_CONFIGURATION_ABOVE], ... ]
   
    # Ensures that the bitmap width accounts for an extra tile
    # and is divisible by 32
    bitmap_width = ((SCREEN_RESOLUTION[0] / 32.0).ceil + 1) * 32
    # Create the priority layers
    ((SCREEN_RESOLUTION[1]/32.0).ceil + MAX_PRIORITY_LAYERS).times{ |i|
      s = Sprite.new(@viewport)
      s.bitmap = Bitmap.new(bitmap_width, MAX_PRIORITY_LAYERS * 32)
      @layer_sprites.push(s)
    }
   
    # Same reasons as bitmap_width, but for height
    bitmap_height = ((SCREEN_RESOLUTION[1] / 32.0).ceil + 1) * 32
    # Create the ground layer (priority 0)
    s = Sprite.new(@viewport)
    s.bitmap = Bitmap.new(bitmap_width, bitmap_height)
    @ground_sprite = s
    @ground_sprite.z = 0

    # Initialize remaining variables
    @redraw_tilemap = true
    @tileset = nil
    @autotiles = []
    proc = Proc.new { |x,y| @redraw_tilemap = true; setup_autotile(x) }
    CallBackController.setup_callback(@autotiles, proc)
   
    @map_data = nil
   
    @priorities = nil
    @old_ox = 0
    @old_oy = 0
    @ox = 0
    @oy = 0
    @ox_float = 0.0
    @oy_float = 0.0
    @shift = 0
    @wrapping = (!DISABLE_WRAP || (XPAT_MAP_INFOS[$game_map.map_id].name =~ /.*\[[Ww][Rr][Aa][Pp]\].*/) == 0) ? 1 : 0
    create_border_sprites
   
    # Set up the DLL calls
    @@update = Win32API.new('XPA_Tilemap', 'DrawMapsBitmap2', 'pppp', 'i')
    @@autotile_update = Win32API.new('XPA_Tilemap', 'UpdateAutotiles', 'pppp', 'i')
    @@initial_draw = Win32API.new('XPA_Tilemap', 'DrawMapsBitmap', 'pppp', 'i')
    @empty_tile = Bitmap.new(32,32)
    Win32API.new('XPA_Tilemap','InitEmptyTile','l','i').call(@empty_tile.object_id)
    @black_tile = Bitmap.new(32,32)
    @black_tile.fill_rect(0,0,32,32,Color.new(0,0,0))
    Win32API.new('XPA_Tilemap','InitBlackTile','l','i').call(@black_tile.object_id)
   
  end
  #---------------------------------------------------------------------------
  # Setup autotile animation data
  #---------------------------------------------------------------------------
  def setup_autotile(i)
    # Get animation frame rate of the autotile
    bitmap = @autotiles[i]
    frames = bitmap.nil? ? nil : autotile_framerate(bitmap.filename)
    # If autotile doesn't animate
    if frames.nil?
      @autotile_frame[i] = [0,0]
      @autotile_framedata[i] = nil
    else
      # Save the frame rate data
      @autotile_framedata[i] = frames
      # Determine how long one animation cycle takes and indicate at what time
      # the next frame of animation occurs
      total = 0
      frame_checkpoints = []
     
      frames.each_index{|j| f = frames[j]
        total += f
        frame_checkpoints[j] = total
      }
      # Get animation frame for this autotile based on game time passed
      current_frame = Graphics.frame_count % total
      frame_checkpoints.each_index{|j| c = frame_checkpoints[j]
        next if c.nil?
        if c > current_frame
          @autotile_frame[i] = [j, c - current_frame]
          break
        end
      }
    end
  end
  #---------------------------------------------------------------------------
  # Creates four 32-pixel thick black sprites to surround the map. This is
  # only applied to maps that do not have wrapping enabled. This helps those
  # who have screen shaking in their maps.
  #---------------------------------------------------------------------------
  def create_border_sprites
    @border_sprites = []
    return if @wrapping == 1
    for i in 0..3
      s = Sprite.new(@viewport)
      s.z = 99999
      if i % 2 == 0
        b = Bitmap.new(SCREEN_RESOLUTION[0] + 64,32)
        s.x = -32
        s.y = i == 0 ? -32 : $game_map.height * 32
      else
        b = Bitmap.new(32,SCREEN_RESOLUTION[1] + 64)
        s.x = i == 1 ? -32 : $game_map.width * 32
        s.y = -32
      end
      b.fill_rect(0, 0, b.width, b.height, Color.new(0,0,0))
      s.bitmap = b
      @border_sprites.push(s)
    end
  end
  #---------------------------------------------------------------------------
  # Dispose tilemap
  #---------------------------------------------------------------------------
  def dispose
    @layer_sprites.each{|sprite| sprite.dispose}
    @ground_sprite.dispose
    @border_sprites.each{|sprite| sprite.dispose}
    CallBackController.clear
  end
  #---------------------------------------------------------------------------
  # Check if disposed tilemap
  #---------------------------------------------------------------------------
  def disposed?
    @layer_sprites[0].disposed?
  end
  #---------------------------------------------------------------------------
  # Get viewport
  #---------------------------------------------------------------------------
  def viewport
    @viewport
  end
  #---------------------------------------------------------------------------
  # Return if tilemap is visible
  #---------------------------------------------------------------------------
  def visible
    layer_sprites[0].visible
  end
  #---------------------------------------------------------------------------
  # Show or hide tilemap
  #---------------------------------------------------------------------------
  def visible=(bool)
    @layer_sprites.each{|sprite| sprite.visible = bool}
    @ground_sprite.visible = bool
  end
  #---------------------------------------------------------------------------
  # Set tileset
  #---------------------------------------------------------------------------
  def tileset=(bitmap)
    @tileset = bitmap
    if @tileset.width % 32 != 0 || @tileset.height % 32 != 0
      file = bitmap.filename
      raise "Your tileset graphic #{file} needs to be divisible by 32!"
    end
    @redraw_tilemap = true
  end
  #---------------------------------------------------------------------------
  # Set autotiles
  #---------------------------------------------------------------------------
  def autotiles=(array)
    CallBackController.delete(@autotiles)
    @autotiles = array
    proc = Proc.new { |i| @redraw_tilemap = true; setup_autotile(i) }
    CallBackController.setup_callback(@autotiles, proc)
    @redraw_tilemap = true
  end
  #---------------------------------------------------------------------------
  # Set map data
  #---------------------------------------------------------------------------
  def map_data=(table)
    CallBackController.delete(@map_data)
    @map_data = table
    proc = Proc.new { @redraw_tilemap = true }
    CallBackController.setup_callback(@map_data, proc)
    @redraw_tilemap = true
  end
  #---------------------------------------------------------------------------
  # Set map priorities
  #---------------------------------------------------------------------------
  def priorities=(table)
    CallBackController.delete(@priorities)
    @priorities = table
    proc = Proc.new { @redraw_tilemap = true }
    CallBackController.setup_callback(@priorities, proc)
    @redraw_tilemap = true
  end
  #---------------------------------------------------------------------------
  # Get horizontal shift
  #---------------------------------------------------------------------------
  def ox
    @ox + @ox_float
  end
  #---------------------------------------------------------------------------
  # Get vertical shift
  #---------------------------------------------------------------------------
  def oy
    @oy + @oy_float
  end
  #---------------------------------------------------------------------------
  # Shift tilemap horizontally
  #---------------------------------------------------------------------------
  def ox=(ox)
    @ox_float = (ox - ox.to_i) % 1
    @ox = ox.floor
    @border_sprites.each{ |s|
      next if s.bitmap.height == 32
      s.ox = @ox
    }
  end
  #---------------------------------------------------------------------------
  # Shift tilemap vertically
  #---------------------------------------------------------------------------
  def oy=(oy)
    @oy_float = (oy - oy.to_i) % 1
    @oy = oy.floor
    @border_sprites.each{ |s|
      next if s.bitmap.width == 32
      s.oy = @oy
    }
  end
  #---------------------------------------------------------------------------
  # Update tilemap graphics
  #---------------------------------------------------------------------------
  def update; end;
  def draw
    # Figure out what the new X and Y coordinates for the ground layer would be
    x = @old_ox - @ox
    @old_ox = @ox
    x += @ground_sprite.x

    y = @old_oy - @oy
    @old_oy = @oy
    y += @ground_sprite.y

    # No reason to do sprite shifting if we're just redrawing everything
    if !@redraw_tilemap
      # If layers would be too far to the left
      if x < @viewport.ox - 31
        # If still too far, then force redraw
        if x + 32 < @viewport.ox - 31
          @redraw_tilemap = true
        else
          # Shift all layers right by 32 and clear out left-most column
          x += 32
          @ground_sprite.bitmap.fill_rect(0, 0, 32, @ground_sprite.bitmap.height, Color.new(0,0,0,0))
          @layer_sprites.each{|sprite|
            sprite.bitmap.fill_rect(0, 0, 32, sprite.bitmap.height, Color.new(0,0,0,0))
          }
          @shift += 1 # Redraw right column bit-flag (0001)
        end
      # If layers would be too far to the right
      elsif x > @viewport.ox
        # If still too far, then force redraw
        if x - 32 > @viewport.ox
          @redraw_tilemap = true
        else
          # Shift all layers left by 32 and clear out right-most column
          x -= 32
          @ground_sprite.bitmap.fill_rect(@ground_sprite.bitmap.width - 32, 0, 32, @ground_sprite.bitmap.height, Color.new(0,0,0,0))
          @layer_sprites.each{|sprite|
            sprite.bitmap.fill_rect(sprite.bitmap.width - 32, 0, 32, sprite.bitmap.height, Color.new(0,0,0,0))
          }
          @shift += 2 # Redraw left column bit-flag (0010)
        end
      end
      # Apply the change in X to the layers
      if !@redraw_tilemap
        @ground_sprite.x = x
        @layer_sprites.each{|sprite| sprite.x = x}

        # If layers would be too far up
        if y < @viewport.oy - 31
          # If still too far, then force redraw
          if y + 32 < @viewport.oy - 31
            @redraw_tilemap = true
          else
            y += 32
            layer = @layer_sprites.shift
            layer.bitmap.clear
            @layer_sprites.push(layer)
            # Clear out the rows in the layers to prepare for drawing in #update
            width = @layer_sprites[0].bitmap.width
            num = @layer_sprites.size
            (1..MAX_PRIORITY_LAYERS).each{ |index|
              @layer_sprites[num-index].bitmap.fill_rect(0, (index - 1) * 32, width, 32, Color.new(0,0,0,0))
            }
            @shift += 4 # Redraw bottom row bit-flag (0100)
          end
        # If layers would be too far down
        elsif y > @viewport.oy
          # If still too far, then force redraw
          if y - 32 > @viewport.oy
            @redraw_tilemap = true
          else
            y -= 32
            layer = @layer_sprites.pop
            layer.bitmap.clear
            @layer_sprites.unshift(layer)
            # Clear out the rows in the layers to prepare for drawing in #update
            width = @layer_sprites[0].bitmap.width
            (1...MAX_PRIORITY_LAYERS).each{ |index|
              @layer_sprites[index].bitmap.fill_rect(0, (MAX_PRIORITY_LAYERS - 1 - index) * 32, width, 32, Color.new(0,0,0,0))
            }
            @shift += 8 # Redraw top row bit-flag (1000)
          end
        end
        # Apply the change to layers' Y and Z values
        if !@redraw_tilemap
          @ground_sprite.y = y
          @layer_sprites.each_index{ |i| sprite = @layer_sprites[i]
            sprite.y = y - 32 * (MAX_PRIORITY_LAYERS - 1 - i)
            sprite.z = sprite.y + (192 - (5 - MAX_PRIORITY_LAYERS) * 32)
          }
        end
      end
    end

    autotile_need_update = []
    # Update autotile animation frames
    for i in 0..6
      autotile_need_update[i] = false
      # If this autotile doesn't animate, skip
      next if @autotile_framedata[i].nil?
      # Reduce frame count
      @autotile_frame[i][1] -= 1
      # Autotile requires update
      if @autotile_frame[i][1] == 0
        @autotile_frame[i][0] = (@autotile_frame[i][0] + 1) % @autotile_framedata[i].size
        @autotile_frame[i][1] = @autotile_framedata[i][@autotile_frame[i][0]]
        autotile_need_update[i] = true
      end
    end
   
   
    # Stop the update unless redrawing, there is shifting, or an autotile needs to update
    return unless @redraw_tilemap || @shift != 0 || !autotile_need_update.index(true).nil?

    # Set up the array for the priority layers
    layers = [@layer_sprites.size + 1]
    # Insert higher priority layers into the array in order (least to most y-value sprite)
    @layer_sprites.each{|sprite| layers.push(sprite.bitmap.object_id) }
    # Insert ground layer last in the array
    layers.push(@ground_sprite.bitmap.object_id)
    # Load autotile bitmap graphics into array
    tile_bms = [self.tileset.object_id]
    self.autotiles.each{|autotile| tile_bms.push(autotile.object_id) }
    # Store autotile animation frame data
    autotiledata = []
    for i in 0..6
      autotiledata.push(@autotile_frame[i][0])
      autotiledata.push(autotile_need_update[i] ? 1 : 0)
    end
    # Fills in remaining information of other tilemaps
    misc_data = [@ox + @viewport.ox, @oy + @viewport.oy,
      self.map_data.object_id, self.priorities.object_id, @shift,
      MAX_PRIORITY_LAYERS, @wrapping]
   
    # If forcing fresh redraw of the map (or drawing for first time)
    if @redraw_tilemap
      # Initialize layer sprite positions and clear them for drawing
      @ground_sprite.bitmap.clear
      @ground_sprite.x = (@viewport.ox - @viewport.ox % 32) - (@ox % 32)
      @ground_sprite.x += 32 if @ground_sprite.x < @viewport.ox - 31
      @ground_sprite.y = (@viewport.oy - @viewport.oy % 32) - (@oy % 32)
      @ground_sprite.y += 32 if @ground_sprite.y < @viewport.oy - 31

      y_buffer = 32 * (MAX_PRIORITY_LAYERS - 1)
      z_buffer = MAX_PRIORITY_LAYERS * 32 + 32
      @layer_sprites.each_index{|i| layer = @layer_sprites[i]
        layer.bitmap.clear
        layer.x = @ground_sprite.x
        layer.y = @ground_sprite.y - y_buffer + 32 * i
        layer.z = layer.y + z_buffer
      }
      # Make DLL call
      @@initial_draw.call(layers.pack("L*"), tile_bms.pack("L*"), autotiledata.pack("L*"), misc_data.pack("L*"))
    elsif @shift != 0
      # Update for shifting
      @@update.call(layers.pack("L*"), tile_bms.pack("L*"), autotiledata.pack("L*"), misc_data.pack("L*"))
    end
    # Check for autotile updates (at least one autotile needs an update)
    # No need if redrawn tilemap since it already handled the updated autotiles
    if !@redraw_tilemap && !autotile_need_update.index(true).nil?
      @@autotile_update.call(layers.pack("L*"), tile_bms.pack("L*"), autotiledata.pack("L*"), misc_data.pack("L*"))
    end
    # Turn off flag
    @redraw_tilemap = false
    # Reset shift flag
    @shift = 0
  end
 
end
#===============================================================================
# ** Game_Player
#===============================================================================
class Game_Player
 
  CENTER_X = ((SCREEN_RESOLUTION[0] / 2) - 16) * 4    # Center screen x-coordinate * 4
  CENTER_Y = ((SCREEN_RESOLUTION[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 - (SCREEN_RESOLUTION[0]/32.0)) * 128).to_i
    max_y = (($game_map.height - (SCREEN_RESOLUTION[1]/32.0)) * 128).to_i
    $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
#===============================================================================
# ** Game_Map
#===============================================================================
class Game_Map
  alias zer0_map_edge_setup setup
  def setup(map_id)
    zer0_map_edge_setup(map_id)
    # Find the displayed area of the map in tiles. No calcualting every step.
    @map_edge = [self.width - (SCREEN_RESOLUTION[0]/32.0), self.height - (SCREEN_RESOLUTION[1]/32.0)]
    @map_edge.collect! {|size| size * 128 }
    # Change the map center if map is smaller than the resolution
    if $game_map.width < SCREEN_RESOLUTION[0] / 32
      Game_Player.const_set(:CENTER_X, $game_map.width * 128)
    else
      Game_Player.const_set(:CENTER_X, ((SCREEN_RESOLUTION[0] / 2) - 16) * 4)
    end
    if $game_map.height < SCREEN_RESOLUTION[1] / 32
      Game_Player.const_set(:CENTER_Y, $game_map.height * 128)
    else
      Game_Player.const_set(:CENTER_Y, ((SCREEN_RESOLUTION[1] / 2) - 16) * 4)
    end
  end

  alias scroll_down_xpat scroll_down
  def scroll_down(distance)
    pre_alias = @display_y + distance
    scroll_down_xpat(distance)
    @display_y = pre_alias if @display_y == (self.height - 15) * 128
    # Find point that the map edge meets the screen edge, using custom size.
    @display_y = [@display_y, @map_edge[1]].min
  end

  alias scroll_right_xpat scroll_right
  def scroll_right(distance)
    pre_alias = @display_x + distance
    scroll_right_xpat(distance)
    @display_x = pre_alias if @display_x == (self.width - 20) * 128
    # Find point that the map edge meets the screen edge, using custom size.
    @display_x = [@display_x, @map_edge[0]].min
  end

end

# Override set-methods to allow callbacks (necessary for Tilemap)
#===============================================================================
# ** Array
#===============================================================================
class Array
  alias flag_changes_to_set []=
  def []=(x, y)
    flag_changes_to_set(x, y)
    CallBackController.call(self, x, y)
  end
end
#===============================================================================
# ** Table
#===============================================================================
class Table
  alias flag_changes_to_set []=
  def []=(*args)
    flag_changes_to_set(*args)
    CallBackController.call(self, *args)
  end
end

if WEATHER_ADJUSTMENT
#===============================================================================
# ** RPG::Weather
#===============================================================================
class RPG::Weather
 
  alias add_more_weather_sprites initialize
  def initialize(vp = nil)
    add_more_weather_sprites(vp)
    total_sprites = SCREEN_RESOLUTION[0] * SCREEN_RESOLUTION[1] / 7680
    if total_sprites > 40
      for i in 1..(total_sprites - 40)
        sprite = Sprite.new(vp)
        sprite.z = 1000
        sprite.visible = false
        sprite.opacity = 0
        @sprites.push(sprite)
      end
    end
  end
 
  def type=(type)
    return if @type == type
    @type = type
    case @type
    when 1
      bitmap = @rain_bitmap
    when 2
      bitmap = @storm_bitmap
    when 3
      bitmap = @snow_bitmap
    else
      bitmap = nil
    end
    for i in 1..@sprites.size
      sprite = @sprites[i]
      if sprite != nil
        sprite.visible = (i <= @max)
        sprite.bitmap = bitmap
      end
    end
  end
 
  def max=(max)
    return if @max == max;
    @max = [[max, 0].max, @sprites.size].min
    for i in 1..@sprites.size
      sprite = @sprites[i]
      if sprite != nil
        sprite.visible = (i <= @max)
      end
    end
  end
 
  def update
    return if @type == 0
    for i in 1..@max
      sprite = @sprites[i]
      if sprite == nil
        break
      end
      if @type == 1
        sprite.x -= 2
        sprite.y += 16
        sprite.opacity -= 8
      end
      if @type == 2
        sprite.x -= 8
        sprite.y += 16
        sprite.opacity -= 12
      end
      if @type == 3
        sprite.x -= 2
        sprite.y += 8
        sprite.opacity -= 8
      end
      x = sprite.x - @ox
      y = sprite.y - @oy
      if sprite.opacity < 64
        sprite.x = rand(SCREEN_RESOLUTION[0] + 100) - 100 + @ox
        sprite.y = rand(SCREEN_RESOLUTION[0] + 200) - 200 + @oy
        sprite.opacity = 160 + rand(96)
      end
    end
  end
 
end
#===============================================================================
# ** Game_Screen
#===============================================================================
class Game_Screen
  #--------------------------------------------------------------------------
  # * Set Weather
  #     type : type
  #     power : strength
  #     duration : time
  #--------------------------------------------------------------------------
  def weather(type, power, duration)
    @weather_type_target = type
    if @weather_type_target != 0
      @weather_type = @weather_type_target
    end
    if @weather_type_target == 0
      @weather_max_target = 0.0
    else
      num = SCREEN_RESOLUTION[0] * SCREEN_RESOLUTION[1] / 76800.0
      @weather_max_target = (power + 1) * num
    end
    @weather_duration = duration
    if @weather_duration == 0
      @weather_type = @weather_type_target
      @weather_max = @weather_max_target
    end
  end
 
end

end # if WEATHER_ADJUSTMENT
#===============================================================================
# ** Spriteset_Map
#===============================================================================
class Spriteset_Map

  alias init_for_centered_small_maps initialize
  #---------------------------------------------------------------------------
  # Resize and reposition viewport so that it fits smaller maps
  #---------------------------------------------------------------------------
  def initialize
    @center_offsets = [0,0]
    if $game_map.width < SCREEN_RESOLUTION[0] / 32
      x = (SCREEN_RESOLUTION[0] - $game_map.width * 32) / 2
    else
      x = 0
    end
    if $game_map.height < SCREEN_RESOLUTION[1] / 32
      y = (SCREEN_RESOLUTION[1] - $game_map.height * 32) / 2
    else
      y = 0
    end
    init_for_centered_small_maps
    w = [$game_map.width  * 32 , SCREEN_RESOLUTION[0]].min
    h = [$game_map.height * 32 , SCREEN_RESOLUTION[1]].min
    @viewport1.resize(x,y,w,h)
  end
  #---------------------------------------------------------------------------
  # Puts the tilemap update method at the end, ensuring that both
  # @tilemap.ox/oy and @viewport1.ox/oy are set.
  #---------------------------------------------------------------------------
  alias update_tilemap_for_real update
  def update
    update_tilemap_for_real
    @tilemap.draw
  end
end

# The following script will only be enabled if the resolution is bigger than the
# default OR if the game does not want certain maps to wrap around.
if DISABLE_WRAP || SCREEN_RESOLUTION[0] > 640 || SCREEN_RESOLUTION[1] > 480
#--------------------------------------------[Unlimited Resolution by Hime]-----
=begin
#===============================================================================
Title: Unlimited Resolution
Date: Oct 24, 2013
Author: Hime ( ** Modified by KK20 ** )
--------------------------------------------------------------------------------   
Terms of Use
Free
--------------------------------------------------------------------------------
Description

This script modifies Graphics.resize_screen to overcome the 640x480 limitation.
It also includes some modifications to module Graphics such as allowing the
default fade transition to cover the entire screen.

Now you can have arbitrarily large game resolutions.
--------------------------------------------------------------------------------
Credits

Unknown author for overcoming the 640x480 limitation
Lantier, from RMW forums for posting the snippet above
Esrever for handling the viewport
Jet, for the custom Graphics code
FenixFyre, for the Plane class fix
Kaelan, for several bug fixes
--------------------------------------------------------------------------------
KK20 Notes:
- Plane class was rewritten from the original script. Certain lines in the
   unknown scripter's code can be omitted entirely to allow this implementation.
- Likewise, the Viewport class needed new methods to handle this Plane rewrite.
- This entire script only applies to games that go beyond the default 640x480
   resolution.

#===============================================================================
=end
unless XPACE
class Bitmap
  #----------------------------------------------------------------------------
  # ● New method: address
  #----------------------------------------------------------------------------
  def address
    @rtlmemory_pi ||= Win32API.new('kernel32','RtlMoveMemory','pii','i')
    @address ||= (  @rtlmemory_pi.call(a="\0"*4, __id__*2+16, 4)
                      @rtlmemory_pi.call(a, a.unpack('L')[0]+8, 4)
                      @rtlmemory_pi.call(a, a.unpack('L')[0]+16, 4)
                      a.unpack('L')[0]    )
  end
end
end
#===============================================================================
# ** Graphics
#===============================================================================
module Graphics
 
  @@super_sprite = Sprite.new
  @@super_sprite.z = (2 ** (0.size * 8 - 2) - 1)
 
  class << self
   
    def freeze(*args, &block)
      ensure_sprite
      @@super_sprite.bitmap = snap_to_bitmap
    end
   
    def transition(time = 10, filename = nil, vague = nil)
      if filename
        @@super_sprite.bitmap = Bitmap.new(filename)
      end
      @@super_sprite.opacity = 255
      incs = 255.0 / time
      time.times do |i|
        @@super_sprite.opacity = 255.0 - incs * i
        Graphics.wait(1)
      end
      @@super_sprite.bitmap.dispose if @@super_sprite.bitmap
      reform_sprite_bitmap
      Graphics.brightness = 255
    end
   
    def reform_sprite_bitmap
      @@super_sprite.bitmap = Bitmap.new(Graphics.width, Graphics.height)
      @@super_sprite.bitmap.fill_rect(@@super_sprite.bitmap.rect, Color.new(0, 0, 0, 255))
    end
   
    def fadeout(frames)
      incs = 255.0 / frames
      frames.times do |i|
        i += 1
        Graphics.brightness = 255 - incs * i
        Graphics.wait(1)
      end
    end
   
    def fadein(frames)
      incs = 255.0 / frames
      frames.times do |i|
        Graphics.brightness = incs * i
        Graphics.wait(1)
      end
    end

    def brightness=(i)
      @@super_sprite.opacity = 255.0 - i
    end
   
    def brightness
      255 - @@super_sprite.opacity
    end
   
    def ensure_sprite
      if @@super_sprite.disposed?
        @@super_sprite = Sprite.new
        @@super_sprite.z = (2 ** (0.size * 8 - 2) - 1)
        reform_sprite_bitmap
      end
    end
   
    # XP does not have a snap_to_bitmap method built-in
    unless XPACE
      define_method(:width)  { Resolution.size.at(0) }
      define_method(:height) { Resolution.size.at(1) }
      #--------------------------------------------------------------------------
      # * snap_to_bitmap
      #--------------------------------------------------------------------------
      def snap_to_bitmap
        @window      ||= Win32API.new('user32','GetActiveWindow', '', 'L')
        @getdc       ||= Win32API.new('user32','GetDC','i','i')
        @ccdc        ||= Win32API.new('gdi32','CreateCompatibleDC','i','i')
        @selectobject||= Win32API.new('gdi32','SelectObject','ii','i')
        @deleteobject||= Win32API.new('gdi32','DeleteObject','i','i')
        @setdibits   ||= Win32API.new('gdi32','SetDIBits','iiiiipi','i')
        @getdibits   ||= Win32API.new('gdi32','GetDIBits','iiiiipi','i')
        @bitblt      ||= Win32API.new('gdi32','BitBlt','iiiiiiiii','i')
        @ccbitmap    ||= Win32API.new('gdi32','CreateCompatibleBitmap','iii','i')
       
        bitmap = Bitmap.new(w = Graphics.width,h = Graphics.height)
        @deleteobject.call(@selectobject.call((hDC = @ccdc.call((dc = @getdc.call(@window.call)))),(hBM = @ccbitmap.call(dc,w,h))))
        @setdibits.call(hDC, hBM, 0, h, (a = bitmap.address), (info = [40,w,h,1,32,0,0,0,0,0,0].pack('LllSSLLllLL')), 0)
        @bitblt.call(hDC, 0, 0, w, h, dc, 0, 0, 0xCC0020)
        @getdibits.call(hDC, hBM, 0, h, a, info, 0)
        @deleteobject.call(hBM)
        @deleteobject.call(hDC)
        return bitmap
      end 
   
      def wait(frame=1)
        frame.times {|s| update }
      end
    end
 
# Graphics.resize_screen is only for XPA games
if XPACE
    #-----------------------------------------------------------------------------
    # Unknown Scripter. Copied from http://pastebin.com/sM2MNJZj
    #-----------------------------------------------------------------------------
    alias :th_large_screen_resize_screen :resize_screen
    def resize_screen(width, height)
      wt, ht = width.divmod(32), height.divmod(32)
      #wt.last + ht.last == 0 || fail('Incorrect width or height')
      #wh = -> w, h, off = 0 { [w + off, h + off].pack('l2').scan /.{4}/ }
      wh = lambda {|w,h,off| [w + off, h + off].pack('l2').scan(/.{4}/) }
      w, h     = wh.call(width, height,0)
      ww, hh   = wh.call(width, height, 32)
      www, hhh = wh.call(wt.first.succ, ht.first.succ,0)
      base = 0x10000000  # fixed?
      #mod = -> adr, val { DL::CPtr.new(base + adr)[0, val.size] = val }
      mod = lambda {|adr,val| (DL::CPtr.new(base + adr))[0, val.size] = val}
      # Didn't see anything happen here
  #    mod.(0x195F, "\x90" * 5)  # ???
  #    mod.(0x19A4, h)
  #    mod.(0x19A9, w)
  #    mod.(0x1A56, h)
  #    mod.(0x1A5B, w)
   
      # The following four seem to change the window size. But I dunno why there
      # are two. I commented out a pair of them but they still do the same thing.
      mod.call(0x20F6, w) && mod.call(0x20FF, w)     
      mod.call(0x2106, h) && mod.call(0x210F, h) 
     
      # speed up y?
      #mod.(0x1C5E3, h) ##
      #mod.(0x1C5E8, w) ##
      #zero = [0].pack ?l
      zero = [0].pack(?l)
     
      # Without these, I can use the default Plane class
  #    mod.(0x1C5E3, zero)
  #    mod.(0x1C5E8, zero)
   
      # And not sure what these do
  #    mod.(0x1F477, h)
  #    mod.(0x1F47C, w)
  #    mod.(0x211FF, hh)
  #    mod.(0x21204, ww)
  #    mod.(0x21D7D, hhh[0])
  #    mod.(0x21E01, www[0])
  #    mod.(0x10DEA8, h)
  #    mod.(0x10DEAD, w)
  #    mod.(0x10DEDF, h)
  #    mod.(0x10DEF0, w)
  #    mod.(0x10DF14, h)
  #    mod.(0x10DF18, w)
  #    mod.(0x10DF48, h)
  #    mod.(0x10DF4C, w)
  #    mod.(0x10E6A7, w)
  #    mod.(0x10E6C3, h)
  #    mod.(0x10EEA9, w)
  #    mod.(0x10EEB9, h)
      th_large_screen_resize_screen(width, height)
    end
   
  end # class << self
 
end # if XPACE

end
#===============================================================================
# ** Viewport
#===============================================================================
class Viewport
  attr_accessor :parent, :children
 
  alias init_children_vps initialize
  def initialize(*args)
    @children = []
    @parent = false
    init_children_vps(*args)
  end
 
  alias dispose_parent dispose
  def dispose
    @children.each{|child| child.dispose} if @parent
    dispose_parent
  end
 
  alias flash_parent flash
  def flash(color, duration)
    if @parent
      @children.each{|child| child.flash_parent(color, duration)}
    else
      flash_parent(color, duration)
    end
  end
 
  alias update_parent update
  def update
    @children.each{|child| child.update} if @parent
    update_parent
  end
 
  alias resize_trigger resize
  def resize(*args)
    @children.each{ |child|
      if args[0].is_a?(Rect)
        rect = args[0]
        new_args = Rect.new(rect.x,rect.y,child.rect.width,child.rect.height)
      else
        new_args = [args[0],args[1]]
        new_args[2] = child.rect.width
        new_args[3] = child.rect.height
      end
      child.resize_trigger(*new_args)
    } if @parent
    resize_trigger(*args)
  end
 
  alias set_trigger_vp_ox ox=
  def ox=(nx)
    return if self.ox == nx
    set_trigger_vp_ox(nx)
    @children.each{|child| child.ox = nx}
  end
 
  alias set_trigger_vp_oy oy=
  def oy=(ny)
    return if self.oy == ny
    set_trigger_vp_oy(ny)
    @children.each{|child| child.oy = ny}
  end
 
  alias tone_parent tone=
  def tone=(t)
    if @parent
      @children.each{|child| child.tone_parent(t)}
    else
      tone_parent(t)
    end
  end

end
#===============================================================================
# ** Plane
#===============================================================================
class Plane
  attr_accessor :offset_x, :offset_y
 
  alias parent_initialize initialize
  def initialize(viewport=nil,parent=true)
    @parent = parent
    @children = []
    parent_initialize(viewport)
    @offset_x = 0
    @offset_y = 0
    # If the parent Plane object; but don't make more children if already have
    # some. This occurs in Spriteset_Map when initializing the Panorama and Fog
    if @parent && viewport
      viewport.parent = true
      create_children
    end
  end
 
  def create_children
    gw = [SCREEN_RESOLUTION[0], $game_map.width * 32].min
    gh = [SCREEN_RESOLUTION[1], $game_map.height * 32].min
    w = (gw - 1) / 640
    h = (gh - 1) / 480
    for y in 0..h
      for x in 0..w
        # This is the top-left default/parent Plane, so skip it
        #next if x == 0 && y == 0
        # Create viewport unless it already exists
        width = w > 0 && x == w ? gw - 640 : 640
        height = h > 0 && y == h ? gh - 480 : 480
        vp = Viewport.new(x * 640, y * 480, width, height, true)
        vp.offset_x = x * 640
        vp.offset_y = y * 480
        # Have to do this in order to prevent overlapping with the parent
        # (for Spriteset_Map viewport1 mainly)
        vp.z = self.viewport.z - 1
        self.viewport.children.push(vp)
        # Create the child Plane
        plane = Plane.new(vp,false)
        plane.offset_x = x * 640
        plane.ox = 0
        plane.offset_y = y * 480
        plane.oy = 0
        # Push to array
        @children.push(plane)
      end
    end
  end
 
  # For the remaining Plane properties, if the parent changes, so do its children
 
  alias dispose_parent dispose
  def dispose
    @children.each{|child| child.dispose} if @parent
    dispose_parent
  end
 
  alias zoom_x_parent zoom_x=
  def zoom_x=(new_val)
    new_val = 0 if new_val < 0
    @children.each{|child| child.zoom_x_parent(new_val)} if @parent
    zoom_x_parent(new_val)
  end
 
  alias zoom_y_parent zoom_y=
  def zoom_y=(new_val)
    new_val = 0 if new_val < 0
    @children.each{|child| child.zoom_y_parent(new_val)} if @parent
    zoom_y_parent(new_val)
  end
 
  alias ox_parent ox=
  def ox=(new_val)
    @children.each{|child| child.ox = new_val} if @parent
    ox_parent(new_val + @offset_x)
  end
 
  alias oy_parent oy=
  def oy=(new_val)
    @children.each{|child| child.oy = new_val} if @parent
    oy_parent(new_val + @offset_y)
  end
 
  alias bitmap_parent bitmap=
  def bitmap=(new_val)
    @children.each{|child| child.bitmap_parent(new_val)} if @parent
    #bitmap_parent(new_val)
  end
 
  alias visible_parent visible=
  def visible=(new_val)
    @children.each{|child| child.visible_parent(new_val)} if @parent
    visible_parent(new_val)
  end
 
  alias z_parent z=
  def z=(new_val)
    # Because the children spawn new Viewports, they have to be lower than the
    # parent's viewport to prevent drawing OVER the parent...unless the Plane's
    # z-value is more than zero, in which case the children viewports NEED to be
    # higher than the parent's. By doing this, we can have panoramas be below
    # the tilemap and fogs be over the tilemap.
    if @parent && @children[0]
      child = @children[0]
      if new_val > 0 && child.viewport.z < self.viewport.z
        @children.each{|child| child.viewport.z += 1}
      elsif new_val <= 0 && child.viewport.z >= self.viewport.z
        @children.each{|child| child.viewport.z -= 1}
      end
    end
   
    @children.each{|child| child.z_parent(new_val)} if @parent
    z_parent(new_val)
  end
 
  alias opacity_parent opacity=
  def opacity=(new_val)
    @children.each{|child| child.opacity_parent(new_val)} if @parent
    opacity_parent(new_val)
  end
 
  alias color_parent color=
  def color=(new_val)
    if @parent
      @children.each{|child| child.color_parent(new_val)}
    else
      color_parent(new_val)
    end
  end
 
  alias blend_type_parent blend_type=
  def blend_type=(new_val)
    @children.each{|child| child.blend_type_parent(new_val)} if @parent
    blend_type_parent(new_val)
  end
 
  alias tone_parent tone=
  def tone=(new_val)
    if @parent
      @children.each{|child| child.tone_parent(new_val)}
    else
      tone_parent(new_val)
    end
  end
 
end
#------------------------------------------[End of Unlimited Resolution]--------
end

# Resize the game window (if using XP) and load the MapInfos file
Resolution.resize_game unless XPACE
XPAT_MAP_INFOS = load_data("Data/MapInfos.rxdata")

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Kise

Works perfectly, thank you.

KK20

Thanks for the confirmation.
Version 0.36 uploaded to resolve that issue.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

whitespirits

Hey guys, love this tilemap great work! I wanted to ask if there is any addon that can manage UI or HUD elements to fit the chosen resolution? Im using Netmaker. Thanks!

KK20

That's impossible unless the scripter of the HUD allowed their script to adjust its positioning based on the game's resolution. How is XPA Tilemap supposed to be aware of these scripts and change their coordinates accordingly?

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

whitespirits

Ye that makes sense, I will need to adjust the in game setup, its mainly the icons bottom left?

kreiss

Why is not possible to do full screen for XP ?

KK20

Largely because I never looked into it fully. RMXP is picky when it comes to fullscreen, namely that it always changes the monitor resolution to 640x480. You have to use Win32API calls to forcefully change the monitor resolution to a custom size. Same thing occurs when switching back to window mode.

I can take another look at it this weekend.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!