SLIP INTO RUBY - UNDER THE HOOD PART 11: DO NINE MEN INTERPRET(ER)? NINE MEN, I NOD.

We finally sift through the gooey innards that is VX Ace's Game_Interpreter class.

  • Trihan
  • 01/19/2017 01:03 AM
  • 7295 views
Okay, so it's been over a year. But it's back! Tell all your friends! If you don't have any friends tell your cat! The time has finally come for the next exciting edition of



So if you remember last time (it was October 2015, I wouldn't blame you if you didn't) we looked at everything from Game_CommonEvent up to Game_Event. Now it's time. The time I never wanted to come. Yes, it's time to crack open the behemoth that makes this whole mother do what it does. It's time to look at Game_Interpreter.

Game_Interpreter

This is going to take up a whole episode by itself, so fasten your seatbelts and get ready to unravel one of the biggest scripts in the entirety of RPG Maker VX Ace. This is the one responsible for making all your fancy event commands -do stuff-, so it's an important one to understand. An in-depth understanding of the interpreter and how it works will enable you to change the behaviour of existing event commands however you see fit.

For future reference, Game_Map, Game_Troop and Game_Event all have their own instance of the interpreter, which is why event commands still work in battle etc.

To begin with, it has two public instance variables:

attr_reader   :map_id             # Map ID
attr_reader   :event_id           # Event ID (normal events only)


They're pretty self-explanatory: Map ID is the ID of the map the interpreter is for (when used on Game_Map), and Event ID is the ID of the event the interpreter is for (when used on Game_Event).

def initialize(depth = 0)
    @depth = depth
    check_overflow
    clear
  end


As you can see here, the constructor takes one argument, depth. The instance variable @depth is set to the supplied value (default of 0), then we call check_overflow (which we'll look at in a sec) and finally we call clear (which we'll also look at in a sec).

def check_overflow
    if @depth >= 100
      msgbox(Vocab::EventOverflow)
      exit
    end
  end


As stated in the comments for this method, under normal conditions depth will not exceed 100. This method is basically here to catch infinite loops resulting from recursive event calls (events calling themselves). Essentially, if @depth is ever equal to or greater than 100, an event overflow error is displayed and the game quits.

def clear
    @map_id = 0
    @event_id = 0
    @list = nil                       # Execution content
    @index = 0                        # Index
    @branch = {}                      # Branch data
    @fiber = nil                      # Fiber
  end


