SLIP INTO RUBY - UNDER THE HOOD PART 2: THE MODULES CONT.

A look at the last two RGSS3 modules and then we get to the good stuff.

  • Trihan
  • 03/24/2015 10:42 PM
  • 8189 views
Hi sports fans! It's time for your weekly dose of more programming than you're likely to be comfortable with! It's time...for



So last week I took you through the more boring of the built-in modules, Vocab, Sound, Cache and DataManager. This week, we're going to finish off the modules walkthrough with SceneManager and BattleManager. I think the latter one might be of particular interest to a few people.

SceneManager

As the name suggests, this module handles scene transitions. It's a bit shorter than BattleManager, so it shouldn't take us long to break this one down.

First we have the instance variables:

@scene = nil                            # current scene object
  @stack = []                             # stack for hierarchical transitions
  @background_bitmap = nil                # background bitmap


Instance variables are...well, variables which are unique to an instance of a class. Where a class is like a blueprint, the instance is the house the architect builds with it. To give an example from part 1, if you had a cat you could say it's an instance of the animal class.

There's a @scene variable, which contains information on the current scene; @stack, which holds a list of scenes that need to be called in case there's more than one; and @background_bitmap, which stores the bitmap being used for the background of the scene.

def self.run
    DataManager.init
    Audio.setup_midi if use_midi?
    @scene = first_scene_class.new
    @scene.main while @scene
  end


