Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

Disgaea job selector error.

Do you want to try posting the script your here, to see if you have edited it, and tell us if your using any other script. I know pretty much nothing about ruby, but it probably would help!
 
What line of code is in line 530? After receiving the problem, you can open up the Script Editor and it should automatically scroll to the line where the error occurred.
And just a reminder, please don't bump until 72 hours (3 days) have passed.
 
Code:
#==============================================================================
#  Script Name: Disgaea - Hour of Darkness Jobs
#  Author(s): khmp
#  Version: 0.0.5
#  Date Last Modified: 1/7/08
#==============================================================================

#==============================================================================
# Refer to the Change Log at the bottom for updates.
#==============================================================================

#==============================================================================
#  Notes:
#------------------------------------------------------------------------------
#  Usage: 
#  Because of the way its designed it requires that an Actor database file be 
#  generated containing the 'blanks' of which the Jobs will be developed from. 
#  So the very first thing you should do is create a project and design the 
#  Jobs which are really the Actors. When the Actor/Job creation is finished 
#  save the database and navigate to your game directory and browse the Data 
#  folder. Duplicate the 'Actors.rxdata' rename the copy 'DHoDActors.rxdata'. 
#  If this file does not exist by the time Scene_DHoDJobs runs an exception 
#  will occur and send you back to Scene_Map. I can understand tweaking of Jobs 
#  may need to occur for balancing etc. Just rename the current one and change 
#  'DHoDActors.rxdata' back to 'Actors.rxdata' after backing up this file. To 
#  avoid any conflict you should exit RMXP while doing this.
#  
#  And in case you hate reading paragraphs or I was vague:
#  1.) Create a new project or open a pre-existing project.
#  2.) Design and create the Actors to reflect the jobs you want in the game.
#      This includes naming.
#      Actor1: name = 'Archer'
#      Actor2: name = 'Hunter'
#      Actor3: name = 'Donkey Slayer'
#  3.) Close the project. Duplicate by copying and pasting 'Actors.rxdata' file
#      in the Data folder and rename it 'DHoDActors.rxdata'.
#  4.) Open the project you want to have the jobs in. Paste the DHoDActors file
#      into the Data folder of this project.
#  5.) Have a cookie!
#==============================================================================

#==============================================================================
#  About:
#------------------------------------------------------------------------------
#  Foreword:
#  This is truly my first script so criticize even if it's a minor detail. That
#  aside this is currently not SDK compatible. Perhaps as a learning experience
#  this will have a SDK counterpart when this script is done. Oh most 
#  importantly this is more of a SCRIPTING RESOURCE. The only thing this really
#  does is provide the graphical user interface in the styling of Disgaea and
#  when you select a Job the Actor of said Job will be returned. So it really
#  is up to the user to do with that knowledge what they want. 
#  
#  Inspiration:
#  Disgaea is a truly epic strategy RPG even though the world seems small. It
#  had a Job switching scheme similar to Final Fantasy Tactics and I thought
#  it would kind of cool to incorporate that into a game. Unfortunately my mind
#  flips between tasks too often to finish anything long term. So I thought I
#  would create a little portion of that system.
#==============================================================================

#==============================================================================
# ** Game_System
#------------------------------------------------------------------------------
#  This class handles data surrounding the system. Backround music, etc.
#  is managed here as well. Refer to "$game_system" for the instance of 
#  this class.
#==============================================================================

class Game_System
  attr_accessor :job_lvls  # The max. Job level achieved by any character.
end

#==============================================================================
# ** Game_Actor
#------------------------------------------------------------------------------
#  This class handles the actor. It's used within the Game_Actors class
#  ($game_actors) and refers to the Game_Party class ($game_party).
#==============================================================================

class Game_Actor < Game_Battler
  alias_method :khmp_dhodjobs_exp=, :exp=
  #----------------------------------------------------------------------------
  # * Change EXP
  #     exp : new EXP
  #----------------------------------------------------------------------------
  def exp=(exp)
    level = @level
    khmp_dhodjobs_exp=(exp)
    # If the actor leveled up
    if level < @level
      # If job_lvls is nil, create it.
      $game_system.job_lvls = Hash.new if $game_system.job_lvls.nil?
      $game_system.job_lvls[$data_classes[class_id].name] = 
        [$game_system.job_lvls[$data_classes[class_id].name], @level].max
    end
  end
end

#==============================================================================
# ** Scene_DHoDJobs
#------------------------------------------------------------------------------
# This class will present a way to choose your Actor's Job like in Disgaea.
#==============================================================================

