Script Body

#=====================================================
# Bitmap + Script                                        Coder: Shadowtext
#=====================================================
# This is a simple script to extend the functionality of the "Bitmap" class.
# It adds two functions to the basic class, which are documented below.
#=====================================================

class Bitmap
  #-----------------------------------------------------------------------------------------
  # draw_bar(x,y,width,height,fill_percent[,fill_color][,back_color])
  #-----------------------------------------------------------------------------------------
  # Draws a simple progress bar / meter on the bitmap.
  #   x = The x coordinate where the bar should be drawn. (Within the Bitmap)
  #   y = The y coordinate where the bar should be drawn. (Within the Bitmap)
  #   width = The width of the bar.
  #  height = The height of the bar.
  #  fill_percent = The percentage that the bar should be filled.
  #  fill_color = The color of the "filled" portion of the bar.
  #  back_color = The color of the "empty" portion of the bar.
  #-----------------------------------------------------------------------------------------
  def draw_bar(x,y,width,height,fill_percent,fill_color = COLOR_BAR_FILL,back_color = COLOR_BAR_BACK)
    fill_width = fill_percent * (width - 7) / 100
    
    # Borders
    self.fill_rect (x + 1, y, width - 2, height, COLOR_BAR_OUTLINE)
    self.fill_rect (x, y + 1, width, height - 2, COLOR_BAR_OUTLINE)
    self.fill_rect (x + 2, y + 1, width - 4, height - 2, COLOR_WHITE)
    self.fill_rect (x + 1, y + 2, width - 2, height - 4, COLOR_WHITE)
    
    # Basin
    self.fill_rect (x + 3, y + 3, width - 7, height - 6, back_color)
    self.fill_rect (x + 3, y + 3, fill_width, height - 6, fill_color)
    
    # Inside Corners
    self.fill_rect (x + 4, y + 3, 1, 1, COLOR_BLACK)
    self.fill_rect (x + 4, y + height - 4, 1, 1, COLOR_BLACK)
    self.fill_rect (x + width - 5, y + 3, 1, 1, COLOR_BLACK)
    self.fill_rect (x + width - 5, y + height - 4, 1, 1, COLOR_BLACK)
    
    # Inside Borders
    self.fill_rect (x + 4, y + 2, width - 8, 1, COLOR_BLACK)
    self.fill_rect (x + 4, y + height - 3, width - 8, 1, COLOR_BLACK)
    self.fill_rect (x + 3, y + 4, 1, height - 8, COLOR_BLACK)
    self.fill_rect (x + width - 4, y + 4, 1, height - 8, COLOR_BLACK)
  end
  
  #-----------------------------------------------------------------------------------------
  # draw_text_outlined(x,y,w,h,text[,align])
  #-----------------------------------------------------------------------------------------
  # Draws outlined text to the bitmap.
  #  x = The x coordinate where the text should be drawn. (Within the Bitmap)
  #  y = The y coordinate where the text should be drawn. (Within the Bitmap)
  #  w = The width of the container block for the text.
  #  h = The height of the container block for the text.
  #  text = The text to draw.
  #  align = Alignment within the container block. 0 = Left, 1 = Center, 2 = Right.
  #-----------------------------------------------------------------------------------------
  def draw_text_outlined(x,y,w,h,text,align = 0)
    color1 = self.font.color.clone
    
    color2 = Color.new(255 - color1.red,255 - color1.green,255 - color1.blue,(color1.alpha / 2))
    color3 = COLOR_CLEAR
    
    self.font.color = color2
    draw_text(x - FONT_OUTLINE_SIZE,y,w,h,text,align)
    draw_text(x,y - FONT_OUTLINE_SIZE,w,h,text,align)
    draw_text(x + FONT_OUTLINE_SIZE,y,w,h,text,align)
    draw_text(x,y + FONT_OUTLINE_SIZE,w,h,text,align)
   
    #self.font.color = color3
    #draw_text(x,y,w,h,text,align)
    
    self.font.color = color1
    draw_text(x,y,w,h,text,align)
  end
