The RPG Maker Resource Kit

RMRK RPG Maker Creation => VX => VX Scripts Database => Topic started by: exhydra on June 14, 2011, 03:18:04 AM

Title: Shake Window [1.5]
Post by: exhydra on June 14, 2011, 03:18:04 AM
Shake Window
Version: 1.5
Author: Exhydra
Date: 6-14-2011

Version History



Description


Shaking the screen not enough to emphasize something that happened in game? Want the metor you just dropped on the players to rock more than the pixels inside of the game? Shake Window will allow you to kindly jiggle or ruthlessly rattle the game window in custom directions as well as allow you to shake the screen while you're at it.

Features


Screenshots

None

Instructions

See Script Header

Script


Code: [Select]
#===============================================================================
# Shake Window
#===============================================================================
# Author       : Exhydra
# Version      : 1.5
# Last Updated : 06/20/2011
#===============================================================================
# Updates
# -----------------------------------------------------------------------------
# » 06/20/11 Cleaned up code; Changed WinAPIs slightly.
# » 06/14/11 [FIX] Maintained the game window position through rapid calls
#            [FIX] Determines if the game is full screen and runs a normal
#                  screen shake instead.
# » 06/13/11 Initial Release.
#===============================================================================
# Future Options
# -----------------------------------------------------------------------------
# » Nothing! Suggestions?
#===============================================================================
# Instructions
# -----------------------------------------------------------------------------
# To Use :
#   Call : (from a Script Event command)
#     gms = $game_map.screen
#     gms.shake_window(power, speed, duration[, route, ds_option])
#
#     'route' Values
#      7 8 9
#      4  (6) - Default; Right then Left (Valid for Window Shake ONLY)
#      1 2 3
#
#     'ds_option' Values
#       (0)   - Default; Shake Window Only
#        1    - Shake Window and Screen
#        2    - Shake Screen Only
#   
#   Examples :
#     gms = $game_map.screen
#     gms.shake_window(5, 5, 15)
#
#     gms = $game_map.screen
#     gms.shake_window(8, 8, 60, 9, 1)
#===============================================================================
# Description
# -----------------------------------------------------------------------------
# - Shakes the window of the game, or shakes the window and the screen or just
#   shakes the screen. This script allows the user to go beyond the limitations
#   of the Shake Screen command within the Event Editor. However, that is just
#   a by-product and can be easily done without including this script.
#===============================================================================

#===============================================================================
# [NOTE:] -
#===============================================================================

#===============================================================================
# -? Game_Screen
# -----------------------------------------------------------------------------
# Summary of Changes:
#    New Method(s)     - shake_window
#    Aliased Method(s) - clear, update_shake
#===============================================================================