class Scene_DHoDJobs
  # Mess with these constants to get them to the values you need them at.
  Background_Image = 'DisgaeaJobs.png' # Name of the image file in background.
  Scr_Width = 640 # The width of your game screen in pixels.
  Scr_Height = 480 # The height of your game screen in pixels.
  Frames_Width = 4 # Amount of frames that are in the char graphic x wise.
  Frames_Height = 4 # Amount of frames that are in the char graphic y wise.
  
  #============================================================================
  #  Note (Job_Chains):
  #----------------------------------------------------------------------------
  #  In Disgaea, Jobs have different rankings, (6 total). But you can make the 
  #  chain depth as far as you want.
  #----------------------------------------------------------------------------
  #  Job_Chains = 
  #  {
  #    0 => ['Fighter', 'Warrior', 'Swordsman', 'Blade Master']
  #  }
  #  The hash key is irrelevant, that's the first number before the '=>', but 
  #  it decides the order of how the classes will appear. This key references 
  #  an Array. This Array holds the ActorIDs of the Jobs that are chained.
  #
  #  For Example let's say that DHoDActors holds the following actor data:
  #  1 -> Fighter         5 -> Neophyte        9 -> Rogue  
  #  2 -> Warrior         6 -> Apprentice      10 -> Thief
  #  3 -> Swordsman       7 -> Mage            11 -> Scout
  #  4 -> Blade Master    8 -> Tome Master     12 -> Assassin
  #  Job_Chains = 
  #  {
  #    0 => ['Fighter', 'Warrior', 'Swordsman', 'Blade Master'],
  #    1 => ['Neophyte', 'Apprentice', 'Mage', 'Tome Master'],
  #    2 => ['Rogue', 'Thief', 'Scout', 'Assassin']
  #  }
  #============================================================================
  Job_Chains = 
  {
    0 => ['Fighter', 'Warrior', 'Swordsman', 'Blade Master'],
    1 => ['Scholar', 'Apprentice', 'Mage', 'Tome Master'],
    2 => ['Rogue', 'Scout', 'Bandit', 'Master Bandit'],
  }

  #============================================================================
  #  Note (Job_Reqs):
  #----------------------------------------------------------------------------
  #  In Disgaea, Jobs had a certain requirement be filled before a job became
  #  active. Such as killing a demon of said Job type or reaching a certain
  #  level of a Job or Jobs. In this case for the sake simplicity, Jobs can
  #  only be acquired by meeting level requirements of other Jobs.
  #----------------------------------------------------------------------------
  #  Job_Reqs = 
  #  {
  #    'Warrior' => { 'Fighter' => 5},
  #    'Swordsman' => { 'Warrior' => 10, 'Rogue' => 5 }
  #  }
  #  The hash key represents the job that has requirements. This key 
  #  references a Hash that holds various Job names and the level that Job
  #  needs to be in order for the hash key to become available to the user.
  #
  #  Hash example above:
  #  In order for the job Warrior to become available for your party one 
  #  member must have reached level 5 as a Fighter. In order for the job
  #  swordsman to become available one member of your party must have reached
  #  level 10 as a Warrior and level 5 as a Rogue.
  #============================================================================
  Job_Reqs = 
  {
    'Warrior' => { 'Fighter' => 20 },
    'Swordsman' => { 'Warrior' => 30 },
    'Blade Master' => { 'Swordsman' => 50 },
  }
  {
    'Apprentice' => { 'Scholar' => 15 },
    'Mage' => { 'Apprentice' => 25 },
    'Tome Master' => { 'Mage' => 40 },
  }
    {
    'Scout' => { 'Rouge' => 12 },
    'Bandit' => { 'Scout' => 28 },
    'Master Bandit' => { 'Bandit' => 55 },
  }
  #============================================================================
  #  Note (Hidden_Jobs):
  #----------------------------------------------------------------------------
  #  In Disgaea, some Jobs were never revealed to the player. Such as the main
  #  character whose Job was Demon Prince. No one else could attain this Job
  #  and the the main character could not switch to another Job.
  #----------------------------------------------------------------------------
  #  Hidden_Jobs = [
  #    'Demon Prince',
  #  ]
  #  Just put the name of any Job that is never avaiable inside this array.
  #============================================================================
  Hidden_Jobs = 
  ['Demon Prince',
    
  ]
  
  #----------------------------------------------------------------------------
  # * Main Processing
  #----------------------------------------------------------------------------
  def main
    # Decide whether work should continue.
    ($scene = Scene_Map.new; return) if !main_checks
    
    # Initialize the instance variables.
    main_variable
    main_job
    
    # Transition into this scene to erase the old.
    Graphics.transition(10)
    
    # Loop till the user wants to leave this scene.
    loop do
      Graphics.update
      Input.update
      update
      break if $scene != self
    end
    
    # Freeze the graphics so the next scene can transition properly.
    Graphics.freeze
    
    # Dispose of the assets.
    main_dispose
  end
  #----------------------------------------------------------------------------
  # * Get the job the user selected.
  # Note: As soon as job is grabbed it is deleted and forgotten.
  #----------------------------------------------------------------------------
  def self.job
    return nil if @@job.nil?
    job = @@job.dup
    @@job = nil
    return job
  end
  #----------------------------------------------------------------------------
  # * Main Processing : Error Checking
  #----------------------------------------------------------------------------
  def main_checks
    # If anything below is missing or nil return false.
    return false if !File.exists?('Data/DHoDActors.rxdata')
    return false if $game_system.nil?
    return false if $game_party.nil?
    return false if $data_classes.nil?
    return false if $data_system.nil?
    return false if Job_Chains.empty?
    $game_system.job_lvls = Hash.new if $game_system.job_lvls.nil?
    return true
  end
  #----------------------------------------------------------------------------
  # * Main Processing : Variable Initialization
  #----------------------------------------------------------------------------
  def main_variable
    # Determine the depth of the longest chain.
    @chain_depth = 0
    Job_Chains.each_value{|chain|@chain_depth = [@chain_depth, chain.size].max}
    
    # Every possible job. Really an actor file but will be jobs from now on.
    @jobs = load_data('Data/DHoDActors.rxdata')
    
    # The sprite interface for the background.
    @background = Sprite.new
    @background.bitmap = RPG::Cache.picture(Background_Image)
    
    # The window that will show the job info.
    @job_info = Window_JobInfo.new
    
    # Get the general size of the characters.
    bitmap = Bitmap.new('Graphics/Characters/' + @jobs[1].character_name)                   
    @char_width = bitmap.width / Frames_Width
    @char_height = bitmap.height / Frames_Height
    bitmap.dispose
    
    # Drawing stuff.
    @drawing_jobs = Array.new
    # Initialize the sprite locations of where we will be drawing the jobs to.
    main_sprite_locations
    
    # The job of the user.
    @job = job_from_name(Job_Chains[Job_Chains.keys[0]][0])
    @@job = nil
    
    # This hash will hold the names of the available jobs.
    @available_jobs = Hash.new
    
    # The index we are at when nevigating the Job Mesh
    @job_index = [0, 0]
    @c_sprite = [1,0]
  end
  #----------------------------------------------------------------------------
  # * Main Processing : Job Related Works
  #----------------------------------------------------------------------------
  def main_job
    # Grab every job from the file and take it's name.
    @jobs.each do |job| 
      next if job.nil?
      # By default all Jobs are available.
      @available_jobs[job.name] = true
    end
    
    # Decide the available jobs.
    main_filter_available_jobs
  end
  #----------------------------------------------------------------------------
  # * Main Processing : Filer Out false Jobs
  #----------------------------------------------------------------------------
  def main_filter_available_jobs
    # Update the Job level Hash held in $game_system
    $game_party.actors.each do |actor|
      next if actor.nil?
      # Get the job of the Actor.
      job = $data_classes[actor.class_id]
      # Skip and move on if this isn't one of the jobs available.
      next if !@available_jobs.has_key?(job.name)
      # Grab the Actor's level and save it.
      $game_system.job_lvls[job.name] = 
        [$game_system.job_lvls[job.name], actor.level].max
    end

    # Now decide which ones are hidden based on requirement.
    @available_jobs.each do |name, status|
      # Move to the next one if it's already hidden.
      next if status == false
      # If the Job has requirement(s).
      if Job_Reqs.has_key?(name)
        # Go through the requirements and decide whether a Job is hidden.
        Job_Reqs[name].each do |job, req|
          # If they have not met the requirement of the Job hide it.
          if $game_system.job_lvls.has_key?(job)
            # The job was noted inside the system is it less than needed?
            @available_jobs[name] = false if $game_system.job_lvls[job] < req
          else
            # The Job was found having Job requirements but the player hasn't
            # made any progress towards any of its requirements. Thus it is
            # hidden.
            @available_jobs[name] = false
          end
        end
      end
    end
    
    # Hide the jobs that will never appear.
    Hidden_Jobs.each do |name|
      @available_jobs[name] = false if @available_jobs.has_key?(name)
    end
  end
  #----------------------------------------------------------------------------
  # * Main Processing : Object Disposal
  #----------------------------------------------------------------------------
  def main_dispose
    # Dispose of the background image and its sprite
    @background.bitmap.dispose if !@background.bitmap.disposed?
    @background.dispose if !@background.disposed?
    
    # Dispose of the job information window.
    @job_info.dispose
    
    # Dispose of the drawing elements.
    @drawing_jobs.each do |arr|
      arr.each do |spr|
        spr.bitmap.dispose if !spr.bitmap.disposed?
        spr.dispose if !spr.disposed?
      end
    end
  end
  #----------------------------------------------------------------------------
  # * Frame Update
  #----------------------------------------------------------------------------
  def update
    # Check for input.
    # Check to see if we should draw meaning the index has changed.
    # Save the old index for comparison the next time update loops.
    # Update the window. It checks to see if it's necessary.
    update_command
    update_draw_jobs
    @old_job_index = Array.new(@job_index)
    @job_info.update(@job)
  end
  #----------------------------------------------------------------------------
  # * Frame Update : Draw Jobs
  #----------------------------------------------------------------------------
  def update_draw_jobs
    @drawing_jobs[@c_sprite[0]][@c_sprite[1]].update
    return if @old_job_index == @job_index
    @@job = @job
    
    # Figure out the columns that need to be drawn.
    columns = Array.new
    
    for i in 0...3
      val = Job_Chains.keys[Job_Chains.keys[@job_index[0]] + (i - 1)]
      val = Job_Chains.keys[0] if val.nil?
      columns << val
    end
    
    # Finally draw the jobs that are available
    i, j = 0, 0
    columns.each do |col|
      # Go through each job and draw it if it's available.
      Job_Chains[col].each do |job_name|
        # Clear out the bitmap so we don't leave old jobs. You'll understand if
        # you remove this line.
        @drawing_jobs[i][j].bitmap.clear
        # Skip the job if it's not available.
        (j += 1; next) if !@available_jobs[job_name]
        
        job = job_from_name(job_name)
        job_pic = RPG::Cache.character(job.character_name, job.character_hue)
        @drawing_jobs[i][j].bitmap.blt(0, 0, job_pic,
                                Rect.new(0, 0, @char_width, @char_height), 255)
        j += 1
      end
      j = 0
      i += 1
    end
    
    height_space = 60
    # Do some eye candy stuff to the sprites.
    for i in 0...3
      for j in 0...@chain_depth
        @drawing_jobs[i][j].y = (Scr_Height / 2 - @char_height / 2) + 
                              (@job_index[1] * height_space - j * height_space)
        @drawing_jobs[i][j].opacity = 255 - ((@job_index[1] - j).abs * 50)
        @drawing_jobs[i][j].loop_animation(nil)
      end
    end
    
    @drawing_jobs[@c_sprite[0]][@c_sprite[1]].blink_on
  end
  #----------------------------------------------------------------------------
  # * Frame Update : Command Input
  #----------------------------------------------------------------------------
  def update_command
    # If the user wants to cancel out send them back to Scene_Map.
    ($scene = Scene_Map.new; @@job = @job; return) if Input.trigger?(Input::C)
    ($scene = Scene_Map.new; @@job = nil; return) if Input.trigger?(Input::B)
    
    # Boolean used to determine whether or not the job information window
    # should be updated.
    need_update = false
    if Input.trigger?(Input::LEFT) # Change selected Job, Left.
      need_update = true
      @job_index[0] -= 1
      @job_index[0] = Job_Chains.size - 1 if @job_index[0] < 0
      @job_index[1] = [Job_Chains[@job_index[0]].size - 1, @job_index[1]].min
    elsif Input.trigger?(Input::RIGHT) # Change selected Job, Right.
      need_update = true
      @job_index[0] += 1
      @job_index[0] = 0 if @job_index[0] > Job_Chains.size - 1
      @job_index[1] = [Job_Chains[@job_index[0]].size - 1, @job_index[1]].min
    elsif Input.trigger?(Input::DOWN) # Change selected Job, Down.
      need_update = true
      @job_index[1] -= 1
      @job_index[1] = Job_Chains[@job_index[0]].size - 1 if @job_index[1] < 0
    elsif Input.trigger?(Input::UP) # Change selected Job, Up.
      need_update = true
      @job_index[1] += 1
      @job_index[1] = 0 if @job_index[1] > Job_Chains[@job_index[0]].size - 1
    end
    
    # If the job was changed tell the Job Information window to update.
    if need_update
      @c_sprite[1] = @job_index[1]
      @job = job_from_name(Job_Chains[@job_index[0]][@job_index[1]])
      @job_index = @old_job_index if @available_jobs[@job.name] == false
      @job = job_from_name(Job_Chains[@job_index[0]][@job_index[1]])
    end
  end
  #----------------------------------------------------------------------------
  # * Get a Job!
  #     name : Name of the actor you want from 'DHoDActors.rxdata'
  #----------------------------------------------------------------------------
  def job_from_name(name)
    @jobs.each do |job|
      next if job.nil?
      return job if job.name == name
    end
  end
  #----------------------------------------------------------------------------
  # * Sprite Locations
  #----------------------------------------------------------------------------
  def main_sprite_locations
    height_space = 60
    width_space = (Scr_Width / 3 - @char_width / 2)
    for i in 0...3
      for j in 0...@chain_depth
        x = width_space * i + width_space / 2
        y = Scr_Height / 2 - @char_height / 2 - (j * height_space)
        @drawing_jobs[i] = Array.new if @drawing_jobs[i].nil?
        @drawing_jobs[i][j] = RPG::Sprite.new
        @drawing_jobs[i][j].x = x
        @drawing_jobs[i][j].y = y
        @drawing_jobs[i][j].zoom_x = 1.1
        @drawing_jobs[i][j].zoom_y = 1.1
        @drawing_jobs[i][j].z = (@chain_depth + 5) + (j * - 1)
        @drawing_jobs[i][j].bitmap = Bitmap.new(@char_width, @char_height)
      end
    end
  end