end
#==============================================================================
# Console
#==============================================================================
# This module works much like the Logging module, only instead of saving to a
# file, output goes to a debugging window. The window only shows up in Debug
# mode, so launching it from RMXP it will show up, but it won't when run from
# Game.exe.
# To use the console, simply call Console::setup() from your Main script, then
# use echo(string) from any other script you want, like you would print().
#
# The Console allows for quick access to information without having to open a log
# and without bringing up a message window that halts progress, so it's perfect
for having to track changes in a loop with many iterations. My own tests show
# no lag, so it should be all good.
#==============================================================================
module Console
 attr_reader    :bufferHandle
 
 GENERIC_READ                  = 0x80000000
 GENERIC_WRITE                 = 0x40000000
 FILE_SHARE_READ              = 0x00000001
 FILE_SHARE_WRITE             = 0x00000002    
 
 CONSOLE_TEXTMODE_BUFFER   = 0x00000001

 def Console::AllocConsole
   return @apiAllocConsole.call
 end
 
 def Console::CreateConsoleScreenBuffer(dwDesiredAccess,dwShareMode,dwFlags)
   return @apiCreateConsoleScreenBuffer.call(dwDesiredAccess,dwShareMode,nil,dwFlags,nil)
 end
 
 def Console::WriteConsole(lpBuffer)
   hFile = @bufferHandle
   return @apiWriteConsole.call(hFile,lpBuffer,lpBuffer.size,0,0)
 end
 
 def Console::SetConsoleActiveScreenBuffer(hScreenBuffer)
   return @apiSetConsoleActiveScreenBuffer.call(hScreenBuffer)
 end
  
  def Console::SetConsoleTitle(title)
    return @apiSetConsoleTitle.call(title)
  end
 
 def self.setup
   unless $DEBUG
     return
   end
   @apiAllocConsole = Win32API.new("kernel32","AllocConsole","","l")
   @apiCreateConsoleScreenBuffer = Win32API.new("kernel32","CreateConsoleScreenBuffer",['l','l','p','l','p'],"l")
   @apiSetConsoleActiveScreenBuffer = Win32API.new("kernel32","SetConsoleActiveScreenBuffer","l","s")
   @apiWriteConsole = Win32API.new("kernel32","WriteConsole","lpnnn","S")
   @apiSetConsoleTitle = Win32API.new("kernel32","SetConsoleTitle","p","s")
    
   access = (GENERIC_READ | GENERIC_WRITE)
   sharemode = (FILE_SHARE_READ | FILE_SHARE_WRITE)
   
   returnCode = AllocConsole()
   @bufferHandle = CreateConsoleScreenBuffer(access,sharemode,CONSOLE_TEXTMODE_BUFFER)
   
   f = File.open("Game.ini")
   lines = f.readlines()
   s = lines[3]
   len = s.size
   title = (s[6,len - 7])
    
   SetConsoleTitle("Debug Console -- #{title}")
   echo "#{title} Output Window\n"
   echo "-------------------------------\n"
   echo "If you are seeing this window, you are running\n"
   echo "#{title} in Debug Mode. This means\n"
   echo "that you're either playing a Debug Version, or\n"
   echo "you are playing from within RPG Maker XP.\n"
   echo "\n"
   echo "Closing this window will close the game. If \n"
   echo "you want to get rid of this window, run the\n"
   echo "program from the Shell, or download a Release\n"
   echo "version.\n"
   echo "\n"
   echo "Gameplay will be paused while the console has\n"
   echo "focus. To resume playing, switch to the Game\n"
   echo "Window.\n"
   echo "-------------------------------\n"
   echo "Debug Ouput:\n"
   echo "-------------------------------\n\n"
   SetConsoleActiveScreenBuffer(@bufferHandle)
 end  
 
 def self.get_input
   loop do
     poo = $stdin
     if poo[poo.size - 1] == "\n"
       break
     end
   end
   echo poo
 end
end

module Kernel
 def echo(string)
   unless $DEBUG
     return
   end
   Console::WriteConsole(string)
 end
