SLIP INTO RUBY PART 4: MAKING A SCENE CONTINUED.
In this edition of Slip into Ruby, we finish our bestiary and talk about the future.
- Trihan
- 03/10/2015 07:08 PM
- 5796 views
Hello class! Today we're going to finish our bestiary projects. What you should have so far is this:
class Scene_Bestiary < Scene_MenuBase def start super @list_window = Window_EnemyList.new(0, 0, 200) create_battler_window @list_window.set_handler(:cancel, method(:return_scene)) end def create_battler_window @battler_window = Window_EnemyBattler.new(@list_window, 200, 0, Graphics.width - 200, Graphics.height - 175) @battler_window.viewport = @viewport end end class Window_EnemyBattler < Window_Base def initialize(enemy_window, x, y, width, height) super(x, y, width, height) @enemy_window = enemy_window refresh end def draw_battler(x, y) bmp = setup_battler_graphic(x, y) src_rect = Rect.new(0, 0, bmp.rect.width, bmp.rect.height) contents.blt(x, y, bmp, src_rect, 150) bmp.dispose end def setup_battler_graphic(x, y) bmp = get_battler rect = Rect.new(x, y, contents_width, contents_height) dest_rect, fits = battler_rect(bmp, rect) dummy_bmp = Bitmap.new(contents_width, contents_height) if fits dummy_bmp.blt(dest_rect.x, dest_rect.y, bmp, bmp.rect) else src_rect = Rect.new((bmp.rect.width - dest_rect.width) / 2, (bmp.rect.height - dest_rect.height) / 2, dest_rect.width, dest_rect.height) dummy_bmp.blt(dest_rect.x, dest_rect.y, bmp, src_rect) end dummy_bmp end def battler_rect(bmp, rect) dest_rect = bmp.rect.dup fits = dest_rect.width <= rect.width && dest_rect.height <= rect.height if !fits dest_rect.width = rect.width if dest_rect.width > rect.width dest_rect.height = rect.height if dest_rect.height > rect.height end dest_rect.x = ((rect.width - dest_rect.width) / 2) dest_rect.y = ((rect.height - dest_rect.height) / 2) return dest_rect, fits end def get_battler Cache.battler(@enemy.battler_name, @enemy.battler_hue) end def update super refresh end def refresh contents.clear @enemy = @enemy_window.item draw_battler(1, 1) end end class Window_EnemyList < Window_Selectable def initialize(x, y, width) super(x, y, width, Graphics.height - y) data = [] self.index = 0 activate refresh end def item_max @data ? @data.size : 1 end def item @data && index >= 0 ? @data[index] : nil end def make_item_list @data = $data_enemies.compact end def draw_item(index) item = @data[index] if item rect = item_rect_for_text(index) draw_text(rect, item.name) end end def refresh make_item_list create_contents draw_all_items end end
Which should look like this:
NOTE: After writing this part I noticed that I had incorrectly used Graphics.width when determining the height of the enemy window. I have now amended this in the code, so anyone who was following up until now will have to do the same or copy/paste the code above. Sorry about that!
Now you may be dismaying at that massive blank space at the bottom right where by all laws of goodness and sense monster stats should be! Let's fix that.
First of all we're going to create a new window to hold the information, which fills up the space. You remember how to create a window, right? We need a new class:
class Window_EnemyInfo < Window_Base def initialize(enemy_window, x, y, width, height) super(x, y, width, height) @enemy_window = enemy_window refresh end def update super refresh end def refresh contents.clear @enemy = @enemy_window.item end end
If I've been doing my job up until now I shouldn't have to explain what this all does, as it's pretty much exactly the same as the code for creating the battler window.
And then to show it, we go back to the start method of Scene_Bestiary:
create_info_window
And as this method doesn't exist yet, we should probably create it.
def create_info_window @info_window = Window_EnemyInfo.new(@list_window, 200, Graphics.height - 175, Graphics.width - 200, 175) end
We should now have this.
Let's add some stats here. We could be all fancy and use icons and stuff, but for now let's stick with simplicity, using standard game terms:
Add the following line to the end of the refresh method in Window_EnemyInfo.
draw_parameters(0, 0)
And the new method draw_parameters.
def draw_parameters(x, y) 6.times {|i| draw_enemy_param(@enemy, x, y + line_height * i, i) } end
And the new method draw_enemy_param!
def draw_enemy_param(enemy, x, y, param_id) change_color(system_color) draw_text(x, y, 120, line_height, Vocab::param(param_id)) change_color(normal_color) draw_text(x + 120, y, 56, line_height, enemy.params[param_id], 2) end
Playtest, and lo and behold suddenly you have this.
Looking pretty nice, huh? Let's take a look at the new stuff line by line:
6.times {|i| draw_enemy_param(@enemy, x, y + line_height * i, i) }
.times is a lovely little iteration method Ruby has; it's basically a shorthand version of a for loop. You put [number of times to loop].times {|name of index variable| stuff to loop through }
So basically this is calling draw_enemy_param 6 times. Note the parameters y + line_height * i and i. Basically this means each successive loop will be placed at a Y equal to the index multiplied by the height required to show one line. The end result speaks for itself. :P
change_color(system_color)
change_color is a built-in method of Window_Base which changes the text colour. There are quite a few system presets which you can use, or you can even specify your own colours using Color.new(r, g, b, a)
draw_text(x, y, 120, line_height, Vocab::param(param_id))
We've used draw_text before, but I'd like to draw your attention to the last parameter. Vocab is a module containing all the terminology used in your game. If you look up param in that module you'll see it returns $data_system.terms.params which is whatever you set the "Parameters" terms to in the terms tab of the database.
draw_text(x + 120, y, 56, line_height, enemy.params[param_id], 2)
Because we have a looping index going from 0-5 (as we're looping 6 times, starting with 0) we can use this to get each successive parameter assigned to an enemy, which in order are Max HP, Max MP, Atk, Def, Mat and Mdf. We're simply displaying the value of that index of the parameters array.
And that's a basic bestiary! Be proud of what you've accomplished. Here's the final code.
class Scene_Bestiary < Scene_MenuBase def start super @list_window = Window_EnemyList.new(0, 0, 200) create_battler_window @list_window.set_handler(:cancel, method(:return_scene)) create_info_window end def create_battler_window @battler_window = Window_EnemyBattler.new(@list_window, 200, 0, Graphics.width - 200, Graphics.height - 175) @battler_window.viewport = @viewport end def create_info_window @info_window = Window_EnemyInfo.new(@list_window, 200, Graphics.height - 175, Graphics.width - 200, 175) end end class Window_EnemyInfo < Window_Base def initialize(enemy_window, x, y, width, height) super(x, y, width, height) @enemy_window = enemy_window refresh end def draw_parameters(x, y) 6.times {|i| draw_enemy_param(@enemy, x, y + line_height * i, i) } end def draw_enemy_param(enemy, x, y, param_id) change_color(system_color) draw_text(x, y, 120, line_height, Vocab::param(param_id)) change_color(normal_color) draw_text(x + 120, y, 56, line_height, enemy.params[param_id], 2) end def update super refresh end def refresh contents.clear @enemy = @enemy_window.item draw_parameters(0, 0) end end class Window_EnemyBattler < Window_Base def initialize(enemy_window, x, y, width, height) super(x, y, width, height) @enemy_window = enemy_window refresh end def draw_battler(x, y) bmp = setup_battler_graphic(x, y) src_rect = Rect.new(0, 0, bmp.rect.width, bmp.rect.height) contents.blt(x, y, bmp, src_rect, 150) bmp.dispose end def setup_battler_graphic(x, y) bmp = get_battler rect = Rect.new(x, y, contents_width, contents_height) dest_rect, fits = battler_rect(bmp, rect) dummy_bmp = Bitmap.new(contents_width, contents_height) if fits dummy_bmp.blt(dest_rect.x, dest_rect.y, bmp, bmp.rect) else src_rect = Rect.new((bmp.rect.width - dest_rect.width) / 2, (bmp.rect.height - dest_rect.height) / 2, dest_rect.width, dest_rect.height) dummy_bmp.blt(dest_rect.x, dest_rect.y, bmp, src_rect) end dummy_bmp end def battler_rect(bmp, rect) dest_rect = bmp.rect.dup fits = dest_rect.width <= rect.width && dest_rect.height <= rect.height if !fits dest_rect.width = rect.width if dest_rect.width > rect.width dest_rect.height = rect.height if dest_rect.height > rect.height end dest_rect.x = ((rect.width - dest_rect.width) / 2) dest_rect.y = ((rect.height - dest_rect.height) / 2) return dest_rect, fits end def get_battler Cache.battler(@enemy.battler_name, @enemy.battler_hue) end def update super refresh end def refresh contents.clear @enemy = @enemy_window.item draw_battler(1, 1) end end class Window_EnemyList < Window_Selectable def initialize(x, y, width) super(x, y, width, Graphics.height - y) data = [] self.index = 0 activate refresh end def item_max @data ? @data.size : 1 end def item @data && index >= 0 ? @data[index] : nil end def make_item_list @data = $data_enemies.compact end def draw_item(index) item = @data[index] if item rect = item_rect_for_text(index) draw_text(rect, item.name) end end def refresh make_item_list create_contents draw_all_items end end
I had debated showing you all sorts of cool stuff, like listing enemy drops and how many times you'd killed a certain enemy, but ultimately I've decided that I'm here to show you the basics and that's going beyond scope. However, just to indulge the showoff in me, here's the kind of thing you could make with a bit more knowledge of Ruby:
Inspired yet?
Okay, so I'm thinking I'm going to update Slip into Ruby every week on a Tuesday, and from this point on I'm going to do a line-by-line analysis of each default script in RPG Maker VX Ace. As always feel free to comment, critique or insult and if there's anything specific you want me to talk about or something you're not quite sure how to script, let me know in the comments and I'll do my best to work it in.
Until next time.