class Game_Screen
  #--------------------------------------------------------------------------
  # » Clear                                                         [ Alias ]
  #--------------------------------------------------------------------------
  alias ex_shakewindow_clear clear unless $@
  def clear
    @sw_power = 0
    @sw_speed = 0
    @sw_duration = 0
    @sw_direction = 0
    @sw_shake = 0
    @sw_hwnd = 0
    @sw_szEval = ""
    @sw_rect = []
   
    ex_shakewindow_clear
  end
 
  #--------------------------------------------------------------------------
  # » Shake Window                                                    [ New ]
  #     power        : Shake Intensity
  #     speed        : Shake Speed
  #     duration     : Shake Duration (60 = 1 sec)
  #    [route]       : Direction Window will Shake Towards
  #     7 8 9
  #     4 ?(6) - Default; Right then Left (Valid for Window Shake ONLY)
  #     1 2 3
  #    [ds_option]   : 'Double Shake' Option
  #      (0)   - Default; Window Shake Only
  #       1    - Window and Screen Shake
  #       2    - Screen Shake Only
  #--------------------------------------------------------------------------
  def shake_window(power, speed, duration, route = 6, ds_option = 0)
    return if @sw_shake != 0
    ds_option      = 2 if Ex_WinAPI.CheckFullScreen == true
   
    case ds_option
      when 1
        start_shake(power, speed, duration)
      when 2
        start_shake(power, speed, duration)
        return
    end
   
    case route
      when 1
        @sw_direction = 1
        @szEval = "@sw_rect[0] + (-@sw_shake), @sw_rect[1] + (@sw_shake)"
      when 2
        @sw_direction = 1
        @szEval = "@sw_rect[0], @sw_rect[1] + (@sw_shake)"
      when 3
        @sw_direction = 1
        @szEval = "@sw_rect[0] + (@sw_shake), @sw_rect[1] + (@sw_shake)"
      when 4
        @sw_direction = -1
        @szEval = "@sw_rect[0] + (@sw_shake), @sw_rect[1]"
      when 6
        @sw_direction = 1
        @szEval = "@sw_rect[0] + (@sw_shake), @sw_rect[1]"
      when 7
        @sw_direction = -1
        @szEval = "@sw_rect[0] + (@sw_shake), @sw_rect[1] + (@sw_shake)"
      when 8
        @sw_direction = -1
        @szEval = "@sw_rect[0], @sw_rect[1] + (@sw_shake)"
      when 9
        @sw_direction = -1
        @szEval = "@sw_rect[0] + (-@sw_shake), @sw_rect[1] + (@sw_shake)"
      else
        @sw_direction = 1
        @szEval = "@sw_rect[0] + (@sw_shake), @sw_rect[1]"
    end
     
    @sw_power = power
    @sw_speed = speed
    @sw_duration = duration
    @sw_hwnd = Ex_WinAPI.AcquireGameWindow
    @sw_rect = Ex_WinAPI.AcquireGameWindowRect
  end

  #--------------------------------------------------------------------------
  # » Update Shake                                                  [ Alias ]
  #--------------------------------------------------------------------------
  alias ex_shakewindow_update_shake update_shake unless $@
  def update_shake
    if @sw_duration >= 1 or @sw_shake != 0
      sw_delta = (@sw_power * @sw_speed * @sw_direction) / 10.0
      if @sw_duration <= 1 and @sw_shake * (@sw_shake + sw_delta) < 0
        @sw_shake = 0
      else
        @sw_shake += sw_delta
      end
      if @sw_shake > @sw_power * 2
        @sw_direction = -1
      end
      if @sw_shake < - @sw_power * 2
        @sw_direction = 1
      end
      if @sw_duration >= 1
        @sw_duration -= 1
      end
     
      if Ex_WinAPI.CheckFullScreen != true
        eval("Ex_WinAPI.SetWindowPosition(@sw_hwnd, " + @szEval + ",
              @sw_rect[2] - @sw_rect[0], @sw_rect[3] - @sw_rect[1])")
      end
     
    end
   
    ex_shakewindow_update_shake
  end
end


