[XP] Organized Quest System

Started by KK20, August 15, 2012, 02:04:59 pm

Previous topic - Next topic

KK20

August 15, 2012, 02:04:59 pm Last Edit: January 29, 2018, 03:16:24 am by KK20
Organized Quest System
Authors: KK20
Version: 1.22
Type: Quest Log
Key Term: Misc System



Introduction

Partly influenced from Final Fantasy Tactics Advance's Quest System and Maplestory, this script is a complete quest system in itself. It organizes quests based on acceptance and completion and uses icons for the user interface.


Features


  • 4 tabs for organizing quests (All, New, Accepted, and Completed)

  • Somewhat unique user interface

  • Lots of customization

  • A quest information window that will display the location, NPC name, rewards, and description

  • Repeatable quests




Screenshots




Demo

Download


Script

If too lazy to check demo, place this script above Main and under Scene_Debug.
Spoiler: ShowHide

=begin
================================================================================
Organized Quest System                                         Version 1.22
Author: KK20                                                   [1/29/2018]
--------------------------------------------------------------------------------

  This script is a full-fledged quest system that uses pictures to represent the
  different types of quests in the User Interface.
 
  Includes:
    ~ Different page tabs to organize which quests to display (the four tabs
      used are 'All', 'New' (not accepted), 'Accepted', and 'Completed'), which
      can be accessed by pressing the 'Shift' key.
    ~ Player rank determined by number of quests completed
    ~ A window displaying the quest's information when selected
    ~ Repeatable quests
    ~ Some customizable features
--------------------------------------------------------------------------------
[[ Version History ]]
1.22[1/29/2018]
   - Fixed an error when changing quest tabs to an empty list, causing a
     "can't convert nil into Integer" error (seems to only occur in Ruby 1.9)
1.21[10/14/2015]
   - Fixed regular expression used in writing the quest description
1.2 [11/24/13]
   - Descriptions now use \n and \v[n] and no longer need to be spaced apart
   - Added new features:
     * Repeatable Quests
     * Can check quest's acceptance status more precisely
     * Rewards can have strings in them
1.1 [1/27/13]
   - Cleaned up code
   - Added new features:
     * Map background
     * More quest descriptions and use of game variables
   - Removed storing data into a Game System variable
1.0 [8/15/12]
   - Released script

********************************************************************************
*                         I N S T R U C T I O N S                              *
********************************************************************************
-----------------
-Getting Started-
-----------------
  To call the quest window, use the following in a script call:
> $scene = Scene_Quest.new
 
  To add quests that are available and can be accepted, use the script call:
> Quest.new(quest_id)
 
  To change the quest to being accepted, use the script call:
> Quest.accept(quest_id)
 
  To complete the quest, use the script call:
> Quest.complete(quest_id)
  (Doing this will automatically give the rewards to the player--This includes
  the items, EXP, and Gold. No need to event that in!
  If you still want to use events to reward, this can be configured below.)
 
  To make a quest repeatable, use the script call:
> Quest.repeat(quest_id)
  (In order for a quest to be repeated, the player must complete the quest
  at least once. Repeated quests are still considered completed quests,
  regardless if the player is accepting it or not.)
 
  ######[ NOTE ]################################################################
  A quest cannot be accepted if the quest is not currently available. A quest
  cannot be completed unless the quest is currently being accepted.
  Example: Quest.new(1)
           Quest.complete(1)
  Result: Quest 1 is still considered "new". The player has completed nothing.
  ##############################################################################
 
  For conditional branch needs:

  To check if the quest is repeatable, use the script call:
> Quest.repeat?(quest_id)
  Returns 'true' if the quest is repeatable, 'false' otherwise.
 
  To check if the quest is completed, use the script call:
> Quest.complete?(quest_id)
  Returns 'true' if the quest is completed or repeatable, 'false' otherwise.

  To check if the quest is currently being accepted, use the script call:
> Quest.accept?(quest_id)
  Returns 'true' if the quest is accepted, completed, or repeatable,
  'false' otherwise.
 
  To check if the quest can be accepted, use the script call:
> Quest.new?(quest_id)
  Returns 'true' if the quest is new, accepted, completed, or repeatable,
  'false' otherwise.
 
  ######[ NOTE ]################################################################
  In the last three commands (thus, excluding Quest.repeat?), you can put a
  second parameter to check if the quest is CURRENTLY new, accepted, or
  completed. All you have to put is true after your quest ID.
 
  Example: Quest.new?(1, true)
  Result: Returns true ONLY if the quest is considered new. If it has been
          accepted or completed, it will return false.
  ##############################################################################
 
  To check how many quests have been completed, use the script call:
> Quest.done
  This will return an integer. If you want to, for example, give a reward for
  completing 7 quests, use a 'Conditional Branch', select 'Script' and
  type in 'Quest.done >= 7' (without the quotes).

---------- 
-Controls-
----------
  Arrow keys to move the cursor around
  'C'/'Enter' to view the quest's information
  'X'/'Esc' to close the Scene_Quest
  'Shift' to change tabs (All -> New -> Accepted -> Completed -> All)
 
  When the quest's information window is being displayed:
  You can still use the Arrow keys to view different quests
  'C'/'Enter' and 'Shift' have no use.
  'X'/'Esc' to remove the quest's information window

----------
-Graphics-
----------
  This script uses images stored in 'Graphics/Pictures' to represent the quests.
  The script was written in support for 48 x 48 graphics. Your images must be
  of this size (no less, no bigger). To configure what graphics you wish to use,
  look below for the configuration.
 
---------------
-Configuration-
---------------
  Located below. All the instructions are there.

