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

What I'm saying is, when I test play, $TEST == false, and $DEBUG == false. There is no indication the editor has set any debug variables at all.
 
Here's a snippet I made based on Main class to help me get some debugging info that could make my life easier. I don't know if I really had to do this anyway. I hope you'll find it useful, if not, eh, well, it may be forgotten in the sands of time...

Code:
begin

  Graphics.freeze

  $scene = Scene_Title.new

  $scene.main while $scene != nil

  Graphics.transition(30)

#=begin

rescue => error

  a = Dir.getwd

  aw = !FileTest.exists?(a+'/ErrorsLog.txt') ? 'w': 'a'

  log = File.open(a+'/ErrorsLog.txt', aw)

  time = Time.new

  year = time.strftime('%Y')

  month = time.strftime('%b')

  day = time.strftime('%d')

  hour = time.strftime('%H')

  min = time.strftime('%M')

  sec = time.strftime('%S')

  logtime = "#{year} #{month} #{day} #{hour}:#{min}:#{sec}"

  log.puts '---', logtime, 'Error! '+error, '---', error.backtrace.join("\n")

  log.close

#=end

rescue Errno::ENOENT

  filename = $!.message.sub("No such file or directory - ", "")

  print "Unable to find file #{filename}."

end

I obviously included the #=begin and #=end just in case you feel like it should be commented out once it doesn't need to be debug anymore.

Edit:

OK, you could also paste this:

Code:
begin

  Graphics.freeze

  $scene = Scene_Title.new

  $scene.main while $scene != nil

  Graphics.transition(30)

rescue Exception => error

  KyoLog.write(error)

rescue Errno::ENOENT

  filename = $!.message.sub("No such file or directory - ", "")

  print "Unable to find file #{filename}."

end

But you'll need to paste this module above Main.

Code:
class Game_Event;  attr_reader :map_id;  end

 

module KyoLog

  CodeList = {

    101 => 'Show Text',                  102 => 'Show Choices',

    402 => 'When [**]',                  403 => 'When Cancel',

    103 => 'Input Number',               104 => 'Xhange Text Options',

    105 => 'Button Input Processing',    106 => 'Wait',

    111 => 'Conditional Branch',         411 => 'Else',

    112 => 'Loop',                       413 => 'Repeat Above',

    113 => 'Break Loop',                 115 => 'Exit Event Processing',

    116 => 'Erase Event',                117 => 'Call Common Event',

    118 => 'Label',                      119 => 'Jump to Label',

    121 => 'Control Switches',           122 => 'Control Variables',

    123 => 'Control Self Switch',        124 => 'Control Timer',

    125 => 'Change Gold',                126 => 'Change Items',

    127 => 'Change Weapons',             128 => 'Change Armor',

    129 => 'Change Party Member',        131 => 'Change Windowskin',

    132 => 'Change Battle BGM',          133 => 'Change Battle End ME',

    134 => 'Change Save Access',         135 => 'Change Menu Access',

    136 => 'Change Encounter',           201 => 'Transfer Player',

    202 => 'Set Event Location',         203 => 'Scroll Map',

    204 => 'Change Map Settings',        205 => 'Change Fog Color Tone',

    206 => 'Change Fog Opacity',         207 => 'Show Animation',

    208 => 'Change Transparent Flag',    209 => 'Set Move Route',

    210 => "Wait for Move's Completion", 221 => 'Prepare for Transition',

    222 => 'Execute Transition',         223 => 'Change Screen Color Tone',

    224 => 'Screen Flash',               225 => 'Screen Shake',

    231 => 'Show Picture',               232 => 'Move Picture',

    233 => 'Rotate Picture',             234 => 'Change Picture Color Tone',

    235 => 'Erase Picture',              236 => 'Set Weather Effects',

    241 => 'Play BGM',                   242 => 'Fade Out BGM0',

    245 => 'Play BGS',                   246 => 'Fade Out BGS',

    247 => 'Memorize BGM/BGS',           248 => 'Restore BGM/BGS',

    249 => 'Play ME',                    250 => 'Play SE',

    251 => 'Stop SE',                    301 => 'Battle Processing',

    601 => 'If Win',                     602 => 'If Escape',

    603 => 'If Lose',                    302 => 'Shop Processing',

    303 => 'Name Input Processing',      311 => 'Change HP',

    312 => 'Change SP',                  313 => 'Change State',

    314 => 'Recover All',                315 => 'Change EXP',

    316 => 'Change Level',               317 => 'Change Parameters',

    318 => 'Change Skills',              319 => 'Change Equipment',

    320 => 'Change Actor Name',          321 => 'Change Actor Class',

    322 => 'Change Actor Graphic',       331 => 'Change Enemy HP',

    332 => 'Change Enemy SP',            333 => 'Change Enemy State',

    334 => 'Enemy Recover All',          335 => 'Enemy Appearance',

    336 => 'Enemy Transform',            337 => 'Show Battle Animation',

    338 => 'Deal Damage',                339 => 'Force Action',

    340 => 'Abort Battle',               351 => 'Call Menu Screen',

    352 => 'Call Save Screen',           353 => 'Game Over',

    354 => 'Return to Title Screen',     355 => 'Script',

    404 => 'Branch End - Choices',       604 => 'Branch End Battle Processing',

    401 => '  Text Line',                  0 => '',

  }

  D, Line = Dir.getwd, '-----'

  @aw = !FileTest.exists?(D+'/ErrorsLog.txt') ? 'w': 'a'

  @e = []

  time  = Time.new

  year  = time.strftime('%Y')

  month = time.strftime('%b')

  day   = time.strftime('%d')

  hour  = time.strftime('%H')

  min   = time.strftime('%M')

  sec   = time.strftime('%S')

  @logtime = "#{year} #{month} #{day} #{hour}:#{min}:#{sec}"

  def self.write(error, type='An error occurred!')

    log = File.open(D+'/ErrorsLog.txt', @aw)

    log.puts Line, @logtime, type, error

    log.puts error.backtrace.join("\n"), Line

    log.close

  end

 

  def self.write_events_info

    e  = File.open(D+'/Events.txt', @aw)

    gp, gm = $game_player, $game_map.events

    x, y   = $data_system.start_x, $data_system.start_y

    e.puts Line, @logtime

    e.puts "Player's Starting Position - X: #{x}, Y: #{y}"

    e.puts "Player's Current Position  - X: #{gp.x}, Y: #{gp.y}"

    e.puts 'Map Events Keys Size = ' + gm.keys.size.to_s

    for n in 1..gm.size

      @e.push "Map #{gm[n].map_id}, ID: #{gm[n].id},"+

              "X: #{gm[n].x}, Y: #{gm[n].y}, Value: #{gm[n]}, "+

              "Trigger: #{gm[n].trigger}\n\n Commands List:\n"

      for m in 0...gm[n].list.size

        c = gm[n].list[m].code;  l = CodeList[c]

        com = c != 0 ? 'command_' : ' '

        command = c != 0 ? "  #{com}#{c} - #{l}" : ''

        @e.push command

      end

    end

    e.puts "\n", @e.join("\n"), Line, Line

    e.close

    @e.clear

  end

