Jump to content
Reedo

RSQS: Reedo's Simple Quest System (CTP1)

Recommended Posts

-- Under Development - Near Release --

 

Reedo's Simple Quest System (RSQS)

Version 0.1 (beta)

September 16, 2013

By Reedo

 

References

None. This is an original script by Reedo.

 

Description

RSQS is an easy-to-use quest system.  This script is purely about managing quests and is not a quest journal.  There are no custom scenes in this script.

 

A quest system itself is about assigning quests, tracking their status, turning them in, and giving rewards.  All of this can be done without any kind of quest journal for viewing existing quests.  A quest journal is an additional script which can work in conjunction with a quest system.

 

While this script does not include a quest journal at this time, I do intend to write one in the near future.  In my estimation, this design (featuring a separation of concerns between the quest system and the quest journal) will result in a cleaner and easier to use quest journal when it is created.

 

This quest system is easily utilized by installing the script, writing a simple "quest loader" script to configure your quests, and then using simple script commands in events to assign and turn in quests.

 

Features

  • Easy-to-use quest system
  • Unlimited quests with unlimited categories
  • Multi-stage quests, unlimited stages per quest (minimum one)
  • Unlimited number of requirements per stage
  • -- party has certain number of items in inventory
  • -- collect specified number of virtual item drops from battle
  • -- kill specified number of certain enemy
  • -- specified switch is set or not set
  • -- specified variable equals the given value
  • Unlimited number of rewards per stage
  • -- award gold
  • -- award experience
  • -- award items
  • -- award armor
  • -- award weapons
  • Automatic quest updates at end of battle
  • Simple event script command interface using $game_reedo_quests
  • assign quests from an event
  • provide quest status feedback from an event
  • turn in a quest from an event
  • Quest configuration via easy-to-write secondary script

Virtual Items

A virtual item is one that does not exist in the database.  It is not a real game item.  Virtual items are dropped by monsters after battle and while a quest tracks the number of virtual items received, there are never any actual items in the player's inventory.  This makes it very easy to collect items for quest turn-in purposes only, without having to create anything in the database ahead of time.

 

When you create a new virtual item, in addition to giving it a name, you will also assign the enemy ID and drop chance for each enemy that can drop the virtual item after a battle.  Virtual items are awarded after a battle, along with any other normal enemy drop items.

 

Known Bugs

None at this time, however, this is a beta script and is not fully tested. 

Please report any bugs found.

 

Requirements

  • Requires separate configuration script to setup quests.
  • Requires on-map events and or common events to manage quests.

Installation

Plug-and-Play* (see requirements)

 

Just insert a new script directly under Materials, name it RSQS, and paste in the script code.

 

Configuration

After inserting the main script, insert a second script beneath RSQS and name it ReedoQuestManager (this name isn't actually important).  This script will be used to load all of your quest definitions.  It contains a single module which must be named "ReedoQuestManager" and which must contain the single method "self.load_quests".

 

 

module ReedoQuestManager
def self.load_quests

end
end

 

 

 

Within the self.load_quests method you will create and configure instances of Reedo_Quest and supporting objects.  Each quest will receive an ID number in the order it is added to the $data_reedo_quests collection, so it is good to add comments to each quest so you can easily recognize the quest ID.

 

A complete ReedoQuestManager example, with comments, follows:

 

 


module ReedoQuestManager
  def self.load_quests
#01   add a comment to remind yourself of the ID number
    #ID 0
#02   create a new quest instance with the given name and category 
    quest = Reedo_Quest.new("Slimy Balls", "Side Quest")
#03   create a new stage instance - every quest needs at least one stage
    stage = Reedo_Quest_Stage.new
#04   optionally, add a short reminder text about the quest;
#     only shown in game optionally - mostly meant for quest journal add-on,
#     or for events which show quest status.
    stage.story_text = "There are too many slimes!\nKill at least 8 and bring\nback 4 Slime Balls."
#05   create a requirement which must be met to turn the quest in
    requirement = Reedo_Quest_Requirement.new
#06   configure the requirement by calling any of the following methods:   
#       add_item(itemId, reqCount) - must have specified qty of item in inventory
#       add_virtual(item, reqCount) - must collect specified qty of virtual items
#       add_kill(enemyId, reqCount) - must kill specified number of enemy
#       add_switch(switchId, state) - switch must equal state
#       add_variable(variableId, value) - variable must equal value
    requirement.add_kill(1, 8) # kill 8 slimes
#06.a virtual items are defined first, then assigned to the requirement
    vitem = Reedo_Quest_Virtual_Item.new
    vitem.set_name("Slime Ball")
    vitem.set_provider(1, 0.5) # slimes have 50% chance to drop "Slime Ball"
    requirement.add_virtual(vitem, 4) # requires 4 "Slime Ball" virtual items
#07   once configured, add requirement to quest stage
    stage.requirements.push(requirement)
#07.a repeat from #5 to add additional requirements
#08   create a reward for completing this stage
    reward = Reedo_Quest_Reward.new
#09   configure the reward by calling any of the following methods:
#       award_gold(value) - give amount of gold
#       award_exp(value) - reward amount of experience to living party members
#       award_item(itemId, count) - add count of item to inventory
#       award_armor(armorId, count) - add count of armor to inventory
#       award_weapon(weaponId, count) - add count of weapon to inventory
    reward.award_gold(25)
    reward.award_exp(20)
#10   once configured, add reward to quest stage   
    stage.rewards.push(reward)
#10.a repeat from #8 to add additional rewards if needed
#11   once configured, add the stage to the quest
    quest.add_stage(stage)
#11.a repeat from #3 to add additional stages if desired
#13   once configured, add the quest to the global quest repository
    $data_reedo_quests.push(quest)
#14   repeat from #1 to add additional quests until complete
 
  end
end

# Once all of your quests are defined, you can use simple events in the game
# to assign quests to the player, give feed-back to the player on the status
# of a quest, and turn a completed quest in.
#
# A basic event page for a quest giver and turn in might look like:
#
mailto:#@>Conditional Branch: Script: $game_reedo_quests.contains?(0)
#  @>Conditional Branch: Script: $game_reedo_quests.get_quest(0).turn_in
#    @> (show text to tell any story that goes along with finsihing the quest)
#    @>Control Self Switch: A=ON (disable the event)
#  :  Else
#    @> (show text to tell the player that they aren't finished yet)
#  :  Branch End
#:  Else
#  @> (show text to offer the player the quest)
#  @>Script: $game_reedo_quests.assign_quest(0)
#:  Branch End

 

 

 

Event Screen Shot

Here is an example of an event which gives a quest, provides quest status, and turns in the quest when

complete.  Note the simple script commands used to branch the event to first either assign the quest or update it and then to either thank the player for finishing the quest or provide them with their status so far.

 

post-34162-0-65171800-1379394148_thumb.jpg

 

Compatibility

Should be compatible with most other scripts.

Modifies DataManager and BattleManager, but maintains existing method implementations on overwritten methods.  All other classes are original.

 

Script

Here is the complete RSQS script:

 

 

 


###############################################################################
##  RSQS - Reedo's Simple Quest System
##  Version 0.1 (beta)
##  September 16, 2013
##  By Reedo
###############################################################################
##  REFERENCES
##
##  None.  This is an original script by Reedo.
###############################################################################
##  FEATURES
##
##  + Easy-to-use quest system
##  + Unlimited quests with unlimited categories
##  + Multi-stage quests, unlimited stages per quest (minimum one)
##  + Unlimited number of requirements per stage
##    - Party has certain number of items in inventory
##    - Collect specified number of virtual item drops from battle
##    - Kill specified number of certain enemy
##    - Specified switch is set or not set
##    - Specified variable equals the given value
##  + Unlimited number of rewards per stage
##    - award gold
##    - award experience
##    - award items
##    - award armor
##    - award weapons
##  + Automatic quest updates at end of battle
##  + Simple event script command interface using $game_reedo_quests
##    - assign quests from an event
##    - provide quest status feedback from an event
##    - turn in a quest from an event
##  + Quest configuration via easy-to-write secondary script
##
###############################################################################
##  COMPATIBILITY
##
##  Should be compatible with most other scripts.
##
##  OVERWRITES (0*)
##    Modifies DataManager and BattleManager, but maintains existing
##    method implementations on overwritten methods.
###############################################################################
##  REQUIREMENTS
##
##  Requires seperate configuration script to setup quests.
##  Requires on-map events and or common events to manage quests.
###############################################################################
##  INSTALLATION
##
##  Plug-and-play*(see requirements)
##
##  Insert below Materials, above other add-on scripts.
###############################################################################
##  RIGHTS & RESTRICTIONS
##
##  As with most Reedo scripts, this script is free to re-use, as-is,
##  in personal, educational, and commercial RPGVX Ace development projects,
##  providing that:  this script is credited in writing displayed readily
##  to the user of the final compiled code assembly.
##
##  Reedo retains all rights of intellect and ownership.
##  You forego all rights of warranty by utilizing this script.
###############################################################################

###############################################################################
##  USER OPTIONS
###############################################################################
module RSQS
  STAGE_COMPLETE_MESSAGE_FORMAT = "Stage Complete: %s"
  STAGE_WORKING_MESSAGE_FORMAT = "You are still working on %s."
  STAGE_WORKING_USE_STORY = true
  QUEST_ITEM_UPDATE_MESSAGE_FORMAT = "Found Quest Item: %s"
  QUEST_REQUIREMENT_MET_MESSAGE_FORMAT = "You've met the requirements for: %s"
 
  QUEST_COMPLETE_ME_NAME = "fanfare1"
  QUEST_COMPLETE_ME_PITCH = 100
  QUEST_COMPLETE_ME_VOLUME = 100
end
###############################################################################
##  MAIN SCRIPT
###############################################################################
##  EDITS BEYOND THIS POINT ARE AT YOUR OWN RISK!!!
###############################################################################
module RSQSDataManager
  def RSQSDataManager.included(mod)
    @m_load_normal_database = mod.method(:load_normal_database)
    @m_create_game_objects = mod.method(:create_game_objects)
    @m_make_save_contents = mod.method(:make_save_contents)
    @m_extract_save_contents = mod.method(:extract_save_contents)
  end
 
  def get_load_normal_database
    @m_load_normal_database
  end
 
  def get_create_game_objects
    @m_create_game_objects
  end
 
  def get_make_save_contents
    @m_make_save_contents
  end
 
  def get_extract_save_contents
    @m_extract_save_contents
  end
 
  module_function :get_load_normal_database
  module_function :get_create_game_objects
  module_function :get_make_save_contents
  module_function :get_extract_save_contents
end

module RSQSBattleManager
  def RSQSBattleManager.included(mod)
    @m_gain_drop_items = mod.method(:gain_drop_items)
  end
 
  def get_gain_drop_items
    @m_gain_drop_items
  end
 
  module_function :get_gain_drop_items
end

module DataManager
  include RSQSDataManager
  
  def self.load_normal_database
    RSQSDataManager.get_load_normal_database[]
    begin
      $data_reedo_quests  = load_data("Data/ReedoQuestData.rvdata2")
    rescue
      $data_reedo_quests = []
    end
  end
 
  def self.create_game_objects
    RSQSDataManager.get_create_game_objects[]
    $game_reedo_quests  = Reedo_Quests.new
    begin
      qm = ReedoQuestManager
    rescue
      qm = nil
    end
    qm.load_quests if qm
  end 
 
  def self.make_save_contents
    contents = RSQSDataManager.get_make_save_contents[]
    contents[:reedo_quests]  = $game_reedo_quests
    contents
  end

  def self.extract_save_contents(contents)
    RSQSDataManager.get_extract_save_contents[contents]
    $game_reedo_quests  = contents[:reedo_quests]
    $game_reedo_quests  = Reedo_Quests.new if !$game_reedo_quests
  end
end

module BattleManager
  include RSQSBattleManager
 
  def self.gain_drop_items
    reedo_gain_quest_updates
    RSQSBattleManager.get_gain_drop_items[]
  end
 
  def self.reedo_gain_quest_updates
    $game_troop.dead_members.each do |enemy|
      $game_reedo_quests.active_quests.each do |quest|
        if quest.current_stage
          if !quest.current_stage.complete?
            quest.current_stage.requirements.each do |requirement|
              requirement.kills.each_key do |enemyid|
                requirement.kill_count[enemyid] += 1 if enemyid == enemy.enemy_id
              end
              requirement.virtual.each_key do |item|
                if item.providers.keys.include?(enemy.enemy_id)
                  if rand(0) <= item.providers[enemy.enemy_id]
                    requirement.virtual_count[item] += 1
                    $game_message.add(sprintf(RSQS::QUEST_ITEM_UPDATE_MESSAGE_FORMAT, item.name))
                  end
                end
              end
            end
            if quest.current_stage.complete?
              $game_message.add(sprintf(RSQS::QUEST_REQUIREMENT_MET_MESSAGE_FORMAT, quest.current_stage.name))
            end
          end
        end
      end
    end
  end
end

class Reedo_Quest_Virtual_Item
  attr_reader   :name           # name of virtual item
  attr_reader   :providers      # hash of enemy ID and drop chance
 
  def initialize
    @name = ""
    @providers = {}
  end
 
  def set_name(itemName)
    @name = itemName
  end
 
  def add_provider(enemyId, chance)
    @providers[enemyId] = chance
  end
end

class Reedo_Quest_Requirement
  attr_reader   :items          # hash of item ID and required count
  attr_reader   :virtual        # hash of virtual item and required count
  attr_reader   :kills          # hash of enemy ID and required count
  attr_reader   :switches       # hash of switch id and state flag
  attr_reader   :variables      # hash of variable name and value
  attr_reader   :virtual_count  # hash of virtual item and current count
  attr_reader   :kill_count     # hash of enemy ID and current count
 
  def initialize
    @items = {}
    @virtual = {}
    @kills = {}
    @switches = {}
    @variables = {}
    @virtual_count = {}
    @kill_count = {}
  end
 
  def add_item(itemId, reqCount)
    @items[itemId] = reqCount
  end
 
  def add_virtual(item, reqCount)
    @virtual[item] = reqCount
    @virtual_count[item] = 0
  end
 
  def add_kill(enemyId, reqCount)
    @kills[enemyId] = reqCount
    @kill_count[enemyId] = 0
  end
 
  def add_switch(switchId, state)
    @switches[switchId] = state
  end
 
  def add_variable(variableId, value)
    @variables[variableId] = value
  end
 
  def kills_left(enemyId)
    @kills[enemyId] - @kill_count[enemyId]
  end
 
  def virtual_left
    @virtual[item] - @virtual_count[item]
  end
 
  def complete?
    @items.each do |key, value|
      return false if $game_party.item_number($data_items[key]) < value
    end
    @virtual.each do |key, value|
      return false if @virtual_count[key] < value
    end
    @kills.each do |key, value|
      return false if @kill_count[key] < value
    end
    @switches.each do |key, value|
      return false if !$game_switches[key] == value
    end
    @variables.each do |key, value|
      return false if !$game_variables[key] == value
    end
    return true
  end
end

class Reedo_Quest_Reward
  attr_reader   :gold           # amount of gold rewarded
  attr_reader   :exp            # amount of experience rewarded
  attr_reader   :items          # hash of reward item id and count
  attr_reader   :armors         # hash of reward armor id and count
  attr_reader   :weapons        # hash of reward weapon id and count
 
  def initialize
    @gold = 0
    @exp = 0
    @items = {}
    @armors = {}
    @weapons = {}
  end
 
  def award_gold(value)
    @gold = value
  end
 
  def award_exp(value)
    @exp = value
  end
 
  def award_item(itemId, count)
    @items[itemId] = count
  end
 
  def award_armor(armorId, count)
    @armors[armorId] = count
  end
 
  def award_weapon(weaponId, count)
    @weapons[weaponId] = cout
  end
end

class Reedo_Quest_Stage
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_reader   :quest                    # the quest the stage belongs to
  attr_accessor :stage_number             # order of stage in quest line
  attr_accessor :stage_name               # name of the stage (optional)
  attr_reader   :requirements             # array of requirements to complete stage
  attr_reader   :rewards                  # array of rewards for completing stage
  attr_accessor :story_text               # message to display
 
  def initialize
    @quest = nil
    @stage_number = 0
    @stage_name = nil
    @requirements = []
    @rewards = []
    @story_text = ""
  end
 
  def complete?
    @requirements.each do |requirement|
      return false if !requirement.complete?
    end
    return true
  end
 
  def name
    return @stage_name if @stage_name and @stage_name.size > 0
    return @quest.quest_name if @quest and @quest.quest_name and @quest.quest_name.size > 0
    return "(???)"
  end
 
  def set_story(text)
    story_text = text
  end
 
  def set_owner(ownerQuest)
    @quest = ownerQuest
  end
end

class Reedo_Quest
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_reader   :category                 # quest type category name (unique id)
  attr_reader   :quest_name               # name of the quest (unique id)
  attr_reader   :description              # brief description of quest
  attr_reader   :stages                   # array of stages
  attr_reader   :stage_index              # index of active quest stage
 
  def initialize
    @category = "Quest"
    @name = ""
    @description = ""
    @stages = []
    @stage_index = 0
  end
 
  def initialize(name, cat)
    @category = cat
    @quest_name = name
    @description = ""
    @stages = []
    @stage_index = 0
  end
 
  def add_stage(stage)
    stage.set_owner(self)
    @stages.push(stage)
  end
 
  def set_description(text)
    @description = text
  end
 
  def assign_rewards
    if current_stage
      current_stage.rewards.each do |reward|
        if reward.gold > 0
          $game_party.gain_gold(reward.gold)
          msg = sprintf(Vocab::ObtainGold, reward.gold)
          $game_message.add('\.' + msg)
        end
        if reward.exp > 0
          $game_party.alive_members.each do |member|
            member.gain_exp(reward.exp)
          end
          msg = sprintf(Vocab::ObtainExp, reward.exp)
          $game_message.add('\.' + msg)
        end
        reward.items.each_key do |itemId|
          item = $data_items[itemId]
          $game_party.gain_item(item, reward.items[itemId])
          $game_message.add(sprintf(Vocab::ObtainItem, item.name))
        end
        reward.armors.each_key do |itemId|
          item = $data_armors[itemId]
          $game_party.gain_item(item, reward.armors[itemId])
          $game_message.add(sprintf(Vocab::ObtainItem, item.name))
        end
        reward.weapons.each_key do |itemId|
          item = $data_weapons[itemId]
          $game_party.gain_item(item, reward.weapons[itemId])
          $game_message.add(sprintf(Vocab::ObtainItem, item.name))
        end
      end
    end
  end
 
  # determines if quest is completed
  def complete?
    @stage_index >= stages.size
  end
 
  # gets the currently active quest stage
  def current_stage
    @stages.sort_by{|item| item.stage_number}[stage_index]
  end
 
  # attempts to turn in a quest if complete; returns true/false indicating success
  def turn_in(showComplete = true, showWorking = false)
    if !complete?
      if current_stage.complete?
        if showComplete
          $game_reedo_quests.play_quest_complete_me
          msg = sprintf(RSQS::STAGE_COMPLETE_MESSAGE_FORMAT, current_stage.name)
          $game_message.add('\.' + msg)
        end
        assign_rewards
        @stage_index += 1
        return true
      end
      if showWorking
        if RSQS::STAGE_WORKING_USE_STORY
          msg = sprintf(current_stage.story_text, current_stage.name)
          $game_message.add('\.' + msg)
        else
          msg = sprintf(RSQS::STAGE_WORKING_MESSAGE_FORMAT, current_stage.name)
          $game_message.add('\.' + msg)
        end
      end
    end
    return false
  end
end

class Reedo_Quests
  def initialize
    @quests = []
    @quest_ids = []
    @quest_complete_me = RPG::ME.new(RSQS::QUEST_COMPLETE_ME_NAME,
    RSQS::QUEST_COMPLETE_ME_VOLUME, RSQS::QUEST_COMPLETE_ME_PITCH)
  end
 
  def active_quests
    @quests.select {|quest| !quest.complete? }
  end
 
  def assign_quest(questId)
    @quests.push($data_reedo_quests[questId])
    @quest_ids.push(questId)
  end
 
  def contains?(questId)
    @quest_ids.include?(questId)
  end
 
  def get_quest(questId)
    @quests[@quest_ids.index(questId)]
  end
 
  def play_quest_complete_me
    @quest_complete_me.play
  end
end

 

 

 

Summary

With this script you can have a simple quest system for your game with a variety of quest requirements, including number of monsters killed and collecting virtual quest items, and a variety of quest rewards.

 

The quest system can also readily support a quest journal scene or other add-ons in the future.

 

As always, comments and/or suggestions are welcome.

Edited by Reedo
  • Like 1

Share this post


Link to post
Share on other sites

Please follow current developments for this script at:

http://forums.rpgmakerweb.com/index.php?/topic/18205-rsqs-reedos-simple-quest-system-beta/?p=175583

 

I will update this thread with the final post when the script is complete.

 

A large update has been made.  The initial journal scene is ready for testing and the editor contains minor bug fixes and expanded help content.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×
Top ArrowTop Arrow Highlighted