Jump to content


Photo

Tutorial: How to edit the RGSS3 game engine in VX Ace Trial


  • Please log in to reply
12 replies to this topic

#1 kal

kal

    Advanced Member

  • Ace Member
  • 119 posts
  • RM Skill - Coder

Posted 01 December 2011 - 09:27 AM

So as you probably know Enterbrain disabled editing in the Script Editor for the trial version of Ace. Do not despair though! We can bypass this quite easily because Ruby is such a dynamic language with very powerful metaprogramming capabilities.

I'll explain how to edit the built RGSS3 engine and your own original scripts to the trial. For this example, I'll do a very simple and quick edit: boost the experience characters receive by 100x.

First create a new project (or use an existing one) and navigate to the Project root folder (where Game.exe is located). Now create a new folder here and call it Scripts. Go inside the Scripts folder and create a new file called "boost_exp.rb".

Open this file in a text editor (Notepad works fine). Now let's code our exp booster:

puts "EXP booster loaded"

module BattleManager

  def self.gain_exp
	$game_party.all_members.each do |actor|
	  actor.gain_exp($game_troop.exp_total * 100)
	end
	wait_for_message
  end

end

I'll explain the code and what's going on here. The first line,
puts "EXP booster loaded"
, uses a new feature in Ace: the console. It simply prints the "EXP booster loaded" to the console window. This useful because you will know that the file was loaded successfully. To enable the console, go in the Game menu (that one with (G)) and then click on the entry with ©. (thanks Cremno for finding this)

Next, we have the code that overrides the
self.gain_exp
method in the class BattleManager. It is pretty simple. The only thing this code changed from the default version is that we multiply
$game_troop.exp_total
by 100, which will result in a all EXP being increased by a factor of 100.

OK, now to the final step: loading this file into our Ace project!

First, go to your player start map and create a new event. Set the even to autorun (it's the second to last option in the lower right menu).

Next create a script event command (the very last event command) and type in the following in the dialog box:

path = File.join(Dir.getwd, "/Scripts/*.rb")
Dir[path].each do |file|
  load file
end

This code iterates over all the files in the [project root folder]\Scripts folder that end with ".rb" (the Ruby file extension). On the third line you see
load file
which will simply load the contents of all the file and evaluate it as Ruby code.

The final step here is to add an "Erase event" command after the Script command. (Erase event is the last command in the second "box" on page 2 in the command editor). We do this to make sure our script is loaded only once.

That's it! Go ahead and start the project. If you enabled the console you should see "EXP booster loaded" when the event executed. Now go and fight a slime and gain like 10 levels. :P

Using this approach we can start playing around with the script engine right now and start developing prototypes. I'm pretty sure you could even get a lot of VX scripts working using this method.


Edit: I just found out that Ace deletes everything in the Project folder when you close the application so it's better to put your scripts in another folder! Say for example that you make a folder called C:\AceScripts where you put your scripts then change the event load code to the following:

path = "C:/AceScripts/*.rb"
Dir[path].each do |file|
  load file
end

Edited by kal, 02 December 2011 - 05:44 AM.


#2 mitchi.exe

mitchi.exe

    Premium Member!

  • Ace Member
  • 127 posts
  • RM Skill - Designer

Posted 01 December 2011 - 09:58 AM

Wow, that was fast.

Spoiler

Posted Image

#3 Hanmac

Hanmac

    Newbie

  • Ace Member
  • 5 posts
  • RM Skill - Coder

Posted 01 December 2011 - 10:28 AM

its possible to get the source of the Game classes? i whould try it on my own but i think i have "bad memory" Posted Image

#4 Amy Pond

Amy Pond

    Advanced Member

  • Ace Member
  • 325 posts
  • LocationUnited Kingdom
  • RM Skill - Coder

Posted 01 December 2011 - 10:47 AM

Is this any use for requiring .so files?

(What, I can dream.. :rolleyes: )

6lsTKNx.png


#5 woratana

woratana

    Game & Web Designer

  • Ace Member
  • 11 posts
  • LocationSomewhere over the rainbow...
  • RM Skill - Coder

Posted 02 December 2011 - 06:32 AM

Nice tip! :D

Although it would be so much easier if it allow editing RGSS3 T__T
E-mail: hotwora@gmail.com

#6 kal

kal

    Advanced Member

  • Ace Member
  • 119 posts
  • RM Skill - Coder

Posted 02 December 2011 - 07:08 AM

Is this any use for requiring .so files?

(What, I can dream.. :rolleyes: )


I'm afraid not, at least not using this method.

Nice tip! :D

Although it would be so much easier if it allow editing RGSS3 T__T


Thanks! I agree though it would really be a lot easier if they hadn't disabled editing in the editor. There are some cool things you can do though using this method that can be quite useful. For example you can implement a reload method that reloads all your scripts without you having to restart the game. That way you can make edits to your source files and simply call the reload method to apply your changes!

Here's how to do that:

First either make a new .rb file in your script folder or just open up an existing one. Now open up the Kernel module like this:

module Kernel

end

The Kernel module is a built in module that gets included in the Object class. Because every Object is an descendant of Object (except BasicObject :P) this means that all methods defined in Kernel are available everywhere.

Next, let's define our reload! method in the Kernel module:

module Kernel
  def reload!
	path = "C:/AceScripts/*.rb"
	Dir[path].each do |file|
	  load file
	end
  end
end

As you can see this is exactly the same method we defined in the previous tutorial. This works because the load method doesn't care if a file has already been loaded (as opposed to the require method) so every time you call load [a file], the file will be evaluated as Ruby code again.

That's it! We can now use the reload! method from the game by making an event that with a Script command that looks like this:
reload!

But why stop here when we can go even further? It's kind of annoying to have to activate an event every time we want to reload our scripts. Maybe there's a way to automate the process? Turns out there is, and it's surprisingly easy (I'll make a new post in this thread for how to do this soon).