end

Here's a sample of the data stored in the ErrorsLog.txt log after exiting the program abruptly due to some (stupid) errors I committed while getting a script ready to be published.

---
2009 Apr 10 18:29:40
Error! undefined method `z=' for nil:NilClass
---
Section089:383:in `main'
Section093:10
---
2009 Apr 10 18:33:52
Error! undefined method `[]' for nil:NilClass
---
Section089:225:in `refresh'
Section089:239:in `update'
Section089:431:in `update'
Section089:403:in `processing'
Section089:400:in `loop'
Section089:405:in `processing'
Section089:394:in `main'
Section093:10

If the RPG Maker had included some debugging / error logging class this would have been easier, but it isn't something we couldn't handle, is it?

Edit...

I included another method that would log some basic info regarding the events present on a map...
 
Here's another good Backup Utility for RPG Maker. It's completely based on Ruby (and its C implementations obviously) but it still runs on both of the most recent versions of the maker.

First get FileUtils, it's quite useful, even on the RPG Maker XP or VX.

Download
(This script is free software.)

Paste is above Main.

Just include this simple wrap module above Main.

Code:
module KyoBackup

  time  = Time.new

  year  = time.strftime('%Y')

  month = time.strftime('%m')

  day   = time.strftime('%d')

  hour  = time.strftime('%H')

  min   = time.strftime('%M')

  sec   = time.strftime('%S')

  @logtime = "#{year}#{month}#{day} #{hour}#{min}#{sec}"

  Backup = 'Backup'

  BUT = " #{@logtime}"

  BN  = File.basename(Dir.getwd)

  DN  = File.dirname(Dir.getwd)

  S   = '/'

  DEST = DN+S+Backup+S+BN+' '+BUT

  

  def self.project

    FileUtils.mkdir(DN+S+Backup) if !File.exists?(DN+S+Backup)

    FileUtils.cp_r(Dir.getwd, DEST)

    print 'Backup ended successfully!'

  end

end

...and include this line in Main, just below "begin".

KyoBackup.project

And you'll see a pop up window telling you that it already backed up your project!!!

The directory path is the following:

If your projects are stored in...

C:/The full path to your current projects working directory/Name of your project

Then the Backup folder should be found here...

C:/The full path to your current projects working directory/Backup/Name of your project Date Time

NOTES:

On VX you might get some error message popping up regarding some file requirement (require 'etc').

