Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

Scripter's corner : Snippets, Classes, Tips and more

SephirothSpawn":2yduw7ji said:
I came up with some pretty cool method modifications for the Hash class that allows you to read and set multi-dimensioned hashes without the work it had before. It also allows you to use classes with [] and []= methods.

...

Couldn't this be done simply with default values? Like so:

[rgss]MULTI_HASH = Proc.new { |hash, key| hash[key] = Hash.new(&MULTI_HASH) }
@hash = Hash.new(&MULTI_HASH)
@hash[0][2] = 4
print @hash # {0=>{2=>4}}
[/rgss]

Haven't tested it, and it could be tricky since MULTI_HASH is recursive. It might be better done as a class with a recursive initializer (again, not tested):

[rgss]class Multi_Hash
  def initialize(depth = 0, default = nil, &default_block)
    if depth == 0
      super(default, &default_block)
    else
      super do |hash, key|
        hash[key] = Multi_Hash(depth - 1, default, &default_block)
      end
    end
  end
  def has_key?(*args)
    return false if args.size == 0
    return super(args[0]) if args.size == 1
    return self[args.shift].has_key(*args)
  end
end
[/rgss]
 

Zeriab

Sponsor

I have created a snippet which reads the the configurations from the F1 menu and removes game pad support: (Keep going left/right? If this fixes the problem then garbage from a (non-existing?) game pad is the cause)

http://paste-bin.com/view/a84fb1d3 (XP)
http://paste-bin.com/view/5fa9f97f (VX)

Changes in the F1 menu are first added to the registry when the game is closed, so you cannot use the method for checking changes at runtime.

I hope that scripters of input modules will use this information to make their scripts even more awesome :3

*huggles*
 
Just remember that you can't automatically save Procs with Marshal.dump. (You'll need to write your own _dump method to include such feature.)
 

Zeriab

Sponsor

Bad idea since it would cause debug mode to be enabled in VX whenever you press F12. (Maybe it won't be a problem for encrypted games RMVX games)
You are also forgetting a space.

Btw. notice that exec is a Kernel method just like system, but that I can make a local variable using the same name.

*hugs*
 

regi

Sponsor

Generally there's nothing wrong in trying to simplify code, but too many conditionals in one line can clutter code or make it hard to understand. Nonetheless both methods work.
 

Zeriab

Sponsor

My mistake about the spaces >_>

It looks like it will work just fine (I haven't checked)
It will complicate the snippet unnecessarily, but it is nice that you are trying to experiment with different structures :cute:

*hugs*
 
Hey guys: Here is a Tool Set script i finally made since i'm using it quite a lot, easier to have in one place !

Originally Made when I started Castlevania 3 years ago; I today compiled all in one place and improved the code I used a tiny bit.

Everything is explained in the Comments, I think you'll like it a lot !

[rgss]<span style="color:#000080; font-style:italic;">=begin
<span style="color:#000080; font-style:italic;">#===============================================================================
<span style="color:#000080; font-style:italic;">IDE Scripts
<span style="color:#000080; font-style:italic;">by Trebor777
<span style="color:#000080; font-style:italic;">v1.00
<span style="color:#000080; font-style:italic;">27/09/2010
<span style="color:#000080; font-style:italic;">Set of methods to help working on scripts outside of RM:
<span style="color:#000080; font-style:italic;">.Contain method to extract all the scripts
<span style="color:#000080; font-style:italic;">.to load or require scripts
<span style="color:#000080; font-style:italic;">.Create a console to override "p" dialog
<span style="color:#000080; font-style:italic;">#===============================================================================
<span style="color:#000080; font-style:italic;">=end
#-------------------------------------------------------------------------------
# Require a file, only once, return true if loaded properly
#-------------------------------------------------------------------------------
def require(dll)
  $LOAD_PATH << "./"
  p "Loading... #{dll}"
  Kernel.send:)require,dll)
end
#-------------------------------------------------------------------------------
# Load a file, like require, but can be called again if any change
# return true if loaded properly
#-------------------------------------------------------------------------------
def load(dll)
  $LOAD_PATH << "./"
  p "Loading... #{dll}"
  begin
    Kernel.send:)load,dll)
  rescue => error
    send_error(error)
  end
