$imported = {} if $imported.nil?
$imported["BattleSymphony-MV"] = true

#==============================================================================
# �� User Guide
#==============================================================================
#
# This script allows RPG Maker MV formatted battlers to be used with the
# Battle Symphony script for RPG Maker VX Ace.
#
# The default action sequences in battle symphony contain 'POSE' commands
# which control 8D Battlers, and 'STANCE' commans to control Holder's
# battlers.
# This extension adds the 'MOTION' command to control MV format battlers.
#
# MV format battlers have 18 motions in a 3x6 grid of 3 frames each.
# The following keywords are used to refer to each motion in the grid:
#
# WALK     THRUST     ESCAPE
# WAIT     SWING      VICTORY
# CHANT    MISSILE    DYING
# GUARD    SKILL      ABNORMAL
# DAMAGE   SPELL      SLEEP
# EVADE    ITEM       DEAD
#
# In addition, familiar keywords from the POSE and STANCE commands may be used
# if you prefer.
#
# The BREAK motion returns the battler to its normal idle motion.
# The ATTACK motion refers to a motion specified in the database note tags,
# normally THRUST, SWING or MISSILE.
#
# NOTE TAGS
#
# The following database note tags should be used to configure your MV battlers:
#
# <MV BATTLER: file> - Actor, Enemy
# Use an MV format battler for this actor or enemy, the file refers to a file
# placed in Graphics/Characters (as with the HB and 8D extensions)
#
# <MV ATTACK MOTION: keyword> - Actor, Enemy, Weapon
# Choose the default attack motion to use for this battler.
# An actor's first weapon with an attack motion set overrides the actor's
# attack motion (which represents unarmed)
#
#==============================================================================
# �� License Information
#==============================================================================
#
# The MIT License (MIT)
#
# Copyright (c) 2014 Cuong Nguyen
# Copyright (c) 2020 Coelocanth
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
#==============================================================================
# �� Basic Settings
#==============================================================================

module SYMPHONY
  module MV_BATTLER
    # Whether to process 'POSE' commands in sequences. Set this to false if you
    # edit all your action sequences to include 'MOTION' commands. It is on by
    # default for demonstration purposes.
    MIMIC_POSE = true

    # Whether to process 'STANCE' commands in sequences. Set this to false if you
    # edit all your action sequences to include 'MOTION' commands. It is on by
    # default for demonstration purposes.
    MIMIC_STANCE = true

    # The default attack motion when there are no database tags
    DEFAULT_ATTACK_MOTION = :thrust
  end
end

#==============================================================================
# �� Direction - Advanced Configuration
#==============================================================================

module Direction
  
    #--------------------------------------------------------------------------
    # self.index_mv
    #--------------------------------------------------------------------------
    def self.index_mv(pose)
      case pose
      # array = [column, row, loop?, frames]
      # frames is optional, default is 12
      when :walk
        array = [0, 0, true]
      when :wait
        array = [0, 1, true]
      when :chant
        array = [0, 2, true]
      when :guard
        array = [0, 3, true]
      when :damage
        array = [0, 4, false]
      when :evade
        array = [0, 5, false]
      #---
      when :thrust, :attack
        array = [1, 0, false]
      when :swing
        array = [1, 1, false]
      when :missile
        array = [1, 2, false]
      when :skill
        array = [1, 3, false]
      when :spell
        array = [1, 4, false]
      when :item
        array = [1, 5, false]
      #---
      when :escape
        array = [2, 0, true]
      when :victory
        array = [2, 1, true]
      when :dying
        array = [2, 2, true]
      when :abnormal
        array = [2, 3, true]
      when :sleep
        array = [2, 4, true]
      when :dead
        array = [2, 5, true]
      #---
      else
        array = [0, 1, true]
      end
      return array
    end
    
    #--------------------------------------------------------------------------
    # self.auto_pose_mv
    #--------------------------------------------------------------------------
    def self.auto_pose_mv(battler)
      return :dead if battler.dead?
      return :sleep unless battler.movable?
      return :abnormal if battler.confusion?
      return :guard if battler.guard?
      return :dying if battler.hp < battler.mhp / 4
      # If you prefer characters to walk in place, change the next line to :walk
      return :wait
    end
      
  end # Direction
  
#==============================================================================
# �� BattleManager
#==============================================================================