end
#======================================================
# Geometry module                             ver 1.0 |
#======================================================
#               By: Shadowtext (shadowtext@gmail.com) |
# This module is designed to do some geometric        |
# functions, including drawing lines and shapes for   |
# whatever crazy purpose you need to draw lines and   |
# shapes for. It also draws text balloons. Yay.       |
#======================================================
module Geometry
  #sqr: Squares the provided value. I'm surprised they didn't have
  #a squaring or exponent raising function to begin with. I tried
  #just redoing Numeric to include "^," but I couldn't figure out
  #how to get the value of numeric. Probably something to do with
  #recievers or something else that I'm too tired to figure out
  #at the moment.
  def self.sqr(i)
    return i * i
  end
  
  #quad: provide it with a, b, and c, and it'll do the quadratic
  #equation for you. Y'know, I don't think I actually ended up
  #using this function, even though the early versions of a lot
  #of my classes had to use 'em. Anyway, a is what you multiply
  #x^2 by, b is what you multiply x by, and c is everything else.
  #If you don't know the quadratic equation, you probably shouldn't
  #be using this function anyway, though.
  def self.quad(a,b,c)
    x = [2]
    x[0] = (-b) + Math.sqrt(Geometry.sqr(b) - 4 * a * c)
    x[0] /= (2 * a)
    x[1] = (-b) - Math.sqrt(Geometry.sqr(b) - 4 * a * c)
    x[1] /= (2 * a)
    return x
  end
  
  #====================================================
  # Point class                                       |
  #====================================================
  # It's like a Rect object, only with only x,y       |
  # values. Anyway, you declare it as Point.new(x,y)  |
  # You can find the distance between two points just |
  # by subtracting them, like d = p1 - p2. To change  |
  # the point's location, use the move! function.     |
  # Finally, to_s turns the thing into a string in the|
  # format of "(x,y)". Like good old Cartesian Coor-  |
  # dinates.                                          |
  #====================================================
  class Point

    def initialize(x,y)
      @ax = x
      @ay = y
    end
    
    def -(i)
      return Math.sqrt(Geometry.sqr(i.x - x) + Geometry.sqr(i.y - y))
    end
    
    def x
      return @ax
    end
    
    def y
      return @ay
    end
    
    def move(mx,my)
      return Point.new(@ax += mx, @ay += my)
    end
    
    def move!(mx,my)
      @ax += mx
      @ay += my
    end
    
    def to_s
      return "(" + @ax.to_s + "," + @ay.to_s + ")"
    end
    
    def is_on?(line)
      unless line.is_a? Line
        print "Type Mismatch in module Geometry.\nPoint.is_on? was passed a variable of type #{line.class}."
        return false
      end
      if self.x < line.smallx
        return false
      end
      if self.x > line.bigx
        return false
      end
      if self.y < line.smally
        return false
      end
      if self.y > line.bigy
        return false
      end
      if line.slope != false
        targ_y = line.get_y(self.x)
        bool = (self.y >= (targ_y - 1)) and (self.y <= (targ_y + 1))
      else
        targ_x = line.get_x(self.y)
        bool = (self.x >= (targ_x - 1)) and (self.x <= (targ_x + 1))
      end
      return bool
    end
  end

  #====================================================
  # Line class                                        |
  #====================================================
  # Declare it with Line.new(x1,x2,y1,y2). I know, it |
  # should probably declare with points, or at least  |
  # as x1,y1,x2,y2. But I did it this way, and I'd    |
  # have to go and change all my declarations if I    |
  # changed it now.                                   |
  # width is the linewidth when drawing, and color is |
  # the color that it's drawn in. smallx, smally, bigx|
  # and bigy are the smallest x, smallest y, biggest x|
  # and biggest y values for the line respectively.   |
  # Length is the actual distance between point 1 and |
  # point 2, and find_point will find a point on the  |
  # the line that is the given percent (* 100) of the |
  # total length away from the starting point, point  |
  # 1.                                                |
  # crosses and intersects will compare it to         | 
  # another line and find if it intersects.           |
  # slope is the change in y divided by the change in |
  # x, which you should know from geography class.    |
  # yint is where the thing touches when x is zero.   |
  # Finally, if you provide the function draw(bmp)    |
  # with a bitmap, it'll draw the line to the bitmap. |
  #====================================================

  class Line
    attr_reader :x1
    attr_reader :x2
    attr_reader :y1
    attr_reader :y2
    attr_accessor :width
    attr_accessor :color
    
    def initialize(x1, x2, y1, y2)
      @x1 = x1
      @x2 = x2
      @y1 = y1
      @y2 = y2
      @color = Color.new(0,0,0,255)
      @width = 2
    end
    
    def to_s
      return "(" + @x1.to_s + "," + @y1.to_s + "); (" + @x2.to_s + "," + @y2.to_s + ")\n\tSlope=" + slope.to_s + "\n\tY-Int=" + yint.to_s
    end
    
    def get_x(y)
      if slope != false
        x = (y - yint) / slope
        return x
      else
        return smallx
      end
    end
    
    def get_y(x)
      if slope == false
        return false
      end
      y = slope * x + yint
      if y < smally or y > smally
        return false
      end
      return y
    end
    
    def smallx
      if @x1 < @x2
        return @x1
      else
        return @x2
      end
    end
    
    def smally
      if @y1 < @y2
        return @y1
      else
        return @y2
      end
    end
    
    def bigx
      if @x1 > @x2
        return @x1
      else
        return @x2
      end
    end
    
    def bigy
      if @y1 > @y2
        return @y1
      else
        return @y2
      end
    end
    
    def length
      changey = @y1 - @y2
      changex = @x1 - @x2
      rad = (changey * changey) + (changex * changex)
      val = Math.sqrt(rad)
      return val
    end
    
    def find_point(distance)
      # Distance should be the percentage from the starting point
      # that the found point should be.
      draw_distance = distance * length / 100.0
      
      if slope == false
        x = @x1
 
        if @y1 - draw_distance < smally
          return Point.new(x,(@y1 + draw_distance).to_i)
        else
          return Point.new(x,(@y1 - draw_distance).to_i)
        end
      end
      if slope == 0
        x = @x1 + draw_distance
        y = @y1
        return Point.new(x,y)
      end
      
      a = Point.new(x1,y2)
      b = Point.new(x2,y2)
      c = Point.new(x1,x1)
      
      ab = a - b
      bc = b - c
      ca = a - c
      cp = draw_distance
      dp = ab * cp / bc
      dc = ca * cp / bc
      
      if c.x == smallx
        x = c.x + dp
      else
        x = c.x - dp
      end
      
      if c.y == smally
        y = c.y + dc
      else
        y = c.y - dc
      end
      
      p = Point.new(x,y)
      return p
    end
  
    def slope
      dify = (@y1 - @y2) * 1.0
      difx = (@x1 - @x2) * 1.0

      if difx == 0
        return false
      end
      return dify / difx
    end
    
    def yint
      if slope == false
        return false
      else
        return @y1 - slope * @x1
      end
    end
    
    def crosses(line,tolerance = 1,check_parallels = false)
      return intersects(line,tolerance)
    end
    
    def check_parallels(line2)
      line1 = self
      x1 = line1.smallx
      x2 = line2.smallx
      x3 = line1.bigx
      x4 = line2.bigx
      bool = false
      print "Point Validity" +
      "\nx1=#{line1.get_y(x1) != false}" +
      "\nx2=#{line2.get_y(x2) != false}" +
      "\nx3=#{line1.get_y(x3) != false}" +
      "\nx4=#{line2.get_y(x4) != false}" +
      
      bool |= line1.get_y(x1) != false
      bool |= line2.get_y(x2) != false
      bool |= line1.get_y(x3) != false
      bool |= line2.get_y(x4) != false
      
      return bool
    end
    
    def intersects(line2,tolerance = 1,check_parallels = false)
      line1 = self
      unless line1.is_a? Line and line2.is_a? Line
        return nil
      end
      if line1.slope == line2.slope
        if check_parallels == true
          return check_parallels(line2)
        end
      else
        return nil
      end
      
      if line1.slope == false
        x = line1.x1
        y = line2.get_y(x)
      elsif line2.slope == false
        x = line2.x1
        y = line1.get_y(x)
      else
        x = (line2.yint - line1.yint) / (line1.slope - line2.slope)
        y = line1.get_y(x)
      end
      
      if x < (line1.smallx - tolerance) or x < (line2.smallx - tolerance)
        return nil
      end
      
      if x > (line1.bigx + tolerance) or x > (line2.bigx + tolerance)
        return nil
      end
      #print Point.new(x,y).to_s + 
      #  "\n\nLine1" + 
      #  "\n\tSmall\tBig" +
      #  "\nX=\t#{line1.smallx}\t#{line1.bigx}" + 
      #  "\nY=\t#{line1.smally}\t#{line1.bigy}" + 
      #  "\n\nLine 2" + 
      #  "\n\tSmall\tBig" +
      #  "\nX=\t#{line2.smallx}\t#{line2.bigx}" + 
      #  "\nY=\t#{line2.smally}\t#{line2.bigy}"
      #if y < (line1.smally - tolerance) or y < (line2.smallx - tolerance)
      #  return nil
      #end
      
      #if y > (line1.bigy + tolerance) or y > (line2.bigy + tolerance)
      #  return nil
      #end
      
      return Point.new(x,y)
    end
    
    def draw(bitmap)
      if slope == false
        bitmap.fill_rect smallx.round, smally.round, @width, bigy.round - smally.round, @color
        return
      end
      for x in smallx..bigx
        y = (((slope * x) + yint) + 0.5).to_i
        height = ([slope,1].max) * @width
        while y + height > (bigy + @width)
          height -= 1
        end
        bitmap.fill_rect x - (@width / 2), y - (@width / 2), @width, height, @color
      end
    end
  end

  #====================================================
  # Polygon class                                     |
  #====================================================
  # The Polygon class is built from an array of points|
  # that you provide during initialization. 3 points  |
  # is the minumum that can make up a polygon. Anyway,|
  # It'll build the polygon for you, and even draw it |
  # and fill it in. The bounding box is a Rect        |
  # produced during initialization that holds every   |
  # point the polygon will have in it. linewidth sets |
  # how big the lines will be drawn, and linecolor and|
  # fillcolor set what color your lines and fills     |
  # should be. The boolean fill, off by default, tells|
  # the draw function to fill in the Polygon. Oh, and |
  # the draw function draws to a provided bitmap.     |
  # Whee.
  #====================================================
  class Polygon
    attr_reader   :points
    attr_reader   :lines
    attr_reader   :linecolor
    attr_accessor :fill
    attr_accessor :fillcolor
    attr_reader :linewidth
    
    def initialize(points)
      unless points.size > 2
        return nil
      end
      @fill = false
      @linecolor = Color.new(0,0,0,255)
      @fillcolor = Color.new(0,0,0,255)
      @linewidth = 2
      @points = points

      lines = []
      for i in 0...points.size
        point = points[i]
        unless point == points.last
          nextpoint = points[i + 1]
        else
          nextpoint = points.first
        end
        unless point.is_a? Point and point.x and point.y
          return nil
        end
        unless nextpoint.is_a? Point and nextpoint.x and nextpoint.y
          return nil
        end
        lines.push Line.new(point.x, nextpoint.x,point.y, nextpoint.y)
      end
      @lines = lines
      define_box
    end
    
    def linecolor=(color)
      @linecolor = color
      for i in @lines
        i.color = color
      end
    end
    
    def linewidth=(width)
      @linewidth = width
      for i in @lines
        i.width = width
      end
    end
    
    def define_box
      smallx = 1024
      smally = 1024
      bigx = 0
      bigy = 0
      for i in @points
        if i.x > bigx
          bigx = i.x
        end
        if i.x < smallx
          smallx = i.x
        end
        if i.y > bigy
          bigy = i.y
        end
        if i.y < smally
          smally = i.y
        end
      end
      smallx -= linewidth
      smally -= linewidth
      bigx += linewidth
      bigy += linewidth
      width = bigx - smallx
      height = bigy - smally
      @bounding_box = Rect.new(smallx, smally, width, height)
    end
      
    def scanline(y, bmp)
      
      test = Line.new(@bounding_box.x,@bounding_box.x + @bounding_box.width,y,y)
      x_points = []
      x_points.push Point.new(@bounding_box.x,y)
      x_points.push Point.new(@bounding_box.x + @bounding_box.width,y)
      
      for i in @lines
        poo = i.crosses test
        #unless x_points.include? poo
          x_points.push poo if poo
        #end
      end

      x_points.uniq!
      x_points.sort! {|a,b| a.x <=> b.x}
      
      poo = x_points.clone
      if x_points.size > 4
        for i in 0...x_points.size
          thispoint = x_points[i]
          if thispoint == x_points.last or i == 0
            next
          end
          nextpoint = x_points[i + 1]
          if nextpoint == x_points.last
            next
          end
          if (thispoint - nextpoint) < 4
            poo.slice! i 
          end
        end
      end
      
      x_points = poo
      if x_points.size <= 3
        return
      end

      for i in 0...x_points.size
        if i % 2 == 1
          thispoint = x_points[i]
          unless x_points[i] == x_points.last
            nextpoint = x_points[i + 1]
          else
            next
          end
          bmp.fill_rect thispoint.x, 
                        y, 
                        nextpoint.x - thispoint.x,
                        1,
                        @fillcolor
        end
      end
    end
    
    def draw bmp
      if @fill == true
        for y in @bounding_box.y...(@bounding_box.y + @bounding_box.height)
          scanline(y,bmp)
        end
      end
      
      for i in @lines
        i.draw bmp
      end
      
      @bitmap = bmp
    end
    
    def to_s
      return @points.join("\n")
    end
  end
  
  #====================================================
  # Ellipse class                                     |
  #====================================================
  # One of those round sideways oval thingies. Anyway,|
  # give it a center point, and two axes (the width   |
  # and height, really), and it'll draw you up an     |
  # ellipse nice and proper. Just tell it to draw and |
  # give it a bitmap to draw to, and it'll do it.     |
  #====================================================
  class Ellipse
    attr_reader   :semimajor
    attr_reader   :semiminor
    attr_accessor :linewidth
    attr_accessor :linecolor
    attr_accessor :fill
    attr_accessor :fillcolor
    
    def initialize(center, axis1, axis2)
      if axis1 > axis2
        @majoraxis = axis1
        @minoraxis = axis2
      else
        @majoraxis = axis2
        @minoraxis = axis1
      end
      @semimajor = @majoraxis / 2
      @semiminor = @minoraxis / 2
      @center = center
      
      c = Math.sqrt(Geometry.sqr(@semimajor) - Geometry.sqr(@semiminor))
      x1 = center.x - c.round
      x2 = center.x + c.round
      y = center.y
      @focus1 = Point.new(x1,y)
      @focus2 = Point.new(x2,y)
      
      @linewidth = 2
      @linecolor = Color.new(0,0,0,255)
      @fillcolor = Color.new(255,255,255,255)
      @fill = false
      @semimajor = semimajor
      @semiminor = semiminor
    end
    
    def get_ys(x)
      i = @center.x
      j = @center.y
      maj = @semimajor
      min = @semiminor
      ys = []
      a2 = Geometry.sqr(maj)
      b2 = Geometry.sqr(min)
      x2 = Geometry.sqr(x - i)
      foo = ((a2 * b2) - (b2 * x2)) / a2
      val = Math.sqrt(foo)
      return [nil,nil] if val.nan? 
      ys[0] = (j - val).round
      ys[1] = (j + val).round
      return ys
    end
    
    def get_xs(y)
      i = @center.x
      j = @center.y
      maj = @semimajor
      min = @semiminor
      a2 = Geometry.sqr(maj)
      b2 = Geometry.sqr(min)
      y2 = Geometry.sqr(y - j)
      foo = ((a2 * b2) - (a2 * y2)) / b2
      val = Math.sqrt(foo)
      return [nil,nil] if val.nan? 
      xs = []
      xs[0] = (i - val).round
      xs[1] = (i + val).round
      return xs
    end
    
    def smallx
      return get_xs(@center.y).min.round
    end
    
    def bigx
      return get_xs(@center.y).max.round
    end
    
    def smally
      return get_ys(@center.x).min.round
    end
    
    def bigy
      return get_ys(@center.x).max.round
    end
    
    def draw(bmp)
      if @fill == true
        for i in smallx..bigx
          ys = get_ys(i)
          if ys[0] and ys[1]
            h = ys.max - ys.min
            bmp.fill_rect i,ys.min,1,h,@fillcolor
          end
        end
      end
      
      for i in smallx..bigx
        ys = get_ys(i)
        if ys[0]
          bmp.fill_rect i - (@linewidth / 2), 
                        ys.min - (@linewidth / 2),
                        @linewidth,
                        @linewidth,
                        @linecolor
        end
        
        if ys[1]
          bmp.fill_rect i - (@linewidth / 2), 
                        ys.max - (@linewidth / 2),
                        @linewidth,
                        @linewidth,
                        @linecolor
        end
      end
      
      for i in smally..bigy
        xs = get_xs(i)
        if xs[0]
          bmp.fill_rect xs[0] - (@linewidth / 2),
                        i - (@linewidth / 2),
                        @linewidth,
                        @linewidth,
                        @linecolor
        end
        if xs[1]
          bmp.fill_rect xs[1] - (@linewidth / 2),
                        i - (@linewidth / 2),
                        @linewidth,
                        @linewidth,
                        @linecolor
        end
      end
    end
  end