================================================================================
Credits:
KK20 -> Making this script
game_guy -> Used his 'Quest Log System' as a basis for this script
Blizzard -> Method to turn a long string into a paragraph
================================================================================
=end
module QuestData
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#@             S T A R T          O F         C O N F I G U R A T I O N        @
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  #=============================================================================
  # NPCPicture - Set to 'true' if you want the NPC graphic to be displayed in
  #              the quest's information window. Set to 'false' if you don't
  #              want to.
  #
  # HideReward - Hides the rewards in the quest description until the player has
  #              completed that quest. Set the quest IDs in the array to use this
  #              feature. Delete the array and put 'true' to apply this to all
  #              quests. Create an empty array if you do not wish to use this
  #              feature. An empty array looks like this-->  []
  #
  # RewardOnComplete - Set to 'true' if you want the player to be rewarded at the
  #                    instance of 'Quest.complete(quest_id)'. 'false' otherwise.
  #
  # ShowMapBackGround - Sets the opacity of the windows to allow viewing of the
  #                     map. Set to 'true' if you want this option. 'false' otherwise.
  #
  # ExitScene - Set to the scene you wish to view upon exiting quest scene.
  #
  # PageTurnSound - Sound effect that plays when player shifts the quest page.
  #=============================================================================
  NPCPicture        = true
  HideReward        = [1, 5]
  RewardOnComplete  = true
  ShowMapBackGround = true
  ExitScene         = Scene_Menu.new(0)
  PageTurnSound     = RPG::AudioFile.new("005-System05", 80)
  #=============================================================================
  # EXPicon - The graphic used when the quest's information window is brought up.
  #           It represents EXP gained from the quest. Name it the same as it is
  #           found in 'Graphics/Icons'.
  #
  # GoldIcon - Same as above. Represents the gold gained from the quest.
  #
  # ** If you do not want graphics, simply use an empty string ---> ""
  #    If you do opt to not use icons, the text will fill in the words for you.
  #    These are ICONS, so the graphics must be 24 x 24.
  #=============================================================================
  EXPicon   = "exp"
  GoldIcon  = "gold"
  #=============================================================================
  # NewQuestPic - The picture you want to represent a quest that hasn't been
  #               accepted. Name it the same as it is in 'Graphics/Pictures'.
  #
  # AcceptedQuestPic - Same as above. Represents a quest that has been accepted.
  #
  # CompletedQuestPic - Same as above. Represents a quest that is complete.
  #
  # RepeatQuestPic - Same as above. Represents a repeatable quest.
  #
  #             Remember: Graphics must be of size 48 x 48.
  #=============================================================================
  NewQuestPic = "quest_new"
  AcceptedQuestPic = "quest_accepted"
  CompletedQuestPic = "quest_complete"
  RepeatQuestPic = "quest_repeat"
 
  def self.name(id)
    case id
    #===========================================================================
    # Quest Name - The name of the quest.
    # Configure:
    #     when x then return "name"
    # x = Quest ID
    # name = Quest name. Use "quotes".
    #===========================================================================
    when 1 then return "Red Chest of Wonders"
    when 2 then return "Spectral Hunter"
    when 3 then return "Forever New"
    when 4 then return "Dead Weight"
    when 5 then return "Stoney Blight"
    end
    return ""
  end
 
  def self.new_description(id)
    case id
    #===========================================================================
    # New Quest Description - Paragraph of the quest's description. This is for
    #   when the quest is new for the player to accept. If the description
    #   is really lengthy, the program will adjust the text into paragraph format.
    #
    #   You may use '\n' to create a new line (e.g. "Hello World!\nYou Rock!").
    #   You may also use '\v[id]' to show a variable in the description. Thus,
    #   doing something like "Found \v[3] out of \v[4]" will replace the
    #   instances with the game variables 3 and 4.
    #
    #   Any quests that are not configured will result in using the message
    #   located between the [] brackets. This text will be centered in the window.
    #   Do note you cannot use \n or \v to format this text.
    #
    # Configure:
    #     when x then return "description"
    # x = Quest ID
    # description = Quest's description. Use "quotes". Can be quite lengthy.
    #===========================================================================
    when 1 then return "I just magically spawned on this weird island and this guy is waving at me. There's some red chest sitting next to him. I guess I should say hello..."
    when 2 then return "Justin has been shifting his eyes frantically. There must be something troubling him."
    when 4 then return "This strong-looking man is grinning a bit too much. Actually...he looks weird. I hope he doesn't--\n\nCrap, he's looking at me."
    when 5 then return "*huff* Why...was I so foolish enough...to carry that many rocks?\nWHAT WAS THE POINT?"
    end
    return ["?   ?   ?"]
  end
 
  def self.accepted_description(id)
    case id
    #===========================================================================
    # Accepted Quest Description - Paragraph of the quest's description. If the
    #   description is really lengthy, the program will adjust the text into
    #   paragraph format.
    #
    #   You may use '\n' to create a new line (e.g. "Hello World!\nYou Rock!").
    #   You may also use '\v[id]' to show a variable in the description. Thus,
    #   doing something like "Found \v[3] out of \v[4]" will replace the
    #   instances with the game variables 3 and 4.
    #
    # Configure:
    #     when x then return "description"
    # x = Quest ID
    # description = Quest's description. Use "quotes". Can be quite lengthy.
    #===========================================================================
    when 1 then return "A guy named Justin wanted us to find a key to open up the red chest he recently found. He isn't sure where this key could be, but I have a strange hunch that it can't be too hard to find. \n \n Let's try checking an obvious place."
    when 2 then return "Justin is quite scared of all the ghosts around the area. To make him feel better, we should probably kill a few for him. I think 5 should be good enough. \n \n Killed: \v[1]/5"
    when 3 then return "Um, there's no way you can be viewing this description right now. You modded the demo!"
    when 4 then return "Strong guy here wants us to find a rock for him.\nTo smash.\nI...\n...why?!"
    when 5 then return "\v[3]"
    end
    return ""
  end
 
  def self.completed_description(id)
    case id
    #===========================================================================
    # New Quest Description - Paragraph of the quest's description. This is for
    #   when the quest has been completed by the player. If the description
    #   is really lengthy, the program will adjust the text into paragraph format.
    #
    #   You may use '\n' to create a new line (e.g. "Hello World!\nYou Rock!").
    #   You may also use '\v[id]' to show a variable in the description. Thus,
    #   doing something like "Found \v[3] out of \v[4]" will replace the
    #   instances with the game variables 3 and 4.
    #
    #   Any quests that are not configured will result in using the accepted
    #   quest description (the ones you wrote above).
    #
    # Configure:
    #     when x then return "description"
    # x = Quest ID
    # description = Quest's description. Use "quotes". Can be quite lengthy.
    #===========================================================================
    when 2 then return "I killed 5 ghosts for Justin. He looks much happier now that there's nothing to scare him anymore. \n \n But now he's just standing there, being a 'useless' NPC. \n What's an NPC anyways?"
    when 4 then return "Okay, I was actually stupid enough to bring him back a rock to smash. I don't see the point in all of this.\n\nRocks Smashed: \v[2]"
    when 5 then return "Well, that settles that. Glad I got something out of all of this.\n\nBut that's the last time I ever help a pompous braggart."
    end
    return nil
  end
 
  def self.location(id)
    case id
    #===========================================================================
    # Quest Location - Place where the player received the quest.
    # Configure:
    #     when x then return "location"
    # x = Quest ID
    # location = Map's name. Use "quotes".
    #===========================================================================
    when 1..5 then return "Mystery Isle"
    when 6 then return "Traudsbogen"
    end
    return "?????"
  end
 
  def self.NPC(id)
    case id
    #===========================================================================
    # Quest NPC - The NPC that the player got the quest from. This can be
    #   displayed in both "string" and/or "picture" format. If using the picture
    #   format (NPCPicture = true), you must name the NPC the same as the
    #   image stored in the 'Graphics/Characters' folder. Otherwise, an error
    #   will occur while running the game.
    # Configure:
    #     when x then return "NPC"
    # x = Quest ID
    # NPC = Name of the NPC as well as the name of the NPC graphic
    #===========================================================================
    when 1..2 then return "Justin"    # Even though these character graphics are
    when 3 then return "Some Woman"   # part of the RTP, you still need to copy
    when 4..5 then return "Stonemason"# and rename the graphics and put them in
    end                               # your Graphics/Characters folder
    return nil
  end
 
  def self.exp(id)
    case id
    #===========================================================================
    # Quest Experience - The amount of EXP given for completing the quest.
    # Configure:
    #     when x then return EXP
    # x = Quest ID
    # EXP = Amount of experience (Player EXP)
    #===========================================================================
    when 1 then return 5
    when 2 then return 50
    when 3 then return 69
    end
    return 0
  end
 
  def self.gold(id)
    case id
    #===========================================================================
    # Quest Gold - The amount of gold given (or lost) for completing the quest.
    # Configure:
    #     when x then return Gold
    # x = Quest ID
    # Gold = Amount of gold given/taken. Use a '-' for lost gold (e.g. -500)
    #===========================================================================
    when 1 then return 20
    when 2 then return 300
    when 3 then return 9001
    end
    return 0
  end
 
  def self.reward(id)
    case id
    #===========================================================================
    # Quest Reward - The items given for completing the quest. The amount of
    #   different items that can be earned should not be more than 5.
    #
    #   You can also put in string values to represent a unique kind of reward.
    #   If RewardOnComplete is true, the game will ignore rewarding string-
    #   based values.
    #
    # Configure:
    #     when x then return [[item_type, item_id, amount], ...] (max size of 5)
    # x = Quest ID
    # item_type = Type of item. Refer below:
    #             1 = Normal Items (potions, key items, medicine, etc.)
    #             2 = Weapon
    #             3 = Armor
    # item_id = Item's ID located in the database
    # amount = Number of items given for completion
    #
    #   For inputing string values, you can copy one of these examples:
    #     when x then return ["String"]
    #     when x then return [[1,2,3], "Your Message"]
    #     when x then return ["You got", [1,1,1], "Nice potion!"]
    #===========================================================================
    when 1 then return [[1,2,5], "New Quest: Spectral Hunter"] #5 High Potions
    when 2 then return [[2,3,1]] #1 Steel Sword
    when 3 then return ["This is", "how you", "skip lines", "", "See?"]
    when 5 then return [[3,25,1]] #1 Ring of Strength
    end
    return nil
  end
 
  def self.rank(value)
    return ''
    case value
    #===========================================================================
    # Quest Rank - The player's rank in terms of completed quests.
    #     Of course, if you do not wish to use this, simply delete the 'when's
    #     and the 'end' directly after, leaving only the 'return' left. Then,
    #     put an empty string after the return like this -->  return ""
    # Configure:
    #     when x then return "rank"
    # x = The number of completed quests. Use (x..y) for all values in between
    #     x and y (for example, (3..6) applies for IDs 3, 4, 5, and 6
    # rank = The rank of the party. Use "quotes".
    #===========================================================================
    when 1 then return "Neophyte"
    when 2..5 then return "Amateur"
    end
    return "Beginner"
  end # <-- Don't delete this 'end' !
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#@             E N D          O F         C O N F I G U R A T I O N            @
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
end