end

#==============================================================================
# ** Window_JobInfo
#------------------------------------------------------------------------------
# This class will show the Job information.
#==============================================================================

class Window_JobInfo < Window_Base
  #----------------------------------------------------------------------------
  # * Object Initialization
  #----------------------------------------------------------------------------
  def initialize
    super(9, 9, 220, 180)
    self.contents = Bitmap.new(width - 32, height - 32)
    self.opacity = 128
    self.z = 9999
  end
  #----------------------------------------------------------------------------
  # * Refresh
  #----------------------------------------------------------------------------
  def refresh
    self.contents.clear
    x, y = 0, 0
    # Just some fun text stuff. Actually don't really like to do it.
        self.contents.draw_text(x, y, width - 32, 24, @job.name, 2)             <<<<<--------------ERROR HERE
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.hp + ': ' + @job.parameters[0,1].to_s)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.sp + ': ' + @job.parameters[1,1].to_s)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.str + ': ' + @job.parameters[2,1].to_s)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.dex + ': ' + @job.parameters[3,1].to_s)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.agi + ': ' + @job.parameters[4,1].to_s)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.int + ': ' + @job.parameters[5,1].to_s)
  end
  #----------------------------------------------------------------------------
  # * Frame Update
  #     actor : the actor whose data must be drawn.
  #----------------------------------------------------------------------------
  def update(job)
    return if job.nil?
    return if job == @job
    @job = job
    refresh
  end