end
#==============================================================================
# Logging
#==============================================================================
# This module allows for quick logging of any data the designer wants. It is worked into
# the Kernel, and therefore after this script has been loaded, the designer has very little
# work for himself.
#
# Simply use the line "log(string)" anywhere in your code, and the string you give it will be logged
# to the logfile. You can also use "log_s(title,string)" to automatically draw a "section" in a log, which is one
# that is set off by its title an ascii box before drawing the actual text.string you give it.
#
# The file will bear the name of your project as defined in "Game.ini." with the spaces removed, and the 
# extension ".log." So if you were working on "Taret Blade," your logfile would be "taretblade.log"
#
# Use this feature when "print" would not be feasible, such as when you have to log a lot of data at once,
# or check something from within a loop or iterator.
#==============================================================================

module Logging
  attr_reader       :separator
  attr_reader       :line

  def self.setup
    if $DEBUG
      @separator = "=============================================================================="
      @line = "------------------------------------------------------------------------------"
      f = File.open("Game.ini")
      lines = f.readlines()
      s = (lines[3].gsub(/( )/,"")).downcase
      len = s.size
      title = (s[6,len - 7]) + ".log"
      
      @logstream = File.new(title,"w+")
      
      chronos = Time.new
      @logstream.puts @separator,
                      "*** BEGIN LOG ***",
                      @line,
                      chronos.to_s,
                      @separator
    end
  end
  
  def self.write(txt, leading_line = true)
    if $DEBUG
      @logstream.puts "" if leading_line
      if txt.is_a?(Array)
        for i in txt
          if i.is_a? String
            if i == "="
              @logstream.puts @separator
            elsif i == "-"
              @logstream.puts @line
            else
              @logstream.puts i
            end
          else
            print "Error: Logging module can only write strings or arrays of strings."
          end
        end
        return
      elsif txt.is_a? String
        if txt == "="
          @logstream.puts @separator
        elsif txt == "-"
          @logstream.puts @line
        else
          @logstream.puts txt
        end
        return
      else
        print "Error: Logging module can only write strings or arrays of strings."
        return
      end
    end
  end
  
  def self.write_section(title,txt)
    @logstream.puts ""
    @logstream.puts @separator
    @logstream.puts title
    @logstream.puts @line
    if txt.is_a?(Array)
      for i in txt
        if i.is_a? String
          if i == "="
            @logstream.puts @separator
          elsif i == "-"
            @logstream.puts @line
          else
            @logstream.puts i
          end
        else
          print "Error: Logging module can only write strings or arrays of strings."
        end
        return
      end
    elsif txt.is_a?(String)
      if txt == "="
        @logstream.puts @separator
      elsif txt == "-"
        @logstream.puts @line
      else
        @logstream.puts txt
      end
      return
    else
      print "Error: Logging module can only write strings or arrays of strings."
      return
    end
  end
  
  def self.close
    if $DEBUG
      chronos = Time.new
      if @logstream
        @logstream.puts @separator,
                        "*** END LOG ***",
                        @line,
                        chronos.to_s,
                        @separator
      end
                    
    end
  end
