SLIP INTO RUBY - UNDER THE HOOD PART 17: WHEN I'M CLEANING WINDOWS!
In which the name doesn't have much to do with the actual content.
Trihan- 04/05/2017 12:19 PM
- 2568 views
Trihan here! You may have noticed that since the last episode I've gone through a bit of rebranding (my scripts, plugins, tutorials and articles are now released under the Trilobytes umbrella) so sit your ass down and prepare your body for the first Trilobytes episode of
Last time we looked at the command window classes, and now it's time to check out some of the other windows!
Window_Help
This is the window that shows descriptions of items and skills and offers other helpful information in various scenes. It has no public instance variables.
The constructor takes one parameter, line_number, which defaults to 2. All we do in this method is call the initialize method of the parent class, which is Window_Base, passing in X and Y coordinates of 0 and 0, the full window width as the width argument, and the fitting height for the passed-in number of lines as the height argument.
This method allows you to set the text that's shown in the window, and takes one parameter, text. First of all, we only do anything in this method if the passed-in text is different from the @text variable. If so, the passed-in text is assigned to @text, and we call the refresh method.
The clear method simply calls set_text passing in an empty string.
This method is used to set the help text to the description of an item/skill etc. and simply calls set_text with a ternary if statement: if an item was passed in, we return the item's description, otherwise a blank string.
The refresh method clears the contents and then calls draw_text_ex passing in an x coordinate of 4, a y coordinate of 0, and @text. Readers with elephantine memories will remember way back when we looked at Window_Base that draw_text_ex is the one that processes text codes, meaning you can also use them in the help window.
Window_Gold
This is the window that shows how many doubloons your party has accumulated. As with the help window, it has no public instance variables.
The constructor is more or less the same as the one above, only it calls a method to get the window width and will only ever be the height of 1 line rather than taking an argument. It also calls its refresh method on creation.
The window_width method determines the width of the window (you don't say?) and is hardcoded at 160. If you want longer/thinner gold windows, this is the value to aim your pistol at.
The refresh method clears the contents (as with every window) and then calls draw_currency_value passing in value (which we'll look at next), currency_unit (which we'll look at after that, at x coordinate 4 and y coordinate 0, with a width of contents.width - 8. We looked at draw_currency_value in an earlier issue (it's a method of Window_Base if you forgot).
This method is just a shorthand alternative to using $game_party.gold.
This method gets the currency unit as defined in Vocab, which gets its value from whatever you set in the database.
Last but not least, the method for opening the window calls refresh and then the open method of the parent class.
Window_MenuCommand
As the name may suggest, this is the command window shown in the menu. It has no public instance variables.
I don't know if we've covered class variables yet, but they're denoted by @@ as opposed to @ for an instance variable. init_command_position is a method of Window_MenuCommand itself rather than a given instance of it, and it sets the class variable @@last_command_symbol to nil. This means that the most recently-selected command in a menu command window can always be referenced.
The constructor simply calls the constructor of the parent (Window_Command) passing in 0 for both the X and Y coordinate, and calls select_last to change the selection to the most recent item that was highlighted in the window.
The window_width method is hardcoded to return 160 pixels.
This method determines how many lines are visible in the window, and is returned as the maximum number of items the window can show.
The overwrite of the parent make_command_list method calls a number of methods to add commands to the menu; first we add the main commands, then the formation command, then any custom commands the developer has added, then the save command, and finally the game end command.
The method for adding main commands calls add_command four times, one for each of the main commands for items, skills, equipment, and actor status. As you may or may not remember from last issue, the arguments being passed in here correspond to the parameters 'name', 'symbol' and 'enabled' respectively. The Vocab module references the terms set in the database. As with other symbol-based handlers, it really doesn't matter what name is used as long as the handler references the same symbol, but for the sake of clarity and ease of use the writers of the default scripts chose to use symbol names matching the command. main_commands_enabled is a method we'll see soon which determines whether the commands should be enabled or not.
This method adds the formation command using much the same syntax as the main commands did.
This method adds original (custom) commands, and is blank by default. The intent is for script developers to alias this to add their own commands.
This method adds the save command, again using the standard command-adding syntax we've seen plenty of now.
The method for adding the game end command differs only in that it has no argument for 'enabled' due to the fact that it will always be available.
The method determining whether main commands are enabled returns the result of calling $game_party's exists method, which as you very likely won't remember from the issue that covered it returns whether its @actors instance variable is not empty. In other words, main commands are only enabled if there's at least 1 party member.
This method determines whether the formation command is enabled, and returns the result of checking whether the size of the party is greater than or equal to 2 AND the formation_disabled flag of $game_system is false. The first condition means the formation command won't be available if you only have 1 party member, which makes sense as there'd be nobody to swap their position with.
This method determines whether the save command is enabled, and simply returns whether the save_disabled property of $game_system is false.
The overwrite of the Window_Selectable process_ok method sets the class variable @@last_command_symbol to the current symbol. This allows us to memorise the command that the player last had highlighted.
And its counterpart, this method selects the symbol contained in @@last_command_symbol.
Window_MenuStatus
This is the menu window which shows the status of party members i.e. their level, HP, MP and states. It has one public instance variable:
As explained by the comment, pending_index is the pending position for use when changing the party formation (so that the command knows both the new and old positions).
Pretty standard fare as constructors go. Call the parent constructor passing in the x and y arguments along with the results of window_width and window_height, set the pending index to -1 (which basically just means "nothing") and call the refresh method.
The window_width method returns the width of the window less 160 pixels.
window_height just returns the height of the window.
The savvy among you may realise that we're subtracting just as many pixels from window_width here as we returned in window_width for Window_MenuCommand. That's because the status window will be placed to the right of the command window, so we need to reduce the width by its width for it to fit on the screen correctly. You'll see this a lot with interconnected windows.
The overwrite of item_max for this window class returns the size of the members property of $game_party, or in other words, how many party members there are.
The height of a single item is defined as (the height of the window less twice the standard padding) divided by 4. This will give us the height of a single "actor" in the status window, as it only shows 4 actors at a time.
The overwrite of draw_item, as with its parent, takes the index as a parameter. We set a temp variable called actor to the member of the party at index and enabled to whether the party's battle members include that actor.
Then we set a temp variable called rect to the result of calling item_rect passing in index (this gives us the rect required to display the given index on the window), draw the item background (which we'll look at in a sec), draw the actor's face, passing in the actor, rect's x + 1, rect's y + 1 and enabled, and then draw the simple status of the actor, passing in actor, the rect's x + 108, and the rect's y + half the height of a line.
So to put it in layman's terms, we'll colour in the background if the party member has been selected for a formation swap, draw their face very slightly to the right and down from the top left of the item rect (and slightly transparent if they're not in the battle party) and then draw their HP, MP etc. centred vertically in the rect.
This method fills in the rect for formation selections. If the passed-in index is equal to @pending_index, fill the item_rect for index with pending_color.
The overwrite of process_ok calls the parent method and then sets the menu_actor property of $game_party to the member corresponding to the currently-selected index.
And to select the last, we call select passing in the index of $game_party's menu_actor property, or 0 if no value is returned.
This method sets the pending index and takes index as a parameter. First, we set a temp variable called last_pending_index to the value of @pending_index, then set @pending_index to the newly passed-in index value, then redraw both the pending and last items (so that actor graphics/stats will be updated in both places).
Window_MenuActor
This window is used to select actors as targets of skills/items. No public instance variables this time. It inherits from Window_MenuStatus.
Pretty standard constructor. We set its visible property to false so that it's invisible to begin with.
This method processes OK key input. First, we set the target_actor property of $game_party to the member corresponding to the selected index unless the item targets the whole party, then we call the ok handler.
This overwrite of the select_last method is identical to the one for Window_MenuStatus only using target_actor instead of menu_actor.
This method sets the cursor position for item selection, taking the item as a parameter. First, we set @cursor_fix to the result of calling the for_user? method on the passed-in item, then set @cursor_all to the result of calling the for_all? method.
If @cursor_fix is true, we select the index of the party's menu actor (the user). Otherwise, if @cursor_all is true, we select index 0 (the party leader). Otherwise, we call select_last to select the most recently-targeted actor.
Window_ItemCategory
This window is for selecting item categories in the item menu or in shops. It inherits from Window_HorzCommand and has one public instance variable:
There's no comment for it, but this is a property to hold the window object displaying the items that the category choice will be filtering.
Nothing special in the constructor.
The width of the window is just the whole screen width.
We're displaying 4 columns maximum. (I'm not actually sure why this is here because Window_HorzCommand already has an identical method. It's overwriting the parent method with one that's 100% identical to it. You could easily remove this and it would have no effect on the engine).
The frame update method calls its parent method, then sets the category property of @item_window to the current symbol if an item window is set.
The overwrite for make_command_list adds 4 commands: one for items, one for weapons, one for armours, and one for key items, using the appropriate Vocab methods and symbols.
This method sets the item window property, taking item_window as a parameter. Sets @item_window to the passed-in object, then calls update (which sets the item window category to the current symbol).
Window_ItemList
This is the window that displays the list of items on the item screen.
The constructor does a couple of things besides calling the parent method. First, we set the instance variable @category to the symbol :none, and then set @data to an empty array.
This method sets the window's category, taking category as a parameter. We return if @category is already equal to the passed-in symbol, then set @category to category, refresh the window, and set its y origin to 0.
The overwrite of col_max returns 2, as the item list will be 2 columns at most.
The overwrite of item_max returns the size of the @data array if it exists, and 1 otherwise.
This method gets the current item: if @data exists and the index is greater than or equal to 0, we return the element of @data at index. Otherwise, we return nil.
This method determines whether the currently-selected item is enabled, by calling the enable? method on the element of @data at the current index.
This method determines whether an item should be included in the list, taking the item as a parameter.
We have a case statement against @category:
When it's :item, we return true if the item is an instance of RPG::Item and not a key item, and false otherwise.
When it's :weapon, we return true if the item is an instance of RPG::Weapon, and false otherwise.
When it's :armor, we return true if the item is an instance of RPG::Armor, and false otherwise.
When it's :key_item, we return true if the item is an instance of RPG::Item and IS a key item, and false otherwise.
Otherwise, we return false.
This method determines whether to display an item as enabled (fully opaque) or disabled (partially transparent) and returns the value of calling the usable? method of $game_party for the passed-in item.
This method makes the list of items that will be displayed in the window. @data is set to the result of calling the select method on the result of calling the all_items method of $game_party using the iteration variable "item" and including the item in the returned array if the include? method returns true with the item as its argument.
...huh?
You may be a bit rusty with select as it's been a while since we've seen it, but basically you call it on an array passing in a block, and it returns a new array using the block as a filter condition to check whether the returned array should include the current element. In this case, all_items gets every item the party is carrying, and then we filter those items through the include? method, which essentially checks that the items are of the correct category. So only items will show for :item, only weapons will show for :weapon, etc.
Then, we push nil to @data if include? returns true when nil is passed in. Fun fact: This will NEVER happen. nil won't return true for any of the is_a? checks, so even if there is a category set it will return false in every scenario and subsequently this line is entirely pointless.
The overwrite for select_last is a similarly-confusing-looking chain of method calls: we're calling select, passing in the element of @data with an index matching the object property of the last_item property of $game_party.
Okay, let's break this down. We know what select does: it takes the index of an item in the window that will be selected, so we know the method chain is ending up with an index somewhere.
@data contains a list of included items, so we know they're going to be item objects. Those aren't indexes, so we need to drill down further. The .index method of arrays returns the index of the first element of the calling array which matches the argument passed in, so what we need is an item object corresponding to the most recent one the party used. Luckily for us, $game_party has a last_item property, but it's an instance of Game_BaseItem and the elements of @data are all elements of $data_items/weapons/armors, so again we need to drill down a bit further. Again, luckily, Game_BaseItem has an object method which returns the element of $data_items/weapons/armors which corresponds to the item it represents. So combining all of this gives us a chain of method calls that will let us find the item in @data which matches the last item object the party used.
The method for drawing items in the window, as with the parent method it overwrites, takes index as a parameter. We set the temp variable item to the element of @data at index. Then, if item isn't nil, we set the temp variable rect to the result of calling item_rect with index as the argument (which gets the rect required to display a single item at the selected index location), reducing its width by 4 (because there's a tiny bit more of a margin on the left than there is on the right, this will make the item info more 'centred'). We then draw the item name, passing in the item object, rect's x, rect's y, and whether the item is enabled. Finally, we draw the party's held quantity of the item, passing in rect and the item object.
This method draws the quantity of an item held by the party, taking two parameters: rect and item. As we saw above, rect is the item rect for drawing the item at the current index, and item is the item object currently selected.
We call the draw_text method, which we looked at back when we covered Window_Base, passing in the rect, a call to sprintf, and 2 for "align right". It's all pretty straightforward except the sprintf, so let's look at that a bit more closely.
The sprintf method is intrinsic to Ruby and it's capable of way more stuff than we see here, but a more in-depth explanation of it is outside the scope of this series.
":%2d" means "a colon followed by an integer two characters wide". The second argument is what the % expression will be replaced with, which in this case is the number of the item that the party holds.
This method updates the help window by calling its set_item method and passing in the selected item.
The overwrite of Window_Selectable's refresh method is almost identical but adds a call to make_item_list to create the list of items for the window.
Window_SkillCommand
This window is used for selecting skills in the skill menu. It has one public instance variable:
This variable holds the window object containing the skill list. It's the equivalent of item_window from Window_ItemCategory.
The constructor takes two parameters for x and y coordinates. First we do the usual call to super passing in the parameter values, then we set @actor to nil.
The window_width method is hardcoded at 160 pixels.
This method sets the actor for the window, taking actor as a parameter. We return if @actor is already equal to the passed-in actor object. Otherwise, @actor is set to actor, then we refresh the window and select the most recently-selected actor.
The window will have 4 visible lines for skill types.
To make the command list, first we return unless @actor contains a value, because otherwise there are no skills to make a list of. Then we take the actor's added skill types, sort them, and perform an each loop on the sorted list using the iteration variable "stype_id":
The temp variable "name" is assigned the skill type from $data_system at index stype_id, then we add a command to the list with the value of name, the symbol :skill, true for enabled, and an ext parameter of stype_id. This allows us to memorise the last skill type (can't use the symbol because they're all :skill).
Similarly to the update method of the item category window, this one calls the super method, then sets the stype_id of the skill window to current_ext if @skill_window contains data.
This method sets the skill window associated with the command list, taking skill_window as a parameter. We set the @skill_window variable to the passed-in object, then call update.
The overwrite for selecting the last item first sets a temp variable called "skill" to the current actor's last skill as an object (the element of $data_skills containing the skill data). If this variable contains a value, we call select_ext on the skill's stype ID, otherwise we call select passing in 0 (to select the first command in the list).
Window_SkillStatus
This window displays the user's status on the skill screen.
The constructor is pretty standard, the only thing of note is that the height passed in to the super constructor is the fitting height for 4 lines. After calling super, @actor is set to nil.
The width of the skill status window is the width of the game window less 160 pixels (the width of the command window).
This is pretty much the same as the previous actor= method; takes actor as a parameter, returns if @actor holds the same object, sets @actor to the passed-in actor, then refreshes the window.
The refresh method clears the contents and returns unless an actor is set. After this check, we draw the actor's face at (0, 0) and draw their simple status at an x coordinate of 108 and a y coordinate of half the height of a line.
Window_SkillList
This window displays the list of skills in the skill menu.
The constructor calls the super method, as normal, then sets @actor to nil, @stype_id to 0 and @data to . stype_id is obviously the skill type that will be shown, and data is an array of eligible skills.
Nothing special in the method for setting the actor variable, it's identical to category= from Window_ItemList.
...and this method is identical to the actor one only it's setting stype_id instead.
col_max is hardcoded at 2, as we'll only have 2 columns of skills shown maximum.
The maximum number of items will either be the size of @data if it isn't nil, or 1 otherwise.
This method gets the skill data at the highlighted index. If @data contains values AND index is greater than or equal to 0, we return the element of @data at index. Otherwise, we return nil.
Identical to the one from Window_ItemList.
Similar to the one from Window_ItemList but we only need to check two things: returns true if the passed-in item exists AND the item's skill type ID is the same as the @stype_id variable (in other words, whether the skill is of the type we currently have selected).
The method for determining whether the item is enabled returns true if @actor contains a value AND the actor can use the item in question.
Pretty much the same as the one for Window_ItemList. If an actor exists, @data is set to the result of calling select on the actor's skills property, with the block using the iteration variable "skill" and including the element in the new array of the skill returns true when passed to include?. If an actor object does not exist, we return an empty array.
The only difference between this and the one from Window_ItemList is that it's using @actor.last_skill instead of $game_party.last_item.
This is almost identical to the one from Window_ItemList. The only differences are the temp variable name (skill instead of item) and it's calling draw_skill_cost instead of draw_item_number.
Drawing a skill cost is a little more involved than drawing item quantities, but not by much:
If the selected actor's TP cost for the given skill is greater than 0, we change text colour to the tp_cost_color (slightly transparent if the skill is disabled) and then draw the TP cost in the rect aligned to the right.
Otherwise, if the actor's MP cost for the given skill is greater than 0, we change text colour to the mp_cost_color (again, slightly transparent if the skill is enabled) and then draw the MP cost in the rect aligned to the right.
Updating the help simply calls the help window's set_item method passing in the selected item.
The refresh method makes the item list, creates the window contents, and then draws all items. Simples!
And that's it for another Slip into Ruby! We still have a lot of windows to cover, but we'll get there eventually. And once we're done with windows, we can finally get to scenes! Excite!
As usual, comments are welcome. Until next time!

Last time we looked at the command window classes, and now it's time to check out some of the other windows!
Window_Help
This is the window that shows descriptions of items and skills and offers other helpful information in various scenes. It has no public instance variables.
def initialize(line_number = 2) super(0, 0, Graphics.width, fitting_height(line_number)) end
The constructor takes one parameter, line_number, which defaults to 2. All we do in this method is call the initialize method of the parent class, which is Window_Base, passing in X and Y coordinates of 0 and 0, the full window width as the width argument, and the fitting height for the passed-in number of lines as the height argument.
def set_text(text) if text != @text @text = text refresh end end
This method allows you to set the text that's shown in the window, and takes one parameter, text. First of all, we only do anything in this method if the passed-in text is different from the @text variable. If so, the passed-in text is assigned to @text, and we call the refresh method.
def clear set_text("") end
The clear method simply calls set_text passing in an empty string.
def set_item(item) set_text(item ? item.description : "") end
This method is used to set the help text to the description of an item/skill etc. and simply calls set_text with a ternary if statement: if an item was passed in, we return the item's description, otherwise a blank string.
def refresh contents.clear draw_text_ex(4, 0, @text) end
The refresh method clears the contents and then calls draw_text_ex passing in an x coordinate of 4, a y coordinate of 0, and @text. Readers with elephantine memories will remember way back when we looked at Window_Base that draw_text_ex is the one that processes text codes, meaning you can also use them in the help window.
Window_Gold
This is the window that shows how many doubloons your party has accumulated. As with the help window, it has no public instance variables.
def initialize super(0, 0, window_width, fitting_height(1)) refresh end
The constructor is more or less the same as the one above, only it calls a method to get the window width and will only ever be the height of 1 line rather than taking an argument. It also calls its refresh method on creation.
def window_width return 160 end
The window_width method determines the width of the window (you don't say?) and is hardcoded at 160. If you want longer/thinner gold windows, this is the value to aim your pistol at.
def refresh contents.clear draw_currency_value(value, currency_unit, 4, 0, contents.width - 8) end
The refresh method clears the contents (as with every window) and then calls draw_currency_value passing in value (which we'll look at next), currency_unit (which we'll look at after that, at x coordinate 4 and y coordinate 0, with a width of contents.width - 8. We looked at draw_currency_value in an earlier issue (it's a method of Window_Base if you forgot).
def value $game_party.gold end
This method is just a shorthand alternative to using $game_party.gold.
def currency_unit Vocab::currency_unit end
This method gets the currency unit as defined in Vocab, which gets its value from whatever you set in the database.
def open refresh super end
Last but not least, the method for opening the window calls refresh and then the open method of the parent class.
Window_MenuCommand
As the name may suggest, this is the command window shown in the menu. It has no public instance variables.
def self.init_command_position @@last_command_symbol = nil end
I don't know if we've covered class variables yet, but they're denoted by @@ as opposed to @ for an instance variable. init_command_position is a method of Window_MenuCommand itself rather than a given instance of it, and it sets the class variable @@last_command_symbol to nil. This means that the most recently-selected command in a menu command window can always be referenced.
def initialize super(0, 0) select_last end
The constructor simply calls the constructor of the parent (Window_Command) passing in 0 for both the X and Y coordinate, and calls select_last to change the selection to the most recent item that was highlighted in the window.
def window_width return 160 end
The window_width method is hardcoded to return 160 pixels.
def visible_line_number item_max end
This method determines how many lines are visible in the window, and is returned as the maximum number of items the window can show.
def make_command_list add_main_commands add_formation_command add_original_commands add_save_command add_game_end_command end
The overwrite of the parent make_command_list method calls a number of methods to add commands to the menu; first we add the main commands, then the formation command, then any custom commands the developer has added, then the save command, and finally the game end command.
def add_main_commands add_command(Vocab::item, :item, main_commands_enabled) add_command(Vocab::skill, :skill, main_commands_enabled) add_command(Vocab::equip, :equip, main_commands_enabled) add_command(Vocab::status, :status, main_commands_enabled) end
The method for adding main commands calls add_command four times, one for each of the main commands for items, skills, equipment, and actor status. As you may or may not remember from last issue, the arguments being passed in here correspond to the parameters 'name', 'symbol' and 'enabled' respectively. The Vocab module references the terms set in the database. As with other symbol-based handlers, it really doesn't matter what name is used as long as the handler references the same symbol, but for the sake of clarity and ease of use the writers of the default scripts chose to use symbol names matching the command. main_commands_enabled is a method we'll see soon which determines whether the commands should be enabled or not.
def add_formation_command add_command(Vocab::formation, :formation, formation_enabled) end
This method adds the formation command using much the same syntax as the main commands did.
def add_original_commands end
This method adds original (custom) commands, and is blank by default. The intent is for script developers to alias this to add their own commands.
def add_save_command add_command(Vocab::save, :save, save_enabled) end
This method adds the save command, again using the standard command-adding syntax we've seen plenty of now.
def add_game_end_command add_command(Vocab::game_end, :game_end) end
The method for adding the game end command differs only in that it has no argument for 'enabled' due to the fact that it will always be available.
def main_commands_enabled $game_party.exists end
The method determining whether main commands are enabled returns the result of calling $game_party's exists method, which as you very likely won't remember from the issue that covered it returns whether its @actors instance variable is not empty. In other words, main commands are only enabled if there's at least 1 party member.
def formation_enabled $game_party.members.size >= 2 && !$game_system.formation_disabled end
This method determines whether the formation command is enabled, and returns the result of checking whether the size of the party is greater than or equal to 2 AND the formation_disabled flag of $game_system is false. The first condition means the formation command won't be available if you only have 1 party member, which makes sense as there'd be nobody to swap their position with.
def save_enabled !$game_system.save_disabled end
This method determines whether the save command is enabled, and simply returns whether the save_disabled property of $game_system is false.
def process_ok @@last_command_symbol = current_symbol super end
The overwrite of the Window_Selectable process_ok method sets the class variable @@last_command_symbol to the current symbol. This allows us to memorise the command that the player last had highlighted.
def select_last select_symbol(@@last_command_symbol) end
And its counterpart, this method selects the symbol contained in @@last_command_symbol.
Window_MenuStatus
This is the menu window which shows the status of party members i.e. their level, HP, MP and states. It has one public instance variable:
attr_reader :pending_index # Pending position (for formation)
As explained by the comment, pending_index is the pending position for use when changing the party formation (so that the command knows both the new and old positions).
def initialize(x, y) super(x, y, window_width, window_height) @pending_index = -1 refresh end
Pretty standard fare as constructors go. Call the parent constructor passing in the x and y arguments along with the results of window_width and window_height, set the pending index to -1 (which basically just means "nothing") and call the refresh method.
def window_width Graphics.width - 160 end
The window_width method returns the width of the window less 160 pixels.
def window_height Graphics.height end
window_height just returns the height of the window.
The savvy among you may realise that we're subtracting just as many pixels from window_width here as we returned in window_width for Window_MenuCommand. That's because the status window will be placed to the right of the command window, so we need to reduce the width by its width for it to fit on the screen correctly. You'll see this a lot with interconnected windows.
def item_max $game_party.members.size end
The overwrite of item_max for this window class returns the size of the members property of $game_party, or in other words, how many party members there are.
def item_height (height - standard_padding * 2) / 4 end
The height of a single item is defined as (the height of the window less twice the standard padding) divided by 4. This will give us the height of a single "actor" in the status window, as it only shows 4 actors at a time.
def draw_item(index) actor = $game_party.members[index] enabled = $game_party.battle_members.include?(actor) rect = item_rect(index) draw_item_background(index) draw_actor_face(actor, rect.x + 1, rect.y + 1, enabled) draw_actor_simple_status(actor, rect.x + 108, rect.y + line_height / 2) end
The overwrite of draw_item, as with its parent, takes the index as a parameter. We set a temp variable called actor to the member of the party at index and enabled to whether the party's battle members include that actor.
Then we set a temp variable called rect to the result of calling item_rect passing in index (this gives us the rect required to display the given index on the window), draw the item background (which we'll look at in a sec), draw the actor's face, passing in the actor, rect's x + 1, rect's y + 1 and enabled, and then draw the simple status of the actor, passing in actor, the rect's x + 108, and the rect's y + half the height of a line.
So to put it in layman's terms, we'll colour in the background if the party member has been selected for a formation swap, draw their face very slightly to the right and down from the top left of the item rect (and slightly transparent if they're not in the battle party) and then draw their HP, MP etc. centred vertically in the rect.
def draw_item_background(index) if index == @pending_index contents.fill_rect(item_rect(index), pending_color) end end
This method fills in the rect for formation selections. If the passed-in index is equal to @pending_index, fill the item_rect for index with pending_color.
def process_ok super $game_party.menu_actor = $game_party.members[index] end
The overwrite of process_ok calls the parent method and then sets the menu_actor property of $game_party to the member corresponding to the currently-selected index.
def select_last select($game_party.menu_actor.index || 0) end
And to select the last, we call select passing in the index of $game_party's menu_actor property, or 0 if no value is returned.
def pending_index=(index) last_pending_index = @pending_index @pending_index = index redraw_item(@pending_index) redraw_item(last_pending_index) end
This method sets the pending index and takes index as a parameter. First, we set a temp variable called last_pending_index to the value of @pending_index, then set @pending_index to the newly passed-in index value, then redraw both the pending and last items (so that actor graphics/stats will be updated in both places).
Window_MenuActor
This window is used to select actors as targets of skills/items. No public instance variables this time. It inherits from Window_MenuStatus.
def initialize super(0, 0) self.visible = false end
Pretty standard constructor. We set its visible property to false so that it's invisible to begin with.
def process_ok $game_party.target_actor = $game_party.members[index] unless @cursor_all call_ok_handler end
This method processes OK key input. First, we set the target_actor property of $game_party to the member corresponding to the selected index unless the item targets the whole party, then we call the ok handler.
def select_last select($game_party.target_actor.index || 0) end
This overwrite of the select_last method is identical to the one for Window_MenuStatus only using target_actor instead of menu_actor.
def select_for_item(item) @cursor_fix = item.for_user? @cursor_all = item.for_all? if @cursor_fix select($game_party.menu_actor.index) elsif @cursor_all select(0) else select_last end end
This method sets the cursor position for item selection, taking the item as a parameter. First, we set @cursor_fix to the result of calling the for_user? method on the passed-in item, then set @cursor_all to the result of calling the for_all? method.
If @cursor_fix is true, we select the index of the party's menu actor (the user). Otherwise, if @cursor_all is true, we select index 0 (the party leader). Otherwise, we call select_last to select the most recently-targeted actor.
Window_ItemCategory
This window is for selecting item categories in the item menu or in shops. It inherits from Window_HorzCommand and has one public instance variable:
attr_reader :item_window
There's no comment for it, but this is a property to hold the window object displaying the items that the category choice will be filtering.
def initialize super(0, 0) end
Nothing special in the constructor.
def window_width Graphics.width end
The width of the window is just the whole screen width.
def col_max return 4 end
We're displaying 4 columns maximum. (I'm not actually sure why this is here because Window_HorzCommand already has an identical method. It's overwriting the parent method with one that's 100% identical to it. You could easily remove this and it would have no effect on the engine).
def update super @item_window.category = current_symbol if @item_window end
The frame update method calls its parent method, then sets the category property of @item_window to the current symbol if an item window is set.
def make_command_list add_command(Vocab::item, :item) add_command(Vocab::weapon, :weapon) add_command(Vocab::armor, :armor) add_command(Vocab::key_item, :key_item) end
The overwrite for make_command_list adds 4 commands: one for items, one for weapons, one for armours, and one for key items, using the appropriate Vocab methods and symbols.
def item_window=(item_window) @item_window = item_window update end
This method sets the item window property, taking item_window as a parameter. Sets @item_window to the passed-in object, then calls update (which sets the item window category to the current symbol).
Window_ItemList
This is the window that displays the list of items on the item screen.
def initialize(x, y, width, height) super @category = :none @data = [] end
The constructor does a couple of things besides calling the parent method. First, we set the instance variable @category to the symbol :none, and then set @data to an empty array.
def category=(category) return if @category == category @category = category refresh self.oy = 0 end
This method sets the window's category, taking category as a parameter. We return if @category is already equal to the passed-in symbol, then set @category to category, refresh the window, and set its y origin to 0.
def col_max return 2 end
The overwrite of col_max returns 2, as the item list will be 2 columns at most.
def item_max @data ? @data.size : 1 end
The overwrite of item_max returns the size of the @data array if it exists, and 1 otherwise.
def item @data && index >= 0 ? @data[index] : nil end
This method gets the current item: if @data exists and the index is greater than or equal to 0, we return the element of @data at index. Otherwise, we return nil.
def current_item_enabled? enable?(@data[index]) end
This method determines whether the currently-selected item is enabled, by calling the enable? method on the element of @data at the current index.
def include?(item) case @category when :item item.is_a?(RPG::Item) && !item.key_item? when :weapon item.is_a?(RPG::Weapon) when :armor item.is_a?(RPG::Armor) when :key_item item.is_a?(RPG::Item) && item.key_item? else false end end
This method determines whether an item should be included in the list, taking the item as a parameter.
We have a case statement against @category:
When it's :item, we return true if the item is an instance of RPG::Item and not a key item, and false otherwise.
When it's :weapon, we return true if the item is an instance of RPG::Weapon, and false otherwise.
When it's :armor, we return true if the item is an instance of RPG::Armor, and false otherwise.
When it's :key_item, we return true if the item is an instance of RPG::Item and IS a key item, and false otherwise.
Otherwise, we return false.
def enable?(item) $game_party.usable?(item) end
This method determines whether to display an item as enabled (fully opaque) or disabled (partially transparent) and returns the value of calling the usable? method of $game_party for the passed-in item.
def make_item_list @data = $game_party.all_items.select {|item| include?(item) } @data.push(nil) if include?(nil) end
This method makes the list of items that will be displayed in the window. @data is set to the result of calling the select method on the result of calling the all_items method of $game_party using the iteration variable "item" and including the item in the returned array if the include? method returns true with the item as its argument.
...huh?
You may be a bit rusty with select as it's been a while since we've seen it, but basically you call it on an array passing in a block, and it returns a new array using the block as a filter condition to check whether the returned array should include the current element. In this case, all_items gets every item the party is carrying, and then we filter those items through the include? method, which essentially checks that the items are of the correct category. So only items will show for :item, only weapons will show for :weapon, etc.
Then, we push nil to @data if include? returns true when nil is passed in. Fun fact: This will NEVER happen. nil won't return true for any of the is_a? checks, so even if there is a category set it will return false in every scenario and subsequently this line is entirely pointless.
def select_last select(@data.index($game_party.last_item.object) || 0) end
The overwrite for select_last is a similarly-confusing-looking chain of method calls: we're calling select, passing in the element of @data with an index matching the object property of the last_item property of $game_party.
Okay, let's break this down. We know what select does: it takes the index of an item in the window that will be selected, so we know the method chain is ending up with an index somewhere.
@data contains a list of included items, so we know they're going to be item objects. Those aren't indexes, so we need to drill down further. The .index method of arrays returns the index of the first element of the calling array which matches the argument passed in, so what we need is an item object corresponding to the most recent one the party used. Luckily for us, $game_party has a last_item property, but it's an instance of Game_BaseItem and the elements of @data are all elements of $data_items/weapons/armors, so again we need to drill down a bit further. Again, luckily, Game_BaseItem has an object method which returns the element of $data_items/weapons/armors which corresponds to the item it represents. So combining all of this gives us a chain of method calls that will let us find the item in @data which matches the last item object the party used.
def draw_item(index) item = @data[index] if item rect = item_rect(index) rect.width -= 4 draw_item_name(item, rect.x, rect.y, enable?(item)) draw_item_number(rect, item) end end
The method for drawing items in the window, as with the parent method it overwrites, takes index as a parameter. We set the temp variable item to the element of @data at index. Then, if item isn't nil, we set the temp variable rect to the result of calling item_rect with index as the argument (which gets the rect required to display a single item at the selected index location), reducing its width by 4 (because there's a tiny bit more of a margin on the left than there is on the right, this will make the item info more 'centred'). We then draw the item name, passing in the item object, rect's x, rect's y, and whether the item is enabled. Finally, we draw the party's held quantity of the item, passing in rect and the item object.
def draw_item_number(rect, item) draw_text(rect, sprintf(":%2d", $game_party.item_number(item)), 2) end
This method draws the quantity of an item held by the party, taking two parameters: rect and item. As we saw above, rect is the item rect for drawing the item at the current index, and item is the item object currently selected.
We call the draw_text method, which we looked at back when we covered Window_Base, passing in the rect, a call to sprintf, and 2 for "align right". It's all pretty straightforward except the sprintf, so let's look at that a bit more closely.
The sprintf method is intrinsic to Ruby and it's capable of way more stuff than we see here, but a more in-depth explanation of it is outside the scope of this series.
":%2d" means "a colon followed by an integer two characters wide". The second argument is what the % expression will be replaced with, which in this case is the number of the item that the party holds.
def update_help @help_window.set_item(item) end
This method updates the help window by calling its set_item method and passing in the selected item.
def refresh make_item_list create_contents draw_all_items end
The overwrite of Window_Selectable's refresh method is almost identical but adds a call to make_item_list to create the list of items for the window.
Window_SkillCommand
This window is used for selecting skills in the skill menu. It has one public instance variable:
attr_reader :skill_window
This variable holds the window object containing the skill list. It's the equivalent of item_window from Window_ItemCategory.
def initialize(x, y) super(x, y) @actor = nil end
The constructor takes two parameters for x and y coordinates. First we do the usual call to super passing in the parameter values, then we set @actor to nil.
def window_width return 160 end
The window_width method is hardcoded at 160 pixels.
def actor=(actor) return if @actor == actor @actor = actor refresh select_last end
This method sets the actor for the window, taking actor as a parameter. We return if @actor is already equal to the passed-in actor object. Otherwise, @actor is set to actor, then we refresh the window and select the most recently-selected actor.
def visible_line_number return 4 end
The window will have 4 visible lines for skill types.
def make_command_list return unless @actor @actor.added_skill_types.sort.each do |stype_id| name = $data_system.skill_types[stype_id] add_command(name, :skill, true, stype_id) end end
To make the command list, first we return unless @actor contains a value, because otherwise there are no skills to make a list of. Then we take the actor's added skill types, sort them, and perform an each loop on the sorted list using the iteration variable "stype_id":
The temp variable "name" is assigned the skill type from $data_system at index stype_id, then we add a command to the list with the value of name, the symbol :skill, true for enabled, and an ext parameter of stype_id. This allows us to memorise the last skill type (can't use the symbol because they're all :skill).
def update super @skill_window.stype_id = current_ext if @skill_window end
Similarly to the update method of the item category window, this one calls the super method, then sets the stype_id of the skill window to current_ext if @skill_window contains data.
def skill_window=(skill_window) @skill_window = skill_window update end
This method sets the skill window associated with the command list, taking skill_window as a parameter. We set the @skill_window variable to the passed-in object, then call update.
def select_last skill = @actor.last_skill.object if skill select_ext(skill.stype_id) else select(0) end end
The overwrite for selecting the last item first sets a temp variable called "skill" to the current actor's last skill as an object (the element of $data_skills containing the skill data). If this variable contains a value, we call select_ext on the skill's stype ID, otherwise we call select passing in 0 (to select the first command in the list).
Window_SkillStatus
This window displays the user's status on the skill screen.
def initialize(x, y) super(x, y, window_width, fitting_height(4)) @actor = nil end
The constructor is pretty standard, the only thing of note is that the height passed in to the super constructor is the fitting height for 4 lines. After calling super, @actor is set to nil.
def window_width Graphics.width - 160 end
The width of the skill status window is the width of the game window less 160 pixels (the width of the command window).
def actor=(actor) return if @actor == actor @actor = actor refresh end
This is pretty much the same as the previous actor= method; takes actor as a parameter, returns if @actor holds the same object, sets @actor to the passed-in actor, then refreshes the window.
def refresh contents.clear return unless @actor draw_actor_face(@actor, 0, 0) draw_actor_simple_status(@actor, 108, line_height / 2) end
The refresh method clears the contents and returns unless an actor is set. After this check, we draw the actor's face at (0, 0) and draw their simple status at an x coordinate of 108 and a y coordinate of half the height of a line.
Window_SkillList
This window displays the list of skills in the skill menu.
def initialize(x, y, width, height) super @actor = nil @stype_id = 0 @data = [] end
The constructor calls the super method, as normal, then sets @actor to nil, @stype_id to 0 and @data to . stype_id is obviously the skill type that will be shown, and data is an array of eligible skills.
def actor=(actor) return if @actor == actor @actor = actor refresh self.oy = 0 end
Nothing special in the method for setting the actor variable, it's identical to category= from Window_ItemList.
def stype_id=(stype_id) return if @stype_id == stype_id @stype_id = stype_id refresh self.oy = 0 end
...and this method is identical to the actor one only it's setting stype_id instead.
def col_max return 2 end
col_max is hardcoded at 2, as we'll only have 2 columns of skills shown maximum.
def item_max @data ? @data.size : 1 end
The maximum number of items will either be the size of @data if it isn't nil, or 1 otherwise.
def item @data && index >= 0 ? @data[index] : nil end
This method gets the skill data at the highlighted index. If @data contains values AND index is greater than or equal to 0, we return the element of @data at index. Otherwise, we return nil.
def current_item_enabled? enable?(@data[index]) end
Identical to the one from Window_ItemList.
def include?(item) item && item.stype_id == @stype_id end
Similar to the one from Window_ItemList but we only need to check two things: returns true if the passed-in item exists AND the item's skill type ID is the same as the @stype_id variable (in other words, whether the skill is of the type we currently have selected).
def enable?(item) @actor && @actor.usable?(item) end
The method for determining whether the item is enabled returns true if @actor contains a value AND the actor can use the item in question.
def make_item_list @data = @actor ? @actor.skills.select {|skill| include?(skill) } : [] end
Pretty much the same as the one for Window_ItemList. If an actor exists, @data is set to the result of calling select on the actor's skills property, with the block using the iteration variable "skill" and including the element in the new array of the skill returns true when passed to include?. If an actor object does not exist, we return an empty array.
def select_last select(@data.index(@actor.last_skill.object) || 0) end
The only difference between this and the one from Window_ItemList is that it's using @actor.last_skill instead of $game_party.last_item.
def draw_item(index) skill = @data[index] if skill rect = item_rect(index) rect.width -= 4 draw_item_name(skill, rect.x, rect.y, enable?(skill)) draw_skill_cost(rect, skill) end end
This is almost identical to the one from Window_ItemList. The only differences are the temp variable name (skill instead of item) and it's calling draw_skill_cost instead of draw_item_number.
def draw_skill_cost(rect, skill) if @actor.skill_tp_cost(skill) > 0 change_color(tp_cost_color, enable?(skill)) draw_text(rect, @actor.skill_tp_cost(skill), 2) elsif @actor.skill_mp_cost(skill) > 0 change_color(mp_cost_color, enable?(skill)) draw_text(rect, @actor.skill_mp_cost(skill), 2) end end
Drawing a skill cost is a little more involved than drawing item quantities, but not by much:
If the selected actor's TP cost for the given skill is greater than 0, we change text colour to the tp_cost_color (slightly transparent if the skill is disabled) and then draw the TP cost in the rect aligned to the right.
Otherwise, if the actor's MP cost for the given skill is greater than 0, we change text colour to the mp_cost_color (again, slightly transparent if the skill is enabled) and then draw the MP cost in the rect aligned to the right.
def update_help @help_window.set_item(item) end
Updating the help simply calls the help window's set_item method passing in the selected item.
def refresh make_item_list create_contents draw_all_items end
The refresh method makes the item list, creates the window contents, and then draws all items. Simples!
And that's it for another Slip into Ruby! We still have a lot of windows to cover, but we'll get there eventually. And once we're done with windows, we can finally get to scenes! Excite!
As usual, comments are welcome. Until next time!