end
#-------------------------------------------------------------------------------
# Will extract all the scripts in rb files ordered with 3digits, and their name:
# 000_script_name.rb
# TAKES an HASH as argument, allows more flexibility for the parameters,
# and easier to read understand # inspired by ruby 1.9.1
#
# You can skip some scripts by providing an array containing the names of the
# scripts to skip, like this:
#                            extract_scripts({ :except => ["Main"] })
# will extract all the scripts except "Main"
#
# You can provide the name of a folder to extract the script ( will extract at
# the root of the folder if none provided:
#                             extract_scripts({ :folder => "My_scriptfolder/" })
#-------------------------------------------------------------------------------
def extract_scripts(options={})
  except = options[:except] || [] # Define Default Values
  folder = options[:folder] || ""
  id = 0 # Set file ID
  $RGSS_SCRIPTS.each do |script|
    name = script[1] # Extract Script Name
    data = script[3] # Extract Code
    next if except.include? name or name.empty? or data.empty? # Skip invalid values
    filename = sprintf("%03d", id) + "_" + name # Generate base file name
    p "Writing: #{filename}"
    File.open(folder+filename+".rb", "wb") do |file|
      file.write data #Write the code
    end
    id += 1 # Increment file ID
  end
end
#-------------------------------------------------------------------------------
# Load the all the scripts(rb files) from a specific folder, up to id "limit",
# and skipping ids in skip
 
# TAKES an HASH as argument, allows more flexibility for the parameters,
# and easier to read understand # inspired by ruby 1.9.1
 
# Example:
# .Load all from folder in Data/scripts: load_scripts( {:folder => "Data/Scripts/" })
# .Load all from root, but skip number 3 and 45: load_scripts({ :skip => [3,45] })
# .Load all from root, up to script with number 62: load_script({ :limit => 62 })
#-------------------------------------------------------------------------------
def load_scripts(options={})
  skip = options[:skip] || []
  folder = options[:folder] || ""
  begin
    # Load the language selection 1st
    Dir.foreach(folder) do |file|
      file_id = file[0,3].to_i
      break if file_id == options[:limit]
      next if file == "." or file == ".." or File.extname(file) != ".rb" or skip.include?(file_id)
      r = load (folder+"#{file}")
      p "Loaded: #{r}"
    end  
  rescue => error
    send_error(error)
  end
end
#-------------------------------------------------------------------------------
if $DEBUG or $TEST
  #-----------------------------------------------------------------------------
  def p(*args) # Output to Console
    args.each { | arg | puts arg.inspect }
  end
  #-----------------------------------------------------------------------------
  def handle
    game="\0"*256
    ini=Win32API.new('kernel32','GetPrivateProfileStringA','pppplp', 'l')
    ini.call('Game','Title','',game,255,".\\Game.ini")
    return Win32API.new('user32','FindWindowEx','llpp','l').call(0,0,nil,game.delete!("\0"))
  end
  #-----------------------------------------------------------------------------
  # Create Console
  #-----------------------------------------------------------------------------
  Win32API.new("kernel32", "AllocConsole", "V", "L").call
  $stdout.reopen("CONOUT$")
  $stdin.reopen('CONIN$')
  Win32API.new("user32", "SetForegroundWindow", "L", "L").call(handle)
  #-----------------------------------------------------------------------------
  # Print error on Console
  #-----------------------------------------------------------------------------
  def send_error(error)
    puts "Error : " + error
    #puts "At : ", caller
    puts "TRACE : " + error.backtrace.join("\n")
    puts " "
    puts " Going to EXIT - Press Enter to Continue - "
    gets if $DEBUG or $TEST
  end
end
[/rgss]


With this :) you can edit your code while testing the demo at the same time... :) if you use load_scripts, the edited scripts will be reloaded with F12 without problems, avoid losing time in closing demo, editing, saving, launch demo

Having a console to display your message, in a non blocking way(no dialogs freezing the game), helps a lot too ^^

HAve fun :thumb:
 
Well thanks a lot trebor. The p method override is a charm. But I don't understad how to edit the code live. can you be a little more especific.
Thanks in advance.
 
Well if you extract the scripts you want,
and then load them with the methods provided,

While you change your code on the extracted file, you can have the demo runnning :), and just F12 after you save the code, that reset the demo, and reload the extracted files :)
 
defining/redefining methods on a per object basis....???

Was sleeping and dreamed of rgss coding.... Any-who, in the dream I had an idea to create an "on_Mouse_Over event like thing for some buttons in my windows.

In my dream, my script looked something similar to (note, this is NOT actual code, but a mock-up):
[rgss]class My_Window < Window_Base
  def initialize
    super
    @button = Sprite.new
    @button.bitmap = Bitmap.new(button.png)
  end
end
[/rgss]

Well, instead of setting up an on_Mouse_Over in class Sprite, in my dream, I set an on_Mouse_Over on @button instead, like so:

[rgss]class My_Window < Window_Base
  def initialize
    super
    @button = Sprite.new
    @button.bitmap = Bitmap.new(button.png)
    set_on_Mouse_Over
  end
 
  def set_on_Mouse_Over
    def @button.on_Mouse_Over
      self.bitmap = Bitmap.new(button_active.png)
    end