Clear is a method that basically just starts you off with a fresh slate interpreter with no data. The map ID and event ID are both set to 0, the list of commands is set to nil, the command index is set to 0, the data of the current branch is set to an empty array, and fiber is set to nil (we'll cover fiber shortly).

def setup(list, event_id = 0)
    clear
    @map_id = $game_map.map_id
    @event_id = event_id
    @list = list
    create_fiber
  end


Setup is a method for setting up map events and takes two arguments: a list of event commands and the ID of the event in question. First, we call the clear method, same as with the constructor. The @map_id instance variable is set to the ID of the map the player is currently on ($game_map being a global variable containing all data for the current map, as we've covered before). @event_id is set to the supplied event ID, which defaults to 0. @list is set to the supplied list of commands, and then we call create_fiber. This isn't part of a balanced diet, it's an integral part of how RPG Maker VX Ace even works.

def create_fiber
    @fiber = Fiber.new { run } if @list
  end


Well. This is...not very illuminating, is it? To put it in layman's terms: "if @list contains data, set @fiber to a new instance of Fiber, and then...do something with run in brackets?"

To quote the Ruby documentation: "Fibers are primitives for implementing light weight cooperative concurrency in Ruby. Basically they are a means of creating code blocks that can be paused and resumed, much like threads. The main difference is that they are never preempted and that the scheduling must be done by the programmer and not the VM."

A Fiber will not run automatically when created; it must be explicitly asked using the Fiber.resume method (which, you guessed it, we'll look at in a bit). You can also have the fiber give up control to the code that called it using Fiber.yield, which, again, we'll see in a bit.

This is a lot to take in for people who aren't used to Object-Oriented Programming or multithreading, so suffice to say that Fiber basically starts a self-sufficient bit of code which retains state and can be paused/resumed as needed, and in this case, all it's doing is calling the "run" method of Game_Interpreter, which is coming up soon.

def marshal_dump
    [@depth, @map_id, @event_id, @list, @index + 1, @branch]
  end


This method returns an array consisting of the depth, the map ID, the event ID, the list of commands, the current index incremented by 1, and the branch data. Although you won't see a call to this method anywhere in the default scripts, it's kind of built-in to the language and will be called whenever Ruby is trying to serialise an interpreter. Without this, you wouldn't be able to save games.

def marshal_load(obj)
    @depth, @map_id, @event_id, @list, @index, @branch = obj
    create_fiber
  end


This is the counterpart to marshal_dump, used when loading data (again, because fibers are not compatible with Marshal); it takes one argument, obj (which is an array of data as defined in marshal_dump) and simply sets each respective instance variable to the element of obj corresponding to it, then creates the fiber using that data.

This also demonstrates a pretty nifty feature of Ruby that I don't think I've ever really gone into, which is that you can assign multiple values in one line by setting a list of variables to an array. For example, if I were to do

@test1, @test2, @test3, @test4 = [1, 2, 3, 4]


basically what would happen is that the array would be iterated, and each value would be assigned to the next variable specified. So @test1 would hold the value 1, @test2 the value 2, and so on. If you supply fewer values in the array than there are variables, the other variables will be set to nil.

To see in action the ramifications of not having this set of methods here, try commenting out marshal_dump and then save your game. What's that? You can't do it? Yeah, that's because Ruby isn't able to serialise your interpreter. :D (similarly, if you comment out marshal_load, you can save your game but can't load it).

def same_map?
    @map_id == $game_map.map_id
  end


This method is used to determine if the map the event started on is the same as the one the player is on currently, and is used for a few purposes which we'll see later. It simply compares the @map_id variable to the map_id method of $game_map, returning true if they're equal and false if they're not.

def setup_reserved_common_event
    if $game_temp.common_event_reserved?
      setup($game_temp.reserved_common_event.list)
      $game_temp.clear_common_event
      true
    else
      false
    end
  end


This method sets up reserved common events: you probably don't remember, but we looked at common_event_reserved? way back in Slip into Ruby: Under the Hood part 3.

So first what we're doing is checking to see if a common event has even been reserved in the first place, and simply returns false if there isn't: if there isn't one waiting to run, what's the point in setting one up?

If an event is waiting to run, we call the setup method, supplying the list of commands for that common event, then clear the common event ID from $game_temp (so that it doesn't keep setting up the same common event forever) and finally returns true to show that the event is ready to run.

def run
    wait_for_message
    while @list[@index] do
      execute_command
      @index += 1
    end
    Fiber.yield
    @fiber = nil
  end


This is the meat and potatoes of the interpreter, and is essentially what makes it work. You'll remember from create_fiber that this method is the only thing the Fiber block contains, so as soon as we call resume on a fiber it'll call this method.

First we call wait_for_message, which as we'll see a bit later simply...waits for a message (as in the boxes you use for dialogue and stuff). Then it starts a while loop for as long as @list contains data in the element corresponding to @index. Each loop all that will happen is that execute_command will be called, then the index will be incremented by 1. Once the list has been fully iterated through, we call Fiber.yield (which returns control to the code that called the fiber) and set @fiber to nil.

Interestingly enough, because the fiber is only resumed in frame update methods (as, you guessed it, we'll see soon), I'm not actually sure what Fiber.yield even -does- here. As far as I can tell everything still works fine without it, because all it's doing is returning control to a method that it goes back to after it runs anyway. If anyone can shed some light on this please feel free.

def running?
    @fiber != nil
  end


This method determines whether the interpreter is running or not, and simply returns true if @fiber is not nil, and false otherwise.

def update
    @fiber.resume if @fiber
  end


The frame update method. If @fiber contains data, resume it. As we've already seen, this means that every frame during which a fiber exists, the interpreter's "run" method will be called (if the fiber isn't already running), which will take over control until the event has run its course (which is also why other non-parallel-process events on a map stop what they're doing when you interact with an event).

def iterate_actor_id(param)
    if param == 0
      $game_party.members.each {|actor| yield actor }
    else
      actor = $game_actors[param]
      yield actor if actor
    end
  end


This method is an actor ID iterator, which takes one parameter (param); as specified in the comments, if the value is 0, goes through each actor. If 1 or more, yields the actor corresponding to that ID.

This looks more complicated than it is, and once we come across a command that uses this and upcoming iteration methods I'll go through the logic in detail.

def iterate_actor_var(param1, param2)
    if param1 == 0
      iterate_actor_id(param2) {|actor| yield actor }
    else
      iterate_actor_id($game_variables[param2]) {|actor| yield actor }
    end
  end


Actor variable iterator, taking two parameters: param1 and param2. param1 specifies whether the variable being used is fixed (0) or itself a variable (1), while param2 specifies the actor or variable ID. If param1 is 0, then we call iterate_actor_id with param2 as the param. If it's 1 or more, we call iterate_actor_id using element param2 of $game_variables and yield that actor instead.

def iterate_actor_index(param)
    if param < 0
      $game_party.members.each {|actor| yield actor }
    else
      actor = $game_party.members[param]
      yield actor if actor
    end
  end


Actor index iterator, taking one parameter: param. If it's 0 or more, yields the actor corresponding to that value. If -1, yields the whole party.

def iterate_enemy_index(param)
    if param < 0
      $game_troop.members.each {|enemy| yield enemy }
    else
      enemy = $game_troop.members[param]
      yield enemy if enemy
    end
  end


Enem index iterator, taking one parameter: param. If it's 0 or more, yields the enemy corresponding to that value. If -1, yields the whole enemy troop.

def iterate_battler(param1, param2)
    if $game_party.in_battle
      if param1 == 0
        iterate_enemy_index(param2) {|enemy| yield enemy }
      else
        iterate_actor_id(param2) {|actor| yield actor }
      end
    end
  end


Battler iterator, taking two parameters: param1 and param2. param1 specifies whether to iterate enemies (0) or actors (1), while param2 specifies the enemy index or actor ID. This check is only done if the party is in battle.

def screen
    $game_party.in_battle ? $game_troop.screen : $game_map.screen
  end


Determines the target of a command that affects the screen. If the party is in battle, return the screen of $game_troop; otherwise, return the screen of $game_map.

def execute_command
    command = @list[@index]
    @params = command.parameters
    @indent = command.indent
    method_name = "command_#{command.code}"
    send(method_name) if respond_to?(method_name)
  end


This method is the one that actually executes event commands. First, the variable command is set to the element of @list corresponding to the current @index. @params is set to the parameters of that command, @indent is set to the indent of the command, the method name is set to the string "command_" followed by the code of the command, then if that method name responds to calls, send it.

WTF did you just say, Trihan? Okay, so not sure if I've already covered this but you can call a method in Ruby by using send(name_of_method) as well as calling it directly by name. This allows you to call methods with dynamically-generated names, as is the case here. respond_to? simply checks that the object in question (in this case, Game_Interpreter) responds to (or can call) the given method. This is why all of the event command methods are named command_ (as we'll see), so that they can be called dynamically.

def command_skip
    @index += 1 while @list[@index + 1].indent > @indent
  end


This method skips commands which are deeper than the current index and increments it.

To put it more simply, while the indent of the next command in the list is greater than that of the current one, increment the index. I'll explain this in more detail when we get to a method that calls it.

def next_event_code
    @list[@index + 1].code
  end


This method simply gets the code of the next event command, by returning the .code property of the element of @list corresponding to 1 more than @index.

def get_character(param)
    if $game_party.in_battle
      nil
    elsif param < 0
      $game_player
    else
      events = same_map? ? $game_map.events : {}
      events[param > 0 ? param : @event_id]
    end
  end


This method returns a particular character and takes one parameter, param. If the party is in battle, returns nil. If the parameter is -1, returns the player. If any other value, gets a list of events on the map if we're still on the same map as the event started on (otherwise an empty array) and then returns events if param is greater than 0, or events[@event_id] if it's 0 (in other words, "this event").

def operate_value(operation, operand_type, operand)
    value = operand_type == 0 ? operand : $game_variables[operand]
    operation == 0 ? value : -value
  end


This method calculates operated values, given three parameters: operation, operand type, and operand.

Sets the variable "value" to the value of operand if operand_type is 0, or the game variable with index operand if it's 1. Sets the variable "operation" to value if it's 0, and the negative of value if it's 1. I'll explain further once we get to a method that calls it.

def wait(duration)
    duration.times { Fiber.yield }
  end


This method "waits" a number of frames, specified by the parameter duration. Simply yields the fiber to its caller for as many times as the supplied duration.

def wait_for_message
    Fiber.yield while $game_message.busy?
  end


This method waits while a message is displaying (without this messages would never actually show as the event would just blow past them without waiting for input). Simply yields the fiber to its caller as long as $game_message.busy? returns true. For those who don't remember the time we covered this, it'll return true as long as the message has text, a choice, number input, or an item choice.

def command_101
    wait_for_message
    $game_message.face_name = @params[0]
    $game_message.face_index = @params[1]
    $game_message.background = @params[2]
    $game_message.position = @params[3]
    while next_event_code == 401       # Text data
      @index += 1
      $game_message.add(@list[@index].parameters[0])
    end
    case next_event_code
    when 102  # Show Choices
      @index += 1
      setup_choices(@list[@index].parameters)
    when 103  # Input Number
      @index += 1
      setup_num_input(@list[@index].parameters)
    when 104  # Select Item
      @index += 1
      setup_item_choice(@list[@index].parameters)
    end
    wait_for_message
  end


That's right, we're only just now getting to the first event command. We're going to be here a while...

All this time you've just been using the "Show Text..." command without really thinking about it. Well, that's all about to change, because we're about to dive into its innards and find out exactly how it works.

First, we call wait_for_message, so if a message is displaying, it'll yield to the caller. I honestly don't actually think the first call here is needed, as it's also called at the end and that'll stop to wait for input anyway.

The face_name property of $game_message is set to @params[0], which is the filename of the face graphic you choose when you double click the box in the "Show Text..." dialog. face_index is set to @params[1], which is the face that you choose from the box on the right showing the actual graphic. background is set to @params[2], which is the numerical value corresponding to "Normal Window", "Dim Background" and "Transparent" in the "Background:" drop-down list. position is set to @params[3], which is the numerical value corresponding to "Bottom", "Middle" and "Top" in the "Position:" drop-down list.

Unfortunately there's no way to change the parameters supplied to these commands without hacking into the actual editor since they're tied into the dialog boxes themselves, but it's handy to know that everything you select in an event command dialog box is stored in the params array so you can refer to it in your code.

Then we enter a while loop, for as long as next_event_code returns 401. This is the internal code used by the event editor to denote "text data" and this is how multiple messages are displayed "smoothly" without the box closing and opening after each one. The while loop simply goes to the next index and adds the parameters[0] property of the element of @list corresponding to @index to $game_message. In English, we add the text of the next message command to the content of the global message variable.

You may have noticed something interesting from this: although "101" is the code of the "Show Text..." command, the actual text to be displayed is created as an entirely separate command with code 401. This is why parameters[0] of command 101 is the faceset name, while parameters[0] of command 401 is the text.

Once the while loop has processed all of the continuous text, we check the next_event_code again to see if it's a command that needs to display something alongside the message box. If it's 102 (Show Choices), we increment the index and call setup_choices with the parameters of the command as an argument. If it's 103 (Input Number), we increment the index and call setup_num_input with the parameters of the command as an argument. If it's 104 (Select Item), we...you get the idea. This basically just sets up choices, numbers or item selection so that they appear alongside the message. If we didn't have this, you'd have to close the message box before the other windows appeared, which would be pretty terrible game design.

And finally, we call wait_for_message, which we've already looked at.

def command_102
    wait_for_message
    setup_choices(@params)
    Fiber.yield while $game_message.choice?
  end


The code for Show Choices is surprisingly brief. It calls wait_for_message, then setup_choices supplying @params as the argument (this is the list of choices and the cancellation choice), and yields the fiber to its caller while $game_message.choice? returns true. For those who don't remember that lesson, it returns true if the size of @choices is > 0, which will obviously be the case if you've put choices in the boxes.

So here we come to another interesting note. Although Show Choices can be used by itself, if you have a Show Choices command following a Show Text, command_102 will never actually be called, because the Show Text does the choice processing for you! I actually think the way Enterbrain did this is pretty neat.

def setup_choices(params)
    params[0].each {|s| $game_message.choices.push(s) }
    $game_message.choice_cancel_type = params[1]
    $game_message.choice_proc = Proc.new {|n| @branch[@indent] = n }
  end


This method sets up the choices you select from when using the Show Choices event command. It iterates through each element of params[0] (which is the list of choices you wrote in the boxes) and runs a block on it, with s as the block variable (meaning each time it iterates, s will be whatever string you put in the next choice box, and it will be pushed to the choices array of $game_message).

The choice_cancel_type of $game_message is set to params[1]; this is the numerical representation of "Disallow", "Choice 1", "Choice 2", "Choice 3", "Choice 4", or "Branch" depending on what you chose in the dialog.

Finally, choice_proc of $game_message is set to a new Proc, with block variable n, where the element of @branch corresponding to @indent is set to n. Wait, what?

Okay, so Proc is basically just a fancy way to write a block of code that can be called in different contexts; if choice_proc is called, the value passed to it will be passed as "n", which will then be stored in @branch[@indent]. We'll revisit this when we finally get to Window_ChoiceList, which won't be for a while yet.

def command_402
    command_skip if @branch[@indent] != @params[0]
  end


command_402 is a special one which is called when the interpreter reaches a choice branch. It skips the command if @branch[@indent] (which contains the index of the chosen option) is not equal to @params[0] (the ID of the current choice).

def command_403
    command_skip if @branch[@indent] != 4
  end


Here's another interesting one. The comment says "When Cancel" so you'd think it would be connected to the cancellation of a choice, but as far as I can tell it's never called by anything. What it does is skips the command if @branch[@indent] isn't equal to 4. Intriguingly, although command_403 never seems to be called by Show Choice, it does try to call command_404...which doesn't exist. I'm not sure whether this is a mistake in the coding of the editor. Another interesting note is that as far as I'm aware it's not actually possible for @branch[@indent] to ever equal 4 in a choice window anyway, as it's 0-indexed and there are only 4 options (0, 1, 2, 3).

def command_103
    wait_for_message
    setup_num_input(@params)
    Fib
er.yield while $game_message.num_input?
end

This method processes number input (when it's not immediately following Show Text). Simply waits for messages, calls setup_num_input with @params as the argument, and yields the fiber to the caller while num_input? from $game_message returns true (which is the case as long as @num_input_variable_id > 0, which will be true for the duration of the number input since this is the variable you picked in the dialog of the command).

def setup_num_input(params)
    $game_message.num_input_variable_id = params[0]
    $game_message.num_input_digits_max = params[1]
  end


And here we see the ever-so-complicated code for setting up number input, which is...setting the num_input_variable_id property of $game_message to params[0], which is the variable you picked in the dialog, and num_inputs_digits_max to params[1], which is the value you entered in the "Digits:" box.

def command_104
    wait_for_message
    setup_item_choice(@params)
    Fiber.yield while $game_message.item_choice?
  end


This method processes the "select item" box (when it's not immediately following Show Text). Simply waits for messages, calls setup_item_choice with @params as the argument, and yields the fiber to the caller while item_choice? from $game_message returns true (as above, but for the item_choice_variable_id instead).

def setup_item_choice(params)
    $game_message.item_choice_variable_id = params[0]
  end


Pretty much the same as setting up number input, only we don't have max digits to worry about this time.

def command_105
    Fiber.yield while $game_message.visible
    $game_message.scroll_mode = true
    $game_message.scroll_speed = @params[0]
    $game_message.scroll_no_fast = @params[1]
    while next_event_code == 405
      @index += 1
      $game_message.add(@list[@index].parameters[0])
    end
    wait_for_message
  end


Show Scrolling Text! So it yields the fiber to the caller while $game_message.visible is true (so that if a message is currently showing, the scrolling text won't interfere with it) then sets some properties in $game_message: scroll_mode is set to true, scroll_speed is set to @params[0], which is the value you set in the dialog, and scroll_no_fast is set to @params[1], which determines whether or not the text can be fast forwarded.

We then enter a while loop as long as next_event_code is 405 (the scrolling text equivalent of 401 for text) and in each iteration increment the index and add parameters[0] (the text) to $game_message.

And finally, we wait for the message to finish scrolling.

def command_108
    @comments = [@params[0]]
    while next_event_code == 408
      @index += 1
      @comments.push(@list[@index].parameters[0])
    end
  end


This method is for the "Comment" command. The @comments array is populated with an array consisting of @params[0], which is just the comment text. Then we enter a while loop as long as next_event_code is 408, where we increment @index and push parameters[0] to @comments. I honestly have no idea what this loop does as I've never been able to engineer an event that had a command with code 408 in it, regardless of how many comments I created and where.

def command_111
    result = false
    case @params[0]
    when 0  # Switch
      result = ($game_switches[@params[1]] == (@params[2] == 0))
    when 1  # Variable
      value1 = $game_variables[@params[1]]
      if @params[2] == 0
        value2 = @params[3]
      else
        value2 = $game_variables[@params[3]]
      end
      case @params[4]
      when 0  # value1 is equal to value2
        result = (value1 == value2)
      when 1  # value1 is greater than or equal to value2
        result = (value1 >= value2)
      when 2  # value1 is less than or equal to value2
        result = (value1 <= value2)
      when 3  # value1 is greater than value2
        result = (value1 > value2)
      when 4  # value1 is less than value2
        result = (value1 < value2)
      when 5  # value1 is not equal to value2
        result = (value1 != value2)
      end
    when 2  # Self switch
      if @event_id > 0
        key = [@map_id, @event_id, @params[1]]
        result = ($game_self_switches[key] == (@params[2] == 0))
      end
    when 3  # Timer
      if $game_timer.working?
        if @params[2] == 0
          result = ($game_timer.sec >= @params[1])
        else
          result = ($game_timer.sec <= @params[1])
        end
      end
    when 4  # Actor
      actor = $game_actors[@params[1]]
      if actor
        case @params[2]
        when 0  # in party
          result = ($game_party.members.include?(actor))
        when 1  # name
          result = (actor.name == @params[3])
        when 2  # Class
          result = (actor.class_id == @params[3])
        when 3  # Skills
          result = (actor.skill_learn?($data_skills[@params[3]]))
        when 4  # Weapons
          result = (actor.weapons.include?($data_weapons[@params[3]]))
        when 5  # Armors
          result = (actor.armors.include?($data_armors[@params[3]]))
        when 6  # States
          result = (actor.state?(@params[3]))
        end
      end
    when 5  # Enemy
      enemy = $game_troop.members[@params[1]]
      if enemy
        case @params[2]
        when 0  # appear
          result = (enemy.alive?)
        when 1  # state
          result = (enemy.state?(@params[3]))
        end
      end
    when 6  # Character
      character = get_character(@params[1])
      if character
        result = (character.direction == @params[2])
      end
    when 7  # Gold
      case @params[2]
      when 0  # Greater than or equal to
        result = ($game_party.gold >= @params[1])
      when 1  # Less than or equal to
        result = ($game_party.gold <= @params[1])
      when 2  # Less than
        result = ($game_party.gold < @params[1])
      end
    when 8  # Item
      result = $game_party.has_item?($data_items[@params[1]])
    when 9  # Weapon
      result = $game_party.has_item?($data_weapons[@params[1]], @params[2])
    when 10  # Armor
      result = $game_party.has_item?($data_armors[@params[1]], @params[2])
    when 11  # Button
      result = Input.press?(@params[1])
    when 12  # Script
      result = eval(@params[1])
    when 13  # Vehicle
      result = ($game_player.vehicle == $game_map.vehicles[@params[1]])
    end
    @branch[@indent] = result
    command_skip if !@branch[@indent]
  end


Conditional Branch is an absolute monster. Let's slay it.

So the first line is as simple as they come: set result to false. Moving on.

Then we have a case statement for @params[0]: this is the value of the radio button in the dialog determining whether it's Switch, Variable, Self Switch, Timer, Actor, Enemy, Character, Vehicle, Gold, Item, Weapon, Armour, Button, or Script.

When 0 (Switch), result is set to the result of comparing $game_switches[@params[1]] to (@params[2] == 0)--in other words, whether the two values are the same. @params[1] is the ID of the switch you chose from the selection dialog. @params[2] is 0 if you chose ON, and 1 if you chose OFF. So if you're doing a branch for switch 43 being ON, @params[1] will be 43, @params[2] will be 0, and it'll then be comparing $game_switches[43] to (0 == 0); if the switch is on, this will end up being [switch state] == true, which will return true if the switch is true, and false otherwise. Obviously this is turned around if checking for the switch being false, as the comparison then becomes (1 == 0) which is false, so if the switch is also false it'll return true.

When 1 (Variable), value1 is set to $game_variables[@params[1]] (the ID of the variable you set in the dialog); then if @params[2] is 0 ("Constant" in the dialog), value2 is set to @params[3], otherwise it's set to $game_variables[@params[3]] ("Variable" in the dialog). We then have a case statement for @params[4]; if it's 0 (Equal to), result is set to value1 == value2. If it's 1 (Greater than or equal to) result is set to value1 >= value2. You've done maths before, you should be able to work out the rest: 2 is less than or equal to, 3 is greater than, 4 is less than, 5 is not equal to.

When 2 (Self Switch), first we check whether @event_id is greater than 0 (because only events can have self switches), then key is set to an array consisting of the current map ID, the current event ID, and @params[1] (the letter of the self switch being checked) and result is set to $game_self_switches[key] == (@params[2] == 0)). So if we're currently on map 5 and checking that event 6 has self switch B ON, key becomes [5, 6, "B"] and result becomes $game_self_switches[[5, 6, "B"]] == (0 == 0); if the self switch is on, this will return true, otherwise it'll return false.

When 3 (Timer), first we check whether $game_timer.working? is true (because there's no point in checking if there isn't even a timer running); if so, and if @params[2] == 0, result is set to $game_timer.sec >= @params[1]. Otherwise, it's set to $game_timer.sec <= @params[1]. @params[2] is 0 if you chose "or More" and 1 if you chose "or Less", while @params[1] is the total number of seconds in the timer, which I believe the editor automatically calculates from the minutes and seconds boxes.

When 4 (Actor), the variable actor is set to $game_actors[@params[1]] (the ID of the actor you chose in the dialog). Then if actor exists, we have a case statement for @params[2]: when 0 (in party) result is true if $game_party includes the given actor, and false otherwise. When 1 (name), result is true if the actor's name is equal to @params[3] (the string entered in the name box). When 2 (Class), result is true if the actor's class ID is equal to @params[3] (the ID of the class selected in the box). When 3 (Skills), result is true if the actor has learned the skill with ID @params[3]. When 4 (Weapons), result is true if the actor has equipped the weapon with ID @params[3]. When 5 (Armours), result is true if the actor has equipped the armour with ID @params[3]. When 6 (States), result is true if the actor has the state with ID @params[3].

When 5 (Enemy), the variable enemy is set to $game_troop.members[@params[1]] (The enemy ID chosen in the dialog). Then if enemy exists, we have a case statement for @params[2]: when 0 (Appeared) result is true if the enemy with the given ID is currently alive. When 1 (State) result is true if the enemy with the given ID has the state with ID @params[3].

When 6 (Character), the variable character is set to the result of calling get_character with @params[1] as its argument. As we saw earlier, this will either be -1 (player), 0 (this event), or the ID of an event on the map. Then if character exists, result is true if the character's direction is equal to @params[2]. Note that internally directions have a numerical representation: down is 2, left is 4, right is 6, up is 8 (which is, incidentally, also the number on a numpad corresponding to that arrow key).

Interesting note: although "Vehicle" is the next option in the list, it actually has a higher number internally.

When 7 (Gold), we have a case statement for @params[2]: when 0 (greater than or equal to) result is true if the party's gold >= @params[1] (the entered value). When 1 (less than or equal to) result is true if the party's gold <= @params[1]. When 2 (less than) result is true if the party's gold < @params[1].

When 8 (Item), result is true if the party possesses the item with the ID @params[1].

When 9 (Weapon), result is true if the party possesses the weapon with ID @params[1]. If you checked the "include equipment" box, @params[2] will be true, false otherwise.

When 10 (Armour), result is true if the party possesses the armour with ID @params[1]. If you checked the "include equipment" box, @params[2] will be true, false otherwise.

When 11 (Button), result is true if the player is pressing the button corresponding to @params[1]. The directions are the same as previously mentioned (down = 2, left = 4, right = 6, up = 8), button A is 11, B is 12, C is 13, X is 14, Y is 15, Z is 16, L is 17 and R is 18.

When 12 (Script), result is true if the evaluated script stored in @params[1] (whatever you entered in the box) returns true.

When 13 (Vehicle), result is true if $game_player.vehicle is the same as $game_map.vehicles[@params[1]]. 0 is Boat, 1 is Ship, 2 is Airship.

Finally, @branch[@indent] is set to result, and we call command_skip if the result was false (because if the condition wasn't met, we don't want to process the code inside it)

def command_411
    command_skip if @branch[@indent]
  end


This method is basically just there to skip the commands in an "else" if the branch result was true (the method itself will be called when the interpreter reaches the "else" line because its internal code is 411). Without this, the code in the else would still run even if one of the other branches had its conditions met.

def command_112
  end


It's...an empty method. Yep, the command method for "Loop" doesn't actually do anything. In fact, you can delete this method and loop will still work fine. I think it's basically just there for completion's sake.

def command_413
    begin
      @index -= 1
    end until @list[@index].indent == @indent
  end


This is what makes the loop actually work. The contents of the begin-end block will repeat until the indent of the current line of code is equal to the current indent. The only thing in the block is a decrement of the index.

Think about how indenting works in event commands, and you'll realise how this actually results in a loop. Picture a loop with a message in it:

Loop
Show Text: "Hello World!"
Repeat Above

112 is the "Loop", 413 is the "Repeat Above", and both have an indent of 0. The Show Text, 101, would have an indent of 1. So what the command_413 is doing is going backwards in the event commands until it finds another command with an indent of 0...which will basically take us back to the Loop. Execution will then continue from there, so it'll execute everything inside the loop again, at which point it'll go backwards until it finds indent 0...and so on and so forth. Unless...

def command_113
    loop do
      @index += 1
      return if @index >= @list.size - 1
      return if @list[@index].code == 413 && @list[@index].indent < @indent
    end
  end


This is how Break Loop works. Oddly enough, breaking a loop starts...with a loop. Inside that loop, we increment the index by 1, and return if the new index is greater than or equal to the size of the event commands list less 1. We also return if the code of the new command is 413 (Repeat Above) and the indent of the new command is less than the one we've just come from.

Note that the first return means that putting a Break Loop command in an event outside of a Loop-Repeat Above block will cause anything after the break to be ignored. GreatRedSpirit has also pointed out that there's a bug where having a Break Loop inside a conditional branch before a nested loop where there are no more commands between the Repeat Above of the inner loop and the Repeat Above of the outer loop will soft lock your game.

def command_115
    @index = @list.size
  end


Exit Event Processing couldn't be simpler. All it does is set the current index to the size of the commands list, so the next time it tries to process a command it's at the end of the event.

def command_117
    common_event = $data_common_events[@params[0]]
    if common_event
      child = Game_Interpreter.new(@depth + 1)
      child.setup(common_event.list, same_map? ? @event_id : 0)
      child.run
    end
  end


This is the method for Call Common Event. First, common_event is set to the data pertaining to the common event specified by @params[0] (the event chosen in the dialog). If that common event exists, child is set to a new instance of Game_Interpreter with depth 1 greater than the current depth, we call setup on the child event, and then call its run method. Note that due to the recursive creation of new interpreters, if you have a map event call a common event which calls itself you'll eventually end up running afoul of the check_overflow method. Don't do this.

def command_118
  end


Another one that doesn't do anything! The method for Label is just as pointless as the one for Loop.

def command_119
    label_name = @params[0]
    @list.size.times do |i|
      if @list[i].code == 118 && @list[i].parameters[0] == label_name
        @index = i
        return
      end
    end
  end


Jump to Label is, as with looping, the meat and potatoes of the block. First, label_name is set to @params[0] (which is the label name specified in the dialog). Then we enter a times loop with @list.size iterations (in other words, a number of iterations equal to the number of commands in the event) using i as an iteration variable. If the code of the list element with index i is 118 (label) and its parameters[0] (which for a label is its name) is equal to label_name, we set @index to i and return (in other words, if we find a label with the supplied name, we'll continue processing the event from that label).

def command_121
    (@params[0]..@params[1]).each do |i|
      $game_switches[i] = (@params[2] == 0)
    end
  end


Control Switches is simple enough. If a single switch is selected, I believe @params[0] and @params[1] will be the same value, while "Batch" will result in 0 being the lower bound and 1 being the upper. Then for each value from the first value to the last (inclusive), we loop through using i as an iteration variable and set $game_switches[i] to @params[2] == 0, which will be true if you selected ON, and false if you selected OFF (since they have values of 0 and 1 respectively).

def command_122
    value = 0
    case @params[3]  # Operand
    when 0  # Constant
      value = @params[4]
    when 1  # Variable
      value = $game_variables[@params[4]]
    when 2  # Random
      value = @params[4] + rand(@params[5] - @params[4] + 1)
    when 3  # Game Data
      value = game_data_operand(@params[4], @params[5], @params[6])
    when 4  # Script
      value = eval(@params[4])
    end
    (@params[0]..@params[1]).each do |i|
      operate_variable(i, @params[2], value)
    end
  end


Control Variables is a little more complex, obviously because it has more options. First of all, value is set to 0. Then we have a case statement for @params[3], which as the comment says is for the operand. When 0 (constant), value is set to @params[4] (the value in the box). When 1 (Variable), value is set to the element of $game_variables corresponding to @params[4]. When 2 (Random), value is set to @params[4] (the value in the first box) plus a random number from 0 to 1 less than the difference between @params[5] (the value in the second box) and @params[4]. Basically, if you wanted, say, a random number from 20 to 40, value would be 20 + a random number from 0 to 21 exclusive (so 0-20). When 3 (Game Data), value is set to the result of calling game_data_operand with @params[4] (type), @params[5] (param 1) and @params[6] (param 2) as arguments. We'll look at game_data_operand in a sec. When 4 (Script), value is set to the result of evaluating the script entered in the box, stored in @params[4].

Finally, we'll loop through each value from @params[0] to @params[1] (which will either be a single value or a lower and upper bound for "Batch" as with switches) using i as the iteration variable, and each iteration we'll call operate_variable with i, @params[2] and value as arguments. Again, we'll look at that in a sec.

def game_data_operand(type, param1, param2)
    case type
    when 0  # Items
      return $game_party.item_number($data_items[param1])
    when 1  # Weapons
      return $game_party.item_number($data_weapons[param1])
    when 2  # Armor
      return $game_party.item_number($data_armors[param1])
    when 3  # Actors
      actor = $game_actors[param1]
      if actor
        case param2
        when 0      # Level
          return actor.level
        when 1      # EXP
          return actor.exp
        when 2      # HP
          return actor.hp
        when 3      # MP
          return actor.mp
        when 4..11  # Parameter
          return actor.param(param2 - 4)
        end
      end
    when 4  # Enemies
      enemy = $game_troop.members[param1]
      if enemy
        case param2
        when 0      # HP
          return enemy.hp
        when 1      # MP
          return enemy.mp
        when 2..9   # Parameter
          return enemy.param(param2 - 2)
        end
      end
    when 5  # Character
      character = get_character(param1)
      if character
        case param2
        when 0  # x-coordinate
          return character.x
        when 1  # y-coordinate
          return character.y
        when 2  # direction
          return character.direction
        when 3  # screen x-coordinate
          return character.screen_x
        when 4  # screen y-coordinate
          return character.screen_y
        end
      end
    when 6  # Party
      actor = $game_party.members[param1]
      return actor ? actor.id : 0
    when 7  # Other
      case param1
      when 0  # map ID
        return $game_map.map_id
      when 1  # number of party members
        return $game_party.members.size
      when 2  # gold
        return $game_party.gold
      when 3  # steps
        return $game_party.steps
      when 4  # play time
        return Graphics.frame_count / Graphics.frame_rate
      when 5  # timer
        return $game_timer.sec
      when 6  # save count
        return $game_system.save_count
      when 7  # battle count
        return $game_system.battle_count
      end
    end
    return 0
  end


This is the method which returns the game data being used for the "Game Data" setting of Control Variables. It takes three parameters: type, param1 and param2.

First, we have a case statement for type.

When 0 (Items), we return the party's currently-held number of the given item.

When 1 (Weapons), we return the party's currently-held number of the given weapon.

When 2 (Armour), we return the party's currently-held number of the given armour.

When 3 (Actors), we set actor to the element of $game_actors with ID param1 (the actor chosen in the box), then if the variable contains data we have another case statement for param2 (the parameter in the second box). When 0 (Level), we return the actor's level. When 1 (Exp), we return the actor's experience. When 2 (HP), we return the actor's HP. When 3 (MP) we return the actor's MP. When between 4 and 11 inclusive (Parameters) we return the actor's parameter with ID param2 - 4 (because obviously the internal parameter IDs start at 0 but param2 starts at 4, so we need to subtract the difference).

When 4 (Enemies), we set enemy to the element of $game_troop with ID param1, then if the variable contains data we have another case statement for param2. When 0 (HP), we return the enemy's HP. When 1 (MP), we return the enemy's MP. When between 2 and 9 inclusive (Parameters) we return the enemy's parameter with ID param2 - 2.

When 5 (Character), we set character to the result of get_character with param1 as its argument, then if the variable contains data we have a case statement for param2. When 0 (X coordinate), we return the character's x. When 1 (Y coordinate), we return the character's Y. When 2 (Direction), we return the character's direction. When 3 (Screen X), we return the character's screen x. When 4 (Screen Y), we return the character's screen y.

When 6 (Party), we set actor to the element of $game_party.members with ID param1, then if the variable contains data we return the actor's ID, otherwise we return 0 (because they're not in the party).

When 7 (Other), we have a case statement for param1. When 0 (Map ID), we return the map ID from $game_map. When 1 (Number of party members) we return the size of $game_party.members. When 2 (Gold), we return the gold of $game_party. When 3 (Steps), we return the steps of $game_party. When 4 (Play time), we return the number of passed frames divided by the frame rate (so for example, say the frame_count is 5938 frames, we divide that by the frame rate (60 fps by default) to see that we've been playing for 98 seconds. When 5 (Timer), we return the number of seconds elapsed in $game_timer. When 6 (Save count), we return the number of times the player has saved the game. When 7 (Battle count), we return the number of battles the player has fought.

Finally, we return 0 if no other case has been hit.

def operate_variable(variable_id, operation_type, value)
    begin
      case operation_type
      when 0  # Set
        $game_variables[variable_id] = value
      when 1  # Add
        $game_variables[variable_id] += value
      when 2  # Sub
        $game_variables[variable_id] -= value
      when 3  # Mul
        $game_variables[variable_id] *= value
      when 4  # Div
        $game_variables[variable_id] /= value
      when 5  # Mod
        $game_variables[variable_id] %= value
      end
    rescue
      $game_variables[variable_id] = 0
    end
  end


The operate_variable method executes the variable operation selected in the Control Variables command, and takes three parameters: variable ID, operation type, and value. First we have a case statement for operation_type. I shouldn't have to go through it at this point after everything else I've covered, but basically 0 sets the variable to value, 1 adds value to it, 2 subtracts value from it, 3 multiplies it by value, 4 divides it by value, and 5 divides it by value and returns the remainder. The rescue part of the begin-end block will catch any exceptions (if we try to divide by 0, for example) and set the variable to 0 instead.

def command_123
    if @event_id > 0
      key = [@map_id, @event_id, @params[0]]
      $game_self_switches[key] = (@params[1] == 0)
    end
  end


This is the method for the Control Self Switch command. First we make sure @event_id is greater than 0, because if we're not in a map event there's no point in doing anything with self switches. key is set to an array consisting of the map ID, event ID and @params[0] (which is the letter of the self switch being checked), then $game_self_switches[key] is set to @params[1] == 0. So if, for example, you're turning self switch C of event 5 on map 3 OFF, $game_self_switches[3, 5, "C"] will be set to (1 == 0), or false.

def command_124
    if @params[0] == 0  # Start
      $game_timer.start(@params[1] * Graphics.frame_rate)
    else                # Stop
      $game_timer.stop
    end
  end


This is the method for the Control Timer command. If @params[0] is 0 (start), call the start method of $game_timer with @params[1] multiplied by the frame rate as an argument (which converts the seconds of the timer dialog into the number of frames). If it's 1 (stop), we call $game_timer's stop method. Simples!

def command_125
    value = operate_value(@params[0], @params[1], @params[2])
    $game_party.gain_gold(value)
  end


This is the method for the Change Gold command. Sets value to the result of operate_value with @params[0], @params[1] and @params[2] as arguments. As you'll no doubt remember from way earlier when we still had hopes and dreams and this episode wasn't the length of all the previous ones combined, these are for the parameters operation (increase or decrease), operand type (constant or variable), and operand (numeric value of variable ID). Then calls the gain_gold method of $game_party with value as the argument.

So carrying on our fine tradition of worked examples, if I choose "Increase", "Constant" and value 500, the values passed to operate_value will be 0, 0 and 500. Then the check for value setting will end up being 500 because operand_type is constant, and operation is increase so the returned value is still value. Had it been "decrease", the value returned would have been -500.

def command_126
    value = operate_value(@params[1], @params[2], @params[3])
    $game_party.gain_item($data_items[@params[0]], value)
  end


Exactly the same thing as before but with items. Only thing to add is that gain_item takes an additional parameter, which is the item object being gained.

def command_127
    value = operate_value(@params[1], @params[2], @params[3])
    $game_party.gain_item($data_weapons[@params[0]], value, @params[4])
  end


Same thing as before but with weapons. Again we're adding an additional parameter for the "include equipment" checkbox, but that's only relevant if we're subtracting weapons.

def command_128
    value = operate_value(@params[1], @params[2], @params[3])
    $game_party.gain_item($data_armors[@params[0]], value, @params[4])
  end


Exactly the same thing as before but with armour. This time I have nothing more to say.

def command_129
    actor = $game_actors[@params[0]]
    if actor
      if @params[1] == 0    # Add
        if @params[2] == 1  # Initialize
          $game_actors[@params[0]].setup(@params[0])
        end
        $game_party.add_actor(@params[0])
      else                  # Remove
        $game_party.remove_actor(@params[0])
      end
    end
  end


This is the method for the Change Party Member command. First, sets actor to the element of $game_actors with ID @params[0]. If the actor exists, then checks to see whether @params[1] is 0 (Add). If so, first checks whether @params[2] is 1 (Initialise checkbox) and if so calls the actor's setup method. Then regardless of the value of @params[2], we call the add_actor method of $game_party, with @params[0] as the argument. If @params[1] is 1 (Remove), we simply call the remove_actor method of $game_party, with @params[0] as the argument.

def command_132
    $game_system.battle_bgm = @params[0]
  end


The Change Battle BGM command. Simply sets the battle_bgm property of $game_system to @params[0]. This will end up being an instance of RPG::BGM consisting of the filename, volume and pitch.

def command_133
    $game_system.battle_end_me = @params[0]
  end


Same thing but for the battle_end_me, the ME that plays when a battle finishes (to this day I can't figure out what ME stands for).

def command_134
    $game_system.save_disabled = (@params[0] == 0)
  end


This is the method for the Change Save Access command, and simply sets the save_disabled property of $game_system to the result of @params[0] == 0. As with everything else using this pattern, 0 is ON, 1 is OFF.

def command_135
    $game_system.menu_disabled = (@params[0] == 0)
  end


Same thing but for the Change Menu Access command.

def command_136
    $game_system.encounter_disabled = (@params[0] == 0)
    $game_player.make_encounter_count
  end


Same thing but for Change Encounter Disable. Calls make_encounter_count in case you're re-enabling encounters after they've been disabled, otherwise the encounter count won't be re-initialised properly.

def command_137
    $game_system.formation_disabled = (@params[0] == 0)
  end


Same thing but for Change Formation Access.

def command_138
    $game_system.window_tone = @params[0]
  end


This is the method for the Change Window Colour command. Sets the window_tone property of $game_system to @params[0], which will be the R, G, B and grey values of the chosen colour (grey will always be 0 as you can't modify it in the window colour dialog).

def command_201
    return if $game_party.in_battle
    Fiber.yield while $game_player.transfer? || $game_message.visible
    if @params[0] == 0                      # Direct designation
      map_id = @params[1]
      x = @params[2]
      y = @params[3]
    else                                    # Designation with variables
      map_id = $game_variables[@params[1]]
      x = $game_variables[@params[2]]
      y = $game_variables[@params[3]]
    end
    $game_player.reserve_transfer(map_id, x, y, @params[4])
    $game_temp.fade_type = @params[5]
    Fiber.yield while $game_player.transfer?
  end


This is the method for the Transfer Player command. Returns if the party is in battle (because there's no map for the player to be transferred from/to). Yields the fiber to its caller while the player is in the process of transferring or a message is visible.

If @params[0] is 0 (direct designation), map_id is set to @params[1], x is set to @params[2] and y is set to @params[3]. If params[0] is 1, these variables are set to the element of $game_variables with ID @params[1], @params[2] and @params[3] instead.

Then, we call the reserve_transfer method of $game_player, passing in the map id, x, y and @params[4] (the direction to face after transfer, using the usual values for directions), and the fade_type property of $game_temp is set to @params[5] (which will be the numerical value for normal, white or none) and finally we once again yield the fiber to its caller if the player is transferring.

def command_202
    if @params[1] == 0                      # Direct designation
      map_id = @params[2]
      x = @params[3]
      y = @params[4]
    else                                    # Designation with variables
      map_id = $game_variables[@params[2]]
      x = $game_variables[@params[3]]
      y = $game_variables[@params[4]]
    end
    vehicle = $game_map.vehicles[@params[0]]
    vehicle.set_location(map_id, x, y) if vehicle
  end


More or less the same thing but for Set Vehicle Location. The main differences are that we have a parameter for which vehicle to move, no parameter for direction, and we're using its set_location method.

def command_203
    character = get_character(@params[0])
    if character
      if @params[1] == 0                      # Direct designation
        character.moveto(@params[2], @params[3])
      elsif @params[1] == 1                   # Designation with variables
        new_x = $game_variables[@params[2]]
        new_y = $game_variables[@params[3]]
        character.moveto(new_x, new_y)
      else                                    # Exchange with another event
        character2 = get_character(@params[2])
        character.swap(character2) if character2
      end
      character.set_direction(@params[4]) if @params[4] > 0
    end
  end


More or less the same thing but for Set Event Location. Uses get_character as usual to get the character object being moved, and uses the moveto method. Note that unlike the previous two methods, this one can't move an event to a different map (which makes sense, as events data is tied to the map it's created on; in order to have the same event on multiple maps, you'd have to make a copy of it) though we do have an additional option for @params[1], which is 2 (Exchange with another event) which just uses the swap method we looked at way back when we covered Game_Character. Finally, if @params[4] > 0 (we didn't choose "retain" for the direction) we call set_direction supplying @params[4] as the argument.

def command_204
    return if $game_party.in_battle
    Fiber.yield while $game_map.scrolling?
    $game_map.start_scroll(@params[0], @params[1], @params[2])
  end


This is the method for the Scroll Map command. Returns if the party is in battle, as there's no map to scroll. Yields the fiber to its caller while the map is in the process of scrolling, then calls the start_scroll method of $game_map, providing @params[0], @params[1] and @params[2] as arguments (the direction, distance and speed for the scroll).

def command_205
    $game_map.refresh if $game_map.need_refresh
    character = get_character(@params[0])
    if character
      character.force_move_route(@params[1])
      Fiber.yield while character.move_route_forcing if @params[1].wait
    end
  end


This is the method for the Set Move Route command. Calls the refresh method of $game_map if need_refresh is true. Calls get_character with @params[0] as the argument and assigns the return value to the character variable, then if that variable contains data calls the force_move_route method with @params[1] as the argument. Yields the fiber to its caller while the character is being forced into a move route if the "wait for completion" box was checked.

def command_206
    $game_player.get_on_off_vehicle
  end


This is the method for the Get on/off Vehicle command, and simply calls the get_on_off_vehicle method of $game_player.

def command_211
    $game_player.transparent = (@params[0] == 0)
  end


This is the method for the Change Transparency command, and simply sets the transparent property of $game_player to the result of @params[0] == 0; as with most other assignments that use this structure, a value of 0 means transparency ON and 1 turns it OFF.

def command_212
    character = get_character(@params[0])
    if character
      character.animation_id = @params[1]
      Fiber.yield while character.animation_id > 0 if @params[2]
    end
  end


This is the method for the Show Animation command. Stores the return value of get_character in the character variable, with @params[0] as its argument, then if the character exists sets its animation ID to @params[1]. Yields the fiber to its caller while the character's animation ID is greater than 0 if the "wait for completion" box was checked.

def command_213
    character = get_character(@params[0])
    if character
      character.balloon_id = @params[1]
      Fiber.yield while character.balloon_id > 0 if @params[2]
    end
  end


Exactly the same for Show Balloon Icon, only difference is that we're setting balloon_id instead of animation_id.

def command_214
    $game_map.events[@event_id].erase if same_map? && @event_id > 0
  end


This is the method for the Temporarily Erase Event command. Calls the erase method of the current event if the player is on the same map as the event is and @event_id is greater than 0 (you may not remember from last time, but all the erase method does is set a flag that stops event pages from processing).

def command_216
    $game_player.followers.visible = (@params[0] == 0)
    $game_player.refresh
  end


This is the method for the Change Player Followers command. Sets the visible property of $game_player.followers to @params[0] == 0 (this works exactly the same as every other method that's used for setting boolean properties) and then calls $game_player's refresh method.

def command_217
    return if $game_party.in_battle
    $game_player.followers.gather
    Fiber.yield until $game_player.followers.gather?
  end


This is the method for the Gather Followers command. Returns if the party is in battle (because obviously battle doesn't have any followers) then calls the gather method of $game_player.followers. Yields the fiber to its caller until all followers are in the same tile.

def command_221
    Fiber.yield while $game_message.visible
    screen.start_fadeout(30)
    wait(30)
  end


This is the method for the Fadeout Screen command. Yields the fiber to its caller while $game_message.visible returns true. Then calls the start_fadeout method of screen, with 30 as the argument (by default fades take 30 frames), and finally calls wait(30) to wait for 30 frames as well.

def command_222
    Fiber.yield while $game_message.visible
    screen.start_fadein(30)
    wait(30)
  end


Exactly the same as previous but calls start_fadein instead of fadeout.

def command_223
    screen.start_tone_change(@params[0], @params[1])
    wait(@params[1]) if @params[2]
  end


This is the method for the Tint Screen command. Calls the start_tone_change method of screen, with @params[0] and @params[1] as arguments (@params[0] is a Tone object containing the RGBGr values chosen for the tint, @params[1] is the time in frames) and then also calls wait for @params[1] frames if the "wait for completion" box was checked.

def command_224
    screen.start_flash(@params[0], @params[1])
    wait(@params[1]) if @params[2]
  end


Same thing but for Screen Flash.

def command_225
    screen.start_shake(@params[0], @params[1], @params[2])
    wait(@params[1]) if @params[2]
  end


Same thing but for Screen Shake. There's actually a bug here because the second line should be "wait(@params[2]) if @params[3]"; they got the indexes wrong. The way it's written, it's waiting for [speed] frames if a duration is set (which is always true). This means that by default waiting for completion on a screen shake doesn't work.

def command_230
    wait(@params[0])
  end


This is the method for the Wait command, and simply waits for @params[0] frames.

def command_231
    if @params[3] == 0    # Direct designation
      x = @params[4]
      y = @params[5]
    else                  # Designation with variables
      x = $game_variables[@params[4]]
      y = $game_variables[@params[5]]
    end
    screen.pictures[@params[0]].show(@params[1], @params[2],
      x, y, @params[6], @params[7], @params[8], @params[9])
  end


This is the method for the Show Picture command. If @params[3] is 0, meaning direct coordinate designation, then x is set to @params[4] and y to @params[5] (obviously, these are the X and Y coordinates you entered in the dialog). Otherwise, the same variables are used as indexes for $game_variables instead. Regardless, we call the show method of screen.pictures[@params[0]], which is the picture number entered in the dialog. As parameters, we supply the name, origin, x, y, zoom x, zoom y, opacity and blend type, as defined by the various elements of @params.

def command_232
    if @params[3] == 0    # Direct designation
      x = @params[4]
      y = @params[5]
    else                  # Designation with variables
      x = $game_variables[@params[4]]
      y = $game_variables[@params[5]]
    end
    screen.pictures[@params[0]].move(@params[2], x, y, @params[6],
      @params[7], @params[8], @params[9], @params[10])
    wait(@params[10]) if @params[11]
  end


This is the method for the Move Picture command, which is almost identical to Show Picture, but calls the move method instead of show, and waits for @params[10] frames if the wait until completed box was checked. The main difference in the arguments is that we don't have a picture number, and we're adding an additional argument for duration. command_232 doesn't actually have a @params[1], presumably to keep the parameter indexes the same for variables common to showing and moving.

def command_233
    screen.pictures[@params[0]].rotate(@params[1])
  end


This is the method for Rotate Picture. Not much to say about this one, it simply calls the rotate method of the given picture with @params[1] (speed) as the argument.

def command_234
    screen.pictures[@params[0]].start_tone_change(@params[1], @params[2])
    wait(@params[2]) if @params[3]
  end


This is the method for Tint Picture. Again quite a simple one, calls start_tone_change on the chosen picture with @params[1] (the tone) and @params[2] (duration) as arguments, waiting for duration frames if the wait for completion box was checked.

def command_235
    screen.pictures[@params[0]].erase
  end


This is the method for Erase Picture. Calls the erase method of the picture. We should be getting the idea by now.

def command_236
    return if $game_party.in_battle
    screen.change_weather(@params[0], @params[1], @params[2])
    wait(@params[2]) if @params[3]
  end


This is the method for Set Weather. Slightly more complex this time but not that bad: returns if the party is in battle, because weather effects can't happen in battles (though all you really have to do to enable weather in battles is comment out that line and add the appropriate creation/disposal/update of a Spriteset_Weather instance to Spriteset_Battle). Calls the change_weather method of screen with @params[0] (weather type), @params[1] (power) and @params[2] (duration) as arguments. As with everything else that has a wait until completion checkbox, will wait @params[2] frames if it's checked.

def command_241
    @params[0].play
  end


This is the method for Play BGM, and simply calls the play method of @params[0], which is the BGM object created by the dialog.

def command_242
    RPG::BGM.fade(@params[0] * 1000)
  end


This is the method for Fadeout BGM, and calls the fade method of the RPG::BGM class with the number of seconds * 1000 as an argument (because the parameter for fade is in milliseconds).

def command_243
    $game_system.save_bgm
  end


This is the method for the Save BGM command, and simply calls the save_bgm method of $game_system.

def command_244
    $game_system.replay_bgm
  end


This is the method for Resume BGM. Surprise surprise, same thing as before but with replay_bgm instead.

def command_245
    @params[0].play
  end


Play BGS, pretty much identical to Play BGM.

def command_246
    RPG::BGS.fade(@params[0] * 1000)
  end


Fadeout BGS, identical to Fadeout BGM. Noticing a pattern?

def command_249
    @params[0].play
  end


Play ME.

def command_250
    @params[0].play
  end


Play SE.

def command_251
    RPG::SE.stop
  end


Stop SE! I'm honestly not sure why they didn't include a fadeout/stop ME command, but there's nothing stopping you from setting up commands you can call via script if you need them. RPG::ME does have fade and stop methods.

def command_261
    Fiber.yield while $game_message.visible
    Fiber.yield
    name = @params[0]
    Graphics.play_movie('Movies/' + name) unless name.empty?
  end


This is the method for Play Movie. Yields the fiber to its caller while a game message is visible, then yields the fiber again (this gives any message box preceding the movie time to fully close before the movie plays; without this line it ends up still being partially visible), sets name to @params[0] (the filename of the movie you chose in the dialog) then calls the play_movie method of the Graphics class with the relative path to the movie as its argument, unless the name is an empty string (this prevents exceptions from trying to play a movie when the choice was "none").

def command_281
    $game_map.name_display = (@params[0] == 0)
  end


This is the method for the Change Map Name Display command. Simply sets the name_display property of $game_map to true if you picked ON (0) and false if you picked OFF (1).

def command_282
    $game_map.change_tileset(@params[0])
  end


This is the method for the Change tileset command, and simply calls the change_tileset method of $game_map with @params[0] as the argument, which is the ID of the tileset to change to.

def command_283
    $game_map.change_battleback(@params[0], @params[1])
  end


This is the method for the Change Battle Background command, and calls $game_map's change_battleback method with @params[0] (the name of the first background) and @params[1] (the name of the second background) as arguments.

def command_284
    $game_map.change_parallax(@params[0], @params[1], @params[2],
                              @params[3], @params[4])
  end


This is the method for the Change Parallax Background command, and calls $game_map's change_parallax method with @params[0] (background name), @params[1] (whether scroll horizontal is checked), @params[2] (horizontal speed), @params[3] (whether scroll vertical is checked) and @params[4] (vertical speed) as arguments.

def command_285
    if @params[2] == 0      # Direct designation
      x = @params[3]
      y = @params[4]
    else                    # Designation with variables
      x = $game_variables[@params[3]]
      y = $game_variables[@params[4]]
    end
    case @params[1]
    when 0      # Terrain Tag
      value = $game_map.terrain_tag(x, y)
    when 1      # Event ID
      value = $game_map.event_id_xy(x, y)
    when 2..4   # Tile ID
      value = $game_map.tile_id(x, y, @params[1] - 2)
    else        # Region ID
      value = $game_map.region_id(x, y)
    end
    $game_variables[@params[0]] = value
  end


This is the method for the Get Location Info command. If @params[2] is 0 (direct designation) then x is @params[3] (the X coordinate of the target tile) and y is @params[4] (the Y coordinate of the target tile). If it's 1 (designation with variables) those parameters are used as indexes to $game_variables instead. We then have a case statement for @params[1] (info type). When 0 (terrain tag), value is set to the terrain tag set at the given X/Y. When 1 (event ID), value is set to the ID of the event at the given X/Y. When 2 to 4 inclusive (tile ID layers 1, 2 and 3), value is set to the ID of the tile on the chosen layer (subtracting 2 from @params[1] because the tile IDs are 0-indexed). Otherwise (technically 5, region ID), value is set to the ID of the region the given tile is part of. Following the case statement, we set the variable with index @params[0] to value.

def command_301
    return if $game_party.in_battle
    if @params[0] == 0                      # Direct designation
      troop_id = @params[1]
    elsif @params[0] == 1                   # Designation with variables
      troop_id = $game_variables[@params[1]]
    else                                    # Map-designated troop
      troop_id = $game_player.make_encounter_troop_id
    end
    if $data_troops[troop_id]
      BattleManager.setup(troop_id, @params[2], @params[3])
      BattleManager.event_proc = Proc.new {|n| @branch[@indent] = n }
      $game_player.make_encounter_count
      SceneManager.call(Scene_Battle)
    end
    Fiber.yield
  end


This is the method for the Battle Processing command. Returns if the party is in battle (do I really have to explain why?). If @params[0] is 0 (direct designation of troop), then troop_id is set to @params[1] (the ID of the troop you'll be fighting). If it's 1 (designation with variables), @params[1] is used as an index for $game_variables instead. Otherwise, it's a map-designated troop so we call the make_encounter_troop_id method of $game_player to figure out what the encounter is. If there's a troop corresponding to the calculated ID, we call the setup method of BattleManager, passing in the troop ID, @params[2] (whether the player can escape) and @params[3] (whether the player can lose) as arguments.

Then we set the event_proc property of BattleManager to a new Proc where @branch[@indent] is set to the passed-in value n. This allows the battle end method of BattleManager to set @branch[@indent] to 0, 1 or 2 depending on whether the player won, escaped or lost, via BattleManager.event_proc.call, which you may not remember us looking at way back in Under the Hood part 2. After that we call the make_encounter_count method of $game_player, which refreshes the encounter counter on the map (because it would kind of suck to end up in a random battle immediately after fighting a boss) and then the call method of SceneManager is called, with Scene_Battle as its argument. This obviously transitions the scene to the battle scene. Finally, we yield the fiber to its caller, which prevents the rest of the event commands from processing before the battle is finished.

def command_601
    command_skip if @branch[@indent] != 0
  end


601 is the code for the "win" branch of a battle. Skips the command if @branch[@indent] isn't 0, because that means the player didn't win.

def command_602
    command_skip if @branch[@indent] != 1
  end


Same as before, but for 602 (escape).

def command_603
    command_skip if @branch[@indent] != 2
  end


Same as before, but for 603 (lose).

def command_302
    return if $game_party.in_battle
    goods = [@params]
    while next_event_code == 605
      @index += 1
      goods.push(@list[@index].parameters)
    end
    SceneManager.call(Scene_Shop)
    SceneManager.scene.prepare(goods, @params[4])
    Fiber.yield
  end


This is the method for the Shop Processing command. Returns if the party is in battle (though who wouldn't want to buy some potions during a boss fight?) and sets goods to @params as an array element.

Initially, @params is just an array consisting of the item type (0 = item, 1 = weapon, 2 = armour), item ID, standard price flag (0 = standard, 1 = specific), 0 if standard or the price if specific, and a true/false value for whether the shop is purchase-only. We then have a while loop as long as the next event code is 605 (additional shop goods), in which the index is incremented and we push the current event command's parameters to goods (those parameters will be an array with the same elements as the initial one, with the exception of the purchase-only flag). Then we call the call method of SceneManager, with Scene_Shop as the argument, and call the prepare method of SceneManager's scene property (which is now an instance of Scene_Shop), with goods and @params (the purchase-only flag) as arguments. This just initialises the instance variables of Scene_Shop, as we'll see when we eventually get to it. Then, as before, the fiber is yielded to its caller so that further event commands aren't processed before we're done shopping.

def command_303
    return if $game_party.in_battle
    if $data_actors[@params[0]]
      SceneManager.call(Scene_Name)
      SceneManager.scene.prepare(@params[0], @params[1])
      Fiber.yield
    end
  end


This is the method for the Name Input Processing command. Returns if the party is in battle (seriously, Enterbrain, you're missing a trick here. If Alex does terribly in battle I want to rename him to something mean). If an actor exists with the ID @params[0], we call Scene_Name, and the prepare method of the scene with @params[0] (the ID of the actor) and @params[1] (the maximum number of characters) as arguments. Then, we yield the fiber to prevent processing of further commands.

def command_311
    value = operate_value(@params[2], @params[3], @params[4])
    iterate_actor_var(@params[0], @params[1]) do |actor|
      next if actor.dead?
      actor.change_hp(value, @params[5])
      actor.perform_collapse_effect if actor.dead?
    end
    SceneManager.goto(Scene_Gameover) if $game_party.all_dead?
  end


This is the method for the Change HP command. Sets value to the result of calling operate_value with the options chosen in the dialog (I shouldn't have to explain the inner workings of this by now, we've gone over it so many times). We call iterate_actor_var passing in @params[0] (0 for fixed actor, 1 for variable) and @params[1] (actor or variable ID) and use the method as an iterator for a loop, using actor as the iteration variable. We go to the next iteration if the current actor is dead; if not, we call change_hp using value and @params[5] (whether the actor can die) as arguments. If the actor is now dead, we call perform_collapse_effect (only relevant in battle). Once the loop is done, we go to Scene_Gameover if the entire party has died. (You may not remember from when we looked at the modules, but goto is similar to call but doesn't use a stack, because if we're going to the game over screen there are no further scenes to transition to anyway).

As I promised we'd look into iterate_actor_var more when we got to it, let's look at a practical example. I've chosen to decrease Eric's HP by the value of variable 50 (let's say it contains 25), not allowing him to die.

This makes the first line: value = operate_value(1, 1, 50). In operate_value, value becomes the value of variable 50, which is 25, and because it's a decrease this is converted to -25.

We then call: iterate_actor_var(0, 1) and as param1 is 0, we call iterate_actor_id(1) with the block {|actor| yield actor}. In iterate_actor_id, param is 1 so actor is set to $game_actors, and because actor exists the "yield actor" line is processed. This passes the actor object to the block from iterate_actor_var, which then yields the actor to command_311, and the object is stored in the "actor" iteration variable. Alex isn't dead, so we skip the next line. We call actor.change_hp(-25, 0), which sets Alex's self.hp to += -25, so his HP ends up going down by 25. He's not dead, so we don't perform the collapse effect, and the party isn't dead so we don't have to switch to the game over scene.

def command_312
    value = operate_value(@params[2], @params[3], @params[4])
    iterate_actor_var(@params[0], @params[1]) do |actor|
      actor.mp += value
    end
  end


This is the method for the Change MP command. Sets value using operate_value as usual, then iterates through the actors chosen in the same way as Change HP does. Then actor.mp is increased by value (which obviously results in a decrease if value is a negative number).

def command_313
    iterate_actor_var(@params[0], @params[1]) do |actor|
      already_dead = actor.dead?
      if @params[2] == 0
        actor.add_state(@params[3])
      else
        actor.remove_state(@params[3])
      end
      actor.perform_collapse_effect if actor.dead? && !already_dead
    end
    $game_party.clear_results
  end


This is the method for the Change State command. Iterates through the actors chosen in the same way as usual. Sets already_dead to true if the actor is dead and false otherwise. If @params[2] is 0 (add state), we call the actor's add_state method with @params[3] (the ID of the state chosen) and if it's 1 (remove state) we call remove_state instead with the same argument. After this, we call perform_collapse_effect if the actor is now dead and wasn't already before the state was added/removed, and after the iteration loop we call clear_results on $game_party (which clears each party member's result property). I'm honestly not quite sure why the call to clear_results is there, as adding a state like this doesn't show in the battle log anyway.

def command_314
    iterate_actor_var(@params[0], @params[1]) do |actor|
      actor.recover_all
    end
  end


This is the method for Recover All. Iterates through the actors chosen using iterate_actor_var and then calls recover_all for each actor.

def command_315
    value = operate_value(@params[2], @params[3], @params[4])
    iterate_actor_var(@params[0], @params[1]) do |actor|
      actor.change_exp(actor.exp + value, @params[5])
    end
  end


This is the method for Change EXP. Pretty much the same as the other Change commands, but calling change_exp instead. @params[5] is the value of the "show level up message" checkbox.

def command_316
    value = operate_value(@params[2], @params[3], @params[4])
    iterate_actor_var(@params[0], @params[1]) do |actor|
      actor.change_level(actor.level + value, @params[5])
    end
  end


Same as before, but for Change Level.

def command_317
    value = operate_value(@params[3], @params[4], @params[5])
    iterate_actor_var(@params[0], @params[1]) do |actor|
      actor.add_param(@params[2], value)
    end
  end


Same as before but for Change Parameters. @params[2] contains the ID of the parameter to be changed.

def command_318
    iterate_actor_var(@params[0], @params[1]) do |actor|
      if @params[2] == 0
        actor.learn_skill(@params[3])
      else
        actor.forget_skill(@params[3])
      end
    end
  end


This is the method for Change Skills. Iterates through the chosen actors; if @params[2] is 0 (learn skill) we call the actor's learn_skill method. If it's 1 (forget skill) we call the actor's forget_skill method. In both cases, @params[3] is used as the argument (the ID of the skill to learn/forget).

def command_319
    actor = $game_actors[@params[0]]
    actor.change_equip_by_id(@params[1], @params[2]) if actor
  end


This is the method for Change Equipment. Gets the actor with ID @params[0] and calls their change_equip_by_id method, passing in @params[1] (slot ID) and @params[2] (item ID) if the actor exists.

def command_320
    actor = $game_actors[@params[0]]
    actor.name = @params[1] if actor
  end


This is the method for Change Name. Gets the actor with ID @params[0] and calls their name method, passing in @params[1] (new name) if the actor exists.

def command_321
    actor = $game_actors[@params[0]]
    actor.change_class(@params[1]) if actor && $data_classes[@params[1]]
  end


Same as before but for Change Class. @params[1] is the ID of the chosen class, and there's an additional validation check to make sure that class actually exists in $data_classes.

def command_322
    actor = $game_actors[@params[0]]
    if actor
      actor.set_graphic(@params[1], @params[2], @params[3], @params[4])
    end
    $game_player.refresh
  end


This is the method for Change Actor Graphic. I'm not sure why the if statement was done as a block here instead of having it on one line like the others, but whatever. After calling set_graphic on the actor with @params[1] (character name), @params[2] (character index), @params[3] (face name) and @params[4] (face index) as arguments, we call the refresh method of $game_player (this refreshes the character name/index and followers).

def command_323
    vehicle = $game_map.vehicles[@params[0]]
    vehicle.set_graphic(@params[1], @params[2]) if vehicle
  end


This is the method for Change Vehicle Graphic. @params[0] contains the ID of the vehicle chosen in the dialog. We call the set_graphic method of the vehicle with @params[1] (character name) and @params[2] (character index) if the vehicle exists.

def command_324
    actor = $game_actors[@params[0]]
    actor.nickname = @params[1] if actor
  end


This is the method for Change Nickname. Gets the actor with ID @params[0] and calls their nickname method, passing in @params[1] (new nickname) if the actor exists.

def command_331
    value = operate_value(@params[1], @params[2], @params[3])
    iterate_enemy_index(@params[0]) do |enemy|
      return if enemy.dead?
      enemy.change_hp(value, @params[4])
      enemy.perform_collapse_effect if enemy.dead?
    end
  end


This is the method for Change Enemy HP. It's pretty much identical to Change HP, only it uses iterate_enemy_index and you can't use a variable to determine which enemy is affected.

def command_332
    value = operate_value(@params[1], @params[2], @params[3])
    iterate_enemy_index(@params[0]) do |enemy|
      enemy.mp += value
    end
  end


This is the method for Change Enemy MP. Pretty much the same as Change MP. Again, it uses iterate_enemy_index and you can't use a variable for the enemy ID.

def command_333
    iterate_enemy_index(@params[0]) do |enemy|
      already_dead = enemy.dead?
      if @params[1] == 0
        enemy.add_state(@params[2])
      else
        enemy.remove_state(@params[2])
      end
      enemy.perform_collapse_effect if enemy.dead? && !already_dead
    end
  end


This is the method for Change Enemy State. Again, pretty much the same as Change State with the aforementioned changes.

def command_334
    iterate_enemy_index(@params[0]) do |enemy|
      enemy.recover_all
    end
  end


This is the method for Enemy Recover All. Second verse, same as the first.

def command_335
    iterate_enemy_index(@params[0]) do |enemy|
      enemy.appear
      $game_troop.make_unique_names
    end
  end


This is the method for Enemy Appear. Calls iterate_enemy_index as usual and calls the chosen enemy's appear method, which just sets its hidden flag to false. We then call the make_unique_names method of $game_troop in case there are already living enemies of the same type in the battle.

def command_336
    iterate_enemy_index(@params[0]) do |enemy|
      enemy.transform(@params[1])
      $game_troop.make_unique_names
    end
  end


This is the method for Enemy Transform. Calls iterate_enemy_index as before and calls the chosen enemy's transform method with @params[1] (The ID of the enemy to transform into) as the argument. Then, as before, we call make_unique_names in case there are already living enemies of the same type.

def command_337
    iterate_enemy_index(@params[0]) do |enemy|
      enemy.animation_id = @params[1] if enemy.alive?
    end
  end


This is the method for Show Battle Animation. Calls iterate_enemy_index and then sets the chosen enemy's animation_id to @params[1] (the ID of the animation to show) if the enemy is alive.

def command_339
    iterate_battler(@params[0], @params[1]) do |battler|
      next if battler.death_state?
      battler.force_action(@params[2], @params[3])
      BattleManager.force_action(battler)
      Fiber.yield while BattleManager.action_forced?
    end
  end


This is the method for Force Action. Calls iterate_battler (because we don't know whether we're forcing an action for an actor or an enemy) with arguments @params[0] (0 for enemy, 1 for actor) and @params[1] (ID of the actor or enemy) using the iteration variable battler. We go to the next iteration if the battler has the death state, because you can't force dead people to do anything. Next, we call the force_action method of the battler, passing in @params[2] (ID of the skill to force) and @params[3] (the index of the target). Then we call the force_action method of BattleManager passing in the battler, which removes the battler from the main action order and flags it as having its action forced.

Finally, we yield the fiber to its caller while an action is being forced, so that no other battle event commands will be processed until the action is done.

def command_340
    BattleManager.abort
    Fiber.yield
  end


This is the method for Abort Battle. Calls the abort method of BattleManager, which sets the phase to :aborting, as we already looked at in an earlier episode. Finally, yields the fiber to its caller to stop any remaining event commands from executing.

def command_351
    return if $game_party.in_battle
    SceneManager.call(Scene_Menu)
    Window_MenuCommand::init_command_position
    Fiber.yield
  end


This is the method for Open Menu Screen. Returns if the party is in battle, then calls SceneManager's call method passing in Scene_Menu. Directly calls the init_command_position of the Window_MenuCommand class (which sets the class variable @@last_command_symbol to nil) then yields the fiber to its caller so that any further event commands won't process.

def command_352
    return if $game_party.in_battle
    SceneManager.call(Scene_Save)
    Fiber.yield
  end


This is the method for Open Save Screen. Pretty much identical to the menu one above.

def command_353
    SceneManager.goto(Scene_Gameover)
    Fiber.yield
  end


This is the method for Game Over. As mentioned previously, we use goto here instead of call because logic dictates that no scenes are going to be called after game over.

def command_354
    SceneManager.goto(Scene_Title)
    Fiber.yield
  end


This is the method for Return to Title Screen. Nothing more to say.

def command_355
    script = @list[@index].parameters[0] + "\n"
    while next_event_code == 655
      @index += 1
      script += @list[@index].parameters[0] + "\n"
    end
    eval(script)
  end


And this is the method for Script. Sets script to the event command's parameters[0] (which is the first line of the script) plus a newline character. While the next event code is 655 (further script lines), we increment index by 1 and add the new command's parameters[0] to the script. Finally, we evaluate the full script and return the result.

And...that's it! We're finally done! That is the whole interpreter! This took me like three days! I'm overusing exclamation marks and I don't care!

Now that we're done with the behemoth that is the game objects, we're moving on to sprites. This shouldn't take too long as for the most part these scripts are pretty small. After that, we'll cover windows, then scenes. I promise the next part won't take as long as this one did, either. :)

As usual, if you have any comments, queries, criticisms, corrections or death threats, you know where to write it. I'm expecting a fair few clarification questions as we've covered a couple of pretty advanced topics, especially Fiber and Proc.

Until next time.

Posts

Pages: 1
Hi Trihan, I'm here to say you're saving my life with those tutorials!
Before them, I could never understand well the way RMVXA worked with scripts.
Please, keep it coming, I'm studying them hard.
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3359
Glad to hear it Shadow_Fox! It's for people like you that I'm doing this, so that means a lot. I'm hard at work on the next one!

Question is, is an article of this length digestible enough, or would it be better to keep breaking it up the way I have been? I could probably cover the entire set of Sprite scripts in an article of a similar size to this one, but I'm concerned that too much text might make it more difficult to get to grips with.
author=Trihan
Question is, is an article of this length digestible enough, or would it be better to keep breaking it up the way I have been? I could probably cover the entire set of Sprite scripts in an article of a similar size to this one, but I'm concerned that too much text might make it more difficult to get to grips with.



For me, you can keep with this size.
author=Trihan
Question is, is an article of this length digestible enough, or would it be better to keep breaking it up the way I have been? I could probably cover the entire set of Sprite scripts in an article of a similar size to this one, but I'm concerned that too much text might make it more difficult to get to grips with.

It's probably best if you split them up by parts. Something like one article for character sprites, another for battle sprites, and that sort of thing. A lot of people are put off by large walls of text, regardless of how useful the content may be.

Plus, you'd get more ms XP
Pages: 1