class Ex_WinAPI
  #--------------------------------------------------------------------------
  # » Windows API Declarations                                [ Windows API ]
  #--------------------------------------------------------------------------
  @@getCurrentThreadId       = Win32API.new('kernel32' ,'GetCurrentThreadId'      , ['']                                , 'L')
  @@getWindowLong            = Win32API.new('user32'   ,'GetWindowLongA'          , ['L', 'I']                          , 'L')
  @@getWindowRect            = Win32API.new('user32'   ,'GetWindowRect'           , ['L', 'P']                          , 'L')
  @@getWindowThreadProcessId = Win32API.new('user32'   ,'GetWindowThreadProcessId', ['L', 'P']                          , 'L')
  @@getClientRect            = Win32API.new('user32'   ,'GetClientRect'           , ['L', 'P']                          , 'L')
  @@setWindowPosition        = Win32API.new('user32'   ,'SetWindowPos'            , ['L', 'L', 'I', 'I', 'I', 'I', 'I'] , 'I')
  @@findWindowEx             = Win32API.new('user32'   ,'FindWindowEx'            , ['L', 'L', 'P', 'P']                , 'L')

  #--------------------------------------------------------------------------
  # » Constants
  #--------------------------------------------------------------------------
  @@gwl_STYLE                = -16
 
  @@ws_MINIMIZEBOX           = 0x20000
  @@ws_DLGFRAME              = 0x00400000
  @@ws_BORDER                = 0x00800000
  @@ws_SYSMENU               = 0x00080000
  @@ws_CLIPSIBLINGS          = 0x04000000
  @@ws_VISIBLE               = 0x10000000

  @@ws_RGSS_NORM             = @@ws_SYSMENU | @@ws_CLIPSIBLINGS | @@ws_VISIBLE |
                               @@ws_BORDER  | @@ws_DLGFRAME     | @@ws_MINIMIZEBOX
 
  #--------------------------------------------------------------------------
  # » Variables
  #--------------------------------------------------------------------------
  @@gameHwnd                 = nil
 
  #--------------------------------------------------------------------------
  # » Acquire Game Window                                     [ Windows API ]
  #--------------------------------------------------------------------------
  def self.AcquireGameWindow
    return @@gameHwnd if @@gameHwnd
    procID     = [0].pack('L')
    threadID   = @@getCurrentThreadId.call
    gameTitle = ($data_system.nil?) ? 0 : $data_system.game_title
    @@gameHwnd = 0

    begin
      @@gameHwnd = @@findWindowEx.call(0, @@gameHwnd, "RGSS Player", gameTitle)

      if @@gameHwnd
        wndThreadID = @@getWindowThreadProcessId.call(@@gameHwnd, procID)
       
        return @@gameHwnd unless wndThreadID != threadID; gameTitle = 0
      end
    end until @@gameHwnd == 0

    raise "ERROR : RGSS player window not found!"
    return 0
  end

  #--------------------------------------------------------------------------
  # » Acquire Game Window Long                                [ Windows API ]
  #--------------------------------------------------------------------------
  def self.AcquireGameWindowLong
    wHwnd   = self.AcquireGameWindow
   
    return @@getWindowLong.call(wHwnd, @@gwl_STYLE)
  end
 
  #--------------------------------------------------------------------------
  # » Acquire Game Window Rect                                [ Windows API ]
  #--------------------------------------------------------------------------
  def self.AcquireGameWindowRect
    aRect      = [0,0,0,0].pack('L4')
    wHwnd      = self.AcquireGameWindow
   
    @@getWindowRect.call(wHwnd, aRect)
    l, t, r, b = aRect.unpack('L4')
    return l, t, r, b
  end
 
  #--------------------------------------------------------------------------
  # » Acquire Client Screen Rect                              [ Windows API ]
  #--------------------------------------------------------------------------
  def self.AcquireClientScreenRect
    aScreen    = [0,0,0,0].pack('L4')
    wHwnd      = self.AcquireGameWindow
   
    @@getClientRect.call(wHwnd, aScreen)
    l, t, r ,b = aScreen.unpack('L4')
    return l, t, r, b
  end

  #--------------------------------------------------------------------------
  # » Check Full Screen                                       [ Windows API ]
  #--------------------------------------------------------------------------
  def self.CheckFullScreen
    aRect        = self.AcquireGameWindowRect
    aScreen      = self.AcquireClientScreenRect
    lCurrScreen  = self.AcquireGameWindowLong

    if aRect[0] <= aScreen[0] and
       aRect[1] <= aScreen[1] and
       aRect[2] >= aScreen[2] and
       aRect[3] >= aScreen[3] or
       lCurrScreen | @@ws_RGSS_NORM != @@ws_RGSS_NORM
      return true
    else
      return false
    end
  end
 
  #--------------------------------------------------------------------------
  # » Set Window Position                                     [ Windows API ]
  #     window_hWnd  : Window HWND
  #     window_x     : Window Screen X (Top Left)
  #     window_y     : Window Screen Y (Top Left)
  #     window_w     : Window Width
  #     window_h     : Window Height
  #--------------------------------------------------------------------------
  def self.SetWindowPosition(window_hWnd, window_x, window_y, window_w, window_h)
    return @@setWindowPosition.call(window_hWnd, 0, window_x, window_y, window_w, window_h, 0)
  end

end

Credit



Thanks


Support



Known Compatibility Issues


Demo


None

Author's Notes



Restrictions