end
[/rgss]

And lo and behold, if I call @button.on_Mouse_Over, it actually changes the graphic! in this way, I can custom define a method for each declared object seperately!

I may not have explained that very well, but I am still sort of half asleep... Weird how I come up with my best ideas in my sleep and they actually translate successfully to real life.

Another one you can actually insert into a project to test out. Real Code just insert it above main:

[rgss]class Foo
  def bar
    p "foobar"
  end
end
 
class Scene_Foo
  def initialize
    @foo = Foo.new
    @foo2 = Foo.new
    method_define
  end
 
  def method_define
    def @foo.barstool
      p "Standing"
    end
    def @foo2.bar
      p "drink please"
    end
  end
 
  def main
    @foo.bar       # prints "foobar"
    @foo.barstool  # prints "Standing"
    @foo2.bar      # prints "drink please"
    @foo2.barstool # Crashes with undefined method
  end
end
 
$scene = Scene_Foo.new
 
$scene.main
[/rgss]

This opens up a whole new world of possibilities....

Did anyone else know this was possible?

*runs off to get more ideas while sleeping*
 

Gust

Member

Pretty cool, I didn't know that was possible. Usually I would do that by storing Proc objects, which are code blocks.

Code:
 

class Button

  attr_accessor : on_mouse_over;

end

 

class Window

  def initialize

    @button = Button.new

    @button.on_mouse_over = Proc.new { |event_info|

      p "hello"

    }

  end

  def test

    @button.on_mouse_over.call(nil)

  end

end

 

Window.new.test # Prints "hello"

 
 
What you have discovered is Ruby's singleton classes. What you have done with those few lines of code is to implicitly define a singleton class on the @button object and define a method on that class. Singleton classes are awesome, but you have to be careful with them. The only thing I can think of right now is that they can't be Marshal'ed, so you shouldn't use singleton classes in the game objects.

Did you know that you can initialize an object with a block? You could do something like this:

[rgss]class Button
  def initialize(&block)
    @block = block
  end
  def on_click
    @block.call
  end
end
 
$button = Button.new { p "Button pressed" }
$button.on_click
[/rgss]

When you prefix the last argument with &, that variable is a Proc object containing the passed block. This isn't the only way to use the block, of course, but it is the best way to store the block for later use.

One excellent thing you could do with this is have the button's event modify things in the scene, like so:

[rgss]class Scene_Test
  # [...]
  def main_window
    # [...]
    @command_window = Window_Command.new(160, s)
    @command_window.visible = false
    @command_window.active = false
    @button = Button.new do
      @command_window.active = true
      @command_window.visible = true
    end
  end
  # [...]
end
[/rgss]
 
Rey, That is awesome. Thank you for explaining all of that. It makes much more sense now, really. And I will definitely be putting something like that to use. You see, I'm redoing the window class and rearranging the window-skin to use scroll bars instead of just displaying an arrow when the window can scroll, among other things. what you have shown me, i think will make some things a little easier on me.

Again, Thank you.
 
I don't think you can efficiently remove the arrows from the window class, but you can add a scroll bar. Scrolling is based on window.ox and window.oy . The arrows appear when the bitmap is larger than the window (minus the margin), so to remove them you'd either have to rewrite the Window class without arrows or intercept #contents so that the "real" contents are never too big and the pretend contents are written to the window. The first solution is pretty hard (though I have such a rewrite if you're interested), and the second solution would end up being really inefficient.
 
I'm already using Selwyn's rewrite and modifying it to work. so far so good, though I did have to make a few corrections as he made a few errors when he cut up the windowskin. I already got it to work, almost, just have to add actual mouse scrolling and a few other features to the windowskin similar to rmvx.
 
Here is something I've noticed more and more in the ruby world :) and it's really helpful and very flexible for coders and makes code easier to read as well!
Maybe some people already figured it:

So instead of using the classical way of using arguments.
[rgss] 
def my_method(arg1= "this", arg2 = "that", etc... )
  # do stuff with arg1
end
 
[/rgss]

here is a more flexible one, using hashes! Although it's mostly useful for the initialize method.

[rgss] 
class Test_Arg
  def initialize(options = {})
    @a = options[:a] || "default_value"
    @b = options[:whatever] || "b default"
  end
end
 
# Several example showing the flexibility it offers
# this makes easier for people to identify what the parameter is as well
a = TestArg.new
b = TestArg.new:)a => "plop")
c = TestArg.new:)whatever => "bebop")
d = TestArg.new:)whatever => "abba", :a => "revolution")
 
 
[/rgss]
Hope it helps ! :biggrin:
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top