When SceneManager is run, the DataManager is initialised. You'll remember the .init method of DataManager from last week, which basically sets up all the data your game uses. Then is sets up midi if your game uses it, creates a new instance of first_scene_class (which we'll get to in a second) and stores it in scene, then calls the main method of @scene while @scene contains data (which it now does, obviously, since we've just set it). Main is a method of Scene_Base which means all scenes have it, and we'll cover it in a later part of Slip into Ruby (a lot later, since we've got a LOT of scripts to go through before we get there).

def self.first_scene_class
    $BTEST ? Scene_Battle : Scene_Title
  end


If you remember the inline if statement I mentioned before, you'll easily be able to tell what's happening here: if $BTEST is true, the first scene class is Scene_Battle. Otherwise, it's Scene_Title.

def self.use_midi?
    $data_system.opt_use_midi
  end


This is the method from run which determines whether to set up midi. $data_system.opt_use_midi is determined by whether the "Initialize MIDI at Startup" box in the System tab of the database is checked.

In Ruby, you don't have to write an if statement or implicit return statement; if you need to return the value of a variable, you can simply write the variable name, as shown here.

def self.scene
    @scene
  end


Quite a simple one, this simply returns the value of @scene. The reason this exists is so that you can simply write SceneManager.scene if you need to return the name of the current scene. Theoretically you could have made @scene what's called an attr_accessor and still do this, but a good convention in programming is what is called "encapsulation" which means variables of a class are made private (inaccessible to anything but the class containing them) and methods written to read/write those variables.

def self.scene_is?(scene_class)
    @scene.instance_of?(scene_class)
  end


This is a handy little method which checks to see whether the currently-running method is a particular class. .instance_of? is a built-in method which checks whether the object calling it is an instance of the class in brackets: for instance, if you called SceneManager.scene_is?(Scene_Battle) in an event, it will return true if you're in battle, and false otherwise.

def self.goto(scene_class)
    @scene = scene_class.new
  end


This is just a shortcut method, but it's necessary because @scene isn't a public variable. If you want to call a new scene directly, for instance if you want to instantly call a shop (you can use the event command for that but for sake of example) you can write SceneManager.goto(Scene_Shop).

def self.call(scene_class)
    @stack.push(@scene)
    @scene = scene_class.new
  end


Alternatively, you can add to a "stack" of scenes to be called by using this method. .push is a built-in method of arrays in Ruby which adds the object in brackets to the array calling it. In this case, it's adding @scene as an element to the @stack array, and then it creates a new instance of the provided class to put into @scene. This means you can call a scene but keep the scene which called it in memory (which is how things like shops and battles work; they have to be able to return to the map after they're done). Note that the default scripts use call for every scene transition except game over in battle, returning to the title screen, and going to the map from the title screen/load screen.

The most exciting part about this method is that you can build new scenes which have multiple transitions! Use .call for the first scene transition, .goto for all the others, then .return when the scene's done its stuff and it'll put you back in the original scene!

def self.return
    @scene = @stack.pop
  end


And this is how we handle that return I just mentioned. .pop is the opposite of .push, which is to say it removes the last element of the named array and stores it in the variable on the left. In this case, we remove the most recent entry in @stack and store it in @scene. In layman's terms, after calling a new scene this method will return to the one that came before it.

def self.clear
    @stack.clear
  end


Self-explanatory. .clear is an array method which clears the array, which is to say all elements are removed from it. This clears the stack. It's only used by default when starting a new game from the title screen and starting to process a new map.

def self.exit
    @scene = nil
  end


Again, self-explanatory. This method is called when quitting the game, and simply sets @scene to nil. Nil is a reserved value for variables which is essentially null, nothing, no value.

def self.snapshot_for_background
    @background_bitmap.dispose if @background_bitmap
    @background_bitmap = Graphics.snap_to_bitmap
    @background_bitmap.blur
  end


Ever wondered why, when you're in a scene where the windows don't completely cover the screen, the image underneath appears blurry? Well this is why.

First of all, we dispose of the current @background_bitmap if it exists (garbage collection for the win!) then we call Graphics.snap_to_bitmap and store the result in @background_bitmap.

A cursory glance at the help file will show us exactly what Graphics.snap_to_bitmap does:

author=help file
Graphics.snap_to_bitmap
Gets the current game screen image as a bitmap object.

This reflects the graphics that should be displayed at that point in time, without relation to the use of the freeze method.

The created bitmap must be freed when it is no longer needed.


Then, having obtained a bitmap object of the current screen image, we blur it. If for any reason you -don't- want the underlying scene to blur when a new scene is on top of it, the last line is the one to remove.

def self.background_bitmap
    @background_bitmap
  end


Nice easy one to end, this method simply returns the value of @background_bitmap (it's used in Scene_Map and Spriteset_Battle, as we'll see a lot later).

You may be wondering why exactly a lot of the methods in this class start with "self." and the reason is so you can do something like "SceneManager.return" without having to create an instance of SceneManager. It's equivalent to a concept called a "singleton" though Ruby's implementation isn't exactly the same. It would be going way beyond the scope of this series to explain the exact difference, so suffice to say that if you have a method definition starting with "self." it means you can call that method by using the class name followed by the method without having to instantiate it.

Phew!

BattleManager

Here it is folks, this is where the fun begins. BattleManager...well, manages battles. Let's crack it open and feast on its delicious innards.

def self.setup(troop_id, can_escape = true, can_lose = false)
    init_members
    $game_troop.setup(troop_id)
    @can_escape = can_escape
    @can_lose = can_lose
    make_escape_ratio
  end


Okay, so when BattleManager is first set up, it takes a troop ID, a boolean can_escape with a default of true, and a boolean can_lose with a default of false.

First we call the init_members method, which we'll look at next. Then we call the setup method of $game_troop using the troop ID, then the instance variables @can_escape and @can_lose are set to the values supplied by the parameters, then make_escape_ratio is called, which I'll examine soon.

def self.init_members
    @phase = :init              # Battle Progress Phase
    @can_escape = false         # Can Escape Flag
    @can_lose = false           # Can Lose Flag
    @event_proc = nil           # Event Callback
    @preemptive = false         # Preemptive Attack Flag
    @surprise = false           # Surprise Flag
    @actor_index = -1           # Actor for Which Command Is Being Entered
    @action_forced = nil        # Force Action
    @map_bgm = nil              # For Memorizing Pre-Battle BGM
    @map_bgs = nil              # For Memorizing Pre-Battle BGS
    @action_battlers = []       # Action Order List
  end


Okay, so we set an instance variable called @phase to the symbol :init. Cool.

SURPRISE CRASH COURSE IN SYMBOLS

I promised a while back that one day I would explain exactly what symbols are and do, and now is that time.

Okay, so basically a symbol is an object in Ruby which has both a string and integer representation. It does not, however, CONTAIN an integer or string; those are stored elsewhere. Confused yet?

Really, all you need to know about symbols to realise why they're so great is that any time you use a symbol in your code you are referring to EXACTLY THE SAME OBJECT, all the time, with no exceptions. It's almost like a pointer, but not. The ramifications of this are clear: let's say instead of this we did "@phase = 'init'". It's no less valid, but then every time we get into a new battle it's going to create a new 'init' string to store in @phase. This takes up extra processing time because comparing literal strings in Ruby isn't the most effective of processes, whereas comparing integers is really quick, and if symbols match in their integer value the symbols are clearly identical.

So yeah, symbols!

After this, we set @can_escape and @can_lose to false. You'll note that the setup method will change these values afterwards, but they're initialised first.

The rest is basic setup: @event_proc (event callback, more on this later) is set to nil, @preemptive (whether the party has a pre-emptive attack) is initialised as false, @surprise (whether the enemy ambushed the party) is initialised as false, @actor_index (index of the actor currently taking an action) is initialised as -1, @action_forced (whether the current action is being forced, which is to say it's not being determined by the usual AI) is intialised as nil, @map_bgm (the BGM that was playing on the map before battle started) is initialised as nil, @map_bgs (the BGS that was playing on the map before battle started) is initialised as nil and @action_battlers (the order list for battle participants) is initialised as an empty array.

def self.on_encounter
    @preemptive = (rand < rate_preemptive)
    @surprise = (rand < rate_surprise && !@preemptive)
  end


This method is called by Game_Player when an encounter begins; it generates a random number between 0 and 1, checks whether it is less than the result of rate_preemptive, and assigns @preemptive with a value of true if so, and false otherwise. Then @surprise is set to the result of a random number between 0 and 1 being less than the result of rate_surprise with a logical AND checking that @preemptive is false. The logic here is obvious: if the attack was preemptive, it can't be a surprise.

def self.rate_preemptive
    $game_party.rate_preemptive($game_troop.agi)
  end


This is the preemptive rate used in the previous method, and calls the rate_preemptive method of $game_party using $game_troop.agi as the parameter. Unfortunately as I'm doing the classes in order, you'll have to wait a while to find out what they do! :P

def self.rate_surprise
    $game_party.rate_surprise($game_troop.agi)
  end


Same thing as preemptive, but for surprise. Bet you can't wait until I cover these classes later!

def self.save_bgm_and_bgs
    @map_bgm = RPG::BGM.last
    @map_bgs = RPG::BGS.last
  end


A simple method which sets @map_bgm and @map_bgs to the BGM and BGS that are currently playing. It's called by Game_Map during battle transition.

def self.play_battle_bgm
    $game_system.battle_bgm.play
    RPG::BGS.stop
  end


Another simple one. Plays the battle_bgm of $game_system (the battle music) and stops any current background sounds.

def self.play_battle_end_me
    $game_system.battle_end_me.play
  end


Plays the ME for battle ending (victory theme).

def self.replay_bgm_and_bgs
    @map_bgm.replay unless $BTEST
    @map_bgs.replay unless $BTEST
  end


Replays the original BGM and BGS from before battle, but only if we're not in a test battle.

def self.make_escape_ratio
    @escape_ratio = 1.5 - 1.0 * $game_troop.agi / $game_party.agi
  end


This was the method called at the end of setup, and sets @escape_ratio to the above algorithm. Using the principles of BEDMAS, we can make this a little more readable: 1.5 - ((1.0 * $game_troop.agi) / $game_party.agi)

Let's take a Slime party against the default 4-member party in VX Ace: this becomes 1.5 - ((1.0 * 10) / 25)) which simplifies to 1.5 - (10.0 / 25) which in turn simplifies to 1.5 - 0.4 = 1.1. This is only roughly the value; VX Ace will actually come up with something like 1.08(bunch of numbers). As the name suggests, @escape_ratio is used when you try to escape battle.

def self.in_turn?
    @phase == :turn
  end


Quite a simple one. Returns true if the value of @phase is the symbol :turn. You'll see this come up later.

def self.turn_end?
    @phase == :turn_end
  end


Similar, determines whether a turn is ending.

def self.aborting?
    @phase == :aborting
  end


And this one checks whether battle is aborting. Don't worry, we'll explain the symbols soon.

def self.can_escape?
    @can_escape
  end


Simply returns whether escaping from battle is possible by returning the value of @can_escape. As you might remember, this was initialised as true but can be set to false (if, say, you call battle from an event and check the box for escape being impossible).

def self.actor
    @actor_index >= 0 ? $game_party.members[@actor_index] : nil
  end


Gotta love inline ifs (technically called ternary operators but I like my name better). Okay, so we check to see if @actor_index is greater than or equal to 0. If it is, we return the @actor_indexth element of the .members property of $game_party, and if it isn't we return nil.

def self.clear_actor
    @actor_index = -1
  end


Clears the actor for whom an action is being chosen by setting @actor_index to -1. The swots of the class will notice that this effectively means self.actor is going to return nil after this is called. There is actually a term for using stuff like -1 to negate indexes, but I forget what it is.

def self.next_command
    begin
      if !actor || !actor.next_command
        @actor_index += 1
        return false if @actor_index >= $game_party.members.size
      end
    end until actor.inputable?
    return true
  end


This looks scary, but it's simple enough.

So we start a begin-end block. The conditional is "until actor.inputable?" what this means is that the code will run once, evaluate the result of calling actor.inputable?, and repeat the block until the conditional is true. In other words, it will continue to run while the condition is false.

The first thing is an if statement checking two things: whether actor does not return a value (which is the case if the actor index is less than 0) OR the next_command method of the current actor returns false.

If either of these conditions are true, we add 1 to @actor_index, then return false if @actor_index is greater than the size of the party (in other words, we've checked every party member and nobody is able to act).

Finally, outside the loop, we return true. If we run through this logically, basically any time an actor can't act, it goes on to the next one, and continues doing this until one of them is able to act, at which point the method returns true to say we're ready to move on to the next actor's command.

def self.prior_command
    begin
      if !actor || !actor.prior_command
        @actor_index -= 1
        return false if @actor_index < 0
      end
    end until actor.inputable?
    return true
  end


This is almost exactly the same as the previous method, but going backwards in the party list.

def self.event_proc=(proc)
    @event_proc = proc
  end


This is used in Game_Interpreter when a battle is called. When I do my probably very long) entry on Game_Interpreter, we'll examine this in more detail.

def self.method_wait_for_message=(method)
    @method_wait_for_message = method
  end


This method allows you to determine which method is called when waiting for a message display to finish. This seems complex, but it'll make more sense when we look at Scene_Battle.

def self.wait_for_message
    @method_wait_for_message.call if @method_wait_for_message
  end


This method supports the previous one: if there's a wait method set, this method calls it.

def self.battle_start
    $game_system.battle_count += 1
    $game_party.on_battle_start
    $game_troop.on_battle_start
    $game_troop.enemy_names.each do |name|
      $game_message.add(sprintf(Vocab::Emerge, name))
    end
    if @preemptive
      $game_message.add(sprintf(Vocab::Preemptive, $game_party.name))
    elsif @surprise
      $game_message.add(sprintf(Vocab::Surprise, $game_party.name))
    end
    wait_for_message
  end


Okay, so first we increase the number of battles fought by 1. Then we call the on_battle_start method of $game_party and the one for $game_troop.

After this, we have another kind of loop. .each is a method you can call on an array, and uses the syntax "objectname.each do |variable each element will be stored in|". Whatever is in the block between that and the "end" will be called once for each element in the array, with the name in pipes being used to refer to the current element. In this case, we're looping through the name of each enemy in the battle, and calling the .add method of $game_message to display the name of each enemy emerging. This is how the battle says the names of the enemies at the start.

After this, if it's a preemptive battle the preemptive message is shown, otherwise if it's a surprise the message for that is shown, and finally we call wait_for_message, which as we covered above will call whatever method has been set as the one to be called when waiting for a message. Confused yet?

$game_message.add is another one we'll have to cover later, but it'll be a good one when we do.

def self.abort
    @phase = :aborting
  end


Pretty simple. Sets @phase to the symbol :aborting, which incidentally will also cause the self.aborting? method to return true.

def self.judge_win_loss
    if @phase
      return process_abort   if $game_party.members.empty?
      return process_defeat  if $game_party.all_dead?
      return process_victory if $game_troop.all_dead?
      return process_abort   if aborting?
    end
    return false
  end


If @phase has a value, return the result of process_abort if there's nobody in the party, process_defeat if all party members died, process_victory if the enemies all died, and process_abort if aborting. If @phase has no value, return false.

def self.process_victory
    play_battle_end_me
    replay_bgm_and_bgs
    $game_message.add(sprintf(Vocab::Victory, $game_party.name))
    display_exp
    gain_gold
    gain_drop_items
    gain_exp
    SceneManager.return
    battle_end(0)
    return true
  end


Nice! We won a battle! Let's see what happened.

The battle end ME plays, victory to us. The map's previous BGM/BGS start again. We add a message saying the party successfully won a battle. We display experience. We gain gold. We gain dropped items. We gain experience. We return to the previous scene (which more often than not will be the map). We end battle with a value of 0. A bunch of this stuff is part of BattleManager and will be covered shortly, but don't worry: we'll get to the stuff that isn't real soon.

def self.process_escape
    $game_message.add(sprintf(Vocab::EscapeStart, $game_party.name))
    success = @preemptive ? true : (rand < @escape_ratio)
    Sound.play_escape
    if success
      process_abort
    else
      @escape_ratio += 0.1
      $game_message.add('\.' + Vocab::EscapeFailure)
      $game_party.clear_actions
    end
    wait_for_message
    return success
  end


Here's what happens if you escape battle.

We add a message saying the party is trying to escape. Success is determined thusly: if the battle was a preemptive strike, you just escape no questions asked (spoiler from later: @preemptive is only true on the first turn, so you only get a guaranteed escape before you've taken a turn in that battle). If it isn't a preemptive battle, success is true if a random number between 0 and 1 is less than @escape_ratio. As you may note from my earlier explanation of escape_ratio, this means that the default party of heroes can never fail to escape from a 2-slime party, and also if you get your party's average AGI high enough in comparison to the enemies' you can guarantee escape success.

Next, the escape sound plays.

If the escape was successful, call the abort battle method. Otherwise, we increase @escape_ratio by 0.1, add a message saying the escape failed and clear the party actions. Then we wait for message and return the value of success. This means that you can escape from any escapable battle after 10 attempts, regardless of the AGI difference. If you ever want to change this, here's the place to do it.

def self.process_abort
    replay_bgm_and_bgs
    SceneManager.return
    battle_end(1)
    return true
  end


If battle is aborted, we play the BGM and BGS from the previous scene. Then we call the return method of SceneManager, and call battle_end with a value of 1, and finally return true. Don't worry, we'll see battle_end and why it has values soon.

def self.process_defeat
    $game_message.add(sprintf(Vocab::Defeat, $game_party.name))
    wait_for_message
    if @can_lose
      revive_battle_members
      replay_bgm_and_bgs
      SceneManager.return
    else
      SceneManager.goto(Scene_Gameover)
    end
    battle_end(2)
    return true
  end


Oh no! The enemy prevailed!

So we add a message saying the party was defeated. Then we wait for message. If @can_lose is true, we revive party members, replay the BGM/BGS, and return to the previous scene. If it's false, game over for you.

...wait, what?

Yep, @can_lose is actually @cant_lose. If it's TRUE, your party cannot get a game over. If it's FALSE, you can. It's slightly misleading.

Finally, we call battle_end with a value of 2 and return true.

def self.revive_battle_members
    $game_party.battle_members.each do |actor|
      actor.hp = 1 if actor.dead?
    end
  end


Assuming the battle can't be lost, here we loop through each party member who participated in battle, assigning their objects to "actor" and setting their HP to 1 if they were dead. You'll see the stuff behind this in more detail when we look at Game_Party and Game_Actor.

def self.battle_end(result)
    @phase = nil
    @event_proc.call(result) if @event_proc
    $game_party.on_battle_end
    $game_troop.on_battle_end
    SceneManager.exit if $BTEST
  end


The result parameter will be 0 if the battle was won, 1 if the party escaped, and 2 if the party lost.

So when the battle ends, we set @phase to nil as we no longer need it. If there is a value in @event_proc, we call that proc supplying result as a parameter. This is only used by default in Game_Interpreter and allows the engine to run the code inside victory/defeat/escape clauses when a battle is called from an event.

Then we call on_battle_end for both $game_party and $game_troop, and finally call SceneManager.exit if we're in a test battle (which ends the battle and returns you to the editor).

def self.input_start
    if @phase != :input
      @phase = :input
      $game_party.make_actions
      $game_troop.make_actions
      clear_actor
    end
    return !@surprise && $game_party.inputable?
  end


if the current @phase is NOT equal to the symbol :input when this method is called, it is first set to :input, then we call the make_actions methods of both $game_party and $game_troop, before clearing the current actor. After the if statement, we return the result of whether the battle was NOT a surprise attack AND someone in the party is capable of acting.

def self.turn_start
    @phase = :turn
    clear_actor
    $game_troop.increase_turn
    make_action_orders
  end


As we can see, at the start of a turn we set @phase to the symbol :turn, clear the actor, increase the turn number for $game_troop (which increases the turn count and progresses the logic which checks battle events on certain turns) and sorts out the order of who will act in what order.

def self.turn_end
    @phase = :turn_end
    @preemptive = false
    @surprise = false
  end


At the end of the turn, we set @phase to the symbol :turn_end, and set @preemptive and @surprise to false, ending the effects of a preemptive or surprise attack.

def self.display_exp
    if $game_troop.exp_total > 0
      text = sprintf(Vocab::ObtainExp, $game_troop.exp_total)
      $game_message.add('\.' + text)
    end
  end


If the experience total of the enemy party is greater than 0, we display a message saying you gained exp. Note the '\.' before the text is added, which works exactly the same way as if you put this in a message command in an event: it adds a 15-frame wait before text is displayed.

def self.gain_gold
    if $game_troop.gold_total > 0
      text = sprintf(Vocab::ObtainGold, $game_troop.gold_total)
      $game_message.add('\.' + text)
      $game_party.gain_gold($game_troop.gold_total)
    end
    wait_for_message
  end


Almost exactly the same as the exp display method, but obviously we also call the .gain_gold method of $game_party, which is what actually adds the gold to your total. For exp this is done in a separate method for some reason: I think it might be because there's an actual loop through the party instead of just calling a party method, but I'm not sure what Enterbrain's actual reason was.

def self.gain_drop_items
    $game_troop.make_drop_items.each do |item|
      $game_party.gain_item(item, 1)
      $game_message.add(sprintf(Vocab::ObtainItem, item.name))
    end
    wait_for_message
  end


Almost exactly the same as gaining gold, only with items, obviously. It's looping through the result of calling the make_drop_items method of $game_troop, but the logic is the same.

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


Should be pretty self-explanatory by now. This will all make way more sense when we start covering the methods that are being called, trust me.

def self.make_action_orders
    @action_battlers = []
    @action_battlers += $game_party.members unless @surprise
    @action_battlers += $game_troop.members unless @preemptive
    @action_battlers.each {|battler| battler.make_speed }
    @action_battlers.sort! {|a,b| b.speed - a.speed }
  end


This is an interesting one. First, we set @action_battlers to an empty array.

We add the members of the party unless it's a surprise attack, then add the members of the enemy troop unless it's a preemptive attack. This obviously prevents the disadvantaged side from being included in the logic determining action order.

A block is called for each element in this array (which either contains all the party members, all the enemies, or both) assigning the current iteration to "battler" and calling its make_speed method, which basically determines how fast it is (again, we'll see it in more detail when we get to Game_Party and Game_Troop).

Finally, we call a block for sorting the list using a and b as references to the current iteration and the next one in the list, checking the difference between their speeds. This will give you a list of battlers in order of their speed value. Note that sort! is different from sort: the latter will return a new array, whereas the former sorts "in place" which is to say it modifies the array directly.

def self.force_action(battler)
    @action_forced = battler
    @action_battlers.delete(battler)
  end


This method sets @action_forced to the battle supplied, and removes that battler from the @action_battlers array. This is used in the "force action" battle command.

def self.action_forced?
    @action_forced != nil
  end


This method returns the result of checking whether @action_forced is not equal to nil. Basically if there's a forced action, it returns true.

def self.action_forced_battler
    @action_forced
  end


This is just an encapsulation method for returning which battler is having their action forced.

def self.clear_action_force
    @action_forced = nil
  end


This clears the forced action.

def self.next_subject
    loop do
      battler = @action_battlers.shift
      return nil unless battler
      next unless battler.index && battler.alive?
      return battler
    end
  end


We end the module with an interesting one. Yay for loops!

First, we shift the @action_battlers array and store the result in battler. Shift returns the first element of the array and removes it, so this will get the next-fastest battler. If there's no battler after this, return nil. Then go to the next iteration of the loop unless the battler has an index in the party and is alive. Finally, we return battler.

The checks here are basically to provide for things like this method being called immediately after escaping a battle; if we didn't skip the code in this event it would likely crash.

And that's BattleManager! Hopefully I've shown you some interesting stuff. As usual, death threats, marriage proposals, critique, commentary, compliments, insults, suggestions and questions are all equally welcome. Next week, we start on the Game objects classes!

Until next time.

Posts

Pages: 1
kentona
I am tired of Earth. These people. I am tired of being caught in the tangle of their lives.
21217
but... what is a Scene?
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3119
It's just RPG Maker's term for a distinct screen in the game. You could just as easily call the class Zaphod_Beeblebrox and it would work exactly the same way.
kentona
I am tired of Earth. These people. I am tired of being caught in the tangle of their lives.
21217
author=Trihan
It's just RPG Maker's term for a distinct screen in the game. You could just as easily call the class Zaphod_Beeblebrox and it would work exactly the same way.
I understand class names, but I was struggling to understand the purpose and use of the Scene objects in the Ace program. What are scenes? You say that there are different screens in the game.

Does this mean that Ace is cycling through 30 or 60 scenes a second?
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3119
There isn't a "scene object" in Ace. There is a @scene instance variable contained in SceneManager, which is basically a pointer to which scene the game is currently "playing".
Trihan
"It's more like a big ball of wibbly wobbly...timey wimey...stuff."
3119
Like, if I'm on the map, @scene is "Scene_Map". If I'm in battle, @scene is "Scene_Battle". If I'm at a shop, it's "Scene_Shop". And so on.
A scene is a loop that handles game logic processing, each scene class is made to handle a different game state. So like Trihan said when you're in the main menu the Scene_Menu loop is being executed. The main game's loop responsibility is to handle these scenes, transition between scenes, and call the update method on them.
kentona
I am tired of Earth. These people. I am tired of being caught in the tangle of their lives.
21217
author=GreatRedSpirit
A scene is a loop that handles game logic processing, each scene class is made to handle a different game state. So like Trihan said when you're in the main menu the Scene_Menu loop is being executed. The main game's loop responsibility is to handle these scenes, transition between scenes, and call the update method on them.

Ah. That makes more sense.

(I also don't have the benefit of looking at the Ace editor while at work)
Pages: 1