module BattleManager
  
    #--------------------------------------------------------------------------
    # alias method: process_victory
    #--------------------------------------------------------------------------
    class <<self; alias bes_mv_process_victory process_victory; end
    def self.process_victory
      $game_party.alive_members.each { |battler| battler.force_pose_mv(:victory) }
      return bes_mv_process_victory
    end
  
    #--------------------------------------------------------------------------
    # alias method: process_defeat
    #--------------------------------------------------------------------------
    class <<self; alias bes_mv_process_defeat process_defeat; end
    def self.process_defeat
      $game_troop.alive_members.each { |battler| battler.force_pose_mv(:victory) }
      return bes_mv_process_defeat
    end
    
end # BattleManager

#==============================================================================
# �� Regular Expression
#==============================================================================

module REGEXP
  module SYMPHONY

    MV_BATTLER = /<MV[_ ]BATTLER:\s*(.*)>/i
    MV_ATTACK_MOTION = /MV[_ ]ATTACK[_ ]MOTION:\s*(.*)>/i
    
  end
end


#==============================================================================
# �� DataManager
#==============================================================================

module DataManager
  
    #--------------------------------------------------------------------------
    # alias method: load_database
    #--------------------------------------------------------------------------
    class <<self; alias load_database_bes_mv load_database; end
    def self.load_database
      load_database_bes_mv
      load_notetags_bes_mv
    end
    
    #--------------------------------------------------------------------------
    # new method: load_notetags_bes_mv
    #--------------------------------------------------------------------------
    def self.load_notetags_bes_mv
      groups = [$data_actors, $data_enemies, $data_weapons]
      groups.each { |group|
        group.each { |obj|
          next if obj.nil?
          obj.battle_symphony_mv_battler
        }
      }
    end
    
end # DataManager

#==============================================================================
# �� RPG::BaseItem
#==============================================================================

class RPG::BaseItem
  
    #--------------------------------------------------------------------------
    # * Public Instance Variables
    #--------------------------------------------------------------------------
    attr_accessor :mv_battler
    attr_accessor :mv_attack_motion
    
    #--------------------------------------------------------------------------
    # new method: battle_symphony_mv_battler
    #--------------------------------------------------------------------------
    def battle_symphony_mv_battler
      self.note.split(/[\r\n]+/).each { |line|
        case line
        when REGEXP::SYMPHONY::MV_BATTLER
          @mv_battler = $1.to_s
        when REGEXP::SYMPHONY::MV_ATTACK_MOTION
          @mv_attack_motion = $1.to_s.upcase
        end
      }
    end
    
end # RPG::BaseItem

#==============================================================================
# �� Game_Battler
#==============================================================================

class Game_Battler < Game_BattlerBase
  
    #--------------------------------------------------------------------------
    # new method: use_mv?
    #--------------------------------------------------------------------------
    def use_mv?
      self.actor? ? !actor.mv_battler.nil? : !enemy.mv_battler.nil?
    end
    
    #--------------------------------------------------------------------------
    # new method: mv_battler
    #--------------------------------------------------------------------------
    def mv_battler
      self.actor? ? actor.mv_battler : enemy.mv_battler
    end
    
    #--------------------------------------------------------------------------
    # new method: mv_attack_motion
    #--------------------------------------------------------------------------
    def mv_attack_motion
      SYMPHONY::MV_BATTLER::DEFAULT_ATTACK_MOTION
    end
      
    #--------------------------------------------------------------------------
    # alias method: set_default_position
    #--------------------------------------------------------------------------
    alias bes_mv_set_default_position set_default_position
    def set_default_position
      bes_mv_set_default_position
      set_mv_default_position if self.use_mv?
    end
    
    #--------------------------------------------------------------------------
    # new method: set_mv_default_position
    #--------------------------------------------------------------------------
    def set_mv_default_position
      self.pose = Direction.auto_pose_mv(self)
    end
    
    #--------------------------------------------------------------------------
    # alias method: break_pose
    #--------------------------------------------------------------------------
    alias bes_mv_break_pose break_pose
    def break_pose
      bes_mv_break_pose
      break_pose_mv if self.use_mv?
    end
    
    #--------------------------------------------------------------------------
    # new method: break_pose_mv
    #--------------------------------------------------------------------------
    def break_pose_mv
      @pose = Direction.auto_pose_mv(self) 
      #---
      return unless SceneManager.scene.spriteset
      return unless self.sprite
      @direction = SYMPHONY::View::PARTY_DIRECTION
      @direction = Direction.opposite(@direction) if self.enemy?
      self.sprite.mirror = [9, 6, 3].include?(@direction)
      #@direction = Direction.opposite(@direction) if self.sprite.mirror
    end
    
    #--------------------------------------------------------------------------
    # new method: force_pose_mv
    #--------------------------------------------------------------------------
    def force_pose_mv(pose)
      return unless self.use_mv?
      return unless self.exist?
      #---
      self.break_pose
      self.pose = pose
      @force_pose = true
    end
  
    #--------------------------------------------------------------------------
    # alias method (from symphony): use_custom_charset?
    #--------------------------------------------------------------------------
    alias bes_mv_use_custom_charset? use_custom_charset?
    def use_custom_charset?
      return true if use_mv?
      return bes_mv_use_custom_charset?
    end

    #--------------------------------------------------------------------------
    # alias method (from symphony): face_coordinate
    #--------------------------------------------------------------------------
    alias bes_mv_face_coordinate face_coordinate
    def face_coordinate(destination_x, destination_y)
        saved_pose = @pose
        bes_mv_face_coordinate(destination_x, destination_y)
        @pose = saved_pose if use_mv?
    end
