[Resolved][RMXP]How to Call Equip Window During Battle Phase

Started by Wraith89, August 17, 2017, 01:36:27 pm

Previous topic - Next topic

Wraith89

This is nothing special, but something I tried to experiment with scripting, and in this case, changing the characters' equipment during battle, as I thought it would be an interesting mechanic. This is just using the default battle system, so nothing really special is going on here. Needless to say, I keep failing at it.  :facepalm:
Basically, Scene_Battle 1 and 3 are the ones I am editing to test this out, but I think I am just doing things without understanding how it works. The issue is mostly with Scene_Battle 3, as 1 is only the menu which you use to edit the battle menu, so I have that done. So as of now, the segment from line 110ish looks like this:
Spoiler: ShowHide
Quotedef update_phase3_basic_command
    # If B button was pressed
    if Input.trigger?(Input::B)
      # Play cancel SE
      $game_system.se_play($data_system.cancel_se)
      # Go to command input for previous actor
      phase3_prior_actor
      return
    end
    # If C button was pressed
    if Input.trigger?(Input::C)
      # Branch by actor command window cursor position
      case @actor_command_window.index
      when 0  # attack
        # Play decision SE
        $game_system.se_play($data_system.decision_se)
        # Set action
        @active_battler.current_action.kind = 0
        @active_battler.current_action.basic = 0
        # Start enemy selection
        start_enemy_select
      when 1  # skill
        # Play decision SE
        $game_system.se_play($data_system.decision_se)
        # Set action
        @active_battler.current_action.kind = 1
        # Start skill selection
        start_skill_select
      when 2  # equip
        $game_system.se_play($data_system.decision_se)
        $scene = Scene_Equip.new

      when 3  # guard     
        # Play decision SE
        $game_system.se_play($data_system.decision_se)
        # Set action
        @active_battler.current_action.kind = 0
        @active_battler.current_action.basic = 1
        # Go to command input for next actor
        phase3_next_actor
      when 4  # item
        # Play decision SE
        $game_system.se_play($data_system.decision_se)
        # Set action
        @active_battler.current_action.kind = 2
        # Start item selection
        start_item_select
      end
      return
    end
  end


I know I have to do more than this to get it to work, and I experimented with a lot of things, but comes up with "uninitialized constant" and other assorted errors. I would like any form of assistance. Thank you.

KK20

Obvious problem is that you can't use Scene_Equip directly. That $scene global variable is a finicky thing to work with. The idea behind scenes, at least in the way Enterbrain designed them, is that they assume you are done with the previous scene and no longer need it. Everything gets disposed and all the little switches and variables that the previous scene stored are now gone. So even if you were able to get to Scene_Equip and go back to Scene_Battle...you'd be right back at the start of a new fight.

The best way to handle this situation is to make Scene_Equip, well, scene-less. Like so
Spoiler: ShowHide