end

module Kernel
  def log(txt, lead = true)
    Logging.write(txt, lead)
  end
  
  def log_s(title, txt)
    Logging.write_section(title, txt)
  end
    
  def log_table(table)
    s = "Table Contents:"
    for y in 0...table.ysize
      s += "\n\t"
      for x in 0...table.xsize
        s += "#{table[x,y]}, "
      end
    end
    
    log s
  end
end
#==============================================================================
# ** Main+ Script                                                                    by: Shadowtext
#------------------------------------------------------------------------------
#  This is a modified "Main" script that allows you to use ".rb" files as external RGSS 
# scripts. You can download some from my site, as well as a few other sites around
# the net. (Most of the others I've seen doing this are Japanese, though).
#
# You can distribute your own scripts easier this way. But keep in mind THIS script
# can't really be "required," so instead of downloading it, you should copy and paste
# it into your "Main" script.
#==============================================================================

begin
  # Prepare for transition
  Graphics.freeze
  
  # The next three lines will allow you to use the "require" statement
  # to access any ".rb" file stored in your project's root directory, or the
  # "scripts" subdirectory of the project's root directory. Note that you
  # must create the "scripts" directory manually.
  $:.clear
  $:.push "."
  $:.push ".//scripts"
  
  # The following require statements are here as examples, and should be commented
  # out if you have not downloaded or do not plan to use the scripts available on this site.
  require "bitmap+.rb"
  require "geometry.rb"
  require "string_table.rb"
  require "textbubble.rb"
  
  $scene = Scene_Title.new
  # Call main method as long as $scene is effective
  while $scene != nil
    $scene.main
  end

  # Fade out
  Graphics.transition(20)