There are about 3 lines that mention 'etc' or Etc., comment them out and it'll work smoothly for sure.
 
A window to add to your scene, which handles number input.
This window will show the input number screen + text
and return the number.

new window_Input
Code:
class Window_Input < Window_Message

  

  def initialize(text, digits)

    super()

    @var_id = 1

    start_input(text, digits)

  end

  

  def start_input(text, digits)

    @old_data = $game_variables[@var_id]

    $game_variables[@var_id] = 0

    $game_temp.message_text = text

    $game_temp.num_input_variable_id = @var_id

    $game_temp.num_input_digits_max = digits

    $game_temp.num_input_start = 1  # from_line

  end

  

  def finished?

    return @input_number_window.nil?

  end

  

  def get

    loop do

      # Update game screen

      Graphics.update

      # Update input information

      Input.update

      # Frame update

      update

      # Abort loop if screen is changed

      if @input_number_window.nil?

        break

      end

    end

    number = $game_variables[@var_id]

    $game_variables[@var_id] = @old_data

    return number

  end

  

end
To use, add a window_input to your scene:

@window = Window_Input.new(text, digits)
@your_number = @window.get
@window.dispose
# no need to update/loop in the scene, just those lines above.
 
new: getter and setter for class_variables

1. version: with eval
[rgss] 
class Module
 
 def akkr_reader(*args)
  args.each{ |sym| class_eval("def #{sym}; return @@#{sym}; end;") }
 end
 
 def akkr_writer(*args)
  args.each{ |sym| class_eval("def #(sym}=(value); return @@#{sym}=value; end;") }
 end
 def akkr_accessor(*args)
  akkr_reader(*args)
  akkr_writer(*args)
 end
end
 
[/rgss]

2. version: without eval but with more evil
[rgss]class Module
 
 def akkr_reader(*args)
  args.each{ |sym| define_method(sym) { Object.instance_method:)class).bind(self).call.send:)class_variable_get,"@@#{sym}") } }
 end
 
 def akkr_writer(*args)
   args.each{ |sym| define_method("#{sym}=") { |value| Object.instance_method:)class).bind(self).call.send:)class_variable_set,"@@#{sym}",value) } }
 end
 def akkr_accessor(*args)
  akkr_reader(*args)
  akkr_writer(*args)
 end
end
[/rgss]
 
Shouldn't these new methods for Module class be private? AFAIK attr_reader and attr_writer or attr_accessor are already private in Module...
 
Hanmac, you should have precised it was creating a method for instancied object of that class, to access the class variable.

Like so:

[rgss] 
class Foo
 
@@foo = 2
 
akkr_reader :foo
 
# Do the same as just before.
def foo
 @@foo
end
 
end
 
 
d = Foo.new
d.doo              # => 2
 
[/rgss]

As it's a class variable, wouldn't be better to make a Class method instead?
[rgss] 
Foo.foo  # => 2
 
[/rgss]
 
if you would like to have class methods use:
[rgss]class << Foo
akkr_reader :a
end
 
Foo.a
[/rgss]

PS: the second version need Ruby 1.8.3
 

Zeriab

Sponsor

That's an interesting little snippet Hanmac ^^
I do not like the name, why is it called akkr_reader? Why not attr_class_reader or something other more intuitive and better self-documenting name? I believe the price for the shorter name is in this case not worth it.

Secondly I find it odd from a design perspective that instance methods have getters and setters which refer to the class variable. It would be more intuitive to have class methods instead. Of course the could be cases where having instance methods return a class variable rather than an instance variable is preferable.
It's not so much that you can create class methods, but more the conceptual meaning of the functionality you provide. Basically, I agree with Trebor777 or at least make its purpose clear in the documentation.

*hugs*
- Zeriab
 
Equivalent of a OR statement for the same variable:

[rgss] 
a == 2 or a == 3 or a == "foo"
 
=> [2,3,"foo"].include? a
 
[/rgss]

Of course only applies for the same kind of condition ( == )
Can be useful to write shorter version if the variable or the values have long names. ;)
 

Zeriab

Sponsor

Nice find trebor :D