Edited by kal, 02 December 2011 - 07:10 AM.


#7 YF

YF

    (。´◕ ‿‿ ◕`。)

  • Ace Member
  • 187 posts
  • RM Skill - Coder

Posted 02 December 2011 - 09:10 AM

It's also possible to rename Scripts.rvdata2 to rvdata (without the 2), move it over to VX, edit it there, and move the file back (rename it back to rvdata2). While it won't show up in the script editor, it'll at least load when you start the test game and at the start of battle tests.

#8 kal

kal

    Advanced Member

  • Ace Member
  • 119 posts
  • RM Skill - Coder

Posted 02 December 2011 - 09:18 AM

How to implement a script auto-reload feature in VX Ace Trial

(Just so you guys know, I'm not trying to be condescending with these tutorials and I know a whole bunch of you guys are better and more experienced scripters than me. I just want beginners to (hopefully) be able to follow this and understand what is going on)

So it turned out to be a little bit more complicated to implement this than I first though. Just a little bit though. I think it's a nice feature to be able to just drop a file into a folder and get autoreloading working instead of having to create a parallel event or something like that, so I used the Scene_Base#update method as a hook.

I'll post the code here and explain what's going on. You only need to drop this file into your scripts folder (or load it manually if you prefer that) and then when you make changes to your other scripts they will be reloaded automatically.


module Autoreload
  SCRIPT_PATH = "C:/AceScripts"
  WAIT_TIME = 60 # wait 60 frames (1 second) before we check for changes
  @counter = 0

  def self.reload!
	@counter += 1
  
	if @counter == WAIT_TIME
	  @counter = 0
	else
	  return
	end
  
	@scripts ||= read_all_scripts # the first time this line is executed set @scripts to our scripts
	new_scripts = read_all_scripts
	@scripts.each_with_index do |script, index|
	  name = script[:filename]
	
      if script[:source] != new_scripts[index][:source] && !__FILE__.include?(name)
		puts "reloading #{name}"
		load_script(name)
	  end
	end
	@scripts = new_scripts
  end

  def self.read_all_scripts
	Dir[SCRIPT_PATH + "/*.rb"].map do |file|
	  {filename: File.basename(file), source: File.read(file)}
	end
  end

  def self.load_script(filename)
	load File.join(SCRIPT_PATH, filename)
  end
  
end
  
class Scene_Base
  alias_method :autoreload_update_orig, :update
  def update
	Autoreload.reload!
	autoreload_update_orig
  end
end

I'm using a few tricks here that you might not have seen before. First of all I am using an instance variable inside a module. And yes, you can do this because modules are objects in Ruby (they are instances of class Module). Since all code inside a module or a class is executed as soon as it's read, that means you can initialize your module instance variables by simply assigning them in the module body, which is what I am doing with the @counter variable.

The core method in this script is the Autoreload.reload! method which reads the source code of all your scripts in the SCRIPT_PATH folder, compares each script to a previously read version of that script, and reloads the script if it differs from the old version.

There's also a timer check using the @counter variable to make sure that we are not re-reading the scripts too often. @counter is incremented by 1 each frame update, and when it reaches the WAIT_TIME limit it resets to 0, and we do a check to see if a script has been changed.

There's a few more things you might not have seen before that I'll explain here:

First, the following line:
@scripts ||= read_all_scripts # the first time this line is executed set @scripts to our scripts

As you might know, instance variables have a default value of nil if they have not yet been set to anything. The ||= operator basically does the same as this: @scripts || @scripts = read_all_scripts
So to understand what that means you gotta understand how the || (the 'or' operator) works. || is a short circuiting operator which means that if it's first operand (@scirpts in this case) evaluates to a true value, the the second operand will never be evaluated because we already know that the whole expression will be true (because one of the operands evaluated to true, and that's all you need in an 'or' statement)

So this means that if @scripts has not been assigned anything yet, meaning that it is nil, the second operand of || (@scripts = read_all_scripts) will be executed. However, when this line is executed again, @scripts will have a value and the second operand will not get evaluated.

The next thing is this line here:

if script[:source] != new_scripts[index][:source] && !__FILE__.include?(name)

The first part compares a newly loaded scripts source to the old script source and sees if they differ. The second part is more interesting.
__FILE__ is a special Ruby variable that points to the path of the source file that is being run. So in this case the expression will evaluate to:
&& !"C:/AceScripts/autoload.rb".include?(name)

So what this does is that it checks to make sure that we are not attempting to reload the autoload script file (even if it has been edited) because that will lead to some weird errors :)

Finally, here's an explanation of the read_all_scripts method:

Dir is a class that deals with (what else!) directories. It has a really cool class method called glob which will "glob" a directory and return all files that matches it's matching string. This string is sort of a simpler version of a regular expression. If you use the notation Dir[path] that is the same thing as calling Dir.glob(path). Just some syntax sugar there. So Dir[SCRIPT_PATH + "/*.rb"] will return an array of all the files in SCRIPT_PATH that end with ".rb" - our scripts!

Next I am using the map method. Map (also known as collect) iterates over a collection and returns an array where each element of the array is the value of the block for that element in the collection. So in this case I am returning a hash that has the filename and the source of the file.

Oh yeah we also have the Scene_Base class which just aliases the original update method and calls Autoreload.reload! before calling the original update method.

One last thing. If you used the snippet in the first tutorial for the event that loads your scripts:


path = "C:/AceScripts/*.rb"
Dir[path].each do |file|
  load file
end

change "load file" to "require file". This ensures that the scripts are only loaded once (especially important for autoreload.rb). If load is used they will be loaded again if you exit and re-enter the map, which can create errors when it comes to autoreload.rb

OK hope some of you found that useful or interesting! If you have any questions on how the code works or anything else feel free to ask.

Edit: oops looks like I still managed to get the __FILE__ check wrong. I fixed it just now.

Edited by kal, 02 December 2011 - 09:37 AM.


#9 Jet

Jet

    Coder

  • Ace Member
  • 92 posts
  • LocationCalifornia
  • RM Skill - Coder

Posted 02 December 2011 - 03:53 PM

They're good tutorials, which is why it'll remain pinned until the full release.
Very, very useful. Good job.

All my scripts are made under this license:
88x31.png

 

Get 20GB of cloud storage with Copy under my referral link.


#10 kal

kal

    Advanced Member

  • Ace Member
  • 119 posts
  • RM Skill - Coder

Posted 03 December 2011 - 04:53 AM

Updated tutorial on how to make an Interactive Console here: http://www.rpgmakerv...for-vx-ace-v-2/

Edited by kal, 05 December 2011 - 12:01 PM.


#11 kal

kal

    Advanced Member

  • Ace Member
  • 119 posts
  • RM Skill - Coder

Posted 04 December 2011 - 02:46 AM

Updated tutorial on how to make an Interactive Console here: http://www.rpgmakerv...for-vx-ace-v-2/

Edited by kal, 05 December 2011 - 12:01 PM.


#12 regendo

regendo

    Ruler of Worlds

  • Ace Member
  • 891 posts
  • LocationGermany
  • RM Skill - Coder

Awards Bar:

Users Awards

Posted 13 December 2011 - 02:20 PM

It's also possible to rename Scripts.rvdata2 to rvdata (without the 2), move it over to VX, edit it there, and move the file back (rename it back to rvdata2). While it won't show up in the script editor, it'll at least load when you start the test game and at the start of battle tests.

This works with XP as well (of course you'll have to change the file to "Scripts.rxdata").
Just wanted to note this, as there are probably quite a few people out there who, like me, do not own RMVX.

I personally like kal's version more, but directly editing the Scripts file works better in some cases, e.g. if you want to edit stuff like the title screen (which obviously pops up before the map you can place your event on).
Anyways, thanks a lot to the two of you for telling us about those workarounds!
The most recent versions of my scripts and my ToU are on github! - /regendo

#13 ntzrmtthihu777

ntzrmtthihu777

    Newbie

  • Ace Member
  • 2 posts

Posted 19 April 2014 - 11:47 AM

Is this any use for requiring .so files?

(What, I can dream.. :rolleyes: )

One would think so; I'm doing ruby-dev on linux and windows for XP, and I can require external files. I think you'd have to compile them with the same version of ruby used by VXAce. Funny thing is that when I build source on windows I still get a .so file, which is actually a linux file extension name... confusing but irrelevant.






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users