rescue Errno::ENOENT
  # Supplement Errno::ENOENT exception
  # If unable to open file, display message and end
  filename = $!.message.sub("No such file or directory - ", "")
  print("Unable to find file #{filename}.")
end
#=====================================================
# String Table Script                                    Coder: Shadowtext
#=====================================================
# This is a script to use string tables rather than putting in text directly
# within RMXP. The main purpose for using String Tables instead of
# just hard-coding the text is that String Tables make translation or 
# dialogue editting much easier.
#
# Admittedly, as few projects get finished as do, translation into other
# languages might seem silly, but here it is anyway.
#
# Also, as mentioned earlier, it can make dialogue editting easier, in that
# you will not have to find the proper events in which to change the
# dialogue, instead just editting it within the string table.
#
#=====================================================
# USING THE STRING TABLE CLASS
#=====================================================
# In the "main" script, add a declaration of "$data_strings = Data_Strings.new"
#
# Now, any time you want to get a string from the table, just do "$data_strings[index]",
# where index is the line (starting from zero) where the string can be found in
# the table.

# You can change the language with $data_strings.lang_id. This should be the
# line number of the language as it appears in Languages.dat.

#=====================================================
# MAKING THE STRING TABLE AND LANGUAGES.DAT FILE:
#=====================================================
# The string table class uses two or more files. The first is a file in the
# "/data/" directory of the project named "Languages.dat." This
# language file is a text file, with each line representing one of the
# languages that the string tables are available in.
# Each line should have the name of the language, followed by a tab,
# and then the font that the language should be displayed in within
# RMXP.