end # Game_Battler

#==============================================================================
# �� Game_Actor
#==============================================================================

class Game_Actor < Game_Battler
    #--------------------------------------------------------------------------
    # new method: mv_attack_motion
    #--------------------------------------------------------------------------
    def mv_attack_motion
      weapons.each do |weapon|
        return weapon.mv_attack_motion unless weapon.mv_attack_motion.nil?
      end
      return actor.mv_attack_motion
    end
  
    #--------------------------------------------------------------------------
    # alias method: use_charset?
    #--------------------------------------------------------------------------
    alias bes_mv_use_charset? use_charset?
    def use_charset?
      return false if use_mv?
      return bes_mv_use_charset?
    end
    
end # Game_Actor
  
  #==============================================================================
  # �� Game_Enemy
  #==============================================================================
  
  class Game_Enemy < Game_Battler
    #--------------------------------------------------------------------------
    # new method: mv_attack_motion
    #--------------------------------------------------------------------------
    def mv_attack_motion
      return enemy.mv_attack_motion
    end

    #--------------------------------------------------------------------------
    # alias method: use_charset?
    #--------------------------------------------------------------------------
    alias bes_mv_use_charset? use_charset?
    def use_charset?
      return false if use_mv?
      return bes_mv_use_charset?
    end
    
end # Game_Enemy
  
#==============================================================================
# �� Sprite_Battler
#==============================================================================

class Sprite_Battler < Sprite_Base
  
    #--------------------------------------------------------------------------
    # alias method: update_bitmap
    #--------------------------------------------------------------------------
    alias bes_mv_update_bitmap update_bitmap
    def update_bitmap
      correct_change_pose if @timer.nil?
      @battler.use_mv? ? update_mvset : bes_mv_update_bitmap
    end
    
    #--------------------------------------------------------------------------
    # alias method: update_origin
    #--------------------------------------------------------------------------
    alias bes_mv_update_origin update_origin
    def update_origin
      bes_mv_update_origin
      update_origin_mv if @battler.use_mv?
    end
    
    #--------------------------------------------------------------------------
    # new method: update_charset
    #--------------------------------------------------------------------------
    def update_mvset
      @battler.set_default_position unless pose
      #---
      update_mvset_bitmap
      update_src_rect
    end
    
    #--------------------------------------------------------------------------
    # alias method: correct_change_pose
    #--------------------------------------------------------------------------
    alias bes_mv_correct_change_pose correct_change_pose
    def correct_change_pose
      bes_mv_correct_change_pose unless @battler.use_mv?
      correct_change_pose_mv if @battler.use_mv?
    end
    
    #--------------------------------------------------------------------------
    # new method: correct_change_pose_mv
    #--------------------------------------------------------------------------
    def correct_change_pose_mv
      array = Direction.index_mv(pose)
      @pattern = @battler.reverse_pose ? 2 : 0
      @timer = array[3].nil? ? 12 : array[3]
      @last_pose = pose
      @back_step = false
    end
    
    #--------------------------------------------------------------------------
    # new method: update_charset_origin
    #--------------------------------------------------------------------------
    def update_origin_mv
      if bitmap
        self.ox = @cw / 2
        self.oy = @ch
      end
    end
    
    #--------------------------------------------------------------------------
    # new method: mv_graphic_changed?
    #--------------------------------------------------------------------------
    def mv_graphic_changed?
      self.bitmap.nil? || @character_name != @battler.mv_battler
    end
    
    #--------------------------------------------------------------------------
    # alias method: set_character_bitmap
    #--------------------------------------------------------------------------
    alias bes_mv_set_character_bitmap set_character_bitmap
    def set_character_bitmap
      bes_mv_set_character_bitmap unless @battler.use_mv?
      return unless @battler.use_mv?
      self.bitmap = Cache.character(@character_name)
      @cw = bitmap.width / 9
      @ch = bitmap.height / 6
    end
    
    #--------------------------------------------------------------------------
    # new method: update_mvset_bitmap
    #--------------------------------------------------------------------------
    def update_mvset_bitmap
      if mv_graphic_changed?
        @character_name = @battler.mv_battler
        set_character_bitmap
      end
    end
    
    #--------------------------------------------------------------------------
    # alias method: update_src_rect
    #--------------------------------------------------------------------------
    alias bes_mv_update_src_rect update_src_rect
    def update_src_rect
      bes_mv_update_src_rect unless @battler.use_mv?
      return unless @battler.use_mv?
      array = Direction.index_mv(pose)
      @timer -= 1
      if @timer <= 0
        @pattern += 1
        if @pattern > 2
          if array[2]
            # looping patterns go 0,1,2,1 like MV
            @pattern = -1
          else
            # Non looping patterns are one shot
            @pattern = 2
          end
        end
        @timer = array[3].nil? ? 12 : array[3]
      end
      #---
      column = array[0] * 3 + @pattern.abs
      row = array[1]
      sx = column * @cw
      sy = row * @ch
      self.src_rect.set(sx, sy, @cw, @ch)
    end
    