@meustrus:
I agree that name is convoluted, which I believe fits when its an object method. (Sorry about the late reply, I didn't notice your reply before ._.)

*hugs*
- Zeriab
 
I must share this, because I wrangled with a solution to this problem before just looking it up in the Ruby documentation. Sometimes the RMXP help file's scripting section is really lacking...

You can create a Hash with default values by calling Hash.new(default) or Hash.new {|hash, key| default}. Further documentation. Such functionality isn't available for Array, but I determined that it would be more trouble than it's worth to give it to Arrays; just use Hashes instead if you need default values.
 

Zeriab

Sponsor

The RMXP help file is generally lacking when it comes to the default ruby stuff.
It is a nice find with a default value constructor based on the key you have found. Something worth remembering

^_^
 
This ain't much but when I was working on a timer request just now I made it... I'll probably hold onto it for convenience sake.

Code:
#===============================================================================

# ** Graphics : Change Frame Rate

#===============================================================================

 

class << Graphics

  #-----------------------------------------------------------------------------

  # * Alias Listings

  #-----------------------------------------------------------------------------

  alias_method :chngframerate_graphics_update,    :update

  #-----------------------------------------------------------------------------

  # * Update

  #-----------------------------------------------------------------------------

  def update

    chngframerate_graphics_update

    if Input.press?(Input::CTRL)

      if Input.trigger?(Input::F5)

        Graphics.frame_rate -= 10

        return

      elsif Input.trigger?(Input::F6)

        Graphics.frame_rate += 10

        return

      elsif Input.trigger?(Input::F7)

        Graphics.frame_rate = 40

        return

      end

    end

  end

end

What it does is allows you to change Graphics.frame_rate by holding CTRL and pressing F5 (decrease), F6 (increase) or F7 (reset to 40).

One thing that bugs me is Enterbrain decided to secure the Graphics.frame_rate value between 0 and 120, which I understand why but it sucks. I tried overwritting/aliasing the method to go beyond 120 just to see what it does but its something more than just doing @frame_rate = n, damn hidden internal methods!


Also, what I posted on the Milliseconds in Timer request...

I learned something new upon testing your request. I wanted to actually test the accuracy of the milliseconds, so I changed Graphics.frame_rate from 40 to 20 to 10 to 1 to 0.1, etc you get the idea, just so I could see the actual milliseconds counting down. Keep in mind, I tested the timer set at 1:00 at all times...

Well, as I started changing Graphics.frame_rate, I noticed the lower it was the higher the timer was and visa versa. For instance, Graphics.frame_rate = 20 would make the timer 2:00 instead of 1:00, and Graphics.frame_rate = 80 would make the timer 0:30... that isn't right! Remember how I said I set the timer at 1:00?

RMXP's frame_rate is supposed to always be 40
RMVX's frame_rate is supposed to always be 60

Many anti-lag scripts I've seen change Graphics.frame_rate to try and speed up the game when it is lagging, which is okay and works sometimes but will affect the Timer and possibly Window_Playtime's time. The solution, you can do a CTRL+SHIFT+F and replace anything that references Graphics.frame_rate with 40 (or 60 for VX), and you won't have to worry about Graphics.frame_rate affecting timed-things.

Go ahead, run the default timer and use this snippet to change your frame rate, you'll see exactly what I'm talking about! The timer's time will jump all over the damn place! Window_Playtime doesn't appear to be effected much, but the Timer is! You might want to keep this in mind when timing things in your own scripts too :P

Use 40 (XP) or 60 (VX) instead of Graphics.frame_rate! Also, never use Time.now for anything outside of checking what the actual time is! If you use Time.now to count-down actual in-game timers, that is a big no-no!

Ask ImmuneEntitiy! He had a Skill Timer script, meaning you have to wait  
 
Actually the timer should be frame_rate independant, and decrease the timer by the time which passed between 2frames not by the number of frames.
 
Holy cow Zeriab, nice one!!! I've got a whole buttload of scripts in my project so it made a good test, it appears that everything still works fine. For those of you who like to penny-pinch (sort to speak) on your HDD space my old script file was 477 KB and the new one was 224 KB so that a little more than halfed the space the Scripts.rxdata took up in the project.

I always figured something like this would shrink the filesize and that is simply amazing! Interesting how much memory in a file that indentation and comments take up. I haven't the time to test tonight, but I'd imagine that maybe it might make your game run just a little bit faster, (or at least start up a little faster, if it takes a second to load everything.) Running my project after I did that, it does seem to eliminated alot of those 'quick pause' moments, like when transitioning between scenes and such.

It would be silly as a scripter or general project developer to do this in mid-development, but I'd probably do this before posting a large scale project, just to save those few grams of file size lol. Not to mention, although people can crack your project and steal your work it'd be harder for them to 'understand' the scripts coding itself, since it makes it harder for a human mind to seperate and keep track of without comments/indents. =P

Next big thing is if you came up with a way to do this and then encrypt all that data, maybe even put every individual script into the one same section, that would really fry people's brains to try and decypher, especially with comments and indentation/excess whitespace removed!

Good job on this man, I was really impressed ! *Hugs*
 
That sort of thing is most commonly done with javascript, as normally a difference of 200kb, however large proportional to the data, is a pittance. I'm surprised it actually improves performance, though. I guess the Ruby interpreter just isn't the absolute greatest, unlike say the PHP interpreter which can compile scripts to machine code every time to improve speed (PHP 6 may have some sort of versioning system that can tell if it needs to recompile a script or not, to cache compilations and save more time?).

Will have to keep this in mind for the future.
 

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