# The other type of file is the string table itself. This will be a text file
# named "(*).txtdata", where (*) is the name of the language named in
# Languages.dat
#
# The format of a string table is very simple. Each line (the first line is 
# considered #0) is a string entry.It starts with the name of the speaker, 
# which is followed by a tab, and then has the text for the entry.
#
#=====================================================
# USEFUL FUNCTIONS AND PROPERTIES
#=====================================================
# $data_strings.language - the name of the active language.
# $data_strings.font - the name of the font for the currently active language.
#
# $data_strings[index].to_s - returns the entry as a string. Change the constants below to
#                                                alter how .to_s behaves.
# $data_strings[index].speaker - returns a string containing the speaker's name for this entry.
# $data_strings[index].text - returns a string containing the text for this entry.
#
#=====================================================

SPEAKER_MOD  =  "\C[1]"              # The string to preface the speaker's name with
                                                      # in the function "to_s()"
SPEAKER_UNMOD  =  "\C[0] -"    # The string to end the speaker's name with
                                                                              # in the function "to_s()"
SHOW_SPEAKER_NAME = true          # Sets whether or not the speaker's name is displayed
                                                                              # in the function "to_s()"
                                                    
class Data_Strings
  attr_reader          :data
  attr_reader          :lang_id
  
  def initialize()
    @data = [] 
    langfil = File.new("data/Languages.dat","r")
    @fonts = []
    @languages = []
    for i in langfil.readlines
      str = i.slice!(/\t[\w\W]*/)
      len = str.size - 1
      @fonts.push str[1,len]
      @languages.push i
    end
    @lang_id = 0
    langfil.close
  end
  
  def language
    return @languages[@lang_id]
  end

  def font
    return @fonts[@lang_id]
  end

  def lang_id=(value)
    @lang_id = value
    $fontface = self.font
  end

  def [](index)
    unless @data[index].is_a? StringEntry
      @data[index] = StringEntry.new(index,self.language)
    end
    if @data[index].language != self.language
      @data[index] = StringEntry.new(index,self.language)
    end
    return @data[index]
  end
end

class StringEntry
  attr_reader          :speaker
  attr_reader          :text
  attr_reader          :index
  attr_reader          :language

  def initialize(index,language)
    @index = index
    @language = language
    get_data()
  end

  def get_data()
    strfile = File.new("data/#{@language}.txtdata","r")
    str = strfile.readlines[@index]
    @text = str.slice!(/\t[\w\W]*/)
    len = @text.size - 1
    @text = @text[1,len][0,len - 1]
    @speaker = str
    strfile.close
  end

  def to_s()
    if @speaker != ""
       if SHOW_SPEAKER_NAME == true
       return "#{SPEAKER_MOD}#{@speaker}#{SPEAKER_UNMOD} -\n\"#{@text}\""
    else
       return "#{@text}\""
    else
    return "#{@text}\""
  end
end
#======================================================
# Message Bubble Sript                        ver 0.8 |
#======================================================
# By: Shadowtext (journeyman.shadowtext@gmail.com)    |
#                                                     |
# This script is designed to draw message bubbles in  |
# RPG Maker XP. It requires the Geometry Module.      |
#======================================================

# NOTE: Uncomment the "require" statement below if you have set up RMXP to
# work with "require" statements. If not, simply make sure you've copies the geometry
# script into your project.

# require "geometry.rb"

class Spriteset_Map
  def bubble_viewport
    $bubble_viewport = @viewport1
    return @viewport1
  end
end

class BubbleHandler
  attr_accessor   :sprite
  attr_accessor   :bubble
  attr_accessor   :text_queue
  attr_accessor   :speaker_queue
  
  def initialize(viewport = $bubble_viewport)
    @sprite = Sprite.new(viewport)
    @sprite.bitmap = Bitmap.new(640,480)
    @sprite.z = 99999
    @text_queue = []
    @speaker_queue = []
  end
  
  def say(name,text)
    if $game_temp.message_window_showing
      return
    end
    
    unless get_character_by_name(name)
      p 'Character "' + name + '" does not exist on this map.'
      return
    end
    
    $game_temp.message_window_showing = true
    @text_queue.push text
    @speaker_queue.push get_character_by_name(name)
    process_messages
    update
  end
  
  def process_messages
    if @text_queue.first
      unless @bubble
        @sprite.bitmap.clear
        speaker = @speaker_queue.shift
        text = @text_queue.shift
        @bubble = Geometry::Balloon.new(speaker.x * 32,speaker.y * 32,text)
        @bubble.draw @sprite.bitmap
      end
    end
  end
    
  def get_character_by_name(name)
    for i in $game_map.events.keys.sort
      if $game_map.events[i].name == name
        return $game_map.events[i]
      end
    end
    return nil
  end
  
  def dispose
    @sprite.bitmap.dispose
    @sprite = nil
    $game_temp.message_window_showing = false
  end
  
  def update
    process_messages
    if @bubble
      @sprite.visible = true
      @sprite.update
      $game_temp.message_window_showing = true
    else
      @sprite.visible = false
      $game_temp.message_window_showing = false
      return
    end
    if Input.trigger?(Input::C)
      $game_system.se_play($data_system.decision_se)
      $game_map.need_refresh = true
      @bubble = nil
    end
  end