#===============================================================================
module Quest
  #Adds the quest under "New", meaning the player has yet to accept it
  def self.new(id)
    $game_party.add_newquest(id)
  end
  #Adds the quest under "Accepted", meaning the player is doing the quest
  def self.accept(id)
    $game_party.accept_quest(id)
  end
  #Adds the quest under "Completed", meaning the quest is finished. Gives reward.
  def self.complete(id)
    $game_party.complete(id)
  end
  #Adds the quest under "New" and "Completed", meaning the quest can be redone
  def self.repeat(id)
    $game_party.repeat(id)
  end
  #Checks if the quest is repeatable
  def self.repeat?(id)
    return $game_party.repeating?(id)
  end
  #Checks if the quest is completed
  def self.complete?(id, only=false)
    return $game_party.completed?(id, only)
  end
  #Checks if the quest is accepted
  def self.accept?(id, only=false)
    return $game_party.has_quest?(id, only)
  end
  #Checks if the quest is unaccepted
  def self.new?(id, only=false)
    return $game_party.has_available?(id, only)
  end
  #Returns quests completed
  def self.done
    return ($game_party.quests_completed.dup | $game_party.quests_repeating).size
  end
 
end
#===============================================================================
# Game_Party Class
#   : Adds the quest data
#===============================================================================
class Game_Party
 
  attr_accessor :quests_new
  attr_accessor :quests_accepted
  attr_accessor :quests_completed
  attr_accessor :quests_repeating
 
  alias kk20_initialize_again initialize
  def initialize
    kk20_initialize_again
    @quests_new = []
    @quests_accepted = []
    @quests_completed = []
    @quests_repeating = []
  end
 
  def add_newquest(id)
    unless has_available?(id)
      @quests_new.push(id)
    end
  end
 
  def accept_quest(id)
    if !@quests_accepted.include?(id) and @quests_new.include?(id)
      @quests_new.delete(id)
      @quests_accepted.push(id)
    end
  end
 
  def complete(id)
    if !completed?(id, true) and @quests_accepted.include?(id)
      @quests_accepted.delete(id)
      @quests_repeating.delete(id)
      @quests_completed.push(id)
      if QuestData::RewardOnComplete
        $game_party.gain_gold(QuestData.gold(id))
        $game_party.actors.each{|actor|
          unless actor.cant_get_exp?
            actor.exp += QuestData.exp(id)
          end
        }
        rewards = QuestData.reward(id)
        return if rewards.nil?
        #~~begin loop~~
        rewards.each{|reward|
          next if reward.is_a?(String)
          case reward[0]
          when 1 then $game_party.gain_item(reward[1], reward[2])
          when 2 then $game_party.gain_weapon(reward[1], reward[2])
          when 3 then $game_party.gain_armor(reward[1], reward[2])
          end
        }
        #~~end loop~~
      end
    end
  end
 
  def repeat(id)
    if completed?(id, true)
      @quests_completed.delete(id)
      @quests_repeating.push(id)
      @quests_new.push(id)
    end
  end
 
  def repeating?(id)
    return @quests_repeating.include?(id)
  end
 
  def completed?(id, only=false)
    if only
      return @quests_completed.include?(id)
    else
      return (@quests_completed.include?(id) or @quests_repeating.include?(id))
    end
  end
 
  def has_quest?(id, only=false)
    if only
      return @quests_accepted.include?(id)
    else
      return (@quests_accepted.include?(id) or @quests_completed.include?(id) or
              @quests_repeating.include?(id))
    end
  end
 
  def has_available?(id, only=false)
    if only
      return @quests_new.include?(id)
    else
      return (@quests_new.include?(id) or @quests_accepted.include?(id) or
              @quests_completed.include?(id) or @quests_repeating.include?(id))
    end
  end
 
end
#===============================================================================
# Window_Base Class
#   : Modified method provided by Blizzard
#===============================================================================
class Window_Base < Window
 
  def draw_even_text(x, y, width, height, text, align = 0)
    # Replace all instances of \v[n] to the game variable's value
    text.gsub!(/\\[Vv]\[([0-9]+)\]/) { $game_variables[$1.to_i] }
    text.gsub!(/[\V\v]\[([0-9]+)\]/) { $game_variables[$1.to_i] }
    # Break up the text into lines
    if text["\n"] != nil
      lines = text.split("\n")
    else
      lines = text.split("\\n")
    end
    result = []
    # For each line generated from \n
    lines.each{|text_line|
      # Divide text into each individual word
      words = text_line.split(' ')
      current_text = words.shift
      # If there were less than two words in that line, just push the text
      if words.empty?
        result.push(current_text == nil ? "" : current_text)
        next
      end
      # Evaluate each word and determine when text overflows to a new line
      words.each_index {|i|
        if self.contents.text_size("#{current_text} #{words[i]}").width > width
          result.push(current_text)
          current_text = words[i]
        else
          current_text = "#{current_text} #{words[i]}"
        end
        result.push(current_text) if i >= words.size - 1
      }
    }
    # Draw results to the window
    result.each_index {|i|
        self.contents.draw_text(x, y + i*height, width, height, result[i], align)}
  end
     
end
#*******************************************************************************
#===============================================================================
# Window for displaying the party's rank and number of new/available/completed quests
#===============================================================================
class Window_QuestRank < Window_Base
 
  def initialize
    super (0, 0, 640, 64)
    self.contents = Bitmap.new(width-32, height-32)
    refresh
  end
 
  def refresh
    self.contents.clear
    quest_rank = QuestData.rank($game_party.quests_completed.size)
    self.contents.draw_text(0, 0, 640, 32, "#{quest_rank}")
   
    new = sprintf("%5s", $game_party.quests_new.size.to_s)
    accepted = sprintf("%5s", $game_party.quests_accepted.size.to_s)
    completed = sprintf("%5s", ($game_party.quests_completed.dup | $game_party.quests_repeating).size.to_s)
    self.contents.draw_text(0, 0, 608, 32, "New:#{new}    " +
      "Accepted:#{accepted}    Completed:#{completed}", 2)
  end
end
#===============================================================================
# Window for displaying the quest's ID and title
#===============================================================================
class Window_QuestTitle < Window_Base
 
  #Type determines what window to view (0 = All, 1 = New, 2 = Accepted, 3 = Complete)
  def initialize(type=0)
    super (0, 64, 640, 64)
    self.contents = Bitmap.new(width-32,height-32)
    @quest_num = 0
    @quest_title = ""
    @type = type
    refresh
  end
 
  #Updates the quest /value/'s ID and name
  alias kk20_update_quest update
  def update(value)
    kk20_update_quest
    return if @quest_num == value
    @quest_num = value
    @quest_title = QuestData.name(value)
    refresh
  end
 
  #Method is called when user shifts the quest tab
  def next_page
    @type = (@type + 1) % 4
    refresh
  end
 
  def refresh
    self.contents.clear
    quest_tab = ""
    case @type
    when 0 then quest_tab = "ALL"
    when 1 then quest_tab = "NEW"
    when 2 then quest_tab = "ACCEPTED"
    when 3 then quest_tab = "COMPLETED"
    end
    if @quest_num != 0 and @quest_num != nil
      quest_number = sprintf("%03d", @quest_num)
      self.contents.draw_text(0, 0, 640, 32, "No. #{quest_number}")
    else
      self.contents.draw_text(0, 0, 640, 32, "There are no quests here.")
    end
    self.contents.draw_text(80, 0, 640, 32, "#{@quest_title}")
    self.contents.draw_text(0, 0, 600, 32, "<#{quest_tab}>", 2)
  end
end
#===============================================================================
# Window for displaying all the quests as pictures
#===============================================================================
class Window_QuestList < Window_Selectable
 
  #Type determines what window to view (0 = All, 1 = New, 2 = Accepted, 3 = Complete)
  def initialize(type=0)
    super (0, 128, 640, 352)
    @column_max = 5
    @type = type
    refresh
    self.index = 0
  end
 
  #For information window
  def quest
    return @data[self.index]
  end
 
  #When the player shifts to next page. Also places cursor to new index if nil.
  def next_page
    @type = (@type + 1) % 4
    refresh
    if (@data[self.index].nil?)
      self.index = [@data.size-1, 0].max
    end
  end
 
  def refresh
    if self.contents != nil
      self.contents.dispose
      self.contents = nil
    end   
    @data = []
    if [0,1].include?(@type)
      for i in 0...$game_party.quests_new.size
        @data.push($game_party.quests_new[i])
      end
    end
    if [0,2].include?(@type)
      for i in 0...$game_party.quests_accepted.size
        @data.push($game_party.quests_accepted[i])
      end
    end
    if [0,3].include?(@type)
      total = $game_party.quests_completed.dup | $game_party.quests_repeating
      for i in 0...total.size
        @data.push(total[i]) unless @data.include?(total[i])
      end
    end
    @data.sort! if @data.size > 1
    @item_max = @data.size
    if @item_max > 0
      self.contents = Bitmap.new(width - 64, row_max * 64)
      for i in 0...@item_max
        draw_item(i)
      end
    end
  end

  def draw_item(index)
    quest_number = @data[index]
    if $game_party.quests_repeating.include?(quest_number)
      bitmap = RPG::Cache.picture(QuestData::RepeatQuestPic)
    elsif $game_party.quests_new.include?(quest_number)
      bitmap = RPG::Cache.picture(QuestData::NewQuestPic)
    elsif $game_party.quests_accepted.include?(quest_number)
      bitmap = RPG::Cache.picture(QuestData::AcceptedQuestPic)
    elsif $game_party.quests_completed.include?(quest_number)
      bitmap = RPG::Cache.picture(QuestData::CompletedQuestPic)
    end
    x = 8 + index % @column_max * (self.width / @column_max)
    y = 8 + index / @column_max * 64
    self.contents.blt(x, y, bitmap, Rect.new(0, 0, 48, 48), 255)
  end
          #####################################################
          # Modified methods to allow 64x64 selection windows #
          #####################################################
  def page_row_max
    return (self.height) / 64
  end
 
  def top_row
    return self.oy / 64
  end

  def top_row=(row)
    if row < 0
      row = 0
    end
    if row > row_max - 1
      row = row_max - 1
    end
    self.oy = row * 64
  end
 
  def update_cursor_rect
    if @index < 0
      self.cursor_rect.empty
      return
    end
    row = @index / @column_max
    if row < self.top_row
      self.top_row = row
    end
    if row > self.top_row + (self.page_row_max - 1)
      self.top_row = row - (self.page_row_max - 1)
    end
    # Calculate cursor coordinates
    x = @index % @column_max * (640 / @column_max)
    y = @index / @column_max * 64 - self.oy
    # Update cursor rectangle
    self.cursor_rect.set(x, y, 64, 64)
  end
end
#===============================================================================
# Window to display quest's information
#===============================================================================
class Window_QuestInfo < Window_Base
 
  def initialize(quest_id)
    super (0, 0, 640, 480)
    self.z = 1000
    self.contents = Bitmap.new(width-32,height-32)
    @quest = quest_id
    refresh
  end
 
  def update_quest(quest_id)
    if @quest != quest_id
      @quest = quest_id
      refresh
    end
  end
 
  def refresh
    # Titles of important text------------------------------
    self.contents.clear
    quest_number = sprintf("%03d", @quest)
    self.contents.font.color = system_color
    self.contents.draw_text(0, 0, 640, 32, "No. #{quest_number}")
    self.contents.draw_text(80, 0, 640, 32, QuestData.name(@quest))
    # If quest is completed OR quest is repeatable but not currently accepted
    if Quest.repeat?(@quest) && Quest.accept?(@quest, true)
      self.contents.font.color = Color.new(0, 230, 255)
      self.contents.draw_text(0, 0, 600, 32, "-REPEATING-", 2)
      self.contents.font.color = system_color
    elsif Quest.complete?(@quest, true) || (Quest.repeat?(@quest) && !Quest.accept?(@quest, true))
      self.contents.font.color = Color.new(233, 188, 10)
      self.contents.draw_text(0, 0, 600, 32, "-COMPLETED-", 2)
      self.contents.font.color = system_color
    end
    self.contents.draw_text(0, 200, 280, 32, "Requester:")
    self.contents.draw_text(0, 360, 280, 32, "Location:")
    self.contents.draw_text(320, 200, 280, 32, "Reward(s):")
    # Description------------------------------
    self.contents.font.color = normal_color
    self.contents.fill_rect(0, 40, 640, 5, Color.new(24,184,231,128))
    self.contents.font.size = 18
    if Quest.complete?(@quest, true) || (Quest.repeat?(@quest) && !Quest.accept?(@quest, true))
      unless QuestData.completed_description(@quest).nil?
        draw_even_text(0, 58, 640-32, 18, QuestData.completed_description(@quest))
      else
        draw_even_text(0, 58, 640-32, 18, QuestData.accepted_description(@quest))
      end
    elsif Quest.accept?(@quest)
      draw_even_text(0, 58, 640-32, 18, QuestData.accepted_description(@quest))
    else
      unless QuestData.new_description(@quest).is_a?(Array)
        draw_even_text(0, 58, 640-32, 18, QuestData.new_description(@quest))
      else
        self.contents.draw_text(0, 103, 640-32, 18, QuestData.new_description(@quest)[0], 1)
      end
    end
    self.contents.fill_rect(0, 180, 640, 5, Color.new(24,184,231,128))
    self.contents.fill_rect(300, 180, 5, 320, Color.new(24,184,231,128))
    # NPC and Location------------------------------
    self.contents.font.size = Font.default_size
    if QuestData::NPCPicture and QuestData.NPC(@quest) != nil
      bitmap = RPG::Cache.character(QuestData.NPC(@quest), 0)
      h = bitmap.height/4
      w = bitmap.width/4
      self.contents.blt(0, 240, bitmap, Rect.new(0, 0, w, h))
      self.contents.draw_text(w+20, h+240, 280, 32, QuestData.NPC(@quest))
    else
      if QuestData.NPC(@quest) != nil
        self.contents.draw_text(20, 240, 280, 32, QuestData.NPC(@quest))
      end
    end
    self.contents.draw_text(20, 400, 280, 32, QuestData.location(@quest))
    # Rewards------------------------------
    if !((QuestData::HideReward == true or QuestData::HideReward.include?(@quest)) and !Quest.complete?(@quest))
      y = 225
      exp_string = ""
      if QuestData.exp(@quest) != 0
        if QuestData::EXPicon != nil and QuestData::EXPicon != ""
          bitmap = RPG::Cache.icon(QuestData::EXPicon)
          self.contents.blt(340, y, bitmap, Rect.new(0, 0, 32, 32))
        else
          exp_string = "EXP"
        end
        self.contents.draw_text(370, y, 280, 32, QuestData.exp(@quest).to_s + " " + exp_string)
        y += 24
      end
      gold_string = ""
      if QuestData.gold(@quest) != 0
        if QuestData::GoldIcon != nil and QuestData::GoldIcon != ""
          bitmap = RPG::Cache.icon(QuestData::GoldIcon)
          self.contents.blt(340, y, bitmap, Rect.new(0, 0, 32, 32))
        else
          gold_string = $data_system.words.gold
        end
        self.contents.draw_text(370, y, 280, 32, QuestData.gold(@quest).to_s + " " + gold_string)
        y += 32
      end
      if QuestData.reward(@quest) != nil
        #~~being loop~~
        QuestData.reward(@quest).each_index{|i|
        item = QuestData.reward(@quest)[i]
        # If string value
        if item.is_a?(String)
          self.contents.draw_text(370, y, 280, 32, item)
          y += 32
          next
        end
        # An actual item
        case item[0]
        when 1
          bitmap = RPG::Cache.icon($data_items[item[1]].icon_name)
          self.contents.draw_text(370, y, 280, 32, $data_items[item[1]].name + " x " + item[2].to_s)
        when 2
          bitmap = RPG::Cache.icon($data_weapons[item[1]].icon_name)
          self.contents.draw_text(370, y, 280, 32, $data_weapons[item[1]].name + " x " + item[2].to_s)
        when 3
          bitmap = RPG::Cache.icon($data_armors[item[1]].icon_name)
          self.contents.draw_text(370, y, 280, 32, $data_armors[item[1]].name + " x " + item[2].to_s)
        end
        self.contents.blt(340, y, bitmap, Rect.new(0, 0, 32, 32))
        y += 32
        }
        #~~end loop~~
      end
      if QuestData.exp(@quest) == 0 and QuestData.gold(@quest) == 0 and QuestData.reward(@quest) == nil
        self.contents.draw_text(370, 225, 280, 32, "None")
      end
    else #If Quest Reward is hidden
      self.contents.draw_text(370, 225, 280, 32, "Unknown")
    end
  end
 
end
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
# The actual quest scene
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
class Scene_Quest

  def main
    #Creates the windows
    @quests_rank = Window_QuestRank.new
    @quests_title = Window_QuestTitle.new(0)
    @quests_list = Window_QuestList.new(0)
    @quest_info = Window_QuestInfo.new(@quests_list.quest)
    @quest_info.visible = false
    @quests_title.update(@quests_list.quest)
    if QuestData::ShowMapBackGround
      @map = Spriteset_Map.new
      [@quests_rank,@quests_title,@quests_list,@quest_info].each{|win| win.back_opacity = 128}
    end
    Graphics.transition
    loop do
      Graphics.update
      Input.update
      update
      if $scene != self
        break
      end
    end
    Graphics.freeze
    @map.dispose unless @map.nil?
    @quests_rank.dispose
    @quests_title.dispose
    @quests_list.dispose
  end
  #------------------------------------------------------------
  def update
    @map.update unless @map.nil?
    @quests_title.update(@quests_list.quest)
    @quests_list.update
    @quest_info.update_quest(@quests_list.quest) unless !@quest_info.visible
    update_controls
  end
  #------------------------------------------------------------
  def update_controls
    if Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      # If quest information is not displayed, exit scene
      if !@quest_info.visible
        $scene = QuestData::ExitScene
      else #If quest info is displayed, return to quest list
        @quest_info.visible = false
        [@quests_title,@quests_list,@quests_rank].each{|win| win.visible = true}
      end
    elsif Input.trigger?(Input::C)
      #Works only if quest info isn't displayed
      if !@quest_info.visible and !@quests_list.quest.nil?
        $game_system.se_play($data_system.decision_se)
        @quest_info.update_quest(@quests_list.quest)
        @quest_info.visible = true
        [@quests_title,@quests_list,@quests_rank].each{|win| win.visible = false}
      end
    elsif Input.trigger?(Input::SHIFT) and !@quest_info.visible
      $game_system.se_play(QuestData::PageTurnSound)
      @quests_title.next_page
      @quests_list.next_page
    end
  end
 
end

If you want save files to work, add this to the end of this script:
Spoiler: ShowHide

# NOTE: this assumes default RMXP Scene_File/Save/Load classes are used.
# Modified/custom versions of these classes may not work with script below.
class Scene_Load < Scene_File
  alias load_kk20_quest_variables read_save_data
  def read_save_data(file)
    load_kk20_quest_variables(file)
    $game_party.quests_new ||= []
    $game_party.quests_accepted ||= []
    $game_party.quests_completed ||= []
    $game_party.quests_repeating ||= []
  end
end

For RMX-OS, add this bit of code at the end of this script:
Spoiler: ShowHide

if !defined?(RMXOS) || RMXOS::VERSION < 1.09
  raise 'ERROR: The "Organized Quest System" requires RMX-OS 1.09 or higher.'
end
module RMXOS
  module Options
    SAVE_DATA[Game_Party].push('@quests_new')
    SAVE_DATA[Game_Party].push('@quests_accepted')
    SAVE_DATA[Game_Party].push('@quests_completed')
    SAVE_DATA[Game_Party].push('@quests_repeating')
  end
end



Instructions

Should all be there in the script, right near the beginning of it.


Compatibility

Aliased Game_Party methods, but I think there shouldn't be any incompatibility problems. Obviously won't work with another quest system.


Credits and Thanks


  • KK20 - For making the script

  • game_guy - For using his 'Quest Log' script as a template

  • Blizzard - For the slice_text method




Author's Notes

This was my first-ever script, back before I even joined Chaos Project and back when I was still learning the basics of RGSS. This script is a bit messy as a result. I am willing to make improvements if necessary.

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

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

Join the CP Discord Server!

Metaron

This looks really good, i'll be sure to give it a try when I get back from holidays.

cheos

*watches video* *eyes widens* omg i have been looking 4 something like this thank you so much your amazing
[Insert Mind Blowing Signature]

exile360

Really nice work! This is pretty much exactly what I've been looking for, most of the quest systems out there are too simplistic and don't offer the kind of design I'm after. Any chance you could please make this work with RMX-OS? I suppose it needs an extension or something as the quests don't save right now. :(

KK20

January 24, 2013, 10:43:01 pm #4 Last Edit: January 24, 2013, 10:44:15 pm by KK20
Paste the quest script below RMX-OS and, at the very end of this script, paste this in:

if !defined?(RMXOS) || RMXOS::VERSION < 1.09
 raise 'ERROR: The "Organized Quest System" requires RMX-OS 1.09 or higher.'
end
module RMXOS
 module Options
   SAVE_DATA[Game_Party].push('@quests_new')
   SAVE_DATA[Game_Party].push('@quests_accepted')
   SAVE_DATA[Game_Party].push('@quests_completed')
   SAVE_DATA[Game_System].push('@stored_desc')
 end
end

I haven't worked with RMX-OS extensively so I'm not sure if this is all I needed to do.

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

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

Join the CP Discord Server!

exile360

Thanks a bunch for the fast reply!

It seems to work fine for new quests, no errors and they save upon exiting the game. However, when a quest is accepted or completed, once I exit and try to log back in, I get this error:
Spoiler: ShowHide

Which refers to this line:           
data[i] = eval($network.load_data[key])

After it has error'ed once and I go in again, it runs again, but the quest list is empty.

Blizzard

This script would require additional configuration in RMX-OS for the additional save data used by this script.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

KK20

January 25, 2013, 03:46:26 pm #7 Last Edit: January 25, 2013, 04:12:38 pm by KK20
It's because of the 'stored_desc' variable that I made, huh? I forgot that it is a 2D Array. Still, I don't understand why that was the fault. Oh well, the only reason I had that was to reduce drawing times/remove lag.

I made an edit and it was saving for me.
Spoiler: ShowHide

=begin
================================================================================
Organized Quest System
Author: KK20
Version 1.0
--------------------------------------------------------------------------------

 This script is a full-fledged quest system that uses pictures to represent the
 different types of quests in the User Interface.
 
 Includes:
   ~ Different page tabs to organize which quests to display (the four tabs
     used are 'All', 'New' (not accepted), 'Accepted', and 'Completed'), which
     can be accessed by pressing the 'Shift' key.
   ~ Player rank determined by number of quests completed
   ~ A window displaying the quest's information when selected
   ~ Some customizable features

********************************************************************************
*                         I N S T R U C T I O N S                              *
********************************************************************************
-----------------
-Getting Started-
-----------------
 To call the quest window, use the following in a script call:
> $scene = Scene_Quest.new
 
 To add quests that are available and can be accepted, use the script call:
> Quest.new(quest_id)
 
 To change the quest to being accepted, use the script call:
> Quest.accept(quest_id)
 
 To complete the quest, use the script call:
> Quest.complete(quest_id)
 (Doing this will automatically give the rewards to the player--This includes
 the items, EXP, and Gold. No need to event that in!
 If you still want to use events to reward, this can be configured below.)
 
 ######[ NOTE ]################################################################
 A quest cannot be accepted if the quest is not currently available. A quest
 cannot be completed unless the quest is currently being accepted.
 Example: Quest.new(1)
          Quest.complete(1)
 Result: Quest 1 is still considered "new". The player has completed nothing.
 ##############################################################################
 
 For conditional branch needs:

 To check if the quest is completed, use the script call:
> Quest.complete?(quest_id)
 Returns 'true' if the quest is completed, 'false' otherwise.

 To check if the quest is currently being accepted, use the script call:
> Quest.accept?(quest_id)
 Returns 'true' if the quest is accepted or completed, 'false' otherwise.
 
 To check if the quest can be accepted, use the script call:
> Quest.new?(quest_id)
 Returns 'true' if the quest is new, accepted, or completed, 'false' otherwise.
 
 To check how many quests have been completed, use the script call:
> Quest.done
 This will return an integer. This script call is exactly the same thing as
 '$game_party.quests_completed.size'. If you want to, for example, give a
 reward for completing 7 quests, use a 'Conditional Branch', select 'Script' and
 type in 'Quest.done >= 7' (without the quotes).

----------  
-Controls-
----------
 Arrow keys to move the cursor around
 'C'/'Enter' to view the quest's information
 'X'/'Esc' to close the Scene_Quest
 'Shift' to change tabs (All -> New -> Accepted -> Completed -> All)
 
 When the quest's information window is being displayed:
 You can still use the Arrow keys to view different quests
 'C'/'Enter' and 'Shift' have no use.
 'X'/'Esc' to remove the quest's information window

----------
-Graphics-
----------
 This script uses images stored in 'Graphics/Pictures' to represent the quests.
 The script was written in support for 48 x 48 graphics. Your images must be
 of this size (no less, no bigger). To configure what graphics you wish to use,
 look below for the configuration.
 
---------------
-Configuration-
---------------
 Located below. All the instructions are there.

================================================================================
Credits:
KK20 -> Making this script
game_guy -> Used his 'Quest Log System' as a basis for this script
Blizzard -> Method to turn a long string into a paragraph
================================================================================
=end
module QuestData
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#@             S T A R T          O F         C O N F I G U R A T I O N        @
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #=============================================================================
 # NPCPicture - Set to 'true' if you want the NPC graphic to be displayed in
 #              the quest's information window. Set to 'false' if you don't
 #              want to.
 #
 # HideReward - Hides the rewards in the quest description until the player has
 #              completed that quest. Set the quest IDs in the array to use this
 #              feature. Delete the array and put 'true' to apply this to all
 #              quests. Create an empty array if you do not wish to use this
 #              feature. An empty array looks like this-->  []
 #
 # RewardOnComplete - Set to 'true' if you want the player to be rewarded at the
 #                    instance of 'Quest.complete(quest_id)'. 'False' otherwise.
 #
 # ExitScene - Set to the scene you wish to view upon exiting quest scene.
 #=============================================================================
 NPCPicture        = false
 HideReward        = [1]
 RewardOnComplete  = true
 ExitScene         = Scene_Menu.new(0)
 #=============================================================================
 # EXPicon - The graphic used when the quest's information window is brought up.
 #           It represents EXP gained from the quest. Name it the same as it is
 #           found in 'Graphics/Icons'.
 #
 # GoldIcon - Same as above. Represents the gold gained from the quest.
 #
 # ** If you do not want graphics, simply use an empty string ---> ""
 #    These are ICONS, so the graphics must be 24 x 24.
 #=============================================================================
 EXPicon   = ""
 GoldIcon  = ""
 #=============================================================================
 # NewQuestPic - The picture you want to represent a quest that hasn't been
 #               accepted. Name it the same as it is in 'Graphics/Pictures'.
 #
 # AcceptedQuestPic - Same as above. Represents a quest that has been accepted.
 #
 # CompletedQuestPic - Same as above. Represents a quest that is complete.
 #
 #             Remember: Graphics must be of size 48 x 48.
 #=============================================================================
 NewQuestPic = "quest_new"
 AcceptedQuestPic = "quest_accepted"
 CompletedQuestPic = "quest_complete"
 
 def self.name(id)
   case id
   #===========================================================================
   # Quest Name - The name of the quest.
   # Configure:
   #     when x then return "name"
   # x = Quest ID
   # name = Quest name. Use "quotes".
   #===========================================================================
   when 1 then return "Red Chest of Wonders"
   when 2 then return "Spectral Hunter"
   when 3 then return "Forever New"
   end
   return ""
 end
 
 def self.description(id)
   case id
   #===========================================================================
   # Quest Description - Paragraph of the quest's description. If the description
   #   is really lengthy, the program will adjust the text into paragraph format.
   #   You may use '/n' to create a new line -- just make sure to space it away
   #   from surrounding words. (e.g. "Hello World! /n You Rock!")
   # Configure:
   #     when x then return "description"
   # x = Quest ID
   # description = Quest's description. Use "quotes". Can be quite lengthy.
   #===========================================================================
   when 1 then return "A guy named Justin wanted us to find a key to open up the red chest he recently found. He isn't sure where this key could be, but I have a strange hunch that it can't be too hard to find. /n /n Let's try checking an obvious place."
   when 2 then return "Justin is quite scared of all the ghosts around the area. To make him feel better, we should probably kill a few for him. I think 5 should be good enough."
   when 3 then return "Haha no description."
   end
   return ""
 end
 
 def self.location(id)
   case id
   #===========================================================================
   # Quest Location - Place where the player received the quest.
   # Configure:
   #     when x then return "location"
   # x = Quest ID
   # location = Map's name. Use "quotes".
   #===========================================================================
   when 1 then return "Mystery Isle"
   when 2 then return "Mystery Isle"
   when 3 then return "Mystery Isle"
   end
   return "????"
 end
 
 def self.NPC(id)
   case id
   #===========================================================================
   # Quest NPC - The NPC that the player got the quest from. This can be
   #   displayed in both "string" and/or "picture" format. If using the picture
   #   format (NPCPicture = true), you must name the NPC the same as the
   #   image stored in the 'Graphics/Characters' folder. Otherwise, an error
   #   will occur while running the game.
   # Configure:
   #     when x then return "NPC"
   # x = Quest ID
   # NPC = Name of the NPC as well as the name of the NPC graphic
   #===========================================================================
   when 1 .. 2 then return "Justin" # Both of these pictures are located in
   when 3 then return "Haha"  # Graphics/Characters, even if they are RTP
   end
   return nil
 end
 
 def self.exp(id)
   case id
   #===========================================================================
   # Quest Experience - The amount of EXP given for completing the quest.
   # Configure:
   #     when x then return EXP
   # x = Quest ID
   # EXP = Amount of experience (Player EXP)
   #===========================================================================
   when 1 then return 5
   when 2 then return 50
   when 3 then return 69
   end
   return 0
 end
 
 def self.gold(id)
   case id
   #===========================================================================
   # Quest Gold - The amount of gold given (or lost) for completing the quest.
   # Configure:
   #     when x then return Gold
   # x = Quest ID
   # Gold = Amount of gold given/taken. Use a '-' for lost gold (e.g. -500)
   #===========================================================================
   when 1 then return 20
   when 2 then return 300
   when 3 then return 9001
   end
   return 0
 end
 
 def self.reward(id)
   case id
   #===========================================================================
   # Quest Reward - The items given for completing the quest. The amount of
   #   different items that can be earned should not be more than 5.
   # Configure:
   #     when x then return [[item_type, item_id, amount], ...] (max size of 5)
   # x = Quest ID
   # item_type = Type of item. Refer below:
   #             1 = Normal Items (potions, key items, medicine, etc.)
   #             2 = Weapon
   #             3 = Armor
   # item_id = Item's ID located in the database
   # amount = Number of items given for completion
   #===========================================================================
   when 1 then return [[1,2,5]] #5 High Potions
   when 2 then return [[2,3,1]] #1 Steel Sword
   end
   return nil
 end
 
 def self.rank(value)
   case value
   #===========================================================================
   # Quest Rank - The player's rank in terms of completed quests
   # Configure:
   #     when x then return "rank"
   # x = The number of completed quests. Use (x..y) for all values in between x and y
   # rank = The rank of the party. Use "quotes".
   #===========================================================================
   when 1 then return "Neophyte"
   when 2 .. 5 then return "Amateur"
   end
   return "Beginner"
 end
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#@             E N D          O F         C O N F I G U R A T I O N            @
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
end

#===============================================================================
module Quest
 #Adds the quest under "New", meaning the player has yet to accept it
 def self.new(id)
   $game_party.add_newquest(id)
 end
 #Adds the quest under "Accepted", meaning the player is doing the quest
 def self.accept(id)
   $game_party.accept_quest(id)
 end
 #Adds the quest under "Completed", meaning the quest is finished. Gives reward.
 def self.complete(id)
   $game_party.complete(id)
 end
 #Checks if the quest is completed
 def self.complete?(id)
   return $game_party.completed?(id)
 end
 #Checks if the quest is currently accepted
 def self.accept?(id)
   return $game_party.has_quest?(id)
 end
 #Checks if the quest is currently unaccepted
 def self.new?(id)
   return $game_party.has_available?(id)
 end
 #Returns quests completed
 def self.done
   return $game_party.quests_completed.size
 end
 
end
#===============================================================================
# Game_Party Class
#   : Adds the quest data
#===============================================================================
class Game_Party
 
 attr_accessor :quests_new
 attr_accessor :quests_accepted
 attr_accessor :quests_completed
 
 alias kk20_initialize_again initialize
 def initialize
   kk20_initialize_again
   @quests_new = []
   @quests_accepted = []
   @quests_completed = []
 end
 
 def add_newquest(id)
   unless @quests_new.include?(id)
     @quests_new.push(id)
   end
 end
 
 def accept_quest(id)
   unless @quests_accepted.include?(id)
     if @quests_new.include?(id)
       @quests_new.delete(id)
       @quests_accepted.push(id)
     end
   end
 end
 
 def complete(id)
   unless @quests_completed.include?(id)
     if @quests_accepted.include?(id)
       @quests_accepted.delete(id)
       @quests_completed.push(id)
       if QuestData::RewardOnComplete
         $game_party.gain_gold(QuestData.gold(id))
         for i in 0...$game_party.actors.size
           actor = $game_party.actors[i]
           if actor.cant_get_exp? == false
             actor.exp += QuestData.exp(id)
           end
         end
         rewards = QuestData.reward(id)
         return if rewards == nil
         #~~begin loop~~
         for i in 0..rewards.size-1
           reward = rewards[i]
           case reward[0]
           when 1 then $game_party.gain_item(reward[1], reward[2])
           when 2 then $game_party.gain_weapon(reward[1], reward[2])
           when 3 then $game_party.gain_armor(reward[1], reward[2])
           end
         end
     #~~end loop~~
       end
     end
   end
 end
 
 def completed?(id)
   return @quests_completed.include?(id)
 end
 
 def has_quest?(id)
   return (@quests_accepted.include?(id) or @quests_completed.include?(id))
 end
 
 def has_available?(id)
   return (@quests_new.include?(id) or @quests_accepted.include?(id) or @quests_completed.include?(id))
 end
 
end
#===============================================================================
# Game_System Class
#   : Stores quest descriptions into an array
#===============================================================================
=begin
class Game_System
 
 attr_accessor :stored_desc
 
 alias kk20_initialize_again initialize
 def initialize
   kk20_initialize_again
   @stored_desc = []
 end
 
end
=end
#===============================================================================
# Window_Base Class
#   : Modified method provided by Blizzard
#===============================================================================
class Window_Base < Window
 
 def draw_even_text(x, y, width, height, text, index, align = 0)
   if x == -200 and $game_system.stored_desc[index] != nil
     new_text = $game_system.stored_desc[index]
   else
     words = text.split(' ')
     if words.size == 1
       new_text = words
     else
       result, current_text = [], words.shift
       #loop
       words.each_index {|i|
       if self.contents.text_size("#{current_text} #{words[i]}").width > width-32 or
       words[i] == "/n"
       #Checks if '/n' is the current word.
         if words[i] == "/n"
           result.push(current_text)
           current_text = ""
         else
           result.push(current_text)
           current_text = words[i]
         end
       else
         # True if the previous word was '/n'
         if current_text == ""
           current_text = words[i]
         else
           current_text = "#{current_text} #{words[i]}"
         end
       end
       #Push the last word in
       result.push(current_text) if i >= words.size - 1}
       #end loop
       new_text = result
       ##$game_system.stored_desc[index] = new_text
     end
   end
   new_text.each_index {|i|
       self.contents.draw_text(x, y + i*height, width, height, new_text[i], align)}
 end
     
end
#*******************************************************************************
#===============================================================================
# Window for displaying the party's rank and number of new/available/completed quests
#===============================================================================
class Window_QuestRank < Window_Base
 
 def initialize
   super (0, 0, 640, 64)
   self.contents = Bitmap.new(width-32, height-32)
   refresh
 end
 
 def refresh
   self.contents.clear
   quest_rank = QuestData.rank($game_party.quests_completed.size)
   self.contents.draw_text(0, 0, 640, 32, "#{quest_rank}")
   
   new = sprintf("%5s", $game_party.quests_new.size.to_s)
   accepted = sprintf("%5s", $game_party.quests_accepted.size.to_s)
   completed = sprintf("%5s", $game_party.quests_completed.size.to_s)
   self.contents.draw_text(0, 0, 608, 32, "New:#{new}    " +
     "Accepted:#{accepted}    Completed:#{completed}", 2)
 end
end
#===============================================================================
# Window for displaying the quest's ID and title
#===============================================================================
class Window_QuestTitle < Window_Base
 
 #Type determines what window to view (0 = All, 1 = New, 2 = Accepted, 3 = Complete)
 def initialize(type=0)
   super (0, 64, 640, 64)
   self.contents = Bitmap.new(width-32,height-32)
   @quest_num = 0
   @quest_title = ""
   @type = type
   refresh
 end
 
 #Updates the quest /value/'s ID and name
 alias kk20_update_quest update
 def update(value)
   kk20_update_quest
   @quest_num = value
   @quest_title = QuestData.name(value)
   refresh
 end
 
 #Method is called when user shifts the quest tab
 def next_page
   @type = (@type + 1) % 4
   refresh
 end
 
 def refresh
   self.contents.clear
   quest_tab = ""
   case @type
   when 0 then quest_tab = "ALL"
   when 1 then quest_tab = "NEW"
   when 2 then quest_tab = "ACCEPTED"
   when 3 then quest_tab = "COMPLETED"
   end
   quest_number = sprintf("%03d", @quest_num)
   if @quest_num != 0 and @quest_num != nil
     self.contents.draw_text(0, 0, 640, 32, "No. #{quest_number}")
   else
     self.contents.draw_text(0, 0, 640, 32, "There are no quests here.")
   end
   self.contents.draw_text(80, 0, 640, 32, "#{@quest_title}")
   self.contents.draw_text(0, 0, 600, 32, "<#{quest_tab}>", 2)
 end
end
#===============================================================================
# Window for displaying all the quests as pictures
#===============================================================================
class Window_QuestList < Window_Selectable
 
 #Type determines what window to view (0 = All, 1 = New, 2 = Accepted, 3 = Complete)
 def initialize(type=0)
   super (0, 128, 640, 352)
   @column_max = 5
   @type = type
   refresh
   self.index = 0
 end
 
 #For information window
 def quest
   return @data[self.index]
 end
 
 #When the player shifts to next page. Also places cursor to new index if nil.
 def next_page
   @type = (@type + 1) % 4
   refresh
   if (@data[self.index] == nil)
     self.index = [@data.size-1, 0].max
   end
 end
 
 def refresh
   if self.contents != nil
     self.contents.dispose
     self.contents = nil
   end    
   @data = []
   case @type
   when 0 #all
     for i in 0...$game_party.quests_new.size
       @data.push($game_party.quests_new[i])
     end
     for i in 0...$game_party.quests_accepted.size
       @data.push($game_party.quests_accepted[i])
     end
     for i in 0...$game_party.quests_completed.size
       @data.push($game_party.quests_completed[i])
     end
   when 1 #New
     for i in 0...$game_party.quests_new.size
       @data.push($game_party.quests_new[i])
     end
   when 2 #accepted
     for i in 0...$game_party.quests_accepted.size
       @data.push($game_party.quests_accepted[i])
     end
   when 3 #Completed
     for i in 0...$game_party.quests_completed.size
       @data.push($game_party.quests_completed[i])
     end
   end
   @data.sort! if @data.size > 1
   @item_max = @data.size
   if @item_max > 0
     self.contents = Bitmap.new(width - 64, row_max * 64)
     for i in 0...@item_max
       draw_item(i)
     end
   end
 end

 def draw_item(index)
   quest_number = @data[index]
   if $game_party.quests_new.include?(quest_number)
     bitmap = RPG::Cache.picture(QuestData::NewQuestPic)
   end
   if $game_party.quests_accepted.include?(quest_number)
     bitmap = RPG::Cache.picture(QuestData::AcceptedQuestPic)
   end
   if $game_party.quests_completed.include?(quest_number)
     bitmap = RPG::Cache.picture(QuestData::CompletedQuestPic)
   end
   x = 8 + index % @column_max * (self.width / @column_max)
   y = 8 + index / @column_max * 64
   self.contents.blt(x, y, bitmap, Rect.new(0, 0, 48, 48), 255)
 end
         #####################################################
         # Modified methods to allow 64x64 selection windows #
         #####################################################
 def page_row_max
   return (self.height) / 64
 end
 
 def top_row
   return self.oy / 64
 end

 def top_row=(row)
   if row < 0
     row = 0
   end
   if row > row_max - 1
     row = row_max - 1
   end
   self.oy = row * 64
 end
 
 def update_cursor_rect
   if @index < 0
     self.cursor_rect.empty
     return
   end
   row = @index / @column_max
   if row < self.top_row
     self.top_row = row
   end
   if row > self.top_row + (self.page_row_max - 1)
     self.top_row = row - (self.page_row_max - 1)
   end
   # Calculate cursor coordinates
   x = @index % @column_max * (640 / @column_max)
   y = @index / @column_max * 64 - self.oy
   # Update cursor rectangle
   self.cursor_rect.set(x, y, 64, 64)
 end
end
#===============================================================================
# Window to display quest's information
#===============================================================================
class Window_QuestInfo < Window_Base
 
 def initialize(quest_id)
   super (0, 0, 640, 480)
   self.z = 1000
   self.contents = Bitmap.new(width-32,height-32)
   @quest = quest_id
   refresh
 end
 
 def update_quest(quest_id)
   if @quest != quest_id
     @quest = quest_id
     refresh
   end
 end
 
 def refresh
   # Titles of important text
   self.contents.clear
   quest_number = sprintf("%03d", @quest)
   self.contents.font.color = system_color
   self.contents.draw_text(0, 0, 640, 32, "No. #{quest_number}")
   self.contents.draw_text(80, 0, 640, 32, QuestData.name(@quest))
   if Quest.complete?(@quest)
     self.contents.font.color = Color.new(233, 188, 10)
     self.contents.draw_text(0, 0, 600, 32, "-COMPLETED-", 2)
     self.contents.font.color = system_color
   end
   self.contents.draw_text(0, 200, 280, 32, "Requester:")
   self.contents.draw_text(0, 360, 280, 32, "Location:")
   self.contents.draw_text(320, 200, 280, 32, "Reward(s):")
   # Description (Prints "???" if quest is not accepted)
   self.contents.font.color = normal_color
   self.contents.fill_rect(0, 40, 640, 5, Color.new(24,184,231,128))
   self.contents.font.size = 18
   if Quest.accept?(@quest)
     draw_even_text(0, 58, 640, 18, QuestData.description(@quest), @quest)
   else
     self.contents.draw_text(275, 103, 640, 18, "?   ?   ?")
   end
   self.contents.fill_rect(0, 180, 640, 5, Color.new(24,184,231,128))
   self.contents.fill_rect(300, 180, 5, 320, Color.new(24,184,231,128))
   # NPC and Location
   self.contents.font.size = Font.default_size
   if QuestData::NPCPicture and QuestData.NPC(@quest) != nil
     bitmap = RPG::Cache.character(QuestData.NPC(@quest), 0)
     h = bitmap.height/4
     w = bitmap.width/4
     self.contents.blt(0, 240, bitmap, Rect.new(0, 0, w, h))
     self.contents.draw_text(w+20, h+240, 280, 32, QuestData.NPC(@quest))
   else
     if QuestData.NPC(@quest) != nil
       self.contents.draw_text(20, 240, 280, 32, QuestData.NPC(@quest))
     end
   end
   self.contents.draw_text(20, 400, 280, 32, QuestData.location(@quest))
   # Rewards
   if !((QuestData::HideReward == true or QuestData::HideReward.include?(@quest)) and !Quest.complete?(@quest))
     y = 225
     exp_string = ""
     if QuestData.exp(@quest) != 0
       if QuestData::EXPicon != nil and QuestData::EXPicon != ""
         bitmap = RPG::Cache.icon(QuestData::EXPicon)
         self.contents.blt(340, y, bitmap, Rect.new(0, 0, 32, 32))
       else
         exp_string = "EXP"
       end
       self.contents.draw_text(370, y, 280, 32, QuestData.exp(@quest).to_s + " " + exp_string)
       y += 24
     end
     if QuestData.gold(@quest) != 0
       if QuestData::GoldIcon != nil and QuestData::GoldIcon != ""
         bitmap = RPG::Cache.icon(QuestData::GoldIcon)
         self.contents.blt(340, y, bitmap, Rect.new(0, 0, 32, 32))
       end
       self.contents.draw_text(370, y, 280, 32, QuestData.gold(@quest).to_s + " Gold")
       y += 32
     end
     if QuestData.reward(@quest) != nil
       #~~being loop~~
       QuestData.reward(@quest).each_index{|i|
       item = QuestData.reward(@quest)[i]
       case item[0]
       when 1
         bitmap = RPG::Cache.icon($data_items[item[1]].icon_name)
         self.contents.draw_text(370, y, 280, 32, $data_items[item[1]].name + " x " + item[2].to_s)
       when 2
         bitmap = RPG::Cache.icon($data_weapons[item[1]].icon_name)
         self.contents.draw_text(370, y, 280, 32, $data_weapons[item[1]].name + " x " + item[2].to_s)
       when 3
         bitmap = RPG::Cache.icon($data_armors[item[1]].icon_name)
         self.contents.draw_text(370, y, 280, 32, $data_armors[item[1]].name + " x " + item[2].to_s)
       end
       self.contents.blt(340, y, bitmap, Rect.new(0, 0, 32, 32))
       y += 32}
       #~~end loop~~
     end
     if QuestData.exp(@quest) == 0 and QuestData.gold(@quest) == 0 and QuestData.reward(@quest) == nil
       self.contents.draw_text(370, 225, 280, 32, "None")
     end
   else #If Quest Reward is hidden
     self.contents.draw_text(370, 225, 280, 32, "Unknown")
   end
 end
 
end
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
# The actual quest scene
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
class Scene_Quest

 def main
   #Creates the windows
   @quests_rank = Window_QuestRank.new
   @quests_title = Window_QuestTitle.new(0)
   @quests_list = Window_QuestList.new(0)
   @quests_title.update(@quests_list.quest)
   Graphics.transition
   loop do
     Graphics.update
     Input.update
     update
     if $scene != self
       break
     end
   end
   Graphics.freeze
   @quests_rank.dispose
   @quests_title.dispose
   @quests_list.dispose
 end
 #------------------------------------------------------------
 def update
   @quests_rank.update
   @quests_title.update(@quests_list.quest)
   @quests_list.update
   
   if @quest_info != nil
     @quest_info.update_quest(@quests_list.quest)
   end

   if @quests_list.active
     update_quests
     return
   end
 end
 #------------------------------------------------------------
 def update_quests
   if Input.trigger?(Input::B)
     #If quest information is not displayed
     if @quest_info == nil
       $game_system.se_play($data_system.cancel_se)
       $scene = QuestData::ExitScene
       return
     else #If quest info is displayed
       $game_system.se_play($data_system.cancel_se)
       @quest_info.dispose
       @quest_info = nil
       return
     end
   end
   if Input.trigger?(Input::C)
     #Works only if quest info isn't displayed
     if @quest_info == nil and @quests_list.quest != nil
       $game_system.se_play($data_system.decision_se)
       @quest_info = Window_QuestInfo.new(@quests_list.quest)
     end
     return
   end
   #Pressing shift opens quest info
   if Input.trigger?(Input::SHIFT) and @quest_info == nil
     $game_system.se_play($data_system.equip_se)
     @quests_title.next_page
     @quests_list.next_page
     return
   end
   
 end
end

if !defined?(RMXOS) || RMXOS::VERSION < 1.09
 raise 'ERROR: The "Organized Quest System" requires RMX-OS 1.09 or higher.'
end
module RMXOS
 module Options
   SAVE_DATA[Game_Party].push('@quests_new')
   SAVE_DATA[Game_Party].push('@quests_accepted')
   SAVE_DATA[Game_Party].push('@quests_completed')
   #SAVE_DATA[Game_System].push('@stored_desc')
 end
end
Basically replace the whole script with this.

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

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

Join the CP Discord Server!

exile360

Getting a syntax error on line 417. o.o
=end

Blizzard

@KK20: It was probably trying to deserialize data that wasn't there, but it was assumed that it was.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

KK20


#================================================================================begin

lol how did that happen? Fixed the script in previous post.

Ah, alright. I guess I should keep that in mind when I work with RMX-OS later.

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

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

Join the CP Discord Server!

exile360

Hehe. Yep, it seems to work perfectly now. Just a small question: is it possible to add a picture or have the current map remain as the background in the quest scene? Why I'm asking is that my windowskin has transparent edges, and the black can be seen through it. If it's just a line of code I can add somewhere, it'd be great, but if it's more work then it's fine as it is, not that important.

Thanks a lot for making it work! :) I'm sure it'll help more people besides me as well, as quest logs are quite an important part for any MMO.

KK20

Now I feel like I need to clean this script up. The whole map back display isn't difficult, but I need to make a few edits.

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

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

Join the CP Discord Server!

exile360

January 25, 2013, 04:50:41 pm #13 Last Edit: January 25, 2013, 04:52:08 pm by exile360
Haha, well it's an amazing script, definitely for your first. Hands down the best quest system out there. No need to make the edits unless you really want to, I feel bad for asking for so much on this forum already and making people waste their time on me without giving anything back. I wish I could do it myself, but scripting just isn't my thing unfortunately. If you ever happen to need any graphics, hit me up and I'll be more than happy to help if I can. :)

KK20

Haha you flatter me so :-*

But yes, this script needed an update. And here it is... Version 1.1~
I included a number of new things.

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

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

Join the CP Discord Server!

exile360

Sounds cool, I will check this out tonight! Is there anything changed in the configuration part or can I just copy my current over this one to save me some rewriting? I have a bunch of quests done already. :P

KK20

I actually added some other configuration items, so a "highlight all my configuration and copy-paste into the new version" won't work. If you copy-paste chunks at a time, then it should be fine.

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

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

Join the CP Discord Server!

exile360

Alright, I finally got around to switching to the new one, sorry for the delay. Seems to work good, and I love the map background with the transparency! Great work. :)
I just have one little problem with it which I can't seem to fix.
Spoiler: ShowHide

As you can see, my HUD covers the windows, but this happens only on the main quest list screen. Once you select a quest and go to the details screen, the HUD stays behind them. I tried searching for z values but can't seem to find the one for the main quest list screen. My HUD z is set to 200 and the only z value in the quest script I can find is set to 1000, which I assume is for the details window. Where is the one for the main one?

Also, you seem to have messed up the information in the comments a bit. :P The 3rd quest description area is supposed to be when the quest is finished, I assume? In the comments it says for new quests, like the first one, though.

KK20

That window belongs to QuestRank. You can make the appropriate change by adding a self.z = 1000 (or any number higher than your HUD's z-value) like so

  def initialize
    super (0, 0, 640, 64)
    self.contents = Bitmap.new(width-32, height-32)
    self.z = 1000
    refresh
  end


Also, thanks for pointing that out. You can see how I like to copy-paste and forget to make changes.

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

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

Join the CP Discord Server!

exile360