[XP] Multiple Message Windows (Non-SDK)

Started by ForeverZer0, March 24, 2010, 04:07:12 pm

Previous topic - Next topic

ForeverZer0

March 24, 2010, 04:07:12 pm Last Edit: March 24, 2010, 04:30:17 pm by ForeverZero
Multiple Message Windows (Non-SDK)
Authors: Wachunga, ForeverZer0
Version: 1.5
Type: Custom Message System
Key Term: Custom Message System



Introduction

This is a heavily edited version of Wachunga's Multiple Message Script.  It has been edited to totally eliminate
the SDK requirement and the code has been optimized to give it a significant increase in performance. It will require
a few very simple edits to Scene_Map and Scene_Battle (I couldn't alias them, and didn't feel it was neccessary to have to over-write two classes just to alter 6 lines of code).  These changes are explained in detail inside the script, even if you have no scripting knowledge, you will be able to do it in 1 minute.  The edits shouldn't effect compatibility with any other scripts you have.


Features


  • Multiple Message Windows
  • Speech and Thought Balloons
  • Position over player/event (follows movement and scrolling)
  • Optional message tail (for speech or thought balloons)
  • Can specify location relative to player/event (up, down, left, right)
  • Can use different windowskin, message tail and font color
  • Letter-by-letter mode
  • Variable speed (and delays)
  • Skippable on button press
  • Autoresize messages
  • Player movement allowed during messages
  • If speaker moves offscreen, message closes (like ChronoTrigger)
  • Everything also works during battle
  • Settings configurable at anytime
  • Can revert back to normal messages at anytime



Screenshots

None right now.


Demo

http://www.mediafire.com/download.php?ixluj3ejniq


Script

Click here for the script.
Spoiler: ShowHide

=begin

Introduction:

 I personally like this script, but I can't stand all the compatibility issues
 that go along with having to use SDK, so I decided to eliminate that problem.
 It is not to hard to do to my own game where I can just go and 'hard-code'
 any changes I need to make that I can't alias, but I wanted to re-make the
 script to be a little easier to implement into any game, so here goes....
 
Changes Made:

 - I changed most of the ANSI syntax to classic syntax to help prevent
   an RGSS bug with conditioning. (ex. Any 'and' and 'or' has been replaced
   with '&&' and '||' respectively)
 - Unneeded double quoted strings have been changed to singles
   (ex.  "TEXT" has been replaced with 'TEXT')
 - Changed all 'for' iterators with 'each' iterators which should greatly
   improve performance.
 - Added compatibility switch if using Blizzard's Simple Shaded Text
 - Redefined Game_Player update method, which will allow character to move
   while a message is showing without using SDK methods.
 - Redefined the 'check_event_trigger_touch' method in Game_Player, which
   fixed a bug if using Blizzard's optimized default scripts.
 - And of course, most importantly, NO SDK!!!!

Instructions:

   Before you read any further, if you have no changes made in the Scene_Map
   or Scene_Battle just replace them with the respective files from the Demo.
   The edits are already in there, and nothing else has been altered. They
   will still work exactly as they should.
   
   There are three very minor edits that need to be made to Scene_Map and
   Scene_Battle 1. The edits are the same with both scripts, but it is better
   to just change them yourself than for me to have to provide an entire
   re-write of both classes for six lines of altered code. It normally would
   not be neccessay, but I couldn't find a simple way to just alias them.
   
   Side Note: These edits will not affect any other script. (%99.9 sure)

   
   Scene_Map and Scene_Battle (Part 1)
   
- Under MAIN
--------------------------------------------------------------------------------  
  @message_window = Window_Message.new
   
    *REPLACE WITH*

  @message_window = []
  @message_window[0] = Window_Message.new(0)
--------------------------------------------------------------------------------
  @message_window.dispose
   
    *REPLACE WITH*

  @message_window.each {|mw| mw.dispose}
--------------------------------------------------------------------------------

- Under UPDATE
--------------------------------------------------------------------------------
  @message_window.update

    *REPLACE WITH*
   
  @message_window.each {|mw| mw.update}
--------------------------------------------------------------------------------




#==============================================================================
# ** Multiple Message Windows
#------------------------------------------------------------------------------
# Originally written by Wachunga
# Optimized and De-SDK'ed by ForeverZer0
# 1.5
# 2006-11-17
#==============================================================================


::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

- Credit for the original script is 100% Wachunga. Original map and events
 are also Wachunga's original.

- Edits have been made by ForeverZer0 to eliminate the SDK requirement
 and improve coding and performance
- I have added a compatibility switch for Blizzard's simple shaded text if you
 are you using that, because it doesn't look right with message text. The
 switch will disable it whenever a message is showing, but otherwise will
 shade everything else.