end

module Geometry
  #====================================================
  # Balloon class                                     |
  #====================================================
  # It's a special kind of Ellipse. It draws a comic- |
  # style speech balloon. You tell it where the       |
  # speaker is, and tell it what ti say, and it'll do |
  # the rest. It's in format Ellipse.new(x,y,text).   |
  # If text is provided as an array, it'll do a multi-|
  # line message, too.                                |
  # Now, it assumes your speaker is the normal size   |
  # of RTP sprites, so the actual spacing might be a  |
  # little off if you're having something else speak. |
  # Shouldn't be too bad, and you can just change the |
  # x a little if that becomes the case, yeah?        |
  # Treat it the same way you would an ellipse once it|
  # has been defined, and draw it the same way and    |
  # all.
  #====================================================
  class Balloon < Ellipse
    def initialize(speaker_x,speaker_y,text)
      @text = text
      test = Bitmap.new(640,480)
      test.font.name = MESSAGE_FONT
      test.font.size = MESSAGE_SIZE
      test.font.color = MESSAGE_COLOR
      lines = 0
      maxwidth = 0
      if text.is_a? Array
        for i in text
          lines += 1
          curwid = test.text_size(i).width
          hi = test.text_size(i).height
          if curwid > maxwidth
            maxwidth = curwid
          end
        end
      end      
      r = Rect.new(0,0,maxwidth,lines * hi)
      test.dispose
      test = nil
      @width = r.width + 32
      @height = r.height + 24
      @maj = Math.sqrt(Geometry.sqr(@width) + Geometry.sqr(@height)).abs
      if speaker_x < (@maj / 2).round
        @x = 0
      elsif speaker_x > (640 - @maj.round)
        @x = 640 - @maj.round
      else
        @x = speaker_x - (@maj / 2).round
      end
      if speaker_y < (20 + @height)
        @y = speaker_y + 52
        tail_y = @y + 36
        ty = @y + 8
      else
        @y = speaker_y - 20 - @height
        tail_y = @y + (@height - 16)
        ty = @y + 8
      end
      tail_x = @x + (@maj / 4)
      cx = @x + (@width / 2)
      cy = @y + (@height / 2)
      center = Point.new(cx,cy)
      super(center,@maj,@height)
      
      if cx < speaker_x
        speaker_x -= 16
      else
        speaker_x += 16
      end
      
      @speaker_p = Point.new(speaker_x,speaker_y)
      @tail_p = Point.new(tail_x,tail_y)
      
      
      tx = @x + ((@maj - @width) / 2)
      @text_rect = Rect.new(tx,ty,@width - 8,@height - 8)
      
      @linewidth = 2
      @linecolor = Color.new(0,0,0,255)
      @fillcolor = Color.new(255,255,255,255)
      @fill = true
    end
    
    def draw_text bmp
      bmp.font.name = MESSAGE_FONT
      bmp.font.size = MESSAGE_SIZE
      bmp.font.color = MESSAGE_COLOR
      if @text.is_a? Array
        for i in 0...@text.size
          str = @text[i]
          hi = bmp.text_size(str).height
          bmp.draw_text @text_rect.x,@text_rect.y + (hi * i),@text_rect.width,hi,str,1
        end
      else
        bmp.draw_text @text_rect.x,@text_rect.y,@text_rect.width,@text_rect.height,@text,1
      end
    end
    
    def draw_tail(bmp)
      more = @tail_p.x.round + 20
      less = @tail_p.x.round - 10
      if @tail_p.y == 36
        p1 = Point.new(@speaker_p.x + 16,@speaker_p.y + 4)
        p2y = @y + 40
        p3y = @y + 40
      else
        p1 = Point.new(@speaker_p.x + 16,@speaker_p.y - 4)
        p2y = @y + @height - 4
        p3y = @y + @height - 4
      end      
      p2 = Point.new((more),p2y)
      p3 = Point.new((less),p3y)
      
      l1 = Line.new(p2.x,p1.x,p2.y,p1.y)
      l2 = Line.new(p3.x,p1.x,p3.y,p1.y)
      l1.color = @linecolor
      l2.color = @linecolor
      l1.width = @linewidth
      l2.width = @linewidth
      
      sml = [l1.x1,l1.x2,l2.x1,l2.x2].min
      big = [l1.x1,l1.x2,l2.x1,l2.x2].max
      for i in sml..big
        line = [l1.get_y(i),l2.get_y(i)]
        h = line.max - line.min
        bmp.fill_rect i,line.min,1,h,@fillcolor
      end
      l1.draw bmp
      l2.draw bmp
    end
    
    def draw bmp
      super
      draw_tail bmp
      draw_text bmp
    end    
  end
end
 
 

The scripts on this page are Ruby-based RGSS code for use in RPG Maker XP.

To view a script, click on its name on the list of scripts at the right. To download, click its name above the code window.