#==============================================================================
# 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