class Sceneless_Equip
  #--------------------------------------------------------------------------
  # * Object Initialization
  #     actor_index : actor index
  #     equip_index : equipment index
  #--------------------------------------------------------------------------
  def initialize(actor_index = 0, equip_index = 0)
    @actor_index = actor_index
    @equip_index = equip_index
  end
  #--------------------------------------------------------------------------
  # * Main Processing
  #--------------------------------------------------------------------------
  def main
    # Get actor
    @actor = $game_actors[@actor_index]
    # Make windows
    @help_window = Window_Help.new
    @left_window = Window_EquipLeft.new(@actor)
    @right_window = Window_EquipRight.new(@actor)
    @item_window1 = Window_EquipItem.new(@actor, 0)
    @item_window2 = Window_EquipItem.new(@actor, 1)
    @item_window3 = Window_EquipItem.new(@actor, 2)
    @item_window4 = Window_EquipItem.new(@actor, 3)
    @item_window5 = Window_EquipItem.new(@actor, 4)
    # Associate help window
    @right_window.help_window = @help_window
    @item_window1.help_window = @help_window
    @item_window2.help_window = @help_window
    @item_window3.help_window = @help_window
    @item_window4.help_window = @help_window
    @item_window5.help_window = @help_window
    # Set cursor position
    @right_window.index = @equip_index
    @abort = false
    refresh
    # Execute transition
    Graphics.transition
    # Main loop
    loop do
      # Update game screen
      Graphics.update
      # Update input information
      Input.update
      # Frame update
      update
      # Abort loop if screen is changed
      break if @abort
    end
    # Prepare for transition
    Graphics.freeze
    # Dispose of windows
    @help_window.dispose
    @left_window.dispose
    @right_window.dispose
    @item_window1.dispose
    @item_window2.dispose
    @item_window3.dispose
    @item_window4.dispose
    @item_window5.dispose
  end
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
    # Set item window to visible
    @item_window1.visible = (@right_window.index == 0)
    @item_window2.visible = (@right_window.index == 1)
    @item_window3.visible = (@right_window.index == 2)
    @item_window4.visible = (@right_window.index == 3)
    @item_window5.visible = (@right_window.index == 4)
    # Get currently equipped item
    item1 = @right_window.item
    # Set current item window to @item_window
    case @right_window.index
    when 0
      @item_window = @item_window1
    when 1
      @item_window = @item_window2
    when 2
      @item_window = @item_window3
    when 3
      @item_window = @item_window4
    when 4
      @item_window = @item_window5
    end
    # If right window is active
    if @right_window.active
      # Erase parameters for after equipment change
      @left_window.set_new_parameters(nil, nil, nil)
    end
    # If item window is active
    if @item_window.active
      # Get currently selected item
      item2 = @item_window.item
      # Change equipment
      last_hp = @actor.hp
      last_sp = @actor.sp
      @actor.equip(@right_window.index, item2 == nil ? 0 : item2.id)
      # Get parameters for after equipment change
      new_atk = @actor.atk
      new_pdef = @actor.pdef
      new_mdef = @actor.mdef
      # Return equipment
      @actor.equip(@right_window.index, item1 == nil ? 0 : item1.id)
      @actor.hp = last_hp
      @actor.sp = last_sp
      # Draw in left window
      @left_window.set_new_parameters(new_atk, new_pdef, new_mdef)
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    # Update windows
    @left_window.update
    @right_window.update
    @item_window.update
    refresh
    # If right window is active: call update_right
    if @right_window.active
      update_right
      return
    end
    # If item window is active: call update_item
    if @item_window.active
      update_item
      return
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update (when right window is active)
  #--------------------------------------------------------------------------
  def update_right
    # If B button was pressed
    if Input.trigger?(Input::B)
      # Play cancel SE
      $game_system.se_play($data_system.cancel_se)
      # Switch to menu screen
      @abort = true
      return
    end
    # If C button was pressed
    if Input.trigger?(Input::C)
      # If equipment is fixed
      if @actor.equip_fix?(@right_window.index)
        # Play buzzer SE
        $game_system.se_play($data_system.buzzer_se)
        return
      end
      # Play decision SE
      $game_system.se_play($data_system.decision_se)
      # Activate item window
      @right_window.active = false
      @item_window.active = true
      @item_window.index = 0
      return
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update (when item window is active)
  #--------------------------------------------------------------------------
  def update_item
    # If B button was pressed
    if Input.trigger?(Input::B)
      # Play cancel SE
      $game_system.se_play($data_system.cancel_se)
      # Activate right window
      @right_window.active = true
      @item_window.active = false
      @item_window.index = -1
      return
    end
    # If C button was pressed
    if Input.trigger?(Input::C)
      # Play equip SE
      $game_system.se_play($data_system.equip_se)
      # Get currently selected data on the item window
      item = @item_window.item
      # Change equipment
      @actor.equip(@right_window.index, item == nil ? 0 : item.id)
      # Activate right window
      @right_window.active = true
      @item_window.active = false
      @item_window.index = -1
      # Remake right window and item window contents
      @right_window.refresh
      @item_window.refresh
      return
    end
  end
end

And you call it up like so from Scene_Battle

s = Sceneless_Equip.new(@active_battler.id)
s.main

The only change is that I got rid of the pesky $scene != self to signify when we are done. Instead, this is handled with a single boolean @abort, which gets turned to true when the B button is pressed. I also got rid of the L and R buttons to toggle between the different actors. Everything else is still the same.

Using the snippet you provided of your Scene_Battle 3, this is what I would do:
Spoiler: ShowHide

      when 2  # equip
        $game_system.se_play($data_system.decision_se)
        Graphics.freeze
        @status_window.visible = false
        @actor_command_window.visible = false
        @spriteset.toggle_viewports
        s = Sceneless_Equip.new(@active_battler.id)
        s.main
        @spriteset.toggle_viewports
        @status_window.visible = true
        @actor_command_window.visible = true
        Graphics.transition

And here's the toggle_viewports method.

class Spriteset_Battle
  def toggle_viewports
    @viewport1.visible = @viewport2.visible =
    @viewport3.visible = @viewport4.visible = !@viewport1.visible
  end
end

We first freeze the graphics so that we can make a smooth transition to our equipment scene. We then hide the windows and viewports since they stick up through the equipment windows (go ahead and remove those lines to see for yourself). Then we call our equip "scene" and its update loop main. It stays at this point until we abort the equip, which it will then bring back the windows and viewports we hid earlier, and makes the Graphics transition to smooth ourselves back into battle.

And the GIF of it working (4MB)
Spoiler: ShowHide

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Wraith89

Thanks a lot! I did not realise those dispose window stuff meant completely discarding the information. It also would have caused problems too as using Scene_Equip directly with some of Blizzard's scripts ended up getting me to the Stat Distribution Window, and of course exiting it led to crashes. I think this should just be a regular script for others to use as well, but it really is just reconfiguring the main scripts anyways.

KK20

It's not just the "dispose window" that discards the information. It's also the fact you're removing the instance stored in $scene variable from memory. In battle, $scene is an instance of Scene_Battle. When you do

$scene = Scene_Equip.new

you're overwriting the Scene_Battle instance with Scene_Equip now. You're telling the program "I'm done with Scene_Battle. Remove it from memory." All the stuff outside of the scene's loop (disposing of windows) is then called to properly dispose its graphics so that they don't "bleed" into the next scene.

In RMVXA, they have a Scene_Manager class that keeps track of the order you go from one scene to the next, so I think it's possible to switch scenes without destroying the previous one. Can't recall.


Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!