Title: Re: Shake Window
Post by: pacdiggity on June 14, 2011, 10:43:57 AM
Looks great Exhyrda, a nice script to enter the scripting scene proper. ;)
For your issues regarding the window falling off the screen, if you have a conditional in an update method or something similar to check if window.y < 0 or window.y > 544 (off the top of my head, I can't remember the proper dimensions XD) you can just have a method run to keep the window on the screen. I'll give it a shot later tonight.
For your full screen thingamy, you could use a similar way of finding that out: there are API's required to make the game window fullscreen, but I can't remember any of them... yeah. I'll get back to you later on about that.
Looks cool, welcome to the world of scripting (MA's the boss). I look forward to more works.
Title: Re: Shake Window
Post by: Arrow on June 14, 2011, 11:22:47 AM
Very cool script, bro.
Title: Re: Shake Window
Post by: pacdiggity on June 14, 2011, 11:59:48 AM
Okay, I think I found that Win32API thing I was talking about. From an old full-screen I wrote yonks ago, I found that to activate fullscreen I used the following call.
Code: [Select]
    $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
So in a conditional to test if the window is in fullscreen, see if you can't use this.
Code: [Select]
if $keybd == Win32API.new ('user32.dll', 'keybd_event', ['i', 'i', 'l', 'l'], 'v')
I think that should work, I'll give it a shot when I'm on a computer with VX.
EDIT:: Dang, doesn't work. I'll look into it more.
Title: Re: Shake Window
Post by: cozziekuns on June 14, 2011, 04:56:07 PM
Not the most elegant solution, but you can run a check for Ex_WinAPI.AcquireGameWindow to see if it's [0, 0, 640, 480], which is always the value that RPG Maker VX uses for its fullscreen option.

Otherwise, really neat script!
Title: Re: Shake Window
Post by: exhydra on June 14, 2011, 06:14:02 PM
Hmm, stopping the screen slide was easier than I thought. I just made sure that @sw_shake was always zero before the script could be started again ('return if @sw_shake != 0') and that seems to work fine. I shoved version 1.1 into a parallel process and it didn't budge after an extended period of time. I also cleaned up some of the @szEval code and made it smaller, although I still think there must be an easier way to do what I did.

However, checking for full screen is still a bit of a problem. I don't think it would be reliable to check for a certain screen size, as people may alter theirs with resolution modifying scripts. What I'm probably going to have to do is check the game window size versus the monitor window size, and if they overlap then the game is full screen. I've been scouring for an easy solution within the RPGVX data structures, looking for a flag that declares that full screen is enabled, but it doesn't look like there is one. You'd think that there would be ... ah well.

Thanks for the feedback so far! 

EDIT : Here's a work-around script, but it feels kind of dirty how I'm determining full screen. Basically comparing GetWindowLong information beside each other instead of checking for a specific window style. I updated the code in the original post.

EDIT II : Here is the code that I tried using before, and it seems to me that it would be better for me to narrow down which window styles (WS_STYLE) are being changed rather than just comparing one GetWindowLong number against the other. I commented in where I was running into problems. All of the available window styles are listed here :

Normal Window Styles : http://msdn.microsoft.com/en-us/library/ms632600%28v=vs.85%29.aspx (http://msdn.microsoft.com/en-us/library/ms632600%28v=vs.85%29.aspx)
Extended Window Styles : http://msdn.microsoft.com/en-us/library/ff700543(v=vs.85).aspx (http://msdn.microsoft.com/en-us/library/ff700543(v=vs.85).aspx)

Code: [Select]
class Ex_FSCheck
  # Changing GWL_EXSTYLE to -20 enables checking for extended window styles
  @GWL_STYLE = -16

  @WS_CAPTION = 0x00C00000
  @WS_VISIBLE = 0x10000000
  @WS_DISABLED = 0x08000000
  @WS_MAXIMIZEBOX = 0x10000
  @WS_MINIMIZEBOX = 0x20000
  @WS_POPUP = 0x80000000

  @WS_EX_MDICHILD = 0x00000040
  @WS_EX_COMPOSITED = 0x02000000
 
  def self.AcquireGameWindowLong(nIndex = @GWL_STYLE)
    get_long = Win32API.new('user32.dll', 'GetWindowLongA', ['L', 'I'], 'l')
    window_hwnd = self.AcquireGameWindow
    wnd_tmp = get_long.call(window_hwnd, nIndex)

    # Is this how I would check these two against each other?
    # This -always- returns true. Even if I use @WS_DISABLED, and I know the window certainly isn't disabled.
    if wnd_tmp && @WS_POPUP == @WS_POPUP
      return true
    else
      return false
    end
  end

  def self.AcquireGameWindow
    window_title = GetPrivateProfileString("Game", "Title", "", "./Game.ini")
    return FindWindow("RGSS Player", window_title)
  end
 
  def self.FindWindow(window_class, window_title)
    find_window = Win32API.new('user32.dll', 'FindWindowA', ['P', 'P'], 'l')
    return find_window.call(window_class, window_title)
  end
 
  def self.GetPrivateProfileString(ini_section, ini_key, default_val, ini_file)
    get_string = Win32API.new('kernel32.dll', 'GetPrivateProfileStringA', ['P', 'P', 'P', 'P', 'L', 'P'], 'l')
    sz_buffer = "\0" * (256 + 1)
    get_string.call(ini_section, ini_key, default_val, sz_buffer, 256, ini_file)
    return sz_buffer.delete!("\0")
  end
end


EDIT III : I found another way to check for full screen, but I feel that it's also a bit flimsy. Well, either way I have lined out works fine, but I'm sure that there are better ways of going about doing it ...

EDIT IV : Alright, narrowed it down a little further. Close enough, I think.

Code: [Select]
class Ex_FSCheck
#===============================================================================
# Full Screen Check
#===============================================================================
# Author       : Exhydra
# Version      : 1.5
# Last Updated : 07/14/2011
#===============================================================================
  #--------------------------------------------------------------------------
  # ? Windows API Declarations                                [ Windows API ]
  #--------------------------------------------------------------------------
  @getCurrentThreadId       = Win32API.new('kernel32', 'GetCurrentThreadId'      , ['']                  , 'L')
  @getWindowRect            = Win32API.new('user32',   'GetWindowRect'           , ['L', 'P']            , 'L')
  @getWindowThreadProcessId = Win32API.new('user32',   'GetWindowThreadProcessId', ['L', 'P']            , 'L')
  @getClientRect            = Win32API.new('user32',   'GetClientRect'           , ['L', 'P']            , 'L')
  @findWindowEx             = Win32API.new('user32',   'FindWindowEx'            , ['L', 'L', 'P', 'P']  , 'L')

  #--------------------------------------------------------------------------
  # ? Acquire Game Window                                     [ Windows API ]
  #--------------------------------------------------------------------------
  def self.AcquireGameWindow
    procID     = [0].pack('L')
    threadID   = @getCurrentThreadId.call
    nextWindow = 0
   
    begin
      nextWindow = @findWindowEx.call(0, nextWindow, "RGSS Player", 0)
     
      if nextWindow
        wndThreadID = @getWindowThreadProcessId.call(nextWindow, procID)
       
        if wndThreadID == threadID
          return nextWindow
        end
      end
    end until nextWindow == 0
   
    raise "ERROR : RGSS player window not found!"
    return 0
  end

  #--------------------------------------------------------------------------
  # ? Acquire Game Window Rect                                [ Windows API ]
  #--------------------------------------------------------------------------
  def self.AcquireGameWindowRect
    aRect      = [0,0,0,0].pack('L4')
    wHwnd      = self.AcquireGameWindow
   
    @getWindowRect.call(wHwnd, aRect)
    l, t, r, b = aRect.unpack('L4')
    return l, t, r, b
  end
 
  #--------------------------------------------------------------------------
  # ? Acquire Client Screen Rect                              [ Windows API ]
  #--------------------------------------------------------------------------
  def self.AcquireClientScreenRect
    aScreen    = [0,0,0,0].pack('L4')
    wHwnd      = self.AcquireGameWindow
   
    @getClientRect.call(wHwnd, aScreen)
    l, t, r ,b = aScreen.unpack('L4')
    return l, t, r, b
  end

  #--------------------------------------------------------------------------
  # ? Check Full Screen                                       [ Windows API ]
  #--------------------------------------------------------------------------
  def self.CheckFullScreen
    aRect         = self.AcquireGameWindowRect
    aScreen       = self.AcquireClientScreenRect
   
    if aRect[0] <= aScreen[0] and
       aRect[1] <= aScreen[1] and
       aRect[2] >= aScreen[2] and
       aRect[3] >= aScreen[3]
      return true
    else
      return false
    end
  end
 
end