end # Sprite_Battler

#===============================================================================
# Scene_Battle
#===============================================================================

class Scene_Battle < Scene_Base
  alias bes_mv_imported_symphony imported_symphony
  def imported_symphony
    # This is "MOTION" to match MV terms
    case @action.upcase
    when "MOTION"
      action_motion
      return
    end
    bes_mv_imported_symphony
  end

  def action_motion
    targets = get_action_targets
    return unless targets.size > 0
    #---
    case @action_values[1]
    # special
    when "BREAK", "CANCEL", "RESET", "NORMAL"
      targets.each { |target| target.break_pose }
      return
    when "ATTACK"
      pose_key = :attack
    # first column
    when "WALK", "FORWARD", "MOVE", "TARGET", "MARCH"
      pose_key = :walk
    when "WAIT", "IDLE", "READY"
      pose_key = :wait
    when "CHANT", "CHANNEL", "CHARGE"
      pose_key = :chant
    when "GUARD", "DEFEND"
      pose_key = :guard
    when "DAMAGE", "DMG", "STRUCK"
      pose_key = :damage
    when "EVADE", "DODGE"
      pose_key = :evade
    # second column
    when "THRUST"
      pose_key = :thrust
    when "SWING", "2H", "2H SWING", "1H", "1H SWING"
      pose_key = :swing
    when "SWING REVERSE", "2H REVERSE", "2H SWING REVERSE", "1H REVERSE", "1H SWING REVERSE"
      pose_key = :swing
      reverse_pose = true
    when "MISSILE"
      pose_key = :missile
    when "SKILL", "PHYSICAL"
      pose_key = :skill
    when "SPELL", "CAST", "INVOKE", "MAGIC"
      pose_key = :spell
    when "ITEM"
      pose_key = :item
    # third column
    when "ESCAPE", "ORIGIN", "BACK", "RETREAT"
      pose_key = :escape
    when "VICTORY"
      pose_key = :victory
    when "DYING", "PIYORI", "CRITICAL", "DAZED", "DAZE", "DIZZY", "WOOZY"
      pose_key = :dying
    when "ABNORMAL"
      pose_key = :abnormal
    when "SLEEP"
      pose_key = :sleep
    when "DEAD", "DOWN", "DOWNED", "FALLEN"
      pose_key = :dead
    else; return
    end
    #---
    targets.each { |target| 
      next unless target.exist?
      next unless target.use_mv?
      target.pose = pose_key
      if pose_key == :attack
        case target.mv_attack_motion
        when "THRUST"
          target.pose = :thrust
        when "MISSILE"
          target.pose = :missile
        when "SWING"
          target.pose = :swing
        else
          target.pose = SYMPHONY::MV_BATTLER::DEFAULT_ATTACK_MOTION
        end
      end
      target.force_pose = true
      target.reverse_pose = reverse_pose
    }
  end

  alias bes_mv_action_pose action_pose
  def action_pose
    action_motion if SYMPHONY::MV_BATTLER::MIMIC_POSE
    bes_mv_action_pose
  end

  alias bes_mv_action_stance action_stance
  def action_stance
    action_motion if SYMPHONY::MV_BATTLER::MIMIC_STANCE
    bes_mv_action_stance
  end
end # Scene_Battle

#===============================================================================
# 
# END OF FILE
# 
#===============================================================================