- This script must be placed below Blizzard's Tons-of-Add-ons if you are
 using it. If you don't you will get an error. It has something to do with
 the Lagless HUD script. Some bug I couldn't isolate... If you find it, let
 me know, else just keep it under Tons.

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

 This custom message system adds numerous features on top of the default
 message system, the most notable being the ability to have multiple message
 windows open at once. The included features are mostly themed around turning
 messages into speech (and thought) balloons, but default-style messages are
 of course still possible.

 Note:
 This version of the script uses the SDK, available from:
 http://www.rmxp.org/forums/showthread.php?t=1802
 
 FEATURES
 
 New in 1.5:
 * \C[#ffffff] for hexadecimal color
 * \C for return to default color
 * display name of item, weapon, armor or skill
   * \N[In] = display name of item with id n (note the "I")
   * \N[Wn] = display name of weapon with id n (note the "W")
   * \N[An] = display name of armor with id n (note the "A")
   * \N[Sn] = display name of skill with id n (note the "S")
 * display icon of item, weapon, armor or skill
   * \I[In] = display icon of item with id n (note the "I")
   * \I[Wn] = display icon of weapon with id n (note the "W")
   * \I[An] = display icon of armor with id n (note the "A")
   * \I[Sn] = display icon of skill with id n (note the "S")
 * display icon and name of item, weapon, armor or skill
   * \I&N[In] = display icon and name of item with id n (note the "I")
   * \I&N[Wn] = display icon and name of weapon with id n (note the "W")
   * \I&N[An] = display icon and name of armor with id n (note the "A")
   * \I&N[Sn] = display icon and name of skill with id n (note the "S")
 * new windowskins available
 * speech windowskin now definable separately from default windowskin
 * fixed bold bug where degree sign would occasionally appear
 * input number window now shares parent window's font
 * changed \Var[n] back to default of \V[n]
 
 New in 1.1:
 * message.autocenter for automatically centering text within messages
 * \N[en] for displaying name of enemy with id n (note the "e")
 * \MAP for displaying the name of the current map
 
 At a glance:
 * multiple message windows
 * speech balloons
   * position over player/event (follows movement and scrolling)
   * optional message tail (for speech or thought balloons)
   * can specify location relative to player/event (up, down, left, right)
 * thought balloons
   * can use different windowskin, message tail and font color
 * letter-by-letter mode
   * variable speed (and delays)
   * skippable on button press
 * autoresize messages
 * player movement allowed during messages
   * if speaker moves offscreen, message closes (like ChronoTrigger)
 * everything also works during battle
 * settings configurable at anytime  
 
 Full list of options:

 (Note that all are case *insensitive*.)
 
 =============================================================================
  Local (specified in message itself and resets at message end)
 =============================================================================
 - \L = letter-by-letter mode toggle
 - \S[n] = set speed at which text appears in letter-by-letter mode
 - \D[n] = set delay (in frames) before next text appears
 - \P[n] = position message over event with id n
           * use n=0 for player
           * in battle, use n=a,b,c,d for actors (e.g. \P[a] for first actor)
             and n=1,...,n for enemies (e.g. \P[1] for first enemy)
             where order is actually the reverse of troop order (in database)
 - \P = position message over current event (default for floating messages)
 - \^ = message appears directly over its event
 - \v = message appears directly below its event
 - \< = message appears directly to the left of its event
 - \> = message appears directly to the right of its event
 - \B = bold text
 - \I = italic text
 - \C[#xxxxxx] = change to color specified by hexadecimal (eg. ffffff = white)
 - \C = change color back to default
 - \! = message autoclose
 - \? = wait for user input before continuing
 - \+ = make message appear at same time as preceding one
        * note: must be at the start of message to work
 - \@ = thought balloon
 - \N[En] = display name of enemy with id n (note the "E")
 - \N[In] = display name of item with id n (note the "I")
 - \N[Wn] = display name of weapon with id n (note the "W")
 - \N[An] = display name of armor with id n (note the "A")
 - \N[Sn] = display name of skill with id n (note the "S")
 - \I[In] = display icon of item with id n (note the "I")
 - \I[Wn] = display icon of weapon with id n (note the "W")
 - \I[An] = display icon of armor with id n (note the "A")
 - \I[Sn] = display icon of skill with id n (note the "S")
 - \I&N[In] = display icon and name of item with id n (note the "I")
 - \I&N[Wn] = display icon and name of weapon with id n (note the "W")
 - \I&N[An] = display icon and name of armor with id n (note the "A")
 - \I&N[Sn] = display icon and name of skill with id n (note the "S")
 - \MAP = display the name of the current map

 These are, of course, in addition to the default options:
 - \V[n] = display value of variable n
 - \N[n] = display name of actor with id n
 - \C[n] = change color to n
 - \G = display gold window
 - \\ = show the '\' character
 
 =============================================================================
  Global (specified below or by Call Script and persist until changed)
 =============================================================================
 
 Zer0 Note:
 If you use any of these in another script, etc. and not a script call, you
 will need to place "$game_system." before each command.
 
 Miscellaneous:
 - message.move_during = true/false
   * allow/disallow player to move during messages
 - message.show_pause = true/false
   * show/hide "waiting for player" pause graphic
 - message.autocenter = true/false
   * enable/disable automatically centering text within messages
 
 Speech/thought balloon related:
 - message.resize = true/false
   * enable/disable automatic resizing of messages (only as big as necessary)
 - message.floating = true/false
   * enable/disable positioning messages above current event by default
     (i.e. equivalent to including \P in every message)
 - message.location = TOP, BOTTOM, LEFT or RIGHT
   * set default location for floating messages relative to their event
 - message.show_tail = true/false
   * show/hide message tail for speech/thought balloons

 Letter-by-letter related:
 - message.letter_by_letter = true/false
   * enable/disable letter-by-letter mode (globally)
 - message.text_speed = 0-20
   * set speed at which text appears in letter-by-letter mode (globally)
 - message.skippable = true/false
   * allow/disallow player to skip to end of message with button press

 Font:
 - message.font_name = font
   * set font to use for text, overriding all defaults
     (font can be any TrueType from Windows/Fonts folder)
 - message.font_size = size
   * set size of text  (default 22), overriding all defaults
 - message.font_color = color
   * set color of text, overriding all defaults
   * you can use same 0-7 as for \C[n] or "#xxxxxx" for hexadecimal

 Note that the default thought and shout balloon windowskins don't
 stretch to four lines very well (unfortunately).
   
 Thanks:
 XRXS code for self-close and wait for input
 Slipknot for a convenient approach to altering settings in-game
 SephirothSpawn for bitmap rotate method
 
=end

#==============================================================================
# Settings
#==============================================================================

 # Zer0 Edit. Blizzard's Simple Shaded Text just looks kinda funny when used
 # on black text (which is default for MMS). If you are using Shaded Text and
 # do not want it to display for the message windows, set this to true. It will
 # still show for everything else.
 
 Blizzard_Shaded_Text_Fix = true
 
 #----------------------------------------------------------------------------
 # Windowskins
 #----------------------------------------------------------------------------
 # Note: all files must be in the Graphics/Windowskins/ folder
 # Tip: tails don't look right on windowskins with gradient backgrounds
 
 # filenames of tail and windowskin used for speech balloons
 FILENAME_SPEECH_TAIL = 'white-tail_speech.png'
 FILENAME_SPEECH_WINDOWSKIN = 'white-windowskin_speech.png'

 # filenames of tail and windowskin used for thought balloons
 FILENAME_THOUGHT_TAIL = 'white-tail_thought.png'
 FILENAME_THOUGHT_WINDOWSKIN = 'white-windowskin_thought.png'
 
 #----------------------------------------------------------------------------
 # Fonts
 #----------------------------------------------------------------------------
 # Note: if floating or resize (i.e. 'speech balloons') are disabled,
 # Font.default_name, Font.default_size and Font.default_color are used
 # (you can change these in Main)
 # During gameplay, you can use message.font_name etc to override all defaults
 
 # defaults for speech text
 SPEECH_FONT_COLOR = "#000000"
 SPEECH_FONT_NAME = ['Comic Sans MS', 'Arial']
 SPEECH_FONT_SIZE = 20
 
 # defaults for thought text
 THOUGHT_FONT_COLOR = "#000000"
 THOUGHT_FONT_NAME = ['Comic Sans MS', 'Arial']
 THOUGHT_FONT_SIZE = 20

 # note that you can use an array of fonts for SPEECH_FONT_NAME, etc.
 # e.g. ['Verdana', 'MS PGothic']
 # (if Verdana is not available, MS PGothic will be used instead)
 
 #----------------------------------------------------------------------------
 # Misc
 #----------------------------------------------------------------------------
 # If using a specialty windowskin (e.g. the default thought balloon one),
 # you can force the window width to always be a multiple of the number
 # specified in this constant (even when using the resize feature).
 # This allows, for example, the windowskin frame to be designed to
 # repeat every 16 pixels so that the frame never looks cut off.
 THOUGHT_WIDTH_MULTIPLE = 16
 # (set to 0 if you're not using any such windowskins)
 
class Game_Message

 # Any of the below can be changed by a Call Script event during gameplay.
 # E.g. turn letter-by-letter mode off with: message.letter_by_letter = false
 
 attr_accessor :move_during
 attr_accessor :letter_by_letter
 attr_accessor :text_speed
 attr_accessor :skippable
 attr_accessor :resize
 attr_accessor :floating
 attr_accessor :autocenter
 attr_accessor :show_tail
 attr_accessor :show_pause
 attr_accessor :location
 attr_accessor :font_name
 attr_accessor :font_size
 attr_accessor :font_color

 def initialize
   # whether or not messages appear one letter at a time
   @letter_by_letter = true
   # note: can also change within a single message with \L

   # the default speed at which text appears in letter-by-letter mode
   @text_speed = 0
   # note: can also change within a single message with \S[n]
   
   # whether or not players can skip to the end of (letter-by-letter) messages
   @skippable = true
   
   # whether or not messages are automatically resized based on the message
   @resize = true
   
   # whether or not message windows are positioned above
   # characters/events by default, i.e. without needing \P every message
   # (only works if resize messages enabled -- otherwise would look very odd)
   @floating = true
   
   # whether or not to automatically center lines within the message
   @autocenter = true
   
   # whether or not event-positioned messages have a tail(for speech balloons)
   # (only works if floating and resized messages enabled -- otherwise would
   # look very odd indeed)
   @show_tail = true
   
   # whether or not to display "waiting for user input" pause graphic
   # (probably want this disabled for speech balloons)
   @show_pause = false

   # whether the player is permitted to move while messages are displayed
   @move_during = true
   
   # the default location for floating messages (relative to the event)
   # note that an off-screen message will be "flipped" automatically
   @location = TOP
   
   # font details
   # overrides all defaults; leave nil to just use defaults (e.g. as above)
   @font_name = nil
   @font_size = nil
   @font_color = nil
 end
end

#==============================================================================
# Private constants (don't edit)
#==============================================================================

 # used for message.location
 TOP = 8
 BOTTOM = 2
 LEFT = 4
 RIGHT = 6

#------------------------------------------------------------------------------
 
class Window_Message < Window_Selectable
 
 def initialize(msgindex)
   super(80, 304, 480, 160)
   self.contents = Bitmap.new(width - 32, height - 32)
   self.visible = false
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   self.z = 9000 + msgindex * 5 # permits messages to overlap legibly
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   @fade_in = false
   @fade_out = false
   @contents_showing = false
   @cursor_width = 0
   self.active = false
   self.index = -1
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   @msgindex = msgindex
   @tail = Sprite.new
   @tail.bitmap =
     if @msgindex == 0
       RPG::Cache.windowskin(FILENAME_SPEECH_TAIL)
     else
       # don't use cached version or else all tails
       # are rotated when multiple are visible at once
       Bitmap.new('Graphics/Windowskins/'+FILENAME_SPEECH_TAIL)
     end
   # keep track of orientation of tail bitmap
   if @tail.bitmap.orientation == nil
     @tail.bitmap.orientation = 0
   end
   # make origin the center, not top left corner
   @tail.ox = @tail.bitmap.width/2
   @tail.oy = @tail.bitmap.height/2
   @tail.z = 9999
   @tail.visible = false
   if $game_system.message.floating && $game_system.message.resize
     @windowskin = FILENAME_SPEECH_WINDOWSKIN
   else
     # use windowskin specified in database
     @windowskin = $game_system.windowskin_name
   end
   if $game_system.message.floating && $game_system.message.resize
     # if used as speech balloons, use constants
     @font_name = SPEECH_FONT_NAME
     @font_color = check_color(SPEECH_FONT_COLOR)
     @font_size = SPEECH_FONT_SIZE
   else
     # use defaults
     @font_name = Font.default_name
     @font_color = Font.default_color
     @font_size = Font.default_size
   end
   # override defaults if necessary
   if $game_system.message.font_name != nil
     @font_name = $game_system.message.font_name
   end
   if $game_system.message.font_color != nil
     @font_color = check_color($game_system.message.font_color)
   end
   if $game_system.message.font_size != nil
     @font_size = $game_system.message.font_size
   end
   @update_text = true
   @letter_by_letter = $game_system.message.letter_by_letter
   @text_speed = $game_system.message.text_speed
   # id of character for speech balloons
   @float_id = nil
   # location of box relative to speaker
   @location = $game_system.message.location
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
 end
 
 def dispose
   terminate_message
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   # have to check all windows before claiming that no window is showing
   if $game_temp.message_text.compact.empty?
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
     $game_temp.message_window_showing = false
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   end
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   if @input_number_window != nil
     @input_number_window.dispose
   end
   super
 end
 
 def terminate_message
   self.active = false
   self.pause = false
   self.index = -1
   self.contents.clear
   # Clear showing flag
   @contents_showing = false

   # Clear variables related to text, choices, and number input
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   @tail.visible = false
   # note that these variables are now indexed arrays
   $game_temp.message_text[@msgindex] = nil
   # Call message callback
   if $game_temp.message_proc[@msgindex] != nil
     # make sure no message boxes are displaying
     if $game_temp.message_text.compact.empty?
       $game_temp.message_proc[@msgindex].call
     end
     $game_temp.message_proc[@msgindex] = nil
   end
   @update_text = true
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   $game_temp.choice_start = 99
   $game_temp.choice_max = 0
   $game_temp.choice_cancel_type = 0
   $game_temp.choice_proc = nil
   $game_temp.num_input_start = 99
   $game_temp.num_input_variable_id = 0
   $game_temp.num_input_digits_max = 0
   # Open gold window
   if @gold_window != nil
     @gold_window.dispose
     @gold_window = nil
   end
 end
 
 def refresh
   self.contents.clear
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   @x = @y = 0 # now instance variables
   @float_id = nil
   @location = $game_system.message.location
   if $game_system.message.floating && $game_system.message.resize
     @windowskin = FILENAME_SPEECH_WINDOWSKIN
   else
     # use windowskin specified in database
     @windowskin = $game_system.windowskin_name
   end
   if $game_system.message.floating && $game_system.message.resize
     # if used as speech balloons, use constants
     @font_name = SPEECH_FONT_NAME
     @font_color = check_color(SPEECH_FONT_COLOR)
     @font_size = SPEECH_FONT_SIZE
   else
     # use default font
     @font_name = Font.default_name
     @font_color = Font.default_color
     @font_size = Font.default_size
   end
   # override font defaults
   if $game_system.message.font_name != nil
     @font_name = $game_system.message.font_name
   end
   if $game_system.message.font_color != nil
     @font_color = check_color($game_system.message.font_color)
   end
   if $game_system.message.font_size != nil
     @font_size = $game_system.message.font_size
   end
   @line_widths = nil
   @wait_for_input = false
   @tail.bitmap =
     if @msgindex == 0
       RPG::Cache.windowskin(FILENAME_SPEECH_TAIL)
     else
       Bitmap.new('Graphics/Windowskins/'+FILENAME_SPEECH_TAIL)
     end
   RPG::Cache.windowskin(FILENAME_SPEECH_TAIL)
   @tail.bitmap.orientation = 0 if @tail.bitmap.orientation == nil
   @text_speed = $game_system.message.text_speed
   @letter_by_letter = $game_system.message.letter_by_letter
   @delay = @text_speed
   @player_skip = false
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   @cursor_width = 0
   # Indent if choice
   if $game_temp.choice_start == 0
     @x = 8
   end
   # If waiting for a message to be displayed
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   if $game_temp.message_text[@msgindex] != nil
     @text = $game_temp.message_text[@msgindex] # now an instance variable
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
     # Control text processing
     begin
       last_text = @text.clone
       @text.gsub!(/\\[Vv]\[([0-9]+)\]/) { $game_variables[$1.to_i] }
     end until @text == last_text
     @text.gsub!(/\\[Nn]\[([0-9]+)\]/) {
       $game_actors[$1.to_i] != nil ? $game_actors[$1.to_i].name : ''
     }
     # Change "\\\\" to "\000" for convenience
     @text.gsub!(/\\\\/) { "\000" }
     @text.gsub!(/\\[Gg]/) { "\002" }
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
     # display icon of item, weapon, armor or skill
     @text.gsub!(/\\[Ii]\[([IiWwAaSs][0-9]+)\]/) { "\013[#{$1}]" }
     # display name of enemy, item, weapon, armor or skill
     @text.gsub!(/\\[Nn]\[([EeIiWwAaSs])([0-9]+)\]/) {
       case $1.downcase
         when 'e'
           entity = $data_enemies[$2.to_i]
         when 'i'
           entity = $data_items[$2.to_i]
         when 'w'
           entity = $data_weapons[$2.to_i]
         when 'a'
           entity = $data_armors[$2.to_i]
         when 's'
           entity = $data_skills[$2.to_i]
       end
       entity != nil ? entity.name : ''
     }
     # display icon and name of item, weapon, armor or skill
     @text.gsub!(/\\[Ii]&[Nn]\[([IiWwAaSs])([0-9]+)\]/) {
       case $1.downcase
         when 'e'
           entity = $data_enemies[$2.to_i]
         when 'i'
           entity = $data_items[$2.to_i]
         when 'w'
           entity = $data_weapons[$2.to_i]
         when 'a'
           entity = $data_armors[$2.to_i]
         when 's'
           entity = $data_skills[$2.to_i]
       end
       entity != nil ? "\013[#{$1+$2}] " + entity.name : ''
     }      
     # display name of current map
     @text.gsub!(/\\[Mm][Aa][Pp]/) { $game_map.name }
     # change font color
     @text.gsub!(/\\[Cc]\[([0-9]+|#[0-9A-Fa-f]{6,6})\]/) { "\001[#{$1}]" }
     # return to default color
     @text.gsub!(/\\[Cc]/) { "\001" }
     # toggle letter-by-letter mode
     @text.gsub!(/\\[Ll]/) { "\003" }
     # change text speed (for letter-by-letter)
     @text.gsub!(/\\[Ss]\[([0-9]+)\]/) { "\004[#{$1}]" }
     # insert delay
     @text.gsub!(/\\[Dd]\[([0-9]+)\]/) { "\005[#{$1}]" }
     # self close message
     @text.gsub!(/\\[!]/) { "\006" }
     # wait for button input
     @text.gsub!(/\\[?]/) { "\007" }
     # bold
     @text.gsub!(/\\[Bb]/) { "\010" }
     # italic
     @text.gsub!(/\\[Ii]/) { "\011" }
     # thought balloon
     if @text.gsub!(/\\[@]/, '') != nil
       @windowskin = FILENAME_THOUGHT_WINDOWSKIN
       @font_name = THOUGHT_FONT_NAME
       @font_size = THOUGHT_FONT_SIZE
       @font_color = check_color(THOUGHT_FONT_COLOR)
       @tail.bitmap =
         if @msgindex == 0
           RPG::Cache.windowskin(FILENAME_THOUGHT_TAIL)
         else
           Bitmap.new('Graphics/Windowskins/'+FILENAME_THOUGHT_TAIL)
         end
       @tail.bitmap.orientation = 0 if @tail.bitmap.orientation == nil
     end
     # Get rid of "\\+" (multiple messages)
     @text.gsub!(/\\[+]/, '')
     # Get rid of "\\^", "\\v", "\\<", "\\>" (relative message location)
     if @text.gsub!(/\\\^/, '') != nil
       @location = 8
     elsif @text.gsub!(/\\[Vv]/, '') != nil
       @location = 2
     elsif @text.gsub!(/\\[<]/, '') != nil
       @location = 4
     elsif @text.gsub!(/\\[>]/, '') != nil
       @location = 6
     end
     # Get rid of "\\P" (position window to given character)
     if @text.gsub!(/\\[Pp]\[([0-9]+)\]/, '') != nil
       @float_id = $1.to_i
     elsif @text.gsub!(/\\[Pp]\[([a-zA-Z])\]/, '') != nil &&
         $game_temp.in_battle
       @float_id = $1.downcase
     elsif @text.gsub!(/\\[Pp]/, '') != nil ||
       ($game_system.message.floating && $game_system.message.resize) &&
       !$game_temp.in_battle
       @float_id = $game_system.map_interpreter.event_id
     end
     if $game_system.message.resize || $game_system.message.autocenter
       # calculate length of lines
       text = @text.clone
       temp_bitmap = Bitmap.new(1,1)
       temp_bitmap.font.name = @font_name
       temp_bitmap.font.size = @font_size
       @line_widths = [0,0,0,0]
       (0..3).each  do |i|
         line = text.split(/\n/)[3-i]
         if line == nil
           next
         end
         line.gsub!(/[\001-\007](\[[#A-Fa-f0-9]+\])?/, '')
         line.gsub!(/\013\[[IiWwAaSs][0-9]+\]/, "\013")
         line.chomp.split(//).each do |c|
           case c
             when "\000"
               c = "\\"
             when "\010"
               # bold
               temp_bitmap.font.bold = !temp_bitmap.font.bold
               next
             when "\011"
               # italics
               temp_bitmap.font.italic = !temp_bitmap.font.italic
               next
             when "\013"
               # icon
               @line_widths[3-i] += 24
               next
           end
           @line_widths[3-i] += temp_bitmap.text_size(c).width
         end
         if (3-i) >= $game_temp.choice_start
           # account for indenting
           @line_widths[3-i] += 8 unless $game_system.message.autocenter
         end
       end
       if $game_temp.num_input_variable_id > 0
         # determine cursor_width as in Window_InputNumber
         # (can't get from @input_number_window because it doesn't exist yet)
         cursor_width = temp_bitmap.text_size('0').width + 8
         # use this width to calculate line width (+8 for indent)
         input_number_width = cursor_width*$game_temp.num_input_digits_max
         input_number_width += 8 unless $game_system.message.autocenter
         @line_widths[$game_temp.num_input_start] = input_number_width
       end
       temp_bitmap.dispose
     end
     resize
     reposition if @float_id != nil
     self.contents.font.name = @font_name
     self.contents.font.size = @font_size
     self.contents.font.color = @font_color
     self.windowskin = RPG::Cache.windowskin(@windowskin)
     # autocenter first line if enabled
     # (subsequent lines are done as "\n" is encountered)
     if $game_system.message.autocenter && @text != ''
       @x = (self.width-40)/2 - @line_widths[0]/2
     end
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   end
 end

 #--------------------------------------------------------------------------
 # * Resize Window
 #--------------------------------------------------------------------------
 def resize
   if !$game_system.message.resize
     # reset to defaults
     self.width = 480
     self.height = 160
     self.contents = Bitmap.new(width - 32, height - 32)
     self.x = 80 # undo any centering
     return
   end
   max_x = @line_widths.max
   max_y = 4
   @line_widths.each {|line| max_y -= 1 if line == 0 && max_y > 1}
   if $game_temp.choice_max  > 0
     # account for indenting
     max_x += 8 unless $game_system.message.autocenter
   end
   new_width = max_x + 40
   if @windowskin == FILENAME_THOUGHT_WINDOWSKIN && THOUGHT_WIDTH_MULTIPLE >0
     # force window width to be a multiple of THOUGHT_WIDTH_MULTIPLE
     # so that specialty windowskins (e.g. thought balloon) look right
     if new_width % THOUGHT_WIDTH_MULTIPLE != 0
       new_width += THOUGHT_WIDTH_MULTIPLE-(new_width%THOUGHT_WIDTH_MULTIPLE)
     end
   end
   self.width = new_width
   self.height = max_y * 32 + 32
   self.contents = Bitmap.new(width - 32, height - 32)
   self.x = 320 - (self.width/2) # center
 end

 #--------------------------------------------------------------------------
 # * Reposition Window
 #--------------------------------------------------------------------------
 def reposition
   if $game_temp.in_battle
     if 'abcd'.include?(@float_id) # must be between a and d
       @float_id = @float_id[0] - 97 # a = 0, b = 1, c = 2, d = 3
       return if $scene.spriteset.actor_sprites[@float_id] == nil
       sprite = $scene.spriteset.actor_sprites[@float_id]
     else
       @float_id -= 1 # account for, e.g., player entering 1 for index 0
       return if $scene.spriteset.enemy_sprites[@float_id] == nil
       sprite = $scene.spriteset.enemy_sprites[@float_id]
     end
     char_height = sprite.height
     char_width = sprite.width
     char_x = sprite.x
     char_y = sprite.y - char_height/2
   else # not in battle...
     char = (@float_id == 0 ? $game_player : $game_map.events[@float_id])
     if char == nil
       # no such character
       @float_id = nil
       return
     end
     # close message (and stop event processing) if speaker is off-screen
     if char.screen_x <= 0 || char.screen_x >= 640 ||
        char.screen_y <= 0 || char.screen_y > 480
       terminate_message
       $game_system.map_interpreter.command_115
       return
     end
     char_height = RPG::Cache.character(char.character_name,0).height / 4
     char_width = RPG::Cache.character(char.character_name,0).width / 4
     # record coords of character's center
     char_x = char.screen_x
     char_y = char.screen_y - char_height/2
   end
   params = [char_height, char_width, char_x, char_y]
   # position window and message tail
   vars = new_position(params)
   x = vars[0]
   y = vars[1]
   # check if any window locations need to be "flipped"
   if @location == 4 && x < 0
     # switch to right
     @location = 6
     vars = new_position(params)
     x = vars[0]
     if (x + self.width) > 640
       # right is no good either...
       if y >= 0
         # switch to top
         @location = 8
         vars = new_position(params)
       else
         # switch to bottom
         @location = 2
         vars = new_position(params)
       end
     end
   elsif @location == 6 && (x + self.width) > 640
     # switch to left
     @location = 4
     vars = new_position(params)
     x = vars[0]
     if x < 0
       # left is no good either...
       if y >= 0
         # switch to top
         @location = 8
         vars = new_position(params)
       else
         # switch to bottom
         @location = 2
         vars = new_position(params)
       end
     end
   elsif @location == 8 && y < 0
     # switch to bottom
     @location = 2
     vars = new_position(params)
     y = vars[1]
     if (y + self.height) > 480
       # bottom is no good either...
       # note: this will probably never occur given only 3 lines of text
       x = vars[0]
       if x >= 0
         # switch to left
         @location = 4
         vars = new_position(params)
       else
         # switch to right
         @location = 6
         vars = new_position(params)
       end
     end
   elsif @location == 2 && (y + self.height) > 480
     # switch to top
     @location = 8
     vars = new_position(params)
     y = vars[1]
     if y < 0
       # top is no good either...
       # note: this will probably never occur given only 3 lines of text
       x = vars[0]
       if x >= 0
         # switch to left
         @location = 4
         vars = new_position(params)
       else
         # switch to right
         @location = 6
         vars = new_position(params)
       end
     end
   end
   x = vars[0]
   y = vars[1]
   tail_x = vars[2]
   tail_y = vars[3]    
   # adjust windows if near edge of screen
   if x < 0
     x = 0
   elsif (x + self.width) > 640
     x = 640 - self.width
   end
   if y < 0
     y = 0
   elsif (y + self.height) > 480
     y = 480 - self.height
   elsif $game_temp.in_battle && @location == 2 && (y > (320 - self.height))
     # when in battle, prevent enemy messages from overlapping battle status
     # (note that it could still happen from actor messages, though)
     y = 320 - self.height
     tail_y = y
   end
   # finalize positions
   self.x = x
   self.y = y
   @tail.x = tail_x
   @tail.y = tail_y
 end
 
 #--------------------------------------------------------------------------
 # * Determine New Window Position
 #--------------------------------------------------------------------------  
 def new_position(params)
   char_height = params[0]
   char_width = params[1]
   char_x = params[2]
   char_y = params[3]
   if @location == 8
     # top
     x = char_x - self.width/2
     y = char_y - char_height/2 - self.height - @tail.bitmap.height/2
     @tail.bitmap.rotation(0)
     tail_x = x + self.width/2
     tail_y = y + self.height
   elsif @location == 2
     # bottom
     x = char_x - self.width/2
     y = char_y + char_height/2 + @tail.bitmap.height/2
     @tail.bitmap.rotation(180)
     tail_x = x + self.width/2
     tail_y = y
   elsif @location == 4
     # left
     x = char_x - char_width/2 - self.width - @tail.bitmap.width/2
     y = char_y - self.height/2
     @tail.bitmap.rotation(270)
     tail_x = x + self.width
     tail_y = y + self.height/2
   elsif @location == 6
     # right
     x = char_x + char_width/2 + @tail.bitmap.width/2
     y = char_y - self.height/2
     @tail.bitmap.rotation(90)
     tail_x = x
     tail_y = y + self.height/2
   end
   return [x,y,tail_x,tail_y]
 end
     
 #--------------------------------------------------------------------------
 # * Update Text
 #--------------------------------------------------------------------------  
 def update_text
   if @text != nil
     # Get 1 text character in c (loop until unable to get text)
     while ((c = @text.slice!(/./m)) != nil)
       # If \\
       if c == "\000"
         # Return to original text
         c = "\\"
       end
       # If \C[n] or \C[#xxxxxx] or \C
       if c == "\001"
         # Change text color
         @text.sub!(/\[([0-9]+|#[0-9A-Fa-f]{6,6})\]/, '')
         if $1 != nil
           self.contents.font.color = check_color($1)
         else
           # return to default color
           if $game_system.message.font_color != nil
             color = check_color($game_system.message.font_color)
           elsif $game_system.message.floating && $game_system.message.resize
             color = check_color(SPEECH_FONT_COLOR)
           else
             # use defaults
             color = Font.default_color
           end
           self.contents.font.color = color
         end
         # go to next text
         next
       end
       # If \G
       if c == "\002"
         # Make gold window
         if @gold_window == nil
           @gold_window = Window_Gold.new
           @gold_window.x = 560 - @gold_window.width
           if $game_temp.in_battle
             @gold_window.y = 192
           else
             @gold_window.y = self.y >= 128 ? 32 : 384
           end
           @gold_window.opacity = self.opacity
           @gold_window.back_opacity = self.back_opacity
         end
         # go to next text
         next
       end
       # If \L
       if c == "\003"
         # toggle letter-by-letter mode
         @letter_by_letter = !@letter_by_letter
         # go to next text
         next
       end
       # If \S[n]
       if c == "\004"
         @text.sub!(/\[([0-9]+)\]/, '')
         speed = $1.to_i
         if speed >= 0
           @text_speed = speed
           # reset player skip after text speed change
           @player_skip = false            
         end
         return
       end
       # If \D[n]
       if c == "\005"
         @text.sub!(/\[([0-9]+)\]/, '')
         delay = $1.to_i
         if delay >= 0
           @delay += delay
           # reset player skip after delay
           @player_skip = false
         end
         return
       end  
       # If \!
       if c == "\006"
         # close message and return from method
         terminate_message
         return
       end
       # If \?
       if c == "\007"
         @wait_for_input = true
         return
       end
       if c == "\010"
         # bold
         self.contents.font.bold = !self.contents.font.bold
         return
       end
       if c == "\011"
         # italics
         self.contents.font.italic = !self.contents.font.italic
         return
       end
       if c == "\013"
         # display icon of item, weapon, armor or skill
         @text.sub!(/\[([IiWwAaSs])([0-9]+)\]/, '')
         case $1.downcase
           when 'i'
             item = $data_items[$2.to_i]
           when 'w'
             item = $data_weapons[$2.to_i]
           when 'a'
             item = $data_armors[$2.to_i]
           when 's'
             item = $data_skills[$2.to_i]
         end
         if item == nil
           return
         end
         bitmap = RPG::Cache.icon(item.icon_name)
         self.contents.blt(4+@x, 32*@y+4, bitmap, Rect.new(0, 0, 24, 24))
         @x += 24
         #self.contents.draw_text(x + 28, y, 212, 32, item.name)
         return
       end
       # If new line text
       if c == "\n"
         # Update cursor width if choice
         if @y >= $game_temp.choice_start
           width = $game_system.message.autocenter ? @line_widths[@y]+8 : @x
           @cursor_width = [@cursor_width, width].max
         end
         # Add 1 to y
         @y += 1
         if $game_system.message.autocenter && @text != ''
           @x = (self.width-40)/2 - @line_widths[@y]/2
         else
           @x = 0
           # Indent if choice
           if @y >= $game_temp.choice_start
             @x = 8
           end
         end
         # go to next text
         next
       end
       # Draw text
       self.contents.draw_text(4 + @x, 32 * @y, 40, 32, c)
       # Add x to drawn text width
       @x += self.contents.text_size( c ).width
       # add text speed to time to display next character
       @delay += @text_speed unless !@letter_by_letter || @player_skip
       return if @letter_by_letter && !@player_skip
     end
   end
   # If choice
   if $game_temp.choice_max > 0
     @item_max = $game_temp.choice_max
     self.active = true
     self.index = 0
   end
   # If number input
   if $game_temp.num_input_variable_id > 0
     digits_max = $game_temp.num_input_digits_max
     number = $game_variables[$game_temp.num_input_variable_id]
     @input_number_window = Window_InputNumber.new(digits_max)
     @input_number_window.set_font(@font_name, @font_size, @font_color)
     @input_number_window.number = number
     @input_number_window.x =
       if $game_system.message.autocenter
         offset = (self.width-40)/2-@line_widths[$game_temp.num_input_start]/2
         self.x + offset + 4
       else
         self.x + 8
       end
     @input_number_window.y = self.y + $game_temp.num_input_start * 32
   end
   @update_text = false
 end
 
 def reset_window
   if $game_temp.in_battle
     self.y = 16
   else
     case $game_system.message_position
     when 0  # up
       self.y = 16
     when 1  # middle
       self.y = 160
     when 2  # down
       self.y = 304
     end
   end
   if $game_system.message_frame == 0
     self.opacity = 255
   else
     self.opacity = 0
   end
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   # transparent speech balloons don't look right, so keep opacity at 255
   # self.back_opacity = 160
   @tail.opacity = 255
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
 end
 
 def update
   super
   # If fade in
   if @fade_in
     self.contents_opacity += 24
     if @input_number_window != nil
       @input_number_window.contents_opacity += 24
     end
     if self.contents_opacity == 255
       @fade_in = false
     end
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
     # return
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   end
   # If inputting number
   if @input_number_window != nil
     @input_number_window.update
     # Confirm
     if Input.trigger?(Input::C)
       $game_system.se_play($data_system.decision_se)
       $game_variables[$game_temp.num_input_variable_id] =
         @input_number_window.number
       $game_map.need_refresh = true
       # Dispose of number input window
       @input_number_window.dispose
       @input_number_window = nil
       terminate_message
     end
     return
   end
   # If message is being displayed
   if @contents_showing
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
     # Confirm or cancel finishes waiting for input or message
     if Input.trigger?(Input::C) || Input.trigger?(Input::B)
       if @wait_for_input
         @wait_for_input = false
         self.pause = false
       elsif $game_system.message.skippable
         @player_skip = true
       end
     end
     if need_reposition?
       reposition # update message position for character/screen movement
       if @contents_showing == false
         # i.e. if char moved off screen
         return
       end
     end
     if @update_text && !@wait_for_input
       if @delay == 0
         update_text
       else
         @delay -= 1
       end
       return
     end

     # If choice isn't being displayed, show pause sign
     if !self.pause && ($game_temp.choice_max == 0 || @wait_for_input)
       self.pause = true unless !$game_system.message.show_pause
     end
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
     # Cancel
     if Input.trigger?(Input::B)
       if $game_temp.choice_max > 0 && $game_temp.choice_cancel_type > 0
         $game_system.se_play($data_system.cancel_se)
         $game_temp.choice_proc.call($game_temp.choice_cancel_type - 1)
         terminate_message
       end
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
       # personal preference: cancel button should also continue
       terminate_message
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
     end
     # Confirm
     if Input.trigger?(Input::C)
       if $game_temp.choice_max > 0
         $game_system.se_play($data_system.decision_se)
         $game_temp.choice_proc.call(self.index)
       end
       terminate_message
     end
     return
   end
   # If display wait message or choice exists when not fading out
   if @fade_out == false && $game_temp.message_text[@msgindex] != nil
     @contents_showing = true
     $game_temp.message_window_showing = true
     reset_window
     refresh
     Graphics.frame_reset
     self.visible = true
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
     if show_message_tail?
       @tail.visible = true
     elsif @tail.visible
       @tail.visible = false
     end
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
     self.contents_opacity = 0
     if @input_number_window != nil
       @input_number_window.contents_opacity = 0
     end
     @fade_in = true
     return
   end
   # If message which should be displayed is not shown, but window is visible
   if self.visible
     @fade_out = true
     self.opacity -= 96
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
     @tail.opacity -= 96 if @tail.opacity > 0
     if need_reposition?
       reposition # update message position for character/screen movement
     end
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
     if self.opacity == 0
       self.visible = false
       @fade_out = false
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
       @tail.visible = false if @tail.visible
       # have to check all windows before claiming that no window is showing
       if $game_temp.message_text.compact.empty?
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
         $game_temp.message_window_showing = false  
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
       end
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
     end
     return
   end
 end
 
 #--------------------------------------------------------------------------
 # * Repositioning Determination
 #--------------------------------------------------------------------------
 def need_reposition?
   if !$game_temp.in_battle && $game_system.message.floating &&
       $game_system.message.resize && @float_id != nil
     if $game_system.message.move_during && @float_id == 0 &&
         (($game_player.last_real_x != $game_player.real_x) ||
         ($game_player.last_real_y != $game_player.real_y))
         # player with floating message moved
         # (note that relying on moving? leads to "jumpy" message boxes)
         return true
     elsif ($game_map.last_display_y != $game_map.display_y) ||
        ($game_map.last_display_x != $game_map.display_x)
       # player movement or scroll event caused the screen to scroll
       return true
     else
       char = $game_map.events[@float_id]
       if char != nil &&
         ((char.last_real_x != char.real_x) ||
         (char.last_real_y != char.real_y))
         # character moved
         return true
       end
     end    
   end
   return false
 end
 
 #--------------------------------------------------------------------------
 # * Show Message Tail Determination
 #--------------------------------------------------------------------------
 def show_message_tail?
   if $game_system.message.show_tail && $game_system.message.floating &&
     $game_system.message.resize && $game_system.message_frame == 0 &&
     @float_id != nil
     return true
   end
   return false
 end
 
 def update_cursor_rect
   if @index >= 0
     n = $game_temp.choice_start + @index
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
     if $game_system.message.autocenter
       x = 4 + (self.width-40)/2 - @cursor_width/2
     else
       x = 8
     end
     self.cursor_rect.set(x, n * 32, @cursor_width, 32)
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   else
     self.cursor_rect.empty
   end
 end

end

#------------------------------------------------------------------------------

class Game_Character
 attr_reader   :last_real_x                   # last map x-coordinate
 attr_reader   :last_real_y                   # last map y-coordinate
 alias wachunga_game_char_update update
 def update
   @last_real_x = @real_x
   @last_real_y = @real_y
   wachunga_game_char_update
 end
end

#------------------------------------------------------------------------------

class Game_Temp
 alias wachunga_mmw_game_temp_initialize initialize
 def initialize
   wachunga_mmw_game_temp_initialize
   @message_text = []
   @message_proc = []
 end
end

#------------------------------------------------------------------------------
 
class Sprite_Battler < RPG::Sprite
 # necessary for positioning messages relative to battlers
 attr_reader :height
 attr_reader :width
end

#------------------------------------------------------------------------------

class Scene_Battle
 # necessary for accessing actor/enemy sprites in battle
 attr_reader :spriteset
end

#------------------------------------------------------------------------------

class Spriteset_Battle
 # necessary for accessing actor/enemy sprites in battle
 attr_reader :actor_sprites
 attr_reader :enemy_sprites
end

#------------------------------------------------------------------------------

class Scene_Map
 
 #--------------------------------------------------------------------------
 # * New Message Window Addition
 #--------------------------------------------------------------------------
 def new_message_window(index)
   if @message_window[index] != nil
     # clear message windows at and after this index
     last_index = @message_window.size - 1
     last_index.downto(index) { |i|
       if @message_window[i] != nil
         @message_window[i].dispose
         @message_window[i] = nil
       end
     }
     @message_window.compact!
   end
   @message_window.push(Window_Message.new(index))
 end

end

#------------------------------------------------------------------------------

class Scene_Battle
 
 #--------------------------------------------------------------------------
 # * New Message Window Addition
 #--------------------------------------------------------------------------
 def new_message_window(index)
   if @message_window[index] != nil
     # clear message windows at and after this index
     last_index = @message_window.size - 1
     last_index.downto(index) { |i|
       if @message_window[i] != nil
         @message_window[i].dispose
         @message_window[i] = nil
       end
     }
     @message_window.compact!
   end
   @message_window.push(Window_Message.new(index))
 end

end

#------------------------------------------------------------------------------

class Game_System
 attr_reader :message
 
 alias wachunga_mmw_game_system_init initialize
 def initialize
   wachunga_mmw_game_system_init
   @message = Game_Message.new
 end
 
 # Zer0 Edit
 alias zer0_shaded_text_fix_upd update
 def update
   zer0_shaded_text_fix_upd
   if @SHADED_TEXT != nil && Blizzard_Shaded_Text_Fix == true
     if $game_temp.message_window_showing
       @SHADED_TEXT = false
     else
       @SHADED_TEXT = true
     end
   end
 end
end

#------------------------------------------------------------------------------

class Game_Player < Game_Character   # Zer0 edit

 # Redefines the update method
 def update
   # Remember whether or not moving in local variables
   last_moving = moving?
   # If moving, event running, move route forcing, and message window
   # display are all not occurring
   unless moving? ||
     @move_route_forcing ||
     ($game_system.map_interpreter.running? &&
     !$game_temp.message_window_showing) ||
     ($game_temp.message_window_showing &&
     !$game_system.message.move_during) ||
     ($game_temp.choice_max > 0 || $game_temp.num_input_digits_max > 0)
     # Move player in the direction the directional button is being pressed
     case Input.dir4
     when 2
       move_down
     when 4
       move_left
     when 6
       move_right
     when 8
       move_up
     end
   end
   # Remember coordinates in local variables
   last_real_x = @real_x
   last_real_y = @real_y
   super
   # If character moves down and is positioned lower than the center
   # of the screen
   if @real_y > last_real_y and @real_y - $game_map.display_y > CENTER_Y
     # Scroll map down
     $game_map.scroll_down(@real_y - last_real_y)
   end
   # If character moves left and is positioned more let on-screen than
   # center
   if @real_x < last_real_x and @real_x - $game_map.display_x < CENTER_X
     # Scroll map left
     $game_map.scroll_left(last_real_x - @real_x)
   end
   # If character moves right and is positioned more right on-screen than
   # center
   if @real_x > last_real_x and @real_x - $game_map.display_x > CENTER_X
     # Scroll map right
     $game_map.scroll_right(@real_x - last_real_x)
   end
   # If character moves up and is positioned higher than the center
   # of the screen
   if @real_y < last_real_y and @real_y - $game_map.display_y < CENTER_Y
     # Scroll map up
     $game_map.scroll_up(last_real_y - @real_y)
   end
   # If not moving
   unless moving?
     # If player was moving last time
     if last_moving
       # Event determinant is via touch of same position event
       result = check_event_trigger_here([1,2])
       # If event which started does not exist
       if result == false
         # Disregard if debug mode is ON and ctrl key was pressed
         unless $DEBUG and Input.press?(Input::CTRL)
           # Encounter countdown
           if @encounter_count > 0
             @encounter_count -= 1
           end
         end
       end
     end
     # If C button was pressed
     if Input.trigger?(Input::C)
       # Same position and front event determinant
       check_event_trigger_here([0])
       check_event_trigger_there([0,1,2])
     end
   end
 end
 
 #--------------------------------------------------------------------------
 # * Touch Event Starting Determinant
 #--------------------------------------------------------------------------
 # This is a fix if Blizzard's Optimized default scripts are being used
 
 def check_event_trigger_touch(x, y)
   result = false                         # <--------- MOVED from....
   # If event is running
   if $game_system.map_interpreter.running?
     return result
   end
   #result = false                        # <--------- ...HERE
   # All event loops
   $game_map.events.each_value {|event|
     # If event coordinates and triggers are consistent
     if event.x == x && event.y == y && [1,2].include?(event.trigger)
       # If starting determinant is front event (other than jumping)
       if !event.jumping? && !event.over_trigger?
         event.start
         result = true
       end
     end
   }
   return result
 end
 
end

#------------------------------------------------------------------------------
class Interpreter
 attr_reader :event_id
 
 alias wachunga_mmw_interp_setup setup
 def setup(list, event_id)
   wachunga_mmw_interp_setup(list, event_id)
   # index of window for the message
   @msgindex = 0
   # whether multiple messages are displaying
   @multi_message = false
 end
 
 def setup_choices(parameters)
   # Set choice item count to choice_max
   $game_temp.choice_max = parameters[0].size
   # Set choice to message_text
   parameters[0].each {|text| $game_temp.message_text[@msgindex] += text + "\n"}
   # Set cancel processing
   $game_temp.choice_cancel_type = parameters[1]
   # Set callback
   current_indent = @list[@index].indent
   $game_temp.choice_proc = Proc.new { |n| @branch[current_indent] = n }
 end
 
 #--------------------------------------------------------------------------
 # * Show Text
 #--------------------------------------------------------------------------
 def command_101
   # If other text has been set to message_text
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   if $game_temp.message_text[@msgindex] != nil
     if @multi_message
       @msgindex += 1
       $scene.new_message_window(@msgindex)
     else
       # End
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
       return false
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
     end
   end
   @msgindex = 0 if !@multi_message
   @multi_message = false
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   # Set message end waiting flag and callback
   @message_waiting = true
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   # just adding indexes
   $game_temp.message_proc[@msgindex] = Proc.new { @message_waiting = false }
   # Set message text on first line
   $game_temp.message_text[@msgindex] = @list[@index].parameters[0] + "\n"
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   line_count = 1
   # Loop
   loop do
     # If next event command text is on the second line or after
     if @list[@index+1].code == 401
       # Add the second line or after to message_text
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
       # just adding index
       $game_temp.message_text[@msgindex]+=@list[@index+1].parameters[0]+"\n"
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
       line_count += 1
     # If event command is not on the second line or after
     else
       # If next event command is show choices
       if @list[@index+1].code == 102
         # If choices fit on screen
         if @list[@index+1].parameters[0].size <= 4 - line_count
           # Advance index
           @index += 1
           # Choices setup
           $game_temp.choice_start = line_count
           setup_choices(@list[@index].parameters)
         end
       # If next event command is input number
       elsif @list[@index+1].code == 103
         # If number input window fits on screen
         if line_count < 4
           # Advance index
           @index += 1
           # Number input setup
           $game_temp.num_input_start = line_count
           $game_temp.num_input_variable_id = @list[@index].parameters[0]
           $game_temp.num_input_digits_max = @list[@index].parameters[1]
         end
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
       # start multimessage if next line is "Show Text" starting with "\\+"
       elsif @list[@index+1].code == 101
         if @list[@index+1].parameters[0][0..1]=="\\+"
           @multi_message = true
           @message_waiting = false
         end
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
       end
       # Continue
       return true
     end
     # Advance index
     @index += 1
   end
 end
 
 #--------------------------------------------------------------------------
 # * Show Choices
 #--------------------------------------------------------------------------
 def command_102
   # If text has been set to message_text
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   # just adding index
   if $game_temp.message_text[@msgindex] != nil
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
     # End
     return false
   end
   # Set message end waiting flag and callback
   @message_waiting = true
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   # adding more indexes
   $game_temp.message_proc[@msgindex] = Proc.new { @message_waiting = false }
   # Choices setup
   $game_temp.message_text[@msgindex] = ''
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   $game_temp.choice_start = 0
   setup_choices(@parameters)
   # Continue
   return true
 end

 #--------------------------------------------------------------------------
 # * Input Number
 #--------------------------------------------------------------------------
 def command_103
   # If text has been set to message_text
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   # just adding index
   if $game_temp.message_text[@msgindex] != nil
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
     # End
     return false
   end
   # Set message end waiting flag and callback
   @message_waiting = true
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   # adding more indexes
   $game_temp.message_proc[@msgindex] = Proc.new { @message_waiting = false }
   # Number input setup
   $game_temp.message_text[@msgindex] = ''
#------------------------------------------------------------------------------
# End Multiple Message Windows Edit
#------------------------------------------------------------------------------
   $game_temp.num_input_start = 0
   $game_temp.num_input_variable_id = @parameters[0]
   $game_temp.num_input_digits_max = @parameters[1]
   # Continue
   return true
 end
 
 #--------------------------------------------------------------------------
 # * Script
 #--------------------------------------------------------------------------
 # Fix for RMXP bug: call script boxes that return false hang the game
 # See, e.g., http://rmxp.org/forums/showthread.php?p=106639  
 #--------------------------------------------------------------------------
 def command_355
   # Set first line to script
   script = @list[@index].parameters[0] + "\n"
   # Loop
   loop do
     # If next event command is second line of script or after
     if @list[@index+1].code == 655
       # Add second line or after to script
       script += @list[@index+1].parameters[0] + "\n"
     # If event command is not second line or after
     else
       # Abort loop
       break
     end
     # Advance index
     @index += 1
   end
   # Evaluation
   result = eval(script)
   # If return value is false
   if result == false
     # End
#------------------------------------------------------------------------------
# Begin Edit
#------------------------------------------------------------------------------
     #return false
#------------------------------------------------------------------------------
# End Edit
#------------------------------------------------------------------------------
   end
   # Continue
   return true
 end
 
 def message
   message = $game_system.message if $game_system != nil
 end
 
end

#------------------------------------------------------------------------------

class Game_Map
 attr_accessor :last_display_x                # last display x-coord * 128
 attr_accessor :last_display_y                # last display y-coord * 128
 
 alias wachunga_mmw_game_map_update update
 def update
   @last_display_x = @display_x
   @last_display_y = @display_y
   wachunga_mmw_game_map_update
 end
 
 def name
   return load_data('Data/MapInfos.rxdata')[@map_id].name
 end
end
 
#------------------------------------------------------------------------------

class Bitmap
 
 attr_accessor :orientation

 #--------------------------------------------------------------------------
 # * Rotation Calculation
 #--------------------------------------------------------------------------
 def rotation(target)
   return if not [0, 90, 180, 270].include?(target) # invalid orientation
   if @rotation != target
     degrees = target - @orientation
     if degrees < 0
       degrees += 360
     end
     rotate(degrees)
   end    
 end
 
 #--------------------------------------------------------------------------
 # * Rotate Square (Clockwise)
 #--------------------------------------------------------------------------
 def rotate(degrees = 90)
   # method originally by SephirothSpawn
   # would just use Sprite.angle but its rotation is buggy
   # (see http://www.rmxp.org/forums/showthread.php?t=12044)
   return if not [90, 180, 270].include?(degrees)
   copy = self.clone
   if degrees == 90
     # Passes Through all Pixels on Dummy Bitmap
     (0...self.height).each {|i|
       (0...self.width).each {|j|
         self.set_pixel(width - i - 1, j, copy.get_pixel(j, i))
       }
     }
   elsif degrees == 180
     (0...self.height).each {|i|
       (0...self.width).each {|j|
         self.set_pixel(width - j - 1, height - i - 1, copy.get_pixel(j, i))
       }
     }      
   elsif degrees == 270
     (0...self.height).each {|i|
       (0...self.width).each {|j|
         self.set_pixel(i, height - j - 1, copy.get_pixel(j, i))
       }
     }
   end
   @orientation = (@orientation + degrees) % 360
 end

end

#------------------------------------------------------------------------------

class Window_Base
 
 #--------------------------------------------------------------------------
 # * Check Color
 #     color : color to check
 #--------------------------------------------------------------------------
 def check_color(color)
   if color.type == Color
     # already a Color object
     return color
   elsif color[0].chr == "#"
     # specified as hexadecimal
     r = color[1..2].hex
     g = color[3..4].hex
     b = color[5..6].hex
     return Color.new(r,g,b)
   else
     # specified as integer (0-7)
     color = color.to_i
     if color >= 0 && color <= 7
       return text_color(color)
     end
   end
   return normal_color
 end
 
end

#------------------------------------------------------------------------------

class Window_InputNumber < Window_Base

 def set_font(fname, fsize, fcolor)
   return if fname == nil && fsize == nil && fcolor == nil
   # Calculate cursor width from number width
   dummy_bitmap = Bitmap.new(32, 32)
   dummy_bitmap.font.name = fname
   dummy_bitmap.font.size = fsize
   @cursor_width = dummy_bitmap.text_size('0').width + 8
   dummy_bitmap.dispose
   self.width = @cursor_width * @digits_max + 32
   self.contents = Bitmap.new(width - 32, height - 32)
   self.contents.font.name = fname
   self.contents.font.size = fsize
   self.contents.font.color = check_color(fcolor)
   refresh
   update_cursor_rect
 end

 def refresh
   self.contents.clear
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   #self.contents.font.color = normal_color
#------------------------------------------------------------------------------
# Begin Multiple Message Windows Edit
#------------------------------------------------------------------------------
   s = sprintf("%0*d", @digits_max, @number)
   (0...@digits_max).each {|i|
     self.contents.draw_text(i * @cursor_width + 4, 0, 32, 32, s[i,1])
   }
 end
 
end




Instructions

Place below default scripts and Tons-of-Add-ons, if you have it, and above main.
Full instructions are within the script.


Compatibility

Probably not compatible with other Custom Message Systems.
Will likely cause issues with scripts that heavily alter the Window_Message class.
If you use a Custom Movement script, it will likely disable the ability to move while a message is showing unless
you do some editing in the update method for Game_Player in the script.
Other than that, should be relatively compatible with most other scripts.
If you want it to be compatible with SDK, use the original version!


Credits and Thanks


  • Wachunga, for the original write of the script
  • ForeverZer0, for the optimization, editing, and de-SDK-ing



Author's Notes

All the graphics needed are in the demo. If anybody finds any bugs, let me know and I'll be happy to try and fix them.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

(Hexamin)

Ooo, goody, I've been wondering what message system I'd be using for my project (seeing as I'm too lazy to make my own).  This looks great!  A+!
Max 1111101000; characters remaining: 1110111000

Jragyn

Does this include a method to increase the choice # size as well?
(ie: having 8 choices instead of just 4 all at once)

It was a feature that I have seen in Ccoa's, and now wondering if its in here :o


A bright light can either illuminate or blind, but how will you know which until you open your eyes?

ForeverZer0

QuoteDoes this include a method to increase the choice # size as well?
(ie: having 8 choices instead of just 4 all at once)



No, not yet. I haven't looked over her script yet. I'll check it out.
I may incude something like that in the future...
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Ravenith

Easily one of the best message systems, now redeemed from the evil SDK :)
*levels up*

SBR*

Wow, nice script! Thank you! But I have got a question: Could you make it so that when you come close enough to the NPC, the NPC will show a message. Maybe with 'Math.hypot'? I tried to do that in an event, but for some reason when making it parallel process, the message shows above the player and when I try to talk to somebody else, he/she says the same as I wanted the NPC with the parallel process to say. That would be a GREAT add-on to this script for ANY game, especcialy online games :P.

ForeverZer0

QuoteCould you make it so that when you come close enough to the NPC, the NPC will show a message.


I'm not exactly sure what your problem is, but if it set to a parallel trigger, it could be the trigger executing over and over again.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

LiTTleDRAgo

umm... im sorry for bumping this thread
but i want to ask is there a way for this script can work with blizz abs?

stripe103

Yea. I'd like to know too. It is the best message script I've seen but if it don't work with Blizz-ABS I can't use it.

ForeverZer0

I imagine it doesn't by default, though it will likely not take a whole lot to make it compatible.
If I remember correctly, pixel-movement scripts tend to mess with it, which BABS has built in.
This messes with allowing the windows to move with with a character as it moves. I did not write the original, but disabling this feature of it should solve the problem. I don't have time at the second to look into it, but I would suggest looking into the Game_Character class from this script and other parts that deal with how the window finds the proper coordinates.
Sometime here when I get a chance, I may take a look into it and see if I can come up with a permanent fix for it in an updated version.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Taiine

If this works right with BABS, AND had a player face portrait system such that is used in Hermes Extends RMXP's MEssage System... I would so be grabbing this.

ForeverZer0

Quote from: Taiine on September 30, 2010, 12:12:59 pm
If this works right with BABS, AND had a player face portrait system such that is used in Hermes Extends RMXP's MEssage System... I would so be grabbing this.


Not sure how the faces would look with auto-sized window balloons. Might look kinda funny.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

LiTTleDRAgo

How about if the faces appear outside of the baloon?

like this
#-------#  ________________________
#       # (                        )
#       #  ------------V-----------
#-------#

statesman88

I think I found a bug... if message.floating is set to false but \p is used at the beginning of a message, it centers the message, but it doesn't use the speech bubble windowskin or tail. You could supplement a fix by creating another command that temporarily deactivates message.floating.

This script is really useful--it doesn't have as many features as Ccoa's UMS, but I like this script's features better. Lots of cool stuff in it. :)

Xuroth

Sorry to necropost but is it possible to change windowskins per message? Like have one character say something, and you could do a script call before the show text command to change it?

AliveDrive

Quote from: Blizzard on September 09, 2011, 02:26:33 am
The permanent solution for your problem would be to stop hanging out with stupid people.

Xuroth

March 03, 2011, 03:23:55 pm #16 Last Edit: March 03, 2011, 05:26:06 pm by Xuroth
well... other than global ($) variables, what others do you define outside of a method? if you look at his script, the area you set the windowskin file name is not in a method. and its a constant.

Or could it use a module for the config, set a variable to the file name, and @windowskin can just be set to that?


I edited the script. I added attr_accessor :windowskin_file into class Game_Message. I then set all relevant variables in each method of each class in the script to point to this. If I try a script call

$game_system.message.windowskin_file = 'thorns' 


it comes back with an error saying it "encountered a Syntax Error while running the script" when i enter the game and activate the call (FYI i also modified the script to add '.png' when searching for the file, so I dont have to add that to calls) it works if you just create a new global variable though... now its not a problem of getting the windows to show different skins from a script call. now the problem is why doesn't it work this way? (just trying to de-bug my own mistake and learn something from it...)

Blizzard

If you are using this as script call, it probably gets broken into two lines. Make sure that the first line ends with the . (period) character so that the system knows that the command expands into the next line.
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.

ForeverZer0

March 03, 2011, 06:01:13 pm #18 Last Edit: March 03, 2011, 06:08:12 pm by ForeverZer0
First, in script calls you can omit the $game_system.message" and simiply use "message". It has been shortcutted in the Interpreter to call the longer name.

Two, file extensions are not needed for image files in RMXP. The system gets the appropriate file without any extension for all the basics: .png, .jpg, .bmp
Any script to add them for you automatically is completely worthless.

I may clean up the script someday and add an extra function or two, but keep in mind it is not my script, but mostly just removed the SDK from it, got rid of some worthless code, and made some minor adjustments to help with performance.


EDIT:
I am going to edit this script either today or tomorrow. I looked at it and see all sorts of things that could be improved.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Xuroth

Wow... so I really edited this and didn't destroy the script for once? Nice! that means more edits! I'll probably add some features I've been wanting.

Thanks Blizz for the tip!

@ForeverZer0: I only added the parts to add .png because when I looked at your config, the windowskins both had the extension listed. Thanks for letting me know its not needed.

I am still going to edit the script myself 1)because I am not redistributing it; 2) It's a learning experience for me; and 3) I can add new functionality with other scripts in my game.

I look forward to seeing your edits. I will try to challenge myself to optimize it myself. Then I can compare what I did and what you did to enhance/clean it and learn some more.

I hope one day to be comparable with "The legends of RMXP/RGSS" like you and Blizz, and some others.

Cheers!