end

=begin
#==============================================================================
# Change Log: v0.0.5
#==============================================================================
1.) Removed extraneous code.
2.) Hidden_Jobs is no longer a Hash but an Array. Instructions reflect this
    change.
3.) Selected job now has blink_on applied to it. Makes it stand out from the
    rest of the group.
4.) Removed any constant the user wasn't meant to touch.
5.) Increased the size of the sprites ever so slightly.

#==============================================================================
# Possible Future Changes:
#==============================================================================
1.) Animate the guy instead of selection box.
2.) Spruce up the window displaying the Job Information.

=end

The self.contents.draw_text(x, y, width - 32, 24, @job.name, 2) part is where I get the errror.
 
The error occurs NoMethodError because the @job instance is an Array object, which looks at the "Data/DHoDActors" (which, basically, returns an array of all actors, but in this case, actors are "Jobs".) I don't think @job is supposed to be an array, I think its supposed to actually be a single actor (a.k.a. 'job' in this case) from the "DHoDActors.rxdata" file, instead of the whole file itself (which, its basically returning the data of), which is an Array object. There is no method 'name' for Array, however there is a method for Game_Actor, so I'd conclude @job is supposed to be a single actor and not an array of actors, however that happens.

What is an array, you ask? Its a object that holds information in brackets like so...

arr = [obj, obj, obj, ...]

In example, your error is returning the whole array, it needs to instead return an object FROM the array...

arr[n] (where n would be the numeric index of the actor its looking for.)

Thats the synopsis of whats the cause of the problem, so at least you know WHY it happened. You might want to use Search in the script and look for "@job =". That brings you to a method job_to_name(), and go from there. If I have time tomorrow, and this still remains unresolved, I'll try and track through the script and find the root of the problem. Good luck :thumb:

(Where's khmp when you need him :/ )
 
If you create a lite demo for people to test your script, it'll be easier for others to help you with your problem. Not that its that big of a deal, but it does take a little work to set the script up before we can even test it. (I already have it setup, but others will need to set it up without a demo.)

I don't promise you that I'll figure out the problem, but I do promise you I'll try to look into it further tonight when I'm not busy.
 
I've tried 'n tried to fix this, but once I get something 'fixed' (or so I think), I run into other errors, I downloaded the origonal demo (by khmp) and it appears to be working fine, so maybe it was a problem with your setup, or maybe you made a bad edit somewhere or maybe you just got a bad version.

Code:
#==============================================================================
#  Script Name: Disgaea - Hour of Darkness Jobs
#  Author(s): khmp
#  Version: 0.0.5
#  Date Last Modified: 1/7/08
#==============================================================================

#==============================================================================
# Refer to the Change Log at the bottom for updates.
#==============================================================================

#==============================================================================
#  Notes:
#------------------------------------------------------------------------------
#  Usage: 
#  Because of the way its designed it requires that an Actor database file be 
#  generated containing the 'blanks' of which the Jobs will be developed from. 
#  So the very first thing you should do is create a project and design the 
#  Jobs which are really the Actors. When the Actor/Job creation is finished 
#  save the database and navigate to your game directory and browse the Data 
#  folder. Duplicate the 'Actors.rxdata' rename the copy 'DHoDActors.rxdata'. 
#  If this file does not exist by the time Scene_DHoDJobs runs an exception 
#  will occur and send you back to Scene_Map. I can understand tweaking of Jobs 
#  may need to occur for balancing etc. Just rename the current one and change 
#  'DHoDActors.rxdata' back to 'Actors.rxdata' after backing up this file. To 
#  avoid any conflict you should exit RMXP while doing this.
#  
#  And in case you hate reading paragraphs or I was vague:
#  1.) Create a new project or open a pre-existing project.
#  2.) Design and create the Actors to reflect the jobs you want in the game.
#      This includes naming.
#      Actor1: name = 'Archer'
#      Actor2: name = 'Hunter'
#      Actor3: name = 'Donkey Slayer'
#  3.) Close the project. Duplicate by copying and pasting 'Actors.rxdata' file
#      in the Data folder and rename it 'DHoDActors.rxdata'.
#  4.) Open the project you want to have the jobs in. Paste the DHoDActors file
#      into the Data folder of this project.
#  5.) Have a cookie!
#==============================================================================

#==============================================================================
#  About:
#------------------------------------------------------------------------------
#  Foreword:
#  This is truly my first script so criticize even if it's a minor detail. That
#  aside this is currently not SDK compatible. Perhaps as a learning experience
#  this will have a SDK counterpart when this script is done. Oh most 
#  importantly this is more of a SCRIPTING RESOURCE. The only thing this really
#  does is provide the graphical user interface in the styling of Disgaea and
#  when you select a Job the Actor of said Job will be returned. So it really
#  is up to the user to do with that knowledge what they want. 
#  
#  Inspiration:
#  Disgaea is a truly epic strategy RPG even though the world seems small. It
#  had a Job switching scheme similar to Final Fantasy Tactics and I thought
#  it would kind of cool to incorporate that into a game. Unfortunately my mind
#  flips between tasks too often to finish anything long term. So I thought I
#  would create a little portion of that system.
#==============================================================================

#==============================================================================
# ** Game_System
#------------------------------------------------------------------------------
#  This class handles data surrounding the system. Backround music, etc.
#  is managed here as well. Refer to "$game_system" for the instance of 
#  this class.
#==============================================================================

class Game_System
  attr_accessor :job_lvls  # The max. Job level achieved by any character.
end

#==============================================================================
# ** Game_Actor
#------------------------------------------------------------------------------
#  This class handles the actor. It's used within the Game_Actors class
#  ($game_actors) and refers to the Game_Party class ($game_party).
#==============================================================================

class Game_Actor < Game_Battler
  alias_method :khmp_dhodjobs_exp=, :exp=
  #----------------------------------------------------------------------------
  # * Change EXP
  #     exp : new EXP
  #----------------------------------------------------------------------------
  def exp=(exp)
    level = @level
    khmp_dhodjobs_exp=(exp)
    # If the actor leveled up
    if level < @level
      # If job_lvls is nil, create it.
      $game_system.job_lvls = Hash.new if $game_system.job_lvls.nil?
      $game_system.job_lvls[$data_classes[class_id].name] = 
        [$game_system.job_lvls[$data_classes[class_id].name], @level].max
    end
  end
end

#==============================================================================
# ** Scene_DHoDJobs
#------------------------------------------------------------------------------
# This class will present a way to choose your Actor's Job like in Disgaea.
#==============================================================================

class Scene_DHoDJobs
  # Mess with these constants to get them to the values you need them at.
  Background_Image = 'DisgaeaJobs.png' # Name of the image file in background.
  Scr_Width = 640 # The width of your game screen in pixels.
  Scr_Height = 480 # The height of your game screen in pixels.
  Frames_Width = 4 # Amount of frames that are in the char graphic x wise.
  Frames_Height = 4 # Amount of frames that are in the char graphic y wise.
  
  #============================================================================
  #  Note (Job_Chains):
  #----------------------------------------------------------------------------
  #  In Disgaea, Jobs have different rankings, (6 total). But you can make the 
  #  chain depth as far as you want.
  #----------------------------------------------------------------------------
  #  Job_Chains = 
  #  {
  #    0 => ['Fighter', 'Warrior', 'Swordsman', 'Blade Master']
  #  }
  #  The hash key is irrelevant, that's the first number before the '=>', but 
  #  it decides the order of how the classes will appear. This key references 
  #  an Array. This Array holds the ActorIDs of the Jobs that are chained.
  #
  #  For Example let's say that DHoDActors holds the following actor data:
  #  1 -> Fighter         5 -> Neophyte        9 -> Rogue  
  #  2 -> Warrior         6 -> Apprentice      10 -> Thief
  #  3 -> Swordsman       7 -> Mage            11 -> Scout
  #  4 -> Blade Master    8 -> Tome Master     12 -> Assassin
  #  Job_Chains = 
  #  {
  #    0 => ['Fighter', 'Warrior', 'Swordsman', 'Blade Master'],
  #    1 => ['Neophyte', 'Apprentice', 'Mage', 'Tome Master'],
  #    2 => ['Rogue', 'Thief', 'Scout', 'Assassin']
  #  }
  #============================================================================
  Job_Chains = 
  {
    0 => ['Fighter', 'Warrior', 'Swordsman', 'Blade Master'],
    1 => ['Neophyte', 'Apprentice', 'Mage', 'Tome Master'],
    2 => ['Rogue', 'Scout', 'Archer', 'Assassin'],
  }

  #============================================================================
  #  Note (Job_Reqs):
  #----------------------------------------------------------------------------
  #  In Disgaea, Jobs had a certain requirement be filled before a job became
  #  active. Such as killing a demon of said Job type or reaching a certain
  #  level of a Job or Jobs. In this case for the sake simplicity, Jobs can
  #  only be acquired by meeting level requirements of other Jobs.
  #----------------------------------------------------------------------------
  #  Job_Reqs = 
  #  {
  #    'Warrior' => { 'Fighter' => 5},
  #    'Swordsman' => { 'Warrior' => 10, 'Rogue' => 5 }
  #  }
  #  The hash key represents the job that has requirements. This key 
  #  references a Hash that holds various Job names and the level that Job
  #  needs to be in order for the hash key to become available to the user.
  #
  #  Hash example above:
  #  In order for the job Warrior to become available for your party one 
  #  member must have reached level 5 as a Fighter. In order for the job
  #  swordsman to become available one member of your party must have reached
  #  level 10 as a Warrior and level 5 as a Rogue.
  #============================================================================
  Job_Reqs = 
  {
    'Warrior' => { 'Fighter' => 5 },
    'Swordsman' => { 'Warrior' => 10 },
    'Blade Master' => { 'Swordsman' => 20, 'Rogue' => 15 }
  }

  #============================================================================
  #  Note (Hidden_Jobs):
  #----------------------------------------------------------------------------
  #  In Disgaea, some Jobs were never revealed to the player. Such as the main
  #  character whose Job was Demon Prince. No one else could attain this Job
  #  and the the main character could not switch to another Job.
  #----------------------------------------------------------------------------
  #  Hidden_Jobs = [
  #    'Demon Prince',
  #  ]
  #  Just put the name of any Job that is never avaiable inside this array.
  #============================================================================
  Hidden_Jobs = 
  [
    
  ]
  
  #----------------------------------------------------------------------------
  # * Main Processing
  #----------------------------------------------------------------------------
  def main
    # Decide whether work should continue.
    ($scene = Scene_Map.new; return) if !main_checks
    
    # Initialize the instance variables.
    main_variable
    main_job
    
    # Transition into this scene to erase the old.
    Graphics.transition(10)
    
    # Loop till the user wants to leave this scene.
    loop do
      Graphics.update
      Input.update
      update
      break if $scene != self
    end
    
    # Freeze the graphics so the next scene can transition properly.
    Graphics.freeze
    
    # Dispose of the assets.
    main_dispose
  end
  #----------------------------------------------------------------------------
  # * Get the job the user selected.
  # Note: As soon as job is grabbed it is deleted and forgotten.
  #----------------------------------------------------------------------------
  def self.job
    return nil if @@job.nil?
    job = @@job.dup
    @@job = nil
    return job
  end
  #----------------------------------------------------------------------------
  # * Main Processing : Error Checking
  #----------------------------------------------------------------------------
  def main_checks
    # If anything below is missing or nil return false.
    return false if !File.exists?('Data/DHoDActors.rxdata')
    return false if $game_system.nil?
    return false if $game_party.nil?
    return false if $data_classes.nil?
    return false if $data_system.nil?
    return false if Job_Chains.empty?
    $game_system.job_lvls = Hash.new if $game_system.job_lvls.nil?
    return true
  end
  #----------------------------------------------------------------------------
  # * Main Processing : Variable Initialization
  #----------------------------------------------------------------------------
  def main_variable
    # Determine the depth of the longest chain.
    @chain_depth = 0
    Job_Chains.each_value{|chain|@chain_depth = [@chain_depth, chain.size].max}
    
    # Every possible job. Really an actor file but will be jobs from now on.
    @jobs = load_data('Data/DHoDActors.rxdata')
    
    # The sprite interface for the background.
    @background = Sprite.new
    @background.bitmap = RPG::Cache.picture(Background_Image)
    
    # The window that will show the job info.
    @job_info = Window_JobInfo.new
    
    # Get the general size of the characters.
    bitmap = Bitmap.new('Graphics/Characters/' + @jobs[1].character_name)                   
    @char_width = bitmap.width / Frames_Width
    @char_height = bitmap.height / Frames_Height
    bitmap.dispose
    
    # Drawing stuff.
    @drawing_jobs = Array.new
    # Initialize the sprite locations of where we will be drawing the jobs to.
    main_sprite_locations
    
    # The job of the user.
    @job = job_from_name(Job_Chains[Job_Chains.keys[0]][0])
    @@job = nil
    
    # This hash will hold the names of the available jobs.
    @available_jobs = Hash.new
    
    # The index we are at when nevigating the Job Mesh
    @job_index = [0, 0]
    @c_sprite = [1,0]
  end
  #----------------------------------------------------------------------------
  # * Main Processing : Job Related Works
  #----------------------------------------------------------------------------
  def main_job
    # Grab every job from the file and take it's name.
    @jobs.each do |job| 
      next if job.nil?
      # By default all Jobs are available.
      @available_jobs[job.name] = true
    end
    
    # Decide the available jobs.
    main_filter_available_jobs
  end
  #----------------------------------------------------------------------------
  # * Main Processing : Filer Out false Jobs
  #----------------------------------------------------------------------------
  def main_filter_available_jobs
    # Update the Job level Hash held in $game_system
    $game_party.actors.each do |actor|
      next if actor.nil?
      # Get the job of the Actor.
      job = $data_classes[actor.class_id]
      # Skip and move on if this isn't one of the jobs available.
      next if !@available_jobs.has_key?(job.name)
      # Grab the Actor's level and save it.
      $game_system.job_lvls[job.name] = 
        [$game_system.job_lvls[job.name], actor.level].max
    end

    # Now decide which ones are hidden based on requirement.
    @available_jobs.each do |name, status|
      # Move to the next one if it's already hidden.
      next if status == false
      # If the Job has requirement(s).
      if Job_Reqs.has_key?(name)
        # Go through the requirements and decide whether a Job is hidden.
        Job_Reqs[name].each do |job, req|
          # If they have not met the requirement of the Job hide it.
          if $game_system.job_lvls.has_key?(job)
            # The job was noted inside the system is it less than needed?
            @available_jobs[name] = false if $game_system.job_lvls[job] < req
          else
            # The Job was found having Job requirements but the player hasn't
            # made any progress towards any of its requirements. Thus it is
            # hidden.
            @available_jobs[name] = false
          end
        end
      end
    end
    
    # Hide the jobs that will never appear.
    Hidden_Jobs.each do |name|
      @available_jobs[name] = false if @available_jobs.has_key?(name)
    end
  end
  #----------------------------------------------------------------------------
  # * Main Processing : Object Disposal
  #----------------------------------------------------------------------------
  def main_dispose
    # Dispose of the background image and its sprite
    @background.bitmap.dispose if !@background.bitmap.disposed?
    @background.dispose if !@background.disposed?
    
    # Dispose of the job information window.
    @job_info.dispose
    
    # Dispose of the drawing elements.
    @drawing_jobs.each do |arr|
      arr.each do |spr|
        spr.bitmap.dispose if !spr.bitmap.disposed?
        spr.dispose if !spr.disposed?
      end
    end
  end
  #----------------------------------------------------------------------------
  # * Frame Update
  #----------------------------------------------------------------------------
  def update
    # Check for input.
    # Check to see if we should draw meaning the index has changed.
    # Save the old index for comparison the next time update loops.
    # Update the window. It checks to see if it's necessary.
    update_command
    update_draw_jobs
    @old_job_index = Array.new(@job_index)
    @job_info.update(@job)
  end
  #----------------------------------------------------------------------------
  # * Frame Update : Draw Jobs
  #----------------------------------------------------------------------------
  def update_draw_jobs
    @drawing_jobs[@c_sprite[0]][@c_sprite[1]].update
    return if @old_job_index == @job_index
    @@job = @job
    
    # Figure out the columns that need to be drawn.
    columns = Array.new
    
    for i in 0...3
      val = Job_Chains.keys[Job_Chains.keys[@job_index[0]] + (i - 1)]
      val = Job_Chains.keys[0] if val.nil?
      columns << val
    end
    
    # Finally draw the jobs that are available
    i, j = 0, 0
    columns.each do |col|
      # Go through each job and draw it if it's available.
      Job_Chains[col].each do |job_name|
        # Clear out the bitmap so we don't leave old jobs. You'll understand if
        # you remove this line.
        @drawing_jobs[i][j].bitmap.clear
        # Skip the job if it's not available.
        (j += 1; next) if !@available_jobs[job_name]
        
        job = job_from_name(job_name)
        job_pic = RPG::Cache.character(job.character_name, job.character_hue)
        @drawing_jobs[i][j].bitmap.blt(0, 0, job_pic,
                                Rect.new(0, 0, @char_width, @char_height), 255)
        j += 1
      end
      j = 0
      i += 1
    end
    
    height_space = 60
    # Do some eye candy stuff to the sprites.
    for i in 0...3
      for j in 0...@chain_depth
        @drawing_jobs[i][j].y = (Scr_Height / 2 - @char_height / 2) + 
                              (@job_index[1] * height_space - j * height_space)
        @drawing_jobs[i][j].opacity = 255 - ((@job_index[1] - j).abs * 50)
        @drawing_jobs[i][j].loop_animation(nil)
      end
    end
    
    @drawing_jobs[@c_sprite[0]][@c_sprite[1]].blink_on
  end
  #----------------------------------------------------------------------------
  # * Frame Update : Command Input
  #----------------------------------------------------------------------------
  def update_command
    # If the user wants to cancel out send them back to Scene_Map.
    ($scene = Scene_Map.new; @@job = @job; return) if Input.trigger?(Input::C)
    ($scene = Scene_Map.new; @@job = nil; return) if Input.trigger?(Input::B)
    
    # Boolean used to determine whether or not the job information window
    # should be updated.
    need_update = false
    if Input.trigger?(Input::LEFT) # Change selected Job, Left.
      need_update = true
      @job_index[0] -= 1
      @job_index[0] = Job_Chains.size - 1 if @job_index[0] < 0
      @job_index[1] = [Job_Chains[@job_index[0]].size - 1, @job_index[1]].min
    elsif Input.trigger?(Input::RIGHT) # Change selected Job, Right.
      need_update = true
      @job_index[0] += 1
      @job_index[0] = 0 if @job_index[0] > Job_Chains.size - 1
      @job_index[1] = [Job_Chains[@job_index[0]].size - 1, @job_index[1]].min
    elsif Input.trigger?(Input::DOWN) # Change selected Job, Down.
      need_update = true
      @job_index[1] -= 1
      @job_index[1] = Job_Chains[@job_index[0]].size - 1 if @job_index[1] < 0
    elsif Input.trigger?(Input::UP) # Change selected Job, Up.
      need_update = true
      @job_index[1] += 1
      @job_index[1] = 0 if @job_index[1] > Job_Chains[@job_index[0]].size - 1
    end
    
    # If the job was changed tell the Job Information window to update.
    if need_update
      @c_sprite[1] = @job_index[1]
      @job = job_from_name(Job_Chains[@job_index[0]][@job_index[1]])
      @job_index = @old_job_index if @available_jobs[@job.name] == false
      @job = job_from_name(Job_Chains[@job_index[0]][@job_index[1]])
    end
  end
  #----------------------------------------------------------------------------
  # * Get a Job!
  #     name : Name of the actor you want from 'DHoDActors.rxdata'
  #----------------------------------------------------------------------------
  def job_from_name(name)
    @jobs.each do |job|
      next if job.nil?
      return job if job.name == name
    end
  end
  #----------------------------------------------------------------------------
  # * Sprite Locations
  #----------------------------------------------------------------------------
  def main_sprite_locations
    height_space = 60
    width_space = (Scr_Width / 3 - @char_width / 2)
    for i in 0...3
      for j in 0...@chain_depth
        x = width_space * i + width_space / 2
        y = Scr_Height / 2 - @char_height / 2 - (j * height_space)
        @drawing_jobs[i] = Array.new if @drawing_jobs[i].nil?
        @drawing_jobs[i][j] = RPG::Sprite.new
        @drawing_jobs[i][j].x = x
        @drawing_jobs[i][j].y = y
        @drawing_jobs[i][j].zoom_x = 1.1
        @drawing_jobs[i][j].zoom_y = 1.1
        @drawing_jobs[i][j].z = (@chain_depth + 5) + (j * - 1)
        @drawing_jobs[i][j].bitmap = Bitmap.new(@char_width, @char_height)
      end
    end
  end
end

#==============================================================================
# ** Window_JobInfo
#------------------------------------------------------------------------------
# This class will show the Job information.
#==============================================================================

class Window_JobInfo < Window_Base
  #----------------------------------------------------------------------------
  # * Object Initialization
  #----------------------------------------------------------------------------
  def initialize
    super(9, 9, 220, 180)
    self.contents = Bitmap.new(width - 32, height - 32)
    self.opacity = 128
    self.z = 9999
  end
  #----------------------------------------------------------------------------
  # * Refresh
  #----------------------------------------------------------------------------
  def refresh
    self.contents.clear
    x, y = 0, 0
    # Just some fun text stuff. Actually don't really like to do it.
    self.contents.draw_text(x, y, width - 32, 24, @job.name, 2)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.hp + ': ' + @job.parameters[0,1].to_s)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.sp + ': ' + @job.parameters[1,1].to_s)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.str + ': ' + @job.parameters[2,1].to_s)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.dex + ': ' + @job.parameters[3,1].to_s)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.agi + ': ' + @job.parameters[4,1].to_s)
    self.contents.draw_text(x, y+=24, width - 32, 24, 
          $data_system.words.int + ': ' + @job.parameters[5,1].to_s)
  end
  #----------------------------------------------------------------------------
  # * Frame Update
  #     actor : the actor whose data must be drawn.
  #----------------------------------------------------------------------------
  def update(job)
    return if job.nil?
    return if job == @job
    @job = job
    refresh
  end
end

=begin
#==============================================================================
# Change Log: v0.0.5
#==============================================================================
1.) Removed extraneous code.
2.) Hidden_Jobs is no longer a Hash but an Array. Instructions reflect this
    change.
3.) Selected job now has blink_on applied to it. Makes it stand out from the
    rest of the group.
4.) Removed any constant the user wasn't meant to touch.
5.) Increased the size of the sprites ever so slightly.

#==============================================================================
# Possible Future Changes:
#==============================================================================
1.) Animate the guy instead of selection box.
2.) Spruce up the window displaying the Job Information.

=end
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top