Syndicate content

Scripting challenge

33 posts / 0 new
Last post
Offline
Townie
Joined: 21 Mar 2011
Scripting challenge

I'm using a quest script I downloaded for RPGMXP, and I modified it so that quest updates will print to the upper left corner of the screen, and will go away after a certain period of time.

While this is relatively simple to have a code put up and wait for a while before taking it down.... I want to be able to run around at the same time. I've tried using a similar timer that can be found in Game_System, even duplicating one and setting one in there.

The result that I have so far is a message that prints in the upper left, without preventing movement, and the message removes itself. Sounds great... but it doesn't seem to matter how long the timer is set for, it sits the same amount of time. Recently, it shuts off after only a flash.  So, is there something I should be paying attention to, or some simple way to run it?

Offline
Townie
Joined: 14 Jan 2011

How are you displaying the text?  with a window or a sprite?

Either way, you can use the 'update' method to 'show', 'fade', 'unshow' the message. When the message is first called, make the sprite/window visible, and set a variable (let's say @timer) to the number of frames you want to display the message for.  Then in each update cycle (1 frame), decrement the variable by 1.  When the variable reaches 0, make the sprite/window invisible. 

Offline
Townie
Joined: 21 Mar 2011

I've got a timer set up in the 'update' method in Game_System. I would call the "unshowing" from game_system.update? and how exactly would I do that?

Offline
Townie
Joined: 14 Jan 2011

Can you compress & upload your project so I can take a look at it?

Offline
Townie
Joined: 21 Mar 2011

http://www.megaupload.com/?d=3DXUK5Q0 Here it be.

Offline
Townie
Joined: 14 Jan 2011

Here's what I was talking about. Since the Window_QuestUpdate is a standalone window that you create/dispose each time it's used, you can run the timer right in the window object itself.

#--------------------------------------------------------------
# CLASS: Window_QuestUpdate
# Summary: A window that is displayed when a quest is updated or received.
#--------------------------------------------------------------
class Window_QuestUpdate < Window_Base

# Pass in true or false. If true, displays "New Quest!". If false, displays "Quest Updated!"
  def initialize(new, index, quest)
    #if new
    #  width = 141
    #else
    #  width = 177
    #end
    width = 300
    @timer = 60
    
    #super(320 - width / 2, 176, width, 64)
    super(10, 10, width, 64)
    self.contents = Bitmap.new(self.width - 32, self.height - 32)
    self.opacity = 0
    self.contents.font.name = $fontface
    self.contents.font.size = $fontsize - 2
    self.contents.font.color = Color.new(255,255,255,255)
    
    $game_system.se_play($data_system.decision_se) 

    if new
      self.contents.draw_text(0, 0, self.width - 32, 32, "New Quest: " + quest[index-1].name)
    else
      self.contents.draw_text(0, 0, self.width - 32, 32, "Quest Updated: " + quest[index-1].name)
    end
#    if $game_system.quest_message_timer_on == false
#      $game_system.begin_quest_message_timer(40)
#    end
    update
  end
  
  def update
    #if a message is over    
#    if $game_system.quest_message_timer_delay == 0
    if @timer == 0
      self.visible = false
      self.dispose
    else
      @timer -= 1
      Graphics.update
    end
  end
end
Offline
Townie
Joined: 21 Mar 2011

Inn't that a b*tch? The solution is always far simpler than the crap I'm trying. I was convinced if the object ran the timer by itself that it would all else until it was finished. I was running a do-loop, which is probably why it did that. Thanks a lot mate, that was more or less the last piece of the puzzle.

Offline
Townie
Joined: 14 Jan 2011

"One is glad to be of service."  Smile

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

You probably shouldn't be running Graphics.update in that code.

Offline
Townie
Joined: 21 Mar 2011

Why not? I notice the other windows that are put up run Graphics.update while waiting for an input command to break out of a loop.

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

If your #update method is being called from a Scene_'s #update method, it's unnecessary. You only need to run Graphics.update once inside a loop, and the loop for your window is inside the Scene_'s main method (basically looks like "do loop; Graphics.update; Input.update; scene.update; break if $scene != scene; end" and scene.update calls @xyz_window.update)

Offline
Townie
Joined: 21 Mar 2011

The result of the new and improved code seems to still be a somewhat random disposal of the window. Removing "Graphics.update" seems to make the random disposal more severe (ie: disposing after 2 frames, instead of 80, but next time, disposing after a longer period)

Offline
Townie
Joined: 21 Mar 2011

Sorry for the double post - 

Added a test, put in a sound effect to play once the timer hits zero. It never plays, even though the window does vanish.

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

Could you show me how you call a sound effect and how you use the window in your scene? Where do you call Window_QuestUpdate.new ?

Offline
Townie
Joined: 21 Mar 2011

[/code]

# Makes the indexed quest available for the player to see
def assign_quest(index)
@quests[index - 1].available = true
window = Window_QuestUpdate.new(true, index, @quests)
end

# Creates a new location and task for the indexed quest
def update_quest(index, new_location, new_description)
@quests[index - 1].update_quest(new_location, new_description)
window = Window_QuestUpdate.new(false, index, @quests)
end

[/code]

These two methods are called from separate Event Scripts whenever a new quest is added, or an existing quest is updated, respectively.

The timer-testing sound effect is placed in the code above, and looks like so:

if @timer == 0
      self.visible = false
      Audio.se_play("Audio/SE/" + "055-Right01", 80, 100)
      self.dispose

After testing, I can guarantee that the Audio.se_play works, just not when it's in there.

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

Alright, some notes:

  1. In your scene's update method (probably Scene_Map) you need to add "@quest_window.update unless @quest_window.nil?" and you need to change #assign_quest and #update_quest to set the window to @quest_window instead of window. I'm assuming that #assign_quest and #update_quest are in this Scene (where are they really?). The best place to put the update is probably right above "@message_window.update".
  2. You could also use the Game_System#se_play method like so: $game_system.se_play("055-Right01")
  3. You don't need to set "self.contents.font.name" or "self.contents.font.size" unless you have a pirated copy of RMXP. You also don't ever have to set "self.contents.font.color" to white but it is sometimes done anyway and the more standard way (inside an extension of Window_Base) is to use "normal_color" instead of "Color.new(255,255,255,255)".
Offline
Townie
Joined: 21 Mar 2011

Script isn't mine. The script builder might've used pirated. I think those were put there, though, in case you wanted to set the font for quest updates different than the rest (which I might end up doing). 

The two functions are in class Game_QuestBook, but that's just the object. I don't think they're part of any scene.

Offline
Townie
Joined: 21 Mar 2011

Clarification: The original design which I received, each time a quest was updated or modified, it would just run a do-loop where it would put up a normal message window, which you pressed a button to get out of. I didn't like the idea, so I've been trying to just put up a message up in the corner (without a background) that would go away on it's own and not interrupt the action in any way. I succeeded in all objectives other than getting the message to go away at a PARTICULAR time. For some reason, it just goes away randomly. On certain tests that I've run, the timer doesn't always work, and it never does what it should do when (if ever) it reaches zero.

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

Could you post the whole script? I'll take a look at it for you and simplify it and make everything work dandily.

Offline
Townie
Joined: 21 Mar 2011
Spoiler: Highlight to view

#=============================================================#
# QUEST SYSTEM BY JAKO DRAKO GIVE CREDIT #
#-------------------------------------------------------------------------------------------------------------------------#
# Creates a system to allow the player gain quests. All quests are stored in the $game_questbook variable. #
# Each quest has a name, location, presenter, reward, task, and a set of old tasks. Rewards can be either #
# money, items, or experience. #
#----------------------------------------------------------------------------------------------------------------#
# Assign quest code: $game_questbook.assign_quest(index) #
# Update quest code: $game_questbook.update_quest(index, new location, new task) #
# Finish quest code: $game_questbook.complete_quest(index) #
#===============================================================================
#
# BEGIN CODE #
#--------------------------------------------------------------
# CLASS: Game_Quest
# Summary: A quest that can be recorded in a Game_QuestBook object
#--------------------------------------------------------------
class Game_Quest

attr_accessor :available
attr_accessor :shift_count

# name = quest name
# location = where the hero must go to complete the quest
# presenter = name of person who gives the quest
# reward_type = string, "Gold", "Item", "Exp", or "none"
# reward_var = amount of gold, item id, or amount of exp
# if reward_type = "none", pass 0 for reward_var
# description = what the hero must do to accomplish the task
def initialize(name, location, presenter, reward_type, reward_var, description)
   @name = name
   @location = location
   @presenter = presenter
   if reward_type.downcase == "none"
     @reward = nil
     else
     @reward = Game_Reward.new(reward_type, reward_var)
   end
   @description = description
   @previous_descriptions = []
   @available = false
   @complete = false
   @temp_status = ""
   @status = "Incomplete"
   @shift_count = 0
end

# gives the hero the reward if it exists and updates the variables
#JC Added: Also moves the final task to old tasks, as it was finished, too.
def finish_quest
   if @available and not @complete
     @complete = true
     unless @reward == nil
       @reward.receive
     end
     @previous_descriptions.unshift(@description)
     @shift_count +=1
     @description = " "
     @status = "Complete!"
   end
end

# change the quest location or description while
# hero is doing it.
# JC Added: If you put in "same" as the location or
# description, it won't update that particular item.
def update_quest(new_location, new_description)
  @new_loc = new_location
  
  if @new_loc != "same"
     @location = new_location
   end
   if @description != "same"
     @previous_descriptions.unshift(@description)
     @shift_count += 1
   end
   if @description != "same" 
     @description = new_description
   end
   
   @temp_status = @status
   @status = "Updated"
end

# Returns the name of the quest or "   " if unavailable
def name
   if @available
     return @name
   else
     return "   "
   end
end
     
# Returns the location of the quest or "" if unavailable
def location
   if @available
      return @location
    else
      return ""
    end
end
   
# Returns the presenter of the quest or "" if unavailable
def presenter
   if @available
     return @presenter
   else
     return ""
   end
end

# Returns the Game_Reward object of the quest if the quest is available
def reward
   if @available
     return @reward
   end
end

# Returns the current task of the quest or "" if unavailable
def description
   if @available
     return @description
     else
     return ""
   end
end

# Returns a previous task of the quest if it has been updated
def old_description(index)
   if @available
     if @previous_descriptions
!= nil return @previous_descriptions
else return "" end else return "" end end # Returns "Incomplete", "Complete!", or "Updated" def status if @available return @status else return "" end end # Resets the status of the quest after it has been updated and viewed once def status_viewed if @status == "Updated" @status = @temp_status end end end #-------------------------------------------------------------- # CLASS: Game_Reward # Summary: A class that is to be used by the Game_Quest class # to give the hero a reward after completing a quest #-------------------------------------------------------------- class Game_Reward attr_accessor :reward_type # Initialize(tpye, initializer) # type = string, "Gold", "Item", or "Exp" # initializer = integer, amount of gold, item id, or amount of exp def initialize(type, initializer) @type = type @reward_var = initializer end # Gives the party the reward def receive reward_window = Window_Reward.new(self) case @type.downcase when "gold" $game_party.gain_gold(@reward_var) when "item" $game_party.gain_item(@reward_var, 1) when "exp" for actor in $game_party.actors if actor.cant_get_exp? == false last_level = actor.level actor.exp += @reward_var if actor.level > last_level levelup_window = Window_LevelUp.new(actor) end end end end end # Returns a string of the reward # if gold: "<x> <currency>" # if item: "<item name>" # if exp: "<x> Exp" def text case @type.downcase when "gold" reward_text = @reward_var.to_s + " " + $data_system.words.gold when "item" reward_text = $data_items[@reward_var].name when "exp" reward_text = @reward_var.to_s + " Exp" else reward_text = "" end return reward_text end def icon case @type.downcase when "gold" # Set icon for gold rewards reward_icon = RPG::Cache.icon("032-Item01") when "item" # Set icon for item rewards reward_icon = RPG::Cache.icon("034-Item03") when "exp" # Set icon for exp rewards reward_icon = RPG::Cache.icon("050-Skill07") end return reward_icon end end #-------------------------------------------------------------- # CLASS: Game_QuestBook # Summary: Works like an array of Game_Quest objects with # a few Game_Quest specific methods. #-------------------------------------------------------------- class Game_QuestBook def initialize() @quests = [] end def [](index) return @quests
end # Number of quests def quest_count return @quests.length end # Creates a new Game_Quest object def add_quest(quest) @quests.push(quest) end # Makes the indexed quest available for the player to see def assign_quest(index) @quests[index - 1].available = true window = Window_QuestUpdate.new(true, index, @quests) end # Creates a new location and task for the indexed quest def update_quest(index, new_location, new_description) @quests[index - 1].update_quest(new_location, new_description) window = Window_QuestUpdate.new(false, index, @quests) end def complete_quest(index) @quests[index - 1].finish_quest end end #-------------------------------------------------------------- # CLASS: Window_QuestList # Summary: A window that displays all quests in the game and # the information specific to the available ones. #-------------------------------------------------------------- class Window_QuestList < Window_Selectable attr_accessor :quest_index def initialize(quest_book) @quest_book = quest_book super(0, 64, 640, 416) @item_max = @quest_book.quest_count @quest_index = 0 @cursor_index = 0 # top_quest and bottom_quest are used to display the list of # quests only 11 at a time and to scroll the list. @top_quest = 0 @bottom_quest = [@top_quest + 10, @item_max - 1].min self.contents = Bitmap.new(self.width - 32, @item_max * 32 + 32) self.contents.font.name = $fontface self.contents.font.size = $fontsize refresh end def refresh self.contents.clear # Create the list labels self.contents.font.color = system_color self.contents.draw_text(0, 0, 58, 32, "Name") self.contents.draw_text(170, 0, 81, 32, "Location") self.contents.draw_text(340, 0, 92, 32, "Presenter") self.contents.draw_text(503, 0, 100, 32, "Status") j = 0 # Draw the items in the list between the quests indexed by # top_quest and bottom_quest for i in @top_quest..@bottom_quest draw_quest(i, j) j += 1 end end def update if self.active and @item_max > 0 and @quest_index >= 0 # Input processing for DOWN ARROW if Input.trigger? (Input::DOWN) and @quest_index < @item_max - 1 $game_system.se_play($data_system.cursor_se) @quest_index += 1 @cursor_index += 1 if @cursor_index > 10 @cursor_index = 10 @top_quest += 1 @bottom_quest += 1 refresh end end # Input processing for UP ARROW if Input.trigger? (Input::UP) and @quest_index > 0 $game_system.se_play($data_system.cursor_se) @quest_index -= 1 @cursor_index -= 1 if @cursor_index < 0 @cursor_index = 0 @top_quest -= 1 @bottom_quest -=1 refresh end end end # Update the help window if self.active and @help_window != nil update_help end # Update the cursor update_cursor_rect end # Draw the cursor def update_cursor_rect if @cursor_index < 0 self.cursor_rect.empty return end row = @cursor_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 cursor_width = self.width - 32 x = 0 y = @cursor_index * 32 + 32 self.cursor_rect.set(x, y, cursor_width, 32) end # Draw the status of the a specific quest def draw_quest(quest_index, y_index) quest = @quest_book[quest_index] self.contents.font.color = normal_color if quest.available == false or quest.status == "Complete!" self.contents.font.color = disabled_color end x = 0 y = y_index * 32 + 32 self.contents.draw_text(x, y, contents.text_size(quest.name).width, 32, quest.name) x += 170 self.contents.draw_text(x, y, contents.text_size(quest.location).width, 32, quest.location) x += 170 self.contents.draw_text(x, y, contents.text_size(quest.presenter).width, 32, quest.presenter) x += 163 self.contents.draw_text(x, y, contents.text_size(quest.status).width, 32, quest.status) quest.status_viewed end # Update the help window. Displays the current task of the quest highlighted def update_help @help_window.set_text(@quest_book[@quest_index].description) end end #-------------------------------------------------------------- # CLASS: Window_QuestInfo # Summary: Displays information on a specific quest. #-------------------------------------------------------------- class Window_QuestInfo < Window_Base def initialize(quest) super(0, 0, 640, 480) @quest = quest self.contents = Bitmap.new(self.width - 32, self.height - 32) self.contents.font.name = $fontface self.contents.font.size = $fontsize generate_text update end def generate_text self.contents.font.color = system_color self.contents.draw_text(0, 32, 130, 32, "Presented by:") location_width = [contents.text_size(@quest.location).width, 82].max self.contents.draw_text(608 - location_width, 32, 82, 32, "Location") self.contents.draw_text(0, 96, 78, 32, "Reward:") self.contents.draw_text(0, 128, 200, 32, "Current Objective:") self.contents.draw_text(0, 192, 220, 32, "Completed Objectives:") self.contents.font.color = normal_color text_width = contents.text_size(@quest.name).width self.contents.draw_text(0, 0, text_width, 32, @quest.name) text_width = contents.text_size(@quest.status).width self.contents.draw_text(608 - text_width, 0, text_width, 32, @quest.status) @quest.status_viewed text_width = contents.text_size(@quest.presenter).width self.contents.draw_text(0, 64, text_width, 32, @quest.presenter) self.contents.draw_text(608 - location_width, 64, location_width, 32, @quest.location) if @quest.reward == nil self.contents.draw_text(83, 96, 96, 32, "No reward") else text_width = contents.text_size(@quest.reward.text).width self.contents.blt(83, 100, @quest.reward.icon, Rect.new(0, 0, 24, 24)) self.contents.draw_text(110, 96, text_width, 32, @quest.reward.text) end text_width = contents.text_size(@quest.description).width self.contents.draw_text(0, 160, 608, 32, @quest.description) self.contents.font.color = disabled_color for i in 0...([@quest.shift_count, 7].min) y = 224 + 32 * i text_width = contents.text_size(@quest.old_description(i)).width self.contents.draw_text(0, y, text_width, 32, @quest.old_description(i)) end end def update loop do Graphics.update Input.update # Close the window if input is received if Input.trigger? (Input::C) $game_system.se_play($data_system.cancel_se) break end if Input.trigger? (Input::B) $game_system.se_play($data_system.cancel_se) break end end dispose end end #-------------------------------------------------------------- # CLASS: Window_Reward # Summary: A simple class that shows the reward received after # completing a quest and then waits for input from the player to # terminate itself. #-------------------------------------------------------------- class Window_Reward < Window_Base def initialize(reward) window_width = [112, reward.text.length * 15 + 32].max super(320 - window_width / 2, 144, window_width, 96) self.contents = Bitmap.new(window_width - 32, 64) self.contents.font.name = $fontface self.contents.font.size = $fontsize self.contents.font.color = normal_color self.contents.draw_text(0, 0, 78, 32, "Reward:") self.contents.draw_text(0, 32, window_width, 32, reward.text) update end def update loop do Graphics.update Input.update #Close the window if input is received if Input.trigger? (Input::C) $game_system.se_play($data_system.decision_se) break end if Input.trigger? (Input::B) $game_system.se_play($data_system.decision_se) break end end Input.update dispose end end #-------------------------------------------------------------- # CLASS: Window_LevelUp # Summary: A simple class that shows the text "[actor name] Level Up!" # then waits for input from the player to terminate itself. #-------------------------------------------------------------- class Window_LevelUp < Window_Base def initialize(actor) #Generate text @text = actor.name + " Level Up!" window_width = actor.name.length * 12 + 129 super(320 - window_width / 2, 176, window_width, 64) self.contents = Bitmap.new(self.width - 32, 32) self.contents.font.name = $fontface self.contents.font.size = $fontsize self.contents.font.color = normal_color self.contents.draw_text(0, 0, self.width - 32, 32, @text) update end def update loop do Graphics.update Input.update #Close the window if input is received if Input.trigger? (Input::C) $game_system.se_play($data_system.decision_se) break end if Input.trigger? (Input::B) $game_system.se_play($data_system.decision_se) break end end Input.update dispose end end #-------------------------------------------------------------- # CLASS: Window_QuestUpdate # Summary: A window that is displayed when a quest is updated or received. #-------------------------------------------------------------- class Window_QuestUpdate < Window_Base # Pass in true or false. If true, displays "New Quest!". If false, displays "Quest Updated!" def initialize(new, index, quest) #if new # width = 141 #else # width = 177 #end width = 300 @timer = 80 #super(320 - width / 2, 176, width, 64) super(10, 10, width, 64) self.contents = Bitmap.new(self.width - 32, self.height - 32) self.opacity = 0 self.contents.font.name = $fontface self.contents.font.size = $fontsize - 1 self.contents.font.color = normal_color Audio.se_play("Audio/SE/" + "055-Right01", 80, 100) # $game_system.se_play($data_system.decision_se) if new self.contents.draw_text(0, 0, self.width - 32, 32, "New Quest: " + quest[index-1].name) else self.contents.draw_text(0, 0, self.width - 32, 32, "Quest Updated: " + quest[index-1].name) end update end def update if @timer == 0 self.visible = false Audio.se_play("Audio/SE/" + "055-Right01", 80, 100) self.dispose else @timer -= 1 Graphics.update end end end #-------------------------------------------------------------- # CLASS: Scene_QuestBook # Summary: Displays the list of available quests and allows the player # to select a specific quest for more information. Uses the Window_QuestList, # Window_Help, and Window_QuestInfo classes. #-------------------------------------------------------------- class Scene_QuestBook def main # Window with list of quests @quest_window = Window_QuestList.new($game_questbook) # Window displaying task of highlighted quest @help_window = Window_Help.new @quest_window.help_window = @help_window Graphics.transition loop do Graphics.update Input.update update if $scene != self break end end Graphics.freeze @quest_window.dispose @help_window.dispose end def update # Update the windows @quest_window.update @help_window.update # Cancel button (B) Returns to the item menu. if Input.trigger? (Input::B) $game_system.se_play($data_system.cancel_se) $scene = Scene_Menu.new(4) end # Select button (C) Selects the highlighted quest for viewing if Input.trigger? (Input::C) if $game_questbook[@quest_window.quest_index].available # Code for if the selected quest is available to the player $game_system.se_play($data_system.decision_se) # Disable the quest list window and help window @quest_window.active = false @quest_window.visible = false @help_window.active = false @help_window.visible = false # Create a new Window_QuestInfo object to display # information about the selected quest @info_window = Window_QuestInfo.new($game_questbook[@quest_window.quest_index]) # Enable the quest list window and the help window @quest_window.active = true @quest_window.visible = true @help_window.active = true @help_window.visible = true @quest_window.refresh else # Code for if the selected quest is unavailable to the player $game_system.se_play($data_system.buzzer_se) return end end end end #-------------------------------------------------------------- # Extra code for class Scene_Title # Summary: This is where all the quests are created. #-------------------------------------------------------------- class Scene_Title def command_new_game $game_system.se_play($data_system.decision_se) Audio.bgm_stop Graphics.frame_count = 0 $game_temp = Game_Temp.new $game_system = Game_System.new $game_switches = Game_Switches.new $game_variables = Game_Variables.new $game_self_switches = Game_SelfSwitches.new $game_screen = Game_Screen.new $game_actors = Game_Actors.new $game_party = Game_Party.new $game_troop = Game_Troop.new $game_map = Game_Map.new $game_player = Game_Player.new # QUESTS $game_questbook = Game_QuestBook.new() # Create all quests here # Format: $game_questbook.add_quest("<name>", "<location>", "<presenter>", "gold/item/exp/none", 0/<gold amount/item id/exp amount>, "<first task>") # Note: Item ID is found in the database # QUESTS ( quest1 = Game_Quest.new("The Switch", "Test World","", "gold", 20, "Pull the mysterious switch, see what happens.") $game_questbook.add_quest(quest1) #quests to be called by their index, for ease $game_party.setup_starting_members $game_map.setup($data_system.start_map_id) $game_player.moveto($data_system.start_x, $data_system.start_y) $game_player.refresh $game_map.autoplay $game_map.update $scene = Scene_Map.new end end #-------------------------------------------------------------- # Extra code for Scene_Save # Summary: Saves the questbook of the player. ($game_questbook) #-------------------------------------------------------------- class Scene_Save < Scene_File alias original_write write_save_data def write_save_data(file) original_write(file) Marshal.dump($game_questbook, file) end end #-------------------------------------------------------------- # Extra code for Scene_Load # Summary: Loads the questbook of the player. ($game_questbook) #-------------------------------------------------------------- class Scene_Load < Scene_File alias original_read read_save_data def read_save_data(file) original_read(file) $game_questbook = Marshal.load(file) end end

Here be.

Offline
Townie
Joined: 21 Mar 2011

These tags fail so damn hard...

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

You really ought to put the spoiler tags outside the code tags.

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

Try this instead:

Spoiler: Highlight to view
#===============================================================================
# QUEST SYSTEM BY JAKO DRAKO GIVE CREDIT
#-------------------------------------------------------------------------------
# Creates a system to allow the player gain quests. All quests are stored in the
# $game_questbook variable.
# Each quest has a name, location, presenter, reward, task, and a set of old
# tasks. Rewards can be either money, items, or experience.
#-------------------------------------------------------------------------------
# Assign quest code: $game_questbook.assign_quest(index)
# Update quest code: $game_questbook.update_quest(index, new location, new task)
# Finish quest code: $game_questbook.complete_quest(index)
#===============================================================================
#
#-------------------------------------------------------------------------------
# CLASS: Game_Quest
# Summary: A quest that can be recorded in a Game_QuestBook object
#-------------------------------------------------------------------------------
class Game_Quest

  attr_accessor :available
  attr_accessor :shift_count

  # name = quest name
  # location = where the hero must go to complete the quest
  # presenter = name of person who gives the quest
  # reward_type = string, "Gold", "Item", "Exp", or "none"
  # reward_var = amount of gold, item id, or amount of exp
  # if reward_type = "none", pass 0 for reward_var
  # description = what the hero must do to accomplish the task
  def initialize(name, location, presenter, reward_type, reward_var, description)
    @name = name
    @location = location
    @presenter = presenter
    if reward_type.downcase == "none"
      @reward = nil
     else
      @reward = Game_Reward.new(reward_type, reward_var)
    end
    @description = description
    @previous_descriptions = []
    @available = false
    @complete = false
    @temp_status = ""
    @status = "Incomplete"
    @shift_count = 0
  end

  # gives the hero the reward if it exists and updates the variables
  #JC Added: Also moves the final task to old tasks, as it was finished, too.
  def finish_quest
    if @available and not @complete
      @complete = true
      unless @reward == nil
        @reward.receive
      end
      @previous_descriptions.unshift(@description)
      @shift_count +=1
      @description = " "
      @status = "Complete!"
    end
  end

  # change the quest location or description while
  # hero is doing it.
  # JC Added: If you put in "same" as the location or
  # description, it won't update that particular item.
  def update_quest(new_location, new_description)
    @new_loc = new_location
    
    if @new_loc != "same"
       @location = new_location
    end
    if @description != "same"
      @previous_descriptions.unshift(@description)
      @shift_count += 1
    end
    if @description != "same" 
      @description = new_description
    end
    
    @temp_status = @status
    @status = "Updated"
  end

  # Returns the name of the quest or "   " if unavailable
  def name
    @available ? @name : "   "
  end
       
  # Returns the location of the quest or "" if unavailable
  def location
    @available ? @location : ""
  end
     
  # Returns the presenter of the quest or "" if unavailable
  def presenter
    @available ? @presenter : ""
  end

  # Returns the Game_Reward object of the quest if the quest is available
  def reward
    @available ? @reward : nil
  end

  # Returns the current task of the quest or "" if unavailable
  def description
    @available ? @description : ""
  end

  # Returns a previous task of the quest if it has been updated
  def old_description(index)
    @available and @previous_descriptions != nil ? @previous_descriptions : ""
  end

  # Returns "Incomplete", "Complete!", or "Updated"
  def status
    @available ? @status : ""
  end

  # Resets the status of the quest after it has been updated and viewed once
  def status_viewed
    @status = @temp_status if @status == "Updated"
  end
end

#-------------------------------------------------------------------------------
# CLASS: Game_Reward
# Summary: A class that is to be used by the Game_Quest class
# to give the hero a reward after completing a quest
#-------------------------------------------------------------------------------
class Game_Reward
     
  attr_accessor :reward_type

  # Initialize(tpye, initializer)
  # type = string, "Gold", "Item", or "Exp"
  # initializer = integer, amount of gold, item id, or amount of exp
  def initialize(type, initializer)
    @type = type
    @reward_var = initializer
  end

  # Gives the party the reward
  def receive
    $game_questbook.windows << Window_Reward.new(self)
    case @type.downcase
    when "gold"
      $game_party.gain_gold(@reward_var)
    when "item"
      $game_party.gain_item(@reward_var, 1)
    when "exp"
      for actor in $game_party.actors
        if actor.cant_get_exp?  == false
          last_level = actor.level
          actor.exp += @reward_var
          if actor.level > last_level
            $game_questbook.windows << Window_LevelUp.new(actor)
          end
        end
      end
    end
  end

  # Returns a string of the reward
  # if gold: " "
  # if item: ""
  # if exp: " Exp"
  def text
    case @type.downcase
    when "gold"
      reward_text = @reward_var.to_s + " " + $data_system.words.gold
    when "item"
      reward_text = $data_items[@reward_var].name
    when "exp"
      reward_text = @reward_var.to_s + " Exp"
    else
      reward_text = ""
    end
    return reward_text
  end

  def icon
    case @type.downcase
    when "gold"
      # Set icon for gold rewards
      reward_icon = RPG::Cache.icon("032-Item01")
    when "item"
      # Set icon for item rewards
      reward_icon = RPG::Cache.icon("034-Item03")
    when "exp"
      # Set icon for exp rewards
      reward_icon = RPG::Cache.icon("050-Skill07")
    end
    return reward_icon
  end
end

#-------------------------------------------------------------------------------
# CLASS: Game_QuestBook
# Summary: Works like an array of Game_Quest objects with
# a few Game_Quest specific methods.
#-------------------------------------------------------------------------------
class Game_QuestBook
  
  attr_reader :windows
  
  def initialize
    @quests = []
    @windows = []
  end
  
  def update
    @windows.each_with_index do |window, i|
      window.disposed? ? @windows.delete_at(i) : window.update
    end
  end
  
  def [](index)
    @quests[index]
  end
    
  # Number of quests
  def quest_count
    return @quests.length
  end

  # Creates a new Game_Quest object
  def add_quest(quest)
    @quests.push(quest)
  end

  # Makes the indexed quest available for the player to see
  def assign_quest(index)
    @quests[index - 1].available = true
    @windows << Window_QuestUpdate.new(true, index, @quests)
  end

  # Creates a new location and task for the indexed quest
  def update_quest(index, new_location, new_description)
    @quests[index - 1].update_quest(new_location, new_description)
    @windows << Window_QuestUpdate.new(false, index, @quests)
  end

  def complete_quest(index)
    @quests[index - 1].finish_quest
  end
end

#-------------------------------------------------------------------------------
# CLASS: Window_QuestList
# Summary: A window that displays all quests in the game and
# the information specific to the available ones.
#-------------------------------------------------------------------------------
class Window_QuestList < Window_Selectable

  attr_accessor :quest_index

  def initialize(quest_book)
    @quest_book = quest_book
    super(0, 64, 640, 416)
    @item_max = @quest_book.quest_count
    @quest_index = 0
    @cursor_index = 0
    # top_quest and bottom_quest are used to display the list of
    # quests only 11 at a time and to scroll the list.
    @top_quest = 0
    @bottom_quest = [@top_quest + 10, @item_max - 1].min
    self.contents = Bitmap.new(self.width - 32, @item_max * 32 + 32)
    refresh
  end

  def refresh
    self.contents.clear
    # Create the list labels
    self.contents.font.color = system_color
    self.contents.draw_text(0, 0, 58, 32, "Name")
    self.contents.draw_text(170, 0, 81, 32, "Location")
    self.contents.draw_text(340, 0, 92, 32, "Presenter")
    self.contents.draw_text(503, 0, 100, 32, "Status")
    j = 0
    # Draw the items in the list between the quests indexed by
    # top_quest and bottom_quest
    for i in @top_quest..@bottom_quest
      draw_quest(i, j)
      j += 1
    end
  end

  def update
    if self.active and @item_max > 0 and @quest_index >= 0
      # Input processing for DOWN ARROW
      if Input.trigger? (Input::DOWN) and @quest_index < @item_max - 1
        $game_system.se_play($data_system.cursor_se)
        @quest_index += 1
        @cursor_index += 1
        if @cursor_index > 10
          @cursor_index = 10
          @top_quest += 1
          @bottom_quest += 1
          refresh
        end
      end
      # Input processing for UP ARROW
      if Input.trigger? (Input::UP) and @quest_index > 0
        $game_system.se_play($data_system.cursor_se)
        @quest_index -= 1
        @cursor_index -= 1
        if @cursor_index < 0
          @cursor_index = 0
          @top_quest -= 1
          @bottom_quest -=1
          refresh
        end
      end
    end
    # Update the help window
    if self.active and @help_window != nil
      update_help
    end
    # Update the cursor
    update_cursor_rect
  end

  # Draw the cursor
  def update_cursor_rect
    if @cursor_index < 0
      self.cursor_rect.empty
      return
    end
    row = @cursor_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
    cursor_width = self.width - 32
    x = 0
    y = @cursor_index * 32 + 32
    self.cursor_rect.set(x, y, cursor_width, 32)
  end

  # Draw the status of the a specific quest
  def draw_quest(quest_index, y_index)
    quest = @quest_book[quest_index]
    self.contents.font.color = normal_color
    if quest.available == false or quest.status == "Complete!"
      self.contents.font.color = disabled_color
    end
    x = 0
    y = y_index * 32 + 32
    self.contents.draw_text(x, y, contents.text_size(quest.name).width, 32, quest.name)
    x += 170
    self.contents.draw_text(x, y, contents.text_size(quest.location).width, 32, quest.location)
    x += 170
    self.contents.draw_text(x, y, contents.text_size(quest.presenter).width, 32, quest.presenter)
    x += 163
    self.contents.draw_text(x, y, contents.text_size(quest.status).width, 32, quest.status)
    quest.status_viewed
  end

  # Update the help window. Displays the current task of the quest highlighted
  def update_help
    @help_window.set_text(@quest_book[@quest_index].description)
  end
end

#-------------------------------------------------------------------------------
# CLASS: Window_QuestInfo
# Summary: Displays information on a specific quest.
# Referenced From: Scene_QuestInfo
#-------------------------------------------------------------------------------
class Window_QuestInfo < Window_Base

  def initialize(quest)
    super(0, 0, 640, 480)
    @quest = quest
    self.contents = Bitmap.new(self.width - 32, self.height - 32)
    self.contents.font.name = $fontface
    self.contents.font.size = $fontsize
    generate_text
    update
  end

  def generate_text
    self.contents.font.color = system_color
    self.contents.draw_text(0, 32, 130, 32, "Presented by:")
    location_width = [contents.text_size(@quest.location).width, 82].max
    self.contents.draw_text(608 - location_width, 32, 82, 32, "Location")
    self.contents.draw_text(0, 96, 78, 32, "Reward:")
    self.contents.draw_text(0, 128, 200, 32, "Current Objective:")
    self.contents.draw_text(0, 192, 220, 32, "Completed Objectives:")
    self.contents.font.color = normal_color
    text_width = contents.text_size(@quest.name).width
    self.contents.draw_text(0, 0, text_width, 32, @quest.name)
    text_width = contents.text_size(@quest.status).width
    self.contents.draw_text(608 - text_width, 0, text_width, 32, @quest.status)
    @quest.status_viewed
    text_width = contents.text_size(@quest.presenter).width
    self.contents.draw_text(0, 64, text_width, 32, @quest.presenter)
    self.contents.draw_text(608 - location_width, 64, location_width, 32, @quest.location)
    if @quest.reward == nil
      self.contents.draw_text(83, 96, 96, 32, "No reward")
    else
      text_width = contents.text_size(@quest.reward.text).width
      self.contents.blt(83, 100, @quest.reward.icon, Rect.new(0, 0, 24, 24))
      self.contents.draw_text(110, 96, text_width, 32, @quest.reward.text)
    end
    text_width =  contents.text_size(@quest.description).width
    self.contents.draw_text(0, 160, 608, 32, @quest.description)
    self.contents.font.color = disabled_color
    for i in 0...([@quest.shift_count, 7].min)
      y  = 224 + 32 * i
      text_width = contents.text_size(@quest.old_description(i)).width
      self.contents.draw_text(0, y, text_width, 32, @quest.old_description(i))
    end
  end

  def update
    loop do
      Graphics.update
      Input.update
      # Close the window if input is received
      if Input.trigger?(Input::C) or Input.trigger?(Input::B)
        $game_system.se_play($data_system.cancel_se)
        break
      end
    end
    dispose
  end
end

#-------------------------------------------------------------------------------
# CLASS: Window_Reward
# Summary: A simple class that shows the reward received after
# completing a quest and then waits for input from the player to
# terminate itself.
# Referenced From: Game_Reward
#-------------------------------------------------------------------------------
class Window_Reward < Window_Base

  def initialize(reward)
    window_width = [112, reward.text.length * 15 + 32].max
    super(320 - window_width / 2, 144, window_width, 96)
    self.contents = Bitmap.new(window_width - 32, 64)
    self.contents.font.name = $fontface
    self.contents.font.size = $fontsize
    self.contents.font.color = normal_color
    self.contents.draw_text(0, 0, 78, 32, "Reward:")
    self.contents.draw_text(0, 32,  window_width, 32, reward.text)
    update
  end

  def update
    # Close the window if input is received
    if Input.trigger?(Input::C) or Input.trigger?(Input::B)
      $game_system.se_play($data_system.decision_se)
      dispose
    end
  end
end

#-------------------------------------------------------------------------------
# CLASS: Window_LevelUp
# Summary: A simple class that shows the text "[actor name] Level Up!"
# then waits for input from the player to terminate itself.
# Referenced From: Game_Reward
#-------------------------------------------------------------------------------
class Window_LevelUp < Window_Base

  def initialize(actor)
    #Generate text
    @text = actor.name + " Level Up!"
    window_width = actor.name.length * 12 + 129
    super(320 - window_width / 2, 176, window_width, 64)
    self.contents = Bitmap.new(self.width - 32, 32)
    self.contents.font.name = $fontface
    self.contents.font.size = $fontsize
    self.contents.font.color = normal_color
    self.contents.draw_text(0, 0,  self.width - 32, 32, @text)
    update
  end

  def update
    # Close the window if input is received
    if Input.trigger?(Input::C) or Input.trigger?(Input::B)
      $game_system.se_play($data_system.decision_se)
      dispose
    end
  end
end


#-------------------------------------------------------------------------------
# CLASS: Window_QuestUpdate
# Summary: A window that is displayed when a quest is updated or received.
# Referenced From: Game_QuestBook
#-------------------------------------------------------------------------------
class Window_QuestUpdate < Window_Base

# Pass in true or false. If true, displays "New Quest!". If false, displays "Quest Updated!"
  def initialize(new, index, quest)
    #if new
    #  width = 141
    #else
    #  width = 177
    #end
    width = 300
    @timer = 80
    
    #super(320 - width / 2, 176, width, 64)
    super(10, 10, width, 64)
    self.contents = Bitmap.new(self.width - 32, self.height - 32)
    self.opacity = 0
    self.contents.font.name = $fontface
    self.contents.font.size = $fontsize - 1
    self.contents.font.color = normal_color
    
    Audio.se_play("Audio/SE/" + "055-Right01", 80, 100)
   # $game_system.se_play($data_system.decision_se) 

    if new
      self.contents.draw_text(0, 0, self.width - 32, 32, "New Quest: " + quest[index-1].name)
    else
      self.contents.draw_text(0, 0, self.width - 32, 32, "Quest Updated: " + quest[index-1].name)
    end
  end
  
  def update    
    @timer -= 1
    if @timer < 0
      self.visible = false
      Audio.se_play("Audio/SE/" + "055-Right01", 80, 100)
      self.dispose
    end
  end
end


#-------------------------------------------------------------------------------
# CLASS: Scene_QuestBook
# Summary: Displays the list of available quests and allows the player
# to select a specific quest for more information. Uses the Window_QuestList,
# Window_Help, and Window_QuestInfo classes.
#-------------------------------------------------------------------------------
class Scene_QuestBook
  def main
    # Window with list of quests
    @quest_window = Window_QuestList.new($game_questbook)
    # Window displaying task of highlighted quest
    @help_window = Window_Help.new
    @quest_window.help_window = @help_window
    
    Graphics.transition
    loop do 
      Graphics.update
      Input.update
      update
      if $scene != self
        break
      end
    end
    
    Graphics.freeze
    @quest_window.dispose
    @help_window.dispose
  end

  def update
    # Update the windows
    @quest_window.update
    @help_window.update
    # Cancel button (B) Returns to the item menu.
    if Input.trigger? (Input::B)
      $game_system.se_play($data_system.cancel_se)
      $scene = Scene_Menu.new(4)
    end
    # Select button (C) Selects the highlighted quest for viewing
    if Input.trigger? (Input::C)
      if $game_questbook[@quest_window.quest_index].available
        # Code for if the selected quest is available to the player
        $game_system.se_play($data_system.decision_se)
        # Disable the quest list window and help window
        @quest_window.active = false
        @quest_window.visible = false
        @help_window.active = false
        @help_window.visible = false
        # Create a new Window_QuestInfo object to display
        # information about the selected quest
        @info_window = Window_QuestInfo.new($game_questbook[@quest_window.quest_index])
        # Enable the quest list window and the help window
        @quest_window.active = true
        @quest_window.visible = true
        @help_window.active = true
        @help_window.visible = true
        @quest_window.refresh
      else
        # Code for if the selected quest is unavailable to the player
        $game_system.se_play($data_system.buzzer_se)
        return
      end
    end
  end
end

#-------------------------------------------------------------------------------
# Extra code for class Scene_Title
# Summary: This is where all the quests are created.
#-------------------------------------------------------------------------------
class Scene_Title
  def command_new_game
     $game_system.se_play($data_system.decision_se)
     Audio.bgm_stop
     Graphics.frame_count = 0
     $game_temp          = Game_Temp.new
     $game_system        = Game_System.new
     $game_switches      = Game_Switches.new
     $game_variables     = Game_Variables.new
     $game_self_switches = Game_SelfSwitches.new
     $game_screen        = Game_Screen.new
     $game_actors        = Game_Actors.new
     $game_party         = Game_Party.new
     $game_troop         = Game_Troop.new
     $game_map           = Game_Map.new
     $game_player        = Game_Player.new
     # QUESTS
     $game_questbook = Game_QuestBook.new()
     # Create all quests here
     # Format: $game_questbook.add_quest("", "", "", "gold/item/exp/none", 0/, "")
     # Note: Item ID is found in the database
     
     # QUESTS (
     quest1 = Game_Quest.new("The Switch", "Test World","", "gold", 20, "Pull the mysterious switch, see what happens.")
     
     $game_questbook.add_quest(quest1) #quests to be called by their index, for ease
     $game_party.setup_starting_members
     $game_map.setup($data_system.start_map_id)
     $game_player.moveto($data_system.start_x, $data_system.start_y)
     $game_player.refresh
     $game_map.autoplay
     $game_map.update
     $scene = Scene_Map.new
  end
end

#-------------------------------------------------------------------------------
# Extra code for Scene_Map
# Summary: Adds window updates to the map
#-------------------------------------------------------------------------------
class Scene_Map
  alias original_update write_update
  def update
    $game_questbook.update
    original_update
  end
end

#-------------------------------------------------------------------------------
# Extra code for Scene_Save
# Summary: Saves the questbook of the player. ($game_questbook)
#-------------------------------------------------------------------------------
class Scene_Save
  alias original_write write_save_data
  def write_save_data(file)
     original_write(file)
     Marshal.dump($game_questbook, file)
  end
end

#-------------------------------------------------------------------------------
# Extra code for Scene_Load
# Summary: Loads the questbook of the player. ($game_questbook)
#-------------------------------------------------------------------------------
class Scene_Load
  alias original_read read_save_data
  def read_save_data(file)
     original_read(file)
     $game_questbook = Marshal.load(file)
  end
end
Offline
Townie
Joined: 21 Mar 2011

Ah yeah, that would've definitely helped. The pre-formatting thing slipped my mind.

Getting syntax errors for the aliases in your Scene_Map add-ins.

Offline
Townie
Joined: 21 Mar 2011

... wish I could edit posts. Anyway, changed it so that $game_questbook.update was run from the original Scene_Map right before the normal message_window.update, and commented out the add-in. Works like a charm now. Now to tamper in a nice fade.

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

Ah, I finally found the problem...the line "alias original_update write_update" should actually read "alias original_update update" instead. So, that fixed your problem?

Offline
Townie
Joined: 21 Mar 2011

It did, and thanks to your thoroughness, I was able to replicate it to Reward, as well. Interesting secret with the "each_with_index", I had to hunt around to figure out what it did. 

Beyond that, what was the key to making it work?

Offline
Townie
Joined: 21 Mar 2011

I'm beginning to get the feeling that I could modify the "@window.each_with_index do" loop to create a queue of windows, where the next window waits for the first window to be disposed before printing the next. Am I on the right track, here? The problem that I see rising in future tests is one where two characters level up with the same quest, or when two updates are given too soon together, to a mash of overlapping windows. Or is that more complicated than I think it is?

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

The "secret" is that every time a window is created, it's added to an array with "$game_questbook.windows << Window_XYZ.new". Then Game_QuestBook has an #update method that updates all the windows; this is called from Scene_Map on every frame, inline with the map process.

If you wanted to make a queue of windows, you would first make all windows invisible on creation (self.visible = false in def initialize), then replace the "each_with_index" loop with something like this:

# Remove all windows at the beginning of the list while they exist and are disposed
@windows.shift while !@windows.empty? and @windows.first.disposed?
unless @windows.empty?
  @windows.first.visible = true
  @windows.first.update
end

This pattern of only manipulating the first element in the list is called a "queue," which in Ruby is built into the Array class (in other languages, there may be a dedicated Queue class).

Offline
Townie
Joined: 21 Mar 2011

I think I'd also have to change the timer system to only countdown if self.visible, otherwise it might dispose before ever being seen. 

I'm going to run a few experiments and see what I can do with this, thanks!

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

If you replace the loop as I suggested, the #update method in the window won't be called unless the window is active.

Offline
Townie
Joined: 21 Mar 2011

Alright, yeah, all that stuff worked wonderfully. I made Window_Levelup get pushed into right_windows[], since I had that message showing up on the other side of the screen, then just duplicated the processes for windows[] for right_windows[]. Works like a charm, and I've more or less run out of ideas to make things run even nicer. Thanks much!

meustrus's picture
Offline
RoyalÜber TownieUltra TownieMega TownieSuper TownieGreat TownieTownie
Joined: 18 Jan 2006

I'm always glad to help someone that isn't afraid to get his hands dirty scripting things for one's self.

Topic locked

Who's online

There are currently 0 users and 3 guests online.