Bestiary

################################################################
# RETCON Library
# Bestiary Version 1.3
# Author: Matt
# Script is free for usage, but please give credit where credit 
# is due. :)
################################################################
# PURPOSE
# This script adds a bestiary (yes, it's spelled bestiary) to your
# game. It hides enemies until they've been encountered, keeps
# track of how many have been defeated, displays their stats,
# as well as a blurb about the enemy.
################################################################
# UPDATE HISTORY
# 1.4 - Added description text offset.
# 1.3 - Added method to check if the bestiary variable exists.
#        If not, call function to create it. Mostly for users 
#        who plug in this script while loading a game save.
#       Checks for draw_text word wrap script, as now is part of
#        the RETCON library.
#       Added lock function.
#       Fixed bug wherein selecting a locked function would cause
#        a user's game to crash.
# 1.2 - Fixed bug which was not ticing off multiple enemy deaths
#         correctly.
#       More code cleaning.
# 1.1 - Added tag+method to skip a monster in the database
#       Used terms as defined in the database rather than hardcode.
#       Added columns variable.
#       Rudimentary code cleanup.
# 1.0 - First draft
################################################################
# METHODOLOGY
# Three new window class are created:
#
# Window_Bestiary - Displays the bestiary window. Subset of 
# Window_Command, this is where monsters are shown/hidden.
#
# Window_Monster - Displays the monster's vital info upon being
# selected from Window_Bestiary. Subset of Window_Selectable.
#
# Window_MonsterDesc - Displays the monster index number, name, as
# well as how many times defeated.
#
# One new scene class is created:
#
# Scene_Bestiary - Manages all the windows for the bestiary.
#
# Five previously initialized classes are modified.
#
# Window_MenuCommand - Modified to include the bestiary method to the 
# menu.
#
# Scene_Menu - Displays the bestiary command in the menu.
#
# Game_Party - Initalizes and saves the bestiary hash, as well as
# making it referable. This is where the bestiary hash is saved as well, so
# make sure to start a new game when testing this script, or to comment it out
# if you want to continue.
#
# Game_Troop - Makes it so enemies seen are set to true in the bestiary hash.
#
# Game_Battler - Tics the bestiary hash when an enemy is defeated.
################################################################
# USAGE
# To the notes section of the monsters tab in the database, you may add a 
# description to the monster to be parsed into the bestiary. Tags for the 
# bestiary follow this format:
#
# <bestiary: *.* />
#
# Wherein *.* is replaced with the descriptor text you wish to have displayed
# in the bestiary. It is two lines, and requires manual carrage returns, so use
# n to delinate where the draw_text_ex function should go to the next line.
# 
# Example:
# <bestiary: The legendary white tiger. His fangs are saidnto be able to crush diamonds. />
#
# Note: If draw_text word wrap script is loaded, n is not needed. It will
# display the text normally.
#
# If you wish to skip a monster in the database from being filed into the hash,
# (used as a sort delinear, not ready to be implemented, or whatever the reason),
# simply add this tag in the notes:
# <skipbestiary />
################################################################
# CONCERNS
#-If enemies "revive", it will not detract from their kill count.
#-Assumes all listings in the database are monsters. If you're organizing
#  your monsters by adding blanks and such, make sure you use the skipbestiary
#  tag.
################################################################
# CUSTOMIZATION

module RETCON
  module Bestiary
  #
  #Name of the bestiary in the command menu.
    Name = "Bestiary"

  #Width of the monster menu. Set to 544, or the maximum width.
    Width = 544

  #What to display in the bestiary when an enemy is locked.
    Hidden = "???"

  #Number of columns to display on the bestiary.
    Columns = 2

  #Make the bestiary accessible in the menu. Set to true to make it available,
  #set to false to make it unavailable.
  #To change this setting in game, during an event, call script and input
  #this text:
  #                 RETCON::Bestiary::Available = true
  #change true to false and back to true when appropriate.
    Available = true

  #Offset for the bestiary description. Measured by pixels.
  #Make negative to raise the text. Make positive to lower the text.
  #line_height is 24 pixels.
    DescOffset = 0

################################################################
#Customization ends here!
#You shouldn't really edit below here, but if you know what
#you're doing, it won't be a big deal. Edit at your own risk and
#all that jazz.
################################################################

    def self.check_for_bestiary
      $game_party.make_the_bestiary if $game_party.bestiary.nil?
    end
  end
end

#################
#CLASS ORIGINALS#
#################

#The bestiary command window, activates when selected.
class Window_Bestiary < Window_Command
  def initialize
    super(0, fitting_height(1))
  end

  def window_height
    Graphics.height - fitting_height(1)
  end

  def window_width
    return RETCON::Bestiary::Width
  end

  #Interprets bestiary hash to display commands.
  def make_command_list
    RETCON::Bestiary::check_for_bestiary
    i = 0
    $game_party.bestiary.each {|k, v|
    i += 1
    if v[0] == false
      add_command(RETCON::Bestiary::Hidden, :bestiary_blank)
      else
      add_command("%03d" %i.to_s  + ". #{k}", :bestiary_lookup)
    end
    }
  end

  def col_max
    return RETCON::Bestiary::Columns
  end

  #Hide and show functions that hide the command menu when showing an enemy
  def hide
    self.opacity = 0
    self.contents_opacity = 0
  end

  def show
    self.opacity = 255
    self.contents_opacity = 255
  end
end

#Window to display the monster once selected.
class Window_Monster < Window_Selectable
  def initialize
    super(0, fitting_height(1), Graphics.width, Graphics.height - fitting_height(1))
    draw_horz_line(line_height * 11)
    self.openness = 0
    open
  end

  #three methods to display the monster window
  def display_monster(enemyindex)
    monsterdb = $data_enemies[enemyindex]
    get_the_entry(monsterdb)
    draw_monster(monsterdb)
    draw_stats(monsterdb)
  end

  def get_the_entry(db)
    #draws the bestiary description text as set in notes.
    monstentry = db.note
    if monstentry =~ /<bestiary: (.*) />/i
        monstentry = $1.to_str
        if !$imported.nil? && $imported["RETCON-wordwrap"] == true
          draw_text(4, line_height * 12 + RETCON::Bestiary::DescOffset, Graphics.width, line_height * 4, monstentry)  
        else
          monstentry = monstentry.gsub(/\n/, "n")
          draw_text_ex(4, line_height * 12 + RETCON::Bestiary::DescOffset, monstentry)  
        end
      end
  end

  #calls and displays the monster image, including hues.
  def draw_monster(db)
    @monstviewport = Viewport.new(6, fitting_height(1) + 6, 412, Graphics.height - fitting_height(5))
    @monstviewport.z = 1000

    @monstpicture = Sprite.new(@monstviewport)    
    @monstpicture.bitmap = Cache.battler(db.battler_name, db.battler_hue)

    #determines centers of the viewpoint and currently loaded image
    ox = @monstviewport.rect.width / 2
    oy = @monstviewport.rect.height / 2
    obx = @monstpicture.bitmap.width / 2
    oby = @monstpicture.bitmap.height / 2

    #realigns the image so it's centered in the viewport
    @monstpicture.x = ox - obx
    @monstpicture.y = oy - oby
  end

  #displays the monsters stats. Does not show MP.
  #Same lines for each stat, but one is aligned left and one aligned right.
  def draw_stats(db)
    stat_window_width = Graphics.width - 430
    stat_window_x = 403
    stats = Array.new
    params = Array.new

    stats = [Vocab::param(0), Vocab::param(2), Vocab::param(3), Vocab::param(4), Vocab::param(5), Vocab::param(6), Vocab::param(7)]
    change_color(system_color)
    7.times{|i| draw_text(stat_window_x, fitting_height(i-1), stat_window_width, line_height, stats[i])}

    params = [db.params[0], db.params[2], db.params[3], db.params[4], db.params[5], db.params[6], db.params[7]]
    change_color(normal_color)
    7.times {|i| draw_text(stat_window_x, fitting_height(i-1), stat_window_width, line_height, params[i], 2)}

    change_color(text_color(17))
    draw_text(stat_window_x, fitting_height(6), stat_window_width, line_height, Vocab::currency_unit)

    change_color(normal_color)
    draw_text(stat_window_x, fitting_height(7), stat_window_width, line_height, db.gold, 2)

    change_color(text_color(23))
    draw_text(stat_window_x, fitting_height(8), stat_window_width, line_height, "EXP")

    change_color(normal_color)
    draw_text(stat_window_x, fitting_height(9), stat_window_width, line_height, db.exp, 2)
  end

  def draw_horz_line(y)
    line_y = y + line_height / 2 - 1
    contents.fill_rect(0, line_y, contents_width, 2, line_color)
  end

  def line_color
    color = normal_color
    color.alpha = 128
    return color
  end

  #erases the picture when window closes.
  def clear_pic
    @monstpicture.dispose 
  end

  def window_width
    return Graphics.width
  end
end

#description window when displaying monster. Shows name as well as times defeated.
class Window_MonsterDesc < Window_Selectable
  def initialize
    super(0, 0, Graphics.width, fitting_height(1))
    self.openness = 0
    open
  end

  def display_desc(enemyname, enemyindex)
    enemynumindex = "%03d" %enemyindex.to_s
    change_color(text_color(6))
    draw_text(0, 0, width-30, line_height, "Entry #" + enemynumindex + ": " + enemyname)  
    change_color(normal_color)
    draw_text(0, 0, width-30, line_height, "Number defeated: " + $game_party.bestiary[enemyname][1].to_s, 2)
  end
end    

#Displays the scene 
class Scene_Bestiary < Scene_MenuBase
  def initialize
    super
    create_bestiary
  end

  #shows the command window for the bestiary
  def create_bestiary
    @monsterlisting = Window_Bestiary.new
    @monsterlisting.set_handler(:bestiary_blank, method(:play_error))
    @monsterlisting.set_handler(:bestiary_lookup, method(:monst_display))
    @monsterlisting.set_handler(:cancel, method(:backout))
    @monsterlisting.active = true
  end

  def backout
    SceneManager.return
  end

  #when a monster is selected, initializes the new windows and makes
  #them the focus
  def monst_display
    @monster = Window_Monster.new
    @monsterdesc = Window_MonsterDesc.new
    monster_info
    @monster.set_handler(:cancel, method(:clear_monster))
    @monsterdesc.set_handler(:cancel, method(:clear_monster))
    @monsterlisting.hide
    @monster.active = true
    @monsterdesc.active = true
  end

  #sends information to window classes to display correct information
  def monster_info
    subjectnumber = @monsterlisting.index+1
    subject = $data_enemies[subjectnumber].name
    @monsterdesc.display_desc(subject, subjectnumber)
    @monster.display_monster(subjectnumber)
  end

  #dummy function to do nothing when locked enemy is selected
  def play_error
    @monsterlisting.active = true
    Sound::play_buzzer
  end

  #refocuses the bestiary command list
  def clear_monster
    @monster.clear_pic
    @monster.close
    @monsterdesc.close
    @monsterlisting.show
    @monsterlisting.active = true
  end
end

###############
#CLASS ADDENDA#
###############

#adds bestiary option to the window, using add_original_commands
class Window_MenuCommand < Window_Command
  alias bestiary_command add_original_commands
  def add_original_commands
    bestiary_command
    add_command(RETCON::Bestiary::Name, :bestiary, RETCON::Bestiary::Available)
  end
end

#adds the bestiary option to the command menu
class Scene_Menu < Scene_MenuBase
  alias bestiary_command_window create_command_window
  def create_command_window
    bestiary_command_window
    #the addition
    @command_window.set_handler(:bestiary,   method(:command_bestiary))
  end

  def command_bestiary
    SceneManager.call(Scene_Bestiary)
  end
end

#initializes the bestiary hash, as well as makes it accessible.
class Game_Party < Game_Unit 
  attr_accessor   :bestiary

  alias beastiary_retain_previous initialize
  def initialize
    beastiary_retain_previous
    make_the_bestiary
  end

  #initalizes the bestiary hash.
  def make_the_bestiary
    enemies = $data_enemies
    monsterlist = Array.new
    @bestiary = Hash.new

    i = 0
    while i < enemies.length
      if i == 0
        i = 1
      end
      if enemies[i].note =~ /<skipbestiary />/i
        p i-1 ##skip method
      else
        monsterlist[i-1] = enemies[i].name
        @bestiary.merge! monsterlist[i-1] => [false, 0]
      end
      i += 1
    end
  end
end

#upon battle initialization, unlocks enemies in the bestiary
class Game_Troop < Game_Unit
  alias bestiary_setup setup
  def setup(troop_id)
    bestiary_setup(troop_id)
    seen_the_enemy
  end

  def seen_the_enemy
    RETCON::Bestiary::check_for_bestiary
    members.each do |enemy|
      if $game_party.bestiary[enemy.original_name][0] == false
        $game_party.bestiary[enemy.original_name][0] = true
      end
    end
  end
end

#checks death in a battle to see if it was enemy or ally.
#if enemy, then add a tick to the bestiary hash.
class Game_Battler < Game_BattlerBase
  alias bestiary_die die
  def die
    bestiary_die
    enemy_check
  end

  def enemy_check
    enemy_dead = false
     $game_troop.members.each do |enemy|
       if enemy.original_name == @original_name && enemy_dead == false
        $game_party.bestiary[enemy.original_name][1] += 1
        enemy_dead = true
        end
      end
    end
end