Advertisement
KK20

Custom Resolution v0.96b (no DLL)

Nov 15th, 2013
1,996
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 42.37 KB | None | 0 0
  1. #===============================================================================
  2. # Custom Resolution (DLL-less)
  3. # Authors: ForeverZer0, KK20
  4. # Version: 0.96b
  5. # Date: 11.15.2013
  6. #===============================================================================
  7. # KK20's Notes
  8. #===============================================================================
  9. # Introduction:
  10. #
  11. #   This script is intended to create screen resolutions other than 640 x 480.
  12. #   The script comes with its own Tilemap rewrite in order to combat larger
  13. #   screen resolutions (because anything beyond 640 x 480 is not drawn).
  14. #
  15. # Instructions:
  16. #
  17. #   Place script above 'Main'. Probably best to put it below all your other
  18. #   custom scripts.
  19. #   This version does not require the 'screenshot.dll'.
  20. #
  21. # Things to Consider:
  22. #
  23. #  - Fullscreen will change the resolution back to 640 x 480. A solution is in
  24. #    the works.
  25. #  - Transitions do not work properly on larger resolutions. You can use a
  26. #    Transitions Add-Ons script if you want better transitions (otherwise, all
  27. #    you will get is the default fade in/out). Links listed below.
  28. #  - Custom scripts that draw windows to the screen will most likely need edits.
  29. #  - Larger resolutions = more processing power = more lag
  30. #
  31. #  ***************************************************************************  
  32. #  * THIS IS STILL A WORK IN PROGRESS; IF YOU FIND ANYTHING PLEASE REPORT IT *
  33. #  ***************************************************************************  
  34. #
  35. #  Links:
  36. #  - Fantasist's Transitions Pack
  37. #    http://forum.chaos-project.com/index.php/topic,1390.0.html
  38. #  - ForeverZer0's Add-ons
  39. #    http://forum.chaos-project.com/index.php/topic,7862.0.html
  40. #  - ThallionDarkshine's Add-ons
  41. #    http://forum.chaos-project.com/index.php/topic,12655.0.html
  42. #  - Drago Transition Pack
  43. #    http://forum.chaos-project.com/index.php/topic,13488.0.html
  44. #
  45. #===============================================================================
  46. # ForeverZer0's Notes from v0.93                   (outdated information)
  47. #===============================================================================
  48. # Introduction:
  49. #
  50. #   My goal in creating this script was to create a system that allowed the user
  51. #   to set the screen size to something other than 640 x 480, but not have make
  52. #   huge sacrifices in compatibility and performance. Although the script is
  53. #   not simply Plug-and-Play, it is about as close as one can achieve with a
  54. #   script of this nature.
  55. #
  56. # Instructions:
  57. #
  58. #  - Place the "screenshot.dll" from Fantasist's Transition Pack script, which
  59. #    can be found here: http://www.sendspace.com/file/yjd54h in your game folder
  60. #  - Place this script above main, below default scripts.
  61. #  - In my experience, unchecking "Reduce Screen Flickering" actually helps the
  62. #    screen not to flicker. Open menu with F1 while playing and set this to what
  63. #    you get the best results with.
  64. #
  65. # Features:
  66. #  
  67. #  - Totally re-written Tilemap and Plane class. Both classes were written to
  68. #    display the map across any screen size automatically. The Tilemap class
  69. #    is probably even more efficient than the original, which will help offset
  70. #    any increased lag due to using a larger screen size with more sprites
  71. #    being displayed.
  72. #  - Every possible autotile graphic (48 per autotile) will be cached for the
  73. #    next time that tile is used.
  74. #  - Autotile animation has been made as efficient as possible, with a system
  75. #    that stores their coodinates, but only if they change. This greatly reduces
  76. #    the number of iterations at each update.
  77. #  - System creates an external file to save pre-cached data priorities and
  78. #    autotiles. This will decrease any loading times even more, and only takes a
  79. #    second, depending on the number of maps you have.
  80. #  - User defined autotile animation speed. Can change with script calls.
  81. #  - Automatic re-sizing of Bitmaps and Viewports that are 640 x 480 to the
  82. #    defined resolution, unless explicitely over-ridden in the method call.
  83. #    The graphics themselves will not be resized, but any existing scripts that
  84. #    use the normal screen size will already be configured to display different
  85. #    sizes of graphics for transitions, battlebacks, pictures, fogs, etc.
  86. #  - Option to have a log file ouput each time the game is ran, which can alert
  87. #    you to possible errors with map sizes, etc.
  88. #
  89. # Issues/Bugs/Possible Bugs:
  90. #
  91. #   - Graphic related scripts and your graphics will need to be customized to
  92. #     fit the new screen size, so this script is not for everyone.
  93. #   - The Z-axis for the Plane class, which is used for Fogs and Panoramas has
  94. #     been altered. It is now multiplied by 1000. This will likely be a minor
  95. #     issue for most, since this class is very rarely used except for Fogs and
  96. #     Panoramas, which are already far above and below respectfully.
  97. #   - Normal transitions using graphics cannot be used. With the exception of
  98. #     a standard fade, like that used when no graphic is defined will be used.
  99. #     Aside from that, only special transitions from Transition Pack can be
  100. #     used.
  101. #===============================================================================
  102. #  Credits/Thanks:
  103. #    - ForeverZer0, for script.
  104. #    - Creators of the Transition Pack and Screenshot.dll
  105. #    - Selwyn, for base resolution script
  106. #    - KK20, for Version 0.94 and above and the Tilemap class
  107. #    - LiTTleDRAgo, for his Core Engine script to remove screenshot.dll dependency
  108. #===============================================================================
  109. #                             CONFIGURATION
  110. #===============================================================================
  111.  
  112.   SCREEN = [1024, 576]
  113.   # Define the resolution of the game screen. These values can be anything
  114.   # within reason. Centering, viewports, etc. will all be taken care of, but it
  115.   # is recommended that you use values divisible by 32 for best results.
  116.  
  117.   UPDATE_COUNT = 8
  118.   # Define the number of frames between autotile updates. The lower the number,
  119.   # the faster the animations cycle. This can be changed in-game with the
  120.   # following script call: $game_map.autotile_speed = SPEED
  121.  
  122.   RESOLUTION_LOG = true
  123.   # This will create a log in the Game directory each time the game is ran in
  124.   # DEBUG mode, which will list possible errors with map sizes, etc.
  125.  
  126. #===============================================================================
  127. # ** Resolution
  128. #===============================================================================
  129.  
  130. class Resolution
  131.  
  132.   attr_reader :version
  133.  
  134.   def initialize
  135.     # Define version.
  136.     @version = 0.96
  137.     # Set instance variables for calling basic Win32 functions.
  138.     ini = Win32API.new('kernel32', 'GetPrivateProfileString','PPPPLP', 'L')
  139.     title = "\0" * 256
  140.     ini.call('Game', 'Title', '', title, 256, '.\\Game.ini')
  141.     title.delete!("\0")
  142.     @window = Win32API.new('user32', 'FindWindow', 'PP', 'I').call('RGSS Player', title)
  143.     set_window_long = Win32API.new('user32', 'SetWindowLong', 'LIL', 'L')
  144.     set_window_pos  = Win32API.new('user32', 'SetWindowPos', 'LLIIIII', 'I')
  145.     @metrics        = Win32API.new('user32', 'GetSystemMetrics', 'I', 'I')
  146.     # Set default size, displaying error if size is larger than the hardware.
  147.     default_size = self.size
  148.     if default_size[0] < SCREEN[0] || default_size[1] < SCREEN[1]
  149.       print("\"#{title}\" requires a minimum screen resolution of [#{SCREEN[0]} x #{SCREEN[1]}]\r\n\r\n" +
  150.             "\tYour Resolution: [#{default_size[0]} x #{default_size[1]}]")
  151.       exit
  152.     end
  153.     # Apply resolution change.
  154.     x = (@metrics.call(0) - SCREEN[0]) / 2
  155.     y = (@metrics.call(1) - SCREEN[1]) / 2
  156.     set_window_long.call(@window, -16, 0x14CA0000)
  157.     set_window_pos.call(@window, 0, x, y, SCREEN[0] + 6, SCREEN[1] + 26, 0)
  158.     @window = Win32API.new('user32', 'FindWindow', 'PP', 'I').call('RGSS Player', title)
  159.   end
  160.   #--------------------------------------------------------------------------
  161.   def size
  162.     # Returns the screen size of the machine.
  163.     return [@metrics.call(0), @metrics.call(1)]
  164.   end
  165.   #--------------------------------------------------------------------------
  166. end
  167.  
  168.  
  169. #==============================================================================
  170. # ** Drago - Core Engine
  171. # Version : 1.39
  172. # Contact : littledrago.blogspot.com / forum.chaos-project.com
  173. #==============================================================================
  174. # =============================================================================
  175. # NOTE:
  176. # -----------------------------------------------------------------------------
  177. # This is a devtool for me to make me easier for scripting
  178. # If you want to use this, put above all custom script & below Scene_Debug
  179. #
  180. # Note that I'm changing a bit RGSS language in this script, so probably some
  181. # syntax in this script won't work if you use them somewhere else
  182. #
  183. # =============================================================================
  184. module LiTTleDRAgo
  185.   #-------------------------------------------------------------------------
  186.   # * Constant
  187.   #-------------------------------------------------------------------------
  188.   VX           = defined?(Window_ActorCommand)
  189.   VXA          = defined?(Window_BattleActor)
  190.   RGSS1        = defined?(Hangup)
  191.   RGSS2        = RUBY_VERSION == '1.8.1' && !RGSS1
  192.   RGSS3        = RUBY_VERSION == '1.9.2'
  193.   APPPATHDRAGO = "#{ENV['APPDATA']}/Drago/"
  194. end
  195.  
  196. #==============================================================================
  197. # ** CoreDLL
  198. #------------------------------------------------------------------------------
  199. #  
  200. #==============================================================================
  201. module CoreDLL
  202.   #-------------------------------------------------------------------------
  203.   # * Constant
  204.   #-------------------------------------------------------------------------
  205.   Rtlmemory_pi = Win32API.new('kernel32','RtlMoveMemory','pii','i')
  206.   Rtlmemory_ip = Win32API.new('kernel32','RtlMoveMemory','ipi','i')
  207.   #-------------------------------------------------------------------------
  208.   # * Get the game window handle (specific to game)
  209.   #-------------------------------------------------------------------------
  210.   unless method_defined?(:hwnd)
  211.     def hwnd
  212.       @window_find ||= Win32API.new('user32', 'FindWindowEx', %w(l l p p), 'i')
  213.       @game_window ||= @window_find.call(0,0,"RGSS Player",0)
  214.       return @game_window
  215.     end  
  216.   end
  217.   #-------------------------------------------------------------------------
  218.   # * Get the Game Window's width and height
  219.   #-------------------------------------------------------------------------
  220.   unless method_defined?(:client_size)
  221.     def client_size
  222.       @window_c_rect ||= Win32API.new('user32', 'GetClientRect', %w(l p), 'i')
  223.       @window_c_rect.call(self.hwnd, (rect = [0, 0, 0, 0].pack('l4')))
  224.       right, bottom = rect.unpack('l4')[2..3]
  225.       return right, bottom
  226.     end  
  227.   end
  228.   #--------------------------------------------------------------------------
  229.   # * snap_to_bitmap
  230.   #--------------------------------------------------------------------------
  231.   unless method_defined?(:snap_to_bitmap)
  232.     def snap_to_bitmap
  233.       @getdc         ||= Win32API.new('user32','GetDC','i','i')
  234.       @ccdc          ||= Win32API.new('gdi32','CreateCompatibleDC','i','i')
  235.       @ccbitmap      ||= Win32API.new('gdi32','CreateCompatibleBitmap','iii','i')
  236.       @deleteobject  ||= Win32API.new('gdi32','DeleteObject','i','i')
  237.       @bitblt        ||= Win32API.new('gdi32','BitBlt','iiiiiiiii','i')
  238.       @setdibits     ||= Win32API.new('gdi32','SetDIBits','iiiiipi','i')
  239.       @getdibits     ||= Win32API.new('gdi32','GetDIBits','iiiiipi','i')
  240.       @selectobject  ||= Win32API.new('gdi32','SelectObject','ii','i')
  241.       width, height = Graphics.width, Graphics.height
  242.       bitmap        = Bitmap.new(width, height)
  243.       info          = [40,width,height,1,32,0,0,0,0,0,0].pack('LllSSLLllLL')
  244.       hDC           = @ccdc.call((dc = @getdc.call(hwnd)))
  245.       hBM           = @ccbitmap.call(dc, width, height)
  246.       @deleteobject.call(@selectobject.call(hDC, hBM))
  247.       @setdibits.call(hDC, hBM, 0, height, (address = bitmap.address), info, 0)
  248.       @bitblt.call(hDC, 0, 0, width, height, dc, 0, 0, 0xCC0020)
  249.       @getdibits.call(hDC, hBM, 0, height, address, info, 0)
  250.       @deleteobject.call(hBM)
  251.       @deleteobject.call(hDC)
  252.       bitmap
  253.     end    
  254.   end
  255. end                        
  256. LiTTleDRAgo.extend(CoreDLL)
  257.  
  258. #==============================================================================
  259. # ** Graphics
  260. #------------------------------------------------------------------------------
  261. #  This module handles all Graphics
  262. #==============================================================================
  263. module Graphics
  264.   #----------------------------------------------------------------------------
  265.   # ● class << self
  266.   #----------------------------------------------------------------------------
  267.   class << self
  268.     #--------------------------------------------------------------------------
  269.     # ● Redefined method: width, height, snap_to_bitmap
  270.     #--------------------------------------------------------------------------
  271.     unless method_defined?(:width)
  272.       def width
  273.         LiTTleDRAgo.client_size.at(0)
  274.       end
  275.     end
  276.    
  277.     unless method_defined?(:height)
  278.       def height
  279.         LiTTleDRAgo.client_size.at(1)
  280.       end
  281.     end
  282.    
  283.     unless method_defined?(:snap_to_bitmap)
  284.       def snap_to_bitmap
  285.         LiTTleDRAgo.snap_to_bitmap
  286.       end
  287.     end
  288.   end
  289. end
  290. #==============================================================================
  291. # ■ Bitmap
  292. #------------------------------------------------------------------------------
  293. #
  294. #==============================================================================
  295. class Bitmap  
  296.   #----------------------------------------------------------------------------
  297.   # ● Constant
  298.   #----------------------------------------------------------------------------
  299.   RtlMoveMemory_pi = CoreDLL::Rtlmemory_pi
  300.   RtlMoveMemory_ip = CoreDLL::Rtlmemory_ip
  301.   #----------------------------------------------------------------------------
  302.   # ● New method: address
  303.   #----------------------------------------------------------------------------
  304.   unless method_defined?(:address)
  305.     def address
  306.       @address ||= (
  307.         RtlMoveMemory_pi.call(a="\0"*4, __id__*2+16, 4)
  308.         RtlMoveMemory_pi.call(a, a.unpack('L')[0]+8, 4)
  309.         RtlMoveMemory_pi.call(a, a.unpack('L')[0]+16, 4)
  310.         a.unpack('L')[0]
  311.       )
  312.     end
  313.   end
  314. end
  315. #===============================================================================
  316. # **                                               END OF Drago - Core Engine
  317. #===============================================================================
  318.  
  319. #===============================================================================
  320. # ** Graphics
  321. #===============================================================================
  322.  
  323. module Graphics
  324.  
  325.   class << self
  326.     alias zer0_graphics_transition transition
  327.   end
  328.  
  329.   def self.transition(duration = 8, *args)
  330.     # Call default transition if no instance of the resolution is defined.
  331.     if $resolution == nil
  332.       zer0_graphics_transition(duration, *args)
  333.     else
  334.       # Skip this section and instantly transition graphics if duration is 0.
  335.       if duration > 0
  336.         # Take a snapshot of the the screen, overlaying screen with graphic.
  337.         #$resolution.snapshot
  338.         zer0_graphics_transition(0)
  339.         # Create screen instance
  340.         sprite = Sprite.new(Viewport.new(0, 0, SCREEN[0], SCREEN[1]))
  341.         sprite.bitmap = Graphics.snap_to_bitmap
  342.         # Use a simple fade if transition is not defined.
  343.         fade = 255 / duration
  344.         duration.times { sprite.opacity -= fade ; update }
  345.         # Dispose sprite and delete snapshot file.
  346.         [sprite, sprite.bitmap].each {|obj| obj.dispose }
  347.         #File.delete('Data/snap')
  348.       end
  349.      zer0_graphics_transition(0)
  350.    end
  351.  end
  352. end  
  353.  
  354. #===============================================================================
  355. # ** RPG::Cache
  356. #===============================================================================
  357.  
  358. module RPG::Cache
  359.  
  360.   AUTO_INDEX = [
  361.  
  362.   [27,28,33,34],  [5,28,33,34],  [27,6,33,34],  [5,6,33,34],
  363.   [27,28,33,12],  [5,28,33,12],  [27,6,33,12],  [5,6,33,12],
  364.   [27,28,11,34],  [5,28,11,34],  [27,6,11,34],  [5,6,11,34],
  365.   [27,28,11,12],  [5,28,11,12],  [27,6,11,12],  [5,6,11,12],
  366.   [25,26,31,32],  [25,6,31,32],  [25,26,31,12], [25,6,31,12],
  367.   [15,16,21,22],  [15,16,21,12], [15,16,11,22], [15,16,11,12],
  368.   [29,30,35,36],  [29,30,11,36], [5,30,35,36],  [5,30,11,36],
  369.   [39,40,45,46],  [5,40,45,46],  [39,6,45,46],  [5,6,45,46],
  370.   [25,30,31,36],  [15,16,45,46], [13,14,19,20], [13,14,19,12],
  371.   [17,18,23,24],  [17,18,11,24], [41,42,47,48], [5,42,47,48],
  372.   [37,38,43,44],  [37,6,43,44],  [13,18,19,24], [13,14,43,44],
  373.   [37,42,43,48],  [17,18,47,48], [13,18,43,48], [1,2,7,8]
  374.    
  375.   ]
  376.  
  377.   def self.autotile(filename)
  378.     key = "Graphics/Autotiles/#{filename}"
  379.     if !@cache.include?(key) || @cache[key].disposed?
  380.       # Cache the autotile graphic.
  381.       @cache[key] = (filename == '') ? Bitmap.new(128, 96) : Bitmap.new(key)
  382.       # Cache each configuration of this autotile.
  383.       new_bm = self.format_autotiles(@cache[key], filename)
  384.       @cache[key].dispose
  385.       @cache[key] = new_bm
  386.     end
  387.     return @cache[key]
  388.   end
  389.  
  390.   def self.format_autotiles(bitmap, filename)
  391.     if bitmap.height > 32
  392.       frames = bitmap.width / 96
  393.       template = Bitmap.new(256*frames,192)
  394.       # Create a bitmap to use as a template for creation.
  395.       (0..frames-1).each{|frame|
  396.       (0...6).each {|i| (0...8).each {|j| AUTO_INDEX[8*i+j].each {|number|
  397.         number -= 1
  398.         x, y = 16 * (number % 6), 16 * (number / 6)
  399.         rect = Rect.new(x + (frame * 96), y, 16, 16)
  400.         template.blt((32 * j + x % 32) + (frame * 256), 32 * i + y % 32, bitmap, rect)
  401.       }}}}
  402.       return template
  403.     else
  404.       return bitmap
  405.     end
  406.   end
  407.  
  408. end
  409. #===============================================================================
  410. # ** Tilemap_DataTable
  411. #===============================================================================
  412. class Tilemap_DataTable
  413.   attr_accessor :updates
  414.   attr_accessor :table
  415.   def initialize(table)
  416.     @table = table
  417.     @updates = []
  418.   end
  419.  
  420.   def updated
  421.     return @updates.size >= 1
  422.   end
  423.  
  424.   def [](x,y=nil,z=nil)
  425.     return @table[x,y,z] unless z.nil?
  426.     return @table[x,y] unless y.nil?
  427.     return @table[x]
  428.   end
  429.  
  430.   def []=(x,y,z=nil,t_id=nil)
  431.     @updates.push([x,y,z,t_id]) unless t_id.nil?
  432.     t_id.nil? ? (z.nil? ? @table[x] = y : @table[x,y] = z) : @table[x,y,z] = t_id
  433.   end
  434.  
  435.   def xsize; return @table.xsize; end
  436.   def ysize; return @table.ysize; end
  437.   def zsize; return @table.zsize; end
  438.    
  439.   def resize(x,y=nil,z=nil); @table.resize(x,y,z); end
  440.  
  441. end
  442. #===============================================================================
  443. # ** Tilemap
  444. #===============================================================================
  445.  
  446. class Tilemap
  447.  
  448.   attr_reader   :map_data, :ox, :oy, :viewport
  449.   attr_accessor :tileset, :autotiles, :priorities
  450.  
  451.   def initialize(viewport)
  452.     # Initialize instance variables to store required data.
  453.     @viewport, @autotiles, @tile_sprites, @ox, @oy = viewport, [], [], 0, 0
  454.     @current_frame, @total_frames = [], []
  455.     @tilemap_drawn = false
  456.     @ox_oy_set = [false, false]
  457.     # Get priority data for this tileset from instance of Game_Map.
  458.     @priorities = $game_map.priorities
  459.     # Holds all the Sprite instances of animating tiles (keys based on tile's ID)
  460.     @animating_tiles = {}
  461.     # Game map's x/y location of the top left corner tile
  462.     @corner_tile_loc = [-1,-1]
  463.   end
  464.   #-----------------------------------------------------------------------------
  465.   # Initialize all tile sprites. Draws three sprites per (x,y).
  466.   #-----------------------------------------------------------------------------
  467.   def init_tiles
  468.     # Determine how many frames of animation this autotile has
  469.     for i in 0..6
  470.       bm = @autotiles[i]
  471.       if bm.nil?
  472.         @total_frames = 1
  473.       elsif bm.height > 32
  474.         @total_frames[i] = bm.width / 256
  475.       else
  476.         @total_frames[i] = bm.width / 32
  477.       end
  478.       @current_frame[i] = 0
  479.     end
  480.     # Turn on flag that the tilemap sprites have been initialized
  481.     @tilemap_drawn = true
  482.    
  483.     @animating_tiles.clear
  484.     # Create a sprite and viewport to use for each priority level.
  485.     (0...((SCREEN[0]/32+2) * (SCREEN[1]/32+2))*3).each{|i|
  486.       @tile_sprites[i/3] = [] if @tile_sprites[i/3].nil?
  487.       @tile_sprites[i/3][i%3] = Sprite.new(@viewport) unless @tile_sprites[i/3][i%3].is_a?(Sprite)
  488.       # Rename to something shorter and easier to work with for below
  489.       tile = @tile_sprites[i/3][i%3]
  490.       # Assign tile's respective ID value
  491.       tile.tile_sprite_id = i
  492.       # Draw sprite at index location (ex. ID 0 should always be the top-left sprite)
  493.       tile.x = (i % ((SCREEN[0]/32+2)*3) / 3 * 32) - 32 + (@ox % 32)
  494.       tile.y = (i / ((SCREEN[0]/32+2)*3) * 32) - 32 + (@oy % 32)
  495.  
  496.       map_x, map_y = (tile.x+@ox)/32, (tile.y+@oy)/32
  497.       @corner_tile_loc = [map_x, map_y] if i == 0
  498.       # If the tile happens to be drawn along the outside borders of the map
  499.       if map_x < 0 || map_x >= $game_map.width || map_y < 0 || map_y >= $game_map.height
  500.         tile.z = 0
  501.         tile.bitmap = RPG::Cache.picture('')
  502.         tile.src_rect.set(0,0,0,0)
  503.       else # Tile is actually on the map
  504.         tile_id = @map_data[map_x,map_y,i%3]
  505.         if @priorities[tile_id] == 0
  506.           tile.z = 0
  507.         else
  508.           tile.z = tile.y + @priorities[tile_id] * 32 + 32
  509.         end
  510.         # No tile exists here
  511.         if tile_id == 0
  512.           tile.bitmap = RPG::Cache.picture('')#@tileset
  513.           tile.src_rect.set(0,0,0,0)
  514.         elsif tile_id >= 384 # non-autotile
  515.           tile.bitmap = @tileset
  516.           tile.src_rect.set(((tile_id - 384) % 8)*32,((tile_id - 384) / 8)*32, 32, 32)
  517.         else # autotile
  518.           tile.bitmap = @autotiles[tile_id/48-1]
  519.           tile.src_rect.set(((tile_id % 48) % 8)*32,((tile_id % 48) / 8)*32, 32, 32)
  520.           @animating_tiles[i] = tile if tile.bitmap.width > 256
  521.         end
  522.       end
  523.     }
  524.     # Sprite ID located at top left corner (ranges from 0..map_width * map_height
  525.     @corner_index = 0
  526.   end
  527.  
  528.   #-----------------------------------------------------------------------------
  529.   # Makes update to ox and oy. Sprites out of range will be moved based on these
  530.   # two values.
  531.   #-----------------------------------------------------------------------------
  532.   def ox=(ox)
  533.     #
  534.     unless @tilemap_drawn
  535.       @ox = ox
  536.       @ox_oy_set[0] = true
  537.       return
  538.     end
  539.    
  540.     return if @ox == ox
  541.     # Shift all tiles left or right by the difference
  542.     shift = @ox - ox
  543.  
  544.     @tile_sprites.each {|set| set.each{|tile| tile.x += shift }}
  545.     @ox = ox
  546.     # Determine if columns need to be shifted
  547.     col_num = @corner_index
  548.     #return unless @tile_sprites[col_num][0].x <= -49 || @tile_sprites[col_num][0].x >= -17
  549.     while @tile_sprites[col_num][0].x <= -49 || @tile_sprites[col_num][0].x >= -17
  550.      
  551.     @corner_tile_loc[0] += (shift < 0 ? 1 : -1)
  552.     modTileId = ((SCREEN[0]+64)*(SCREEN[1]+64))/1024    
  553.     # If new ox is greater than old ox
  554.     if shift < 0
  555.       # Move all sprites in left column to the right side and change bitmaps
  556.       # and z-values
  557.       (0...(SCREEN[1]/32+2)).each{|n|
  558.         j = ((SCREEN[0]/32+2) * n + col_num) % modTileId
  559.         @tile_sprites[j].each_index{|i|
  560.           tile = @tile_sprites[j][i]
  561.           @animating_tiles.delete(tile.tile_sprite_id)
  562.           tile.x += 64 + SCREEN[0]
  563.          
  564.           map_x, map_y = (tile.x+@ox)/32, (tile.y+@oy)/32
  565.           tile_id = @map_data[map_x,map_y,i]
  566.          
  567.           if tile_id.nil?
  568.             tile.z = [map_y * 32, 0].max
  569.             tile.bitmap = RPG::Cache.picture('')
  570.             tile.src_rect.set(0,0,0,0)
  571.             next
  572.           else
  573.             if @priorities[tile_id] == 0
  574.               tile.z = 0
  575.             else
  576.               tile.z = 32 + (tile.y/32) * 32 + @priorities[tile_id] * 32
  577.             end
  578.           end
  579.           if tile_id == 0
  580.             tile.bitmap = RPG::Cache.picture('')
  581.             tile.src_rect.set(0,0,0,0)
  582.           elsif tile_id >= 384
  583.             tile.bitmap = @tileset
  584.             tile.src_rect.set(((tile_id - 384) % 8) * 32,((tile_id - 384) / 8) *32, 32, 32)
  585.           else
  586.             auto_id = tile_id/48-1
  587.             tile.bitmap = @autotiles[auto_id]
  588.             tile.src_rect.set(((tile_id % 48) % 8)*32 + @current_frame[auto_id] * 256,((tile_id % 48) / 8)*32, 32, 32)
  589.             @animating_tiles[tile.tile_sprite_id] = tile if @total_frames[auto_id] > 1
  590.           end
  591.         }
  592.       }
  593.       # New corner should be the tile immediately right of the previous tile
  594.       col_num /= SCREEN[0]/32+2
  595.       col_num *= SCREEN[0]/32+2
  596.       @corner_index = (@corner_index + 1) % (SCREEN[0]/32+2) + col_num
  597.     else
  598.       # Shift right column to the left
  599.       # Gets the right column
  600.       row_index = col_num / (SCREEN[0]/32+2)
  601.       row_index *= (SCREEN[0]/32+2)
  602.       col_num = (@corner_index - 1) % (SCREEN[0]/32+2) + row_index
  603.      
  604.       (0...(SCREEN[1]/32+2)).each{|n|
  605.         j = ((SCREEN[0]/32+2) * n + col_num) % modTileId
  606.         @tile_sprites[j].each_index{|i|
  607.           tile = @tile_sprites[j][i]
  608.           @animating_tiles.delete(tile.tile_sprite_id)
  609.           tile.x -= 64 + SCREEN[0]
  610.          
  611.           map_x, map_y = (tile.x+@ox)/32, (tile.y+@oy)/32
  612.           tile_id = @map_data[map_x,map_y,i]
  613.           if tile_id.nil?
  614.             tile.z = [map_y * 32, 0].max
  615.             tile.bitmap = @tileset
  616.             tile.src_rect.set(0,0,0,0)
  617.             next
  618.           else
  619.             if @priorities[tile_id] == 0
  620.               tile.z = 0
  621.             else
  622.               tile.z = 32 + (tile.y/32) * 32 + @priorities[tile_id] * 32
  623.             end
  624.           end
  625.           if tile_id == 0
  626.             tile.bitmap = RPG::Cache.picture('')
  627.             tile.src_rect.set(0,0,0,0)
  628.           elsif tile_id >= 384
  629.             tile.bitmap = @tileset
  630.             tile.src_rect.set(((tile_id - 384) % 8)*32,((tile_id - 384) / 8)*32, 32, 32)
  631.           else
  632.             auto_id = tile_id/48-1
  633.             tile.bitmap = @autotiles[auto_id]
  634.             tile.src_rect.set(((tile_id % 48) % 8)*32 + @current_frame[auto_id] * 256,((tile_id % 48) / 8)*32, 32, 32)
  635.             @animating_tiles[tile.tile_sprite_id] = tile if @total_frames[auto_id] > 1
  636.           end
  637.         }
  638.       }
  639.       col_num /= SCREEN[0]/32+2
  640.       col_num *= SCREEN[0]/32+2
  641.       @corner_index = (@corner_index - 1) % (SCREEN[0]/32+2) + col_num
  642.     end
  643.     col_num = @corner_index
  644.     end #end of while
  645.   end
  646.  
  647.   #-----------------------------------------------------------------------------
  648.  
  649.   def oy=(oy)
  650.     #
  651.     unless @tilemap_drawn
  652.       @oy = oy
  653.       @ox_oy_set[1] = true
  654.       return
  655.     end
  656.    
  657.     return if @oy == oy
  658.     # Shift all tiles up or down by the difference, and change z-value
  659.     shift = @oy - oy
  660.  
  661.     @tile_sprites.each {|set| set.each{|tile| tile.y += shift; tile.z += shift unless tile.z == 0 }}
  662.     @oy = oy
  663.     # Determine if rows need to be shifted
  664.     row_num = @corner_index
  665.     #return unless @tile_sprites[row_num][0].y <= -49 || @tile_sprites[row_num][0].y >= -17
  666.     while @tile_sprites[row_num][0].y <= -49 || @tile_sprites[row_num][0].y >= -17
  667.    
  668.      
  669.     # Needed for resetting the new corner index much later.
  670.     modTileId = ((SCREEN[0]+64)*(SCREEN[1]+64))/1024
  671.     @corner_tile_loc[1] += (shift < 0 ? 1 : -1)
  672.     # If new oy is greater than old oy
  673.     if shift < 0
  674.       row_num /= SCREEN[0]/32+2
  675.       row_num *= SCREEN[0]/32+2
  676.       # Move all sprites in top row to the bottom side and change bitmaps
  677.       # and z-values
  678.       (0...(SCREEN[0]/32+2)).each{|n|
  679.         # Run through each triad of sprites from left to right
  680.         j = n + row_num
  681.         @tile_sprites[j].each_index{|i|
  682.           # Get each individual tile on each layer
  683.           tile = @tile_sprites[j][i]
  684.           @animating_tiles.delete(tile.tile_sprite_id)
  685.           tile.y += 64 + SCREEN[1]
  686.           # Determine what map coordinate this tile now resides at...
  687.           map_x, map_y = (tile.x+@ox)/32, (tile.y+@oy)/32
  688.           # ...and get its tile_id
  689.           tile_id = @map_data[map_x,map_y,i]
  690.           # If no tile exists here (effectively out of array bounds)
  691.           if tile_id.nil?
  692.             tile.z = [map_y * 32, 0].max
  693.             tile.bitmap = RPG::Cache.picture('')
  694.             tile.src_rect.set(0,0,0,0)
  695.             next
  696.           else # Tile exists. Figure out its z-coordinate based on priority
  697.             if @priorities[tile_id] == 0
  698.               tile.z = 0
  699.             else
  700.               tile.z = 32 + (tile.y/32) * 32 + @priorities[tile_id] * 32
  701.             end
  702.           end
  703.           # If empty tile
  704.           if tile_id == 0
  705.             tile.bitmap = RPG::Cache.picture('')
  706.             tile.src_rect.set(0,0,0,0)
  707.           # If not an autotile
  708.           elsif tile_id >= 384
  709.             tile.bitmap = @tileset
  710.             tile.src_rect.set(((tile_id - 384) % 8) * 32,((tile_id - 384) / 8) *32, 32, 32)
  711.           else # Autotile
  712.             auto_id = tile_id/48-1
  713.             tile.bitmap = @autotiles[auto_id]
  714.             tile.src_rect.set(((tile_id % 48) % 8)*32 + @current_frame[auto_id] * 256,((tile_id % 48) / 8)*32, 32, 32)
  715.             @animating_tiles[tile.tile_sprite_id] = tile if @total_frames[auto_id] > 1
  716.           end
  717.         }
  718.       }
  719.      
  720.       @corner_index = (@corner_index + (SCREEN[0]/32+2)) % modTileId
  721.     else
  722.       row_num = (@corner_index - (SCREEN[0]/32+2)) % modTileId
  723.       row_num /= SCREEN[0]/32+2
  724.       row_num *= SCREEN[0]/32+2
  725.       (0...(SCREEN[0]/32+2)).each{|n|
  726.         # Run through each triad of sprites from left to right
  727.         j = n + row_num
  728.         @tile_sprites[j].each_index{|i|
  729.           # Get each individual tile on each layer
  730.           tile = @tile_sprites[j][i]
  731.           @animating_tiles.delete(tile.tile_sprite_id)
  732.           tile.y -= 64 + SCREEN[1]
  733.           # Determine what map coordinate this tile now resides at...
  734.           map_x, map_y = (tile.x+@ox)/32, (tile.y+@oy)/32
  735.           # ...and get its tile_id
  736.           tile_id = @map_data[map_x,map_y,i]
  737.           # If no tile exists here (effectively out of array bounds)
  738.           if tile_id.nil?
  739.             tile.z = [map_y * 32, 0].max
  740.             tile.bitmap = RPG::Cache.picture('')
  741.             tile.src_rect.set(0,0,0,0)
  742.             next
  743.           else # Tile exists. Figure out its z-coordinate based on priority
  744.             if @priorities[tile_id] == 0
  745.               tile.z = 0
  746.             else
  747.               tile.z = 32 + (tile.y/32) * 32 + @priorities[tile_id] * 32
  748.             end
  749.           end
  750.           # If empty tile
  751.           if tile_id == 0
  752.             tile.bitmap = RPG::Cache.picture('')
  753.             tile.src_rect.set(0,0,0,0)
  754.           # If not an autotile
  755.           elsif tile_id >= 384
  756.             tile.bitmap = @tileset
  757.             tile.src_rect.set(((tile_id - 384) % 8) * 32,((tile_id - 384) / 8) *32, 32, 32)
  758.           else # Autotile
  759.             auto_id = tile_id/48-1
  760.             tile.bitmap = @autotiles[auto_id]
  761.             tile.src_rect.set(((tile_id % 48) % 8)*32 + @current_frame[auto_id] * 256,((tile_id % 48) / 8)*32, 32, 32)
  762.             @animating_tiles[tile.tile_sprite_id] = tile if @total_frames[auto_id] > 1
  763.           end
  764.         }
  765.       }
  766.       @corner_index = (@corner_index - (SCREEN[0]/32+2)) % modTileId
  767.     end
  768.     row_num = @corner_index
  769.     end # end of while
  770.   end
  771.   #-----------------------------------------------------------------------------
  772.   # Dispose all the tile sprites
  773.   #-----------------------------------------------------------------------------
  774.   def dispose
  775.     # Dispose all of the sprites
  776.     @tile_sprites.each {|set| set.each{|tile| tile.dispose }}
  777.     @tile_sprites.clear
  778.     @animating_tiles.clear
  779.   end
  780.   #-----------------------------------------------------------------------------
  781.   # Set map data
  782.   #-----------------------------------------------------------------------------
  783.   def map_data=(data)
  784.     # Set the map data to new class
  785.     if data.is_a?(Tilemap_DataTable)
  786.       @map_data = data
  787.     else
  788.       @map_data = Tilemap_DataTable.new(data)
  789.     end
  790.     @map_data.table = @map_data.table.clone
  791.     @map_data.updates = []
  792.    
  793.     @animating_tiles.clear
  794.     @tilemap_drawn = false
  795.   end
  796.   #-----------------------------------------------------------------------------
  797.   # Update the tile sprites; make changes to the map_data and update autotiles
  798.   #-----------------------------------------------------------------------------
  799.   def update
  800.     # Can't update anything if the ox and oy have not yet been set
  801.     return if @ox_oy_set != [true, true]
  802.     # If the tilemap sprites have not been initialized, GO DO IT
  803.     if !@tilemap_drawn
  804.       init_tiles
  805.     end
  806.    
  807.     # If made any changes to $game_map.data, the proper graphics will be drawn
  808.     if @map_data.updated
  809.       @map_data.updates.each{|item|
  810.         x,y,z,tile_id = item
  811.         # If this changed tile is visible on screen
  812.         if x.between?(@corner_tile_loc[0], @corner_tile_loc[0]+(SCREEN[0]/32 + 1)) and
  813.         y.between?(@corner_tile_loc[1], @corner_tile_loc[1]+(SCREEN[1]/32 + 1))
  814.          
  815.           x_dif = x - @corner_tile_loc[0]
  816.           y_dif = y - @corner_tile_loc[1]
  817.          
  818.           id = @corner_index + x_dif
  819.           id -= SCREEN[0]/32+2 if id/(SCREEN[0]/32+2) > @corner_index/(SCREEN[0]/32+2)
  820.          
  821.           id += y_dif * (SCREEN[0]/32+2)
  822.           id -= (SCREEN[0]/32+2)*(SCREEN[1]/32+2) if id >= (SCREEN[0]/32+2)*(SCREEN[1]/32+2)
  823.          
  824.           tile = @tile_sprites[id][z]
  825.           @animating_tiles.delete(tile.tile_sprite_id)
  826.           #Figure out its z-coordinate based on priority
  827.           if @priorities[tile_id] == 0
  828.             tile.z = 0
  829.           else
  830.             tile.z = 32 + (tile.y/32) * 32 + @priorities[tile_id] * 32
  831.           end
  832.           # If empty tile
  833.           if tile_id == 0
  834.             tile.bitmap = RPG::Cache.picture('')
  835.             tile.src_rect.set(0,0,0,0)
  836.           # If not an autotile
  837.           elsif tile_id >= 384
  838.             tile.bitmap = @tileset
  839.             tile.src_rect.set(((tile_id - 384) % 8) * 32,((tile_id - 384) / 8) *32, 32, 32)
  840.           else # Autotile
  841.             auto_id = tile_id/48-1
  842.             tile.bitmap = @autotiles[auto_id]
  843.             tile.src_rect.set(((tile_id % 48) % 8)*32 + @current_frame[auto_id] * 256,((tile_id % 48) / 8)*32, 32, 32)
  844.             @animating_tiles[tile.tile_sprite_id] = tile if @total_frames[auto_id] > 1
  845.           end
  846.         end
  847.       }
  848.       @map_data.updates = []
  849.     end
  850.  
  851.     # Update the sprites.
  852.     if Graphics.frame_count % $game_map.autotile_speed == 0
  853.       # Increase current frame of tile by one, looping by width.
  854.       for i in 0..6
  855.         @current_frame[i] = (@current_frame[i] + 1) % @total_frames[i]
  856.       end
  857.       @animating_tiles.each_value{|tile|
  858.         frames = tile.bitmap.width
  859.         tile.src_rect.set((tile.src_rect.x + 256) % frames, tile.src_rect.y, 32, 32)
  860.       }
  861.     end
  862.   end
  863. end
  864.  
  865. #===============================================================================
  866. # Game_Map
  867. #===============================================================================
  868.  
  869. class Game_Map
  870.  
  871.   attr_reader :tile_size, :autotile_speed, :autotile_data, :priority_data
  872.  
  873.   alias zer0_load_autotile_data_init initialize
  874.   def initialize
  875.     # Call original method.
  876.     zer0_load_autotile_data_init
  877.     # Store the screen dimensions in tiles to save on calculations later.
  878.     @tile_size = [SCREEN[0], SCREEN[1]].collect {|n| (n / 32.0).ceil }
  879.     @autotile_speed = UPDATE_COUNT
  880.   end
  881.  
  882.   alias zer0_map_edge_setup setup
  883.   def setup(map_id)
  884.     # Call original method.
  885.     zer0_map_edge_setup(map_id)
  886.     # Change Map's data into a special Table class
  887.     @map.data = Tilemap_DataTable.new(@map.data)
  888.     # Find the displayed area of the map in tiles. No calcualting every step.
  889.     @map_edge = [self.width - @tile_size[0], self.height - @tile_size[1]]
  890.     @map_edge.collect! {|size| size * 128 }
  891.   end
  892.  
  893.   def scroll_down(distance)
  894.     # Find point that the map edge meets the screen edge, using custom size.
  895.     @display_y = [@display_y + distance, @map_edge[1]].min
  896.   end
  897.  
  898.   def scroll_right(distance)
  899.     # Find point that the map edge meets the screen edge, using custom size.
  900.     @display_x = [@display_x + distance, @map_edge[0]].min
  901.   end
  902.  
  903.   def autotile_speed=(speed)
  904.     # Keep the speed above 0 to prevent the ZeroDivision Error.
  905.     @autotile_speed = speed
  906.     @autotile_speed = 1 if @autotile_speed < 1
  907.   end
  908.  
  909. end
  910.  
  911. #===============================================================================
  912. # ** Game_Player
  913. #===============================================================================
  914.  
  915. class Game_Player
  916.  
  917.   CENTER_X = ((SCREEN[0] / 2) - 16) * 4    # Center screen x-coordinate * 4
  918.   CENTER_Y = ((SCREEN[1] / 2) - 16) * 4    # Center screen y-coordinate * 4
  919.  
  920.   def center(x, y)
  921.     # Recalculate the screen center based on the new resolution.
  922.     max_x = ($game_map.width - $game_map.tile_size[0]) * 128
  923.     max_y = ($game_map.height - $game_map.tile_size[1]) * 128
  924.     $game_map.display_x = [0, [x * 128 - CENTER_X, max_x].min].max
  925.     $game_map.display_y = [0, [y * 128 - CENTER_Y, max_y].min].max
  926.   end  
  927. end
  928.  
  929. #===============================================================================
  930. # ** Sprite
  931. #===============================================================================
  932. class Sprite
  933.   attr_accessor :tile_sprite_id
  934.   alias tile_sprite_id_init initialize
  935.   def initialize(view = nil)
  936.     # No defined ID
  937.     @tile_sprite_id = nil
  938.     # Call original method.
  939.     tile_sprite_id_init(view)
  940.   end
  941. end
  942.  
  943. #===============================================================================
  944. # ** Viewport
  945. #===============================================================================
  946. class Viewport
  947.  
  948.   alias zer0_viewport_resize_init initialize
  949.   def initialize(x=0, y=0, width=SCREEN[0], height=SCREEN[1], override=false)
  950.     if x.is_a?(Rect)
  951.       # If first argument is a Rectangle, just use it as the argument.
  952.       zer0_viewport_resize_init(x)
  953.     elsif [x, y, width, height] == [0, 0, 640, 480] && !override
  954.       # Resize fullscreen viewport, unless explicitly overridden.
  955.       zer0_viewport_resize_init(Rect.new(0, 0, SCREEN[0], SCREEN[1]))
  956.     else
  957.       # Call method normally.
  958.       zer0_viewport_resize_init(Rect.new(x, y, width, height))
  959.     end
  960.   end
  961.  
  962.   def resize(*args)
  963.     # Resize the viewport. Can call with (X, Y, WIDTH, HEIGHT) or (RECT).
  964.     self.rect = args[0].is_a?(Rect) ? args[0] : Rect.new(*args)
  965.   end
  966. end
  967. #===============================================================================
  968. # ** Plane
  969. #===============================================================================
  970.  
  971. class Plane < Sprite
  972.  
  973.   def z=(z)
  974.     # Change the Z value of the viewport, not the sprite.
  975.     super(z * 1000)
  976.   end
  977.  
  978.   def ox=(ox)
  979.     return if @bitmap == nil
  980.     # Have viewport stay in loop on X-axis.
  981.     super(ox % @bitmap.width)
  982.   end
  983.  
  984.   def oy=(oy)
  985.     return if @bitmap == nil
  986.     # Have viewport stay in loop on Y-axis.
  987.     super(oy % @bitmap.height)
  988.   end
  989.  
  990.   def bitmap
  991.     # Return the single bitmap, before it was tiled.
  992.     return @bitmap
  993.   end
  994.  
  995.   def bitmap=(tile)
  996.     @bitmap = tile
  997.     # Calculate the number of tiles it takes to span screen in both directions.
  998.     xx = 1 + (SCREEN[0].to_f / tile.width).ceil
  999.     yy = 1 + (SCREEN[1].to_f / tile.height).ceil
  1000.     # Create appropriately sized bitmap, then tile across it with source image.
  1001.     plane = Bitmap.new(@bitmap.width * xx, @bitmap.height * yy)
  1002.     (0..xx).each {|x| (0..yy).each {|y|
  1003.       plane.blt(x * @bitmap.width, y * @bitmap.height, @bitmap, @bitmap.rect)
  1004.     }}
  1005.     # Set the bitmap to the sprite through its super class (Sprite).
  1006.     super(plane)
  1007.   end
  1008.  
  1009.   # Redefine methods dealing with coordinates (defined in super) to do nothing.
  1010.   def x; end
  1011.   def y; end
  1012.   def x=(x); end
  1013.   def y=(y); end
  1014. end
  1015. #===============================================================================
  1016. # ** Integer
  1017. #===============================================================================
  1018.  
  1019. class Integer
  1020.    
  1021.   def gcd(num)
  1022.     # Returns the greatest common denominator of self and num.
  1023.     min, max = self.abs, num.abs
  1024.     while min > 0
  1025.       tmp = min
  1026.       min = max % min
  1027.       max = tmp
  1028.     end
  1029.     return max
  1030.   end
  1031.  
  1032.   def lcm(num)
  1033.     # Returns the lowest common multiple of self and num.
  1034.     return [self, num].include?(0) ? 0 : (self / self.gcd(num) * num).abs
  1035.   end
  1036. end
  1037. #===============================================================================
  1038. # ** Resolution Log
  1039. #===============================================================================
  1040. if RESOLUTION_LOG
  1041.     undersize, mapinfo = [], load_data('Data/MapInfos.rxdata')
  1042.     # Create a text file and write the header.
  1043.     file = File.open('Resolution Log.txt', 'wb')
  1044.     file.write("[RESOLUTION LOG]\r\n\r\n")
  1045.     time = Time.now.strftime("%x at %I:%M:%S %p")
  1046.     file.write("  Logged on #{time}\r\n\r\n")
  1047.     lcm = SCREEN[0].lcm(SCREEN[1]).to_f
  1048.     aspect = [(lcm / SCREEN[1]), (lcm / SCREEN[0])].collect {|num| num.round }
  1049.     file.write("RESOLUTION:\r\n  #{SCREEN[0].to_i} x #{SCREEN[1].to_i}\r\n")
  1050.     file.write("ASPECT RATIO:\r\n  #{aspect[0]}:#{aspect[1]}\r\n")
  1051.     file.write("MINIMUM MAP SIZE:\r\n  #{(SCREEN[0] / 32).ceil} x #{(SCREEN[1] / 32).ceil}\r\n\r\n")
  1052.     file.write("UNDERSIZED MAPS:\r\n")
  1053.     mapinfo.keys.each {|key|
  1054.       map = load_data(sprintf("Data/Map%03d.rxdata", key))
  1055.       next if map.width*32 >= SCREEN[0] && map.height*32 >= SCREEN[1]
  1056.       undersize.push(key)
  1057.     }
  1058.     unless undersize.empty?
  1059.       file.write("The following maps are too small for the defined resolution. They should be adjusted to prevent graphical errors.\r\n\r\n")
  1060.       undersize.sort.each {|id| file.write("    MAP[#{id}]:  #{mapinfo[id].name}\r\n") }
  1061.       file.write("\r\n")
  1062.     else
  1063.       file.write('    All maps are sized correctly.')
  1064.     end
  1065.     file.close
  1066. end
  1067.  
  1068. # Call the resolution, setting it to a global variable for plug-ins.
  1069. $resolution = Resolution.new
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement