RPG Maker Forums

I need to calculate the text length for a message after it has been escaped (\i[x], \n[x], etc.) but before I call super() on my Window_Base so I can adjust the window width based on the text-length.

Problem:

# Calculate line width if conv_text.kind_of?(Array) ... else text_width = ??? end ... super(x,y,text_width+OFFSETS,height)Window_Message actually calculates the line_width:

#-------------------------------------------------------------------------- # * Process All Text #-------------------------------------------------------------------------- def process_all_text open_and_wait text = convert_escape_characters($game_message.all_text) pos = {} new_page(text, pos) process_character(text.slice!(0, 1), text, pos) until text.empty? # This is the line we need. # <<<<<<<<<<<<<<<<<<<<<<<< now pos[:x] contains the line_width after 'applying' escape characters and icons. endBut there are 2 reasons why I can't use this. First of all, process_all_text() can not be called before the popup window has been fully initialized. Secondly, I can not change the width of the window anymore after it has been initialized (after super is called).. So that's a small dilemma.

Why not calculate the linewidth myself? Because I also want compatibility with yanfly's msg system (and possible other addons).

Full script (W.I.P.):

#===============================================================================# Configurable section#===============================================================================module Nap  $imported ||= {}  $imported[:nap_popup_window] = 1.02#===============================================================================# Window Popup#===============================================================================    class Window_Popup < Window_Base    #---------------------------------------------------------------------------    # Settings    #---------------------------------------------------------------------------    LOCATION_Y = 70             # Y location of the window.    FOLDER = 'Popup'            # Folder that contains the images for the popup.    OFFSET_X = 5                # Text offset for the left and right sides.    OFFSET_Y = 4                # Text offset for the top and bottom sides.    PADDING = 2                 # Spacing between lines.    IMAGE_TEXT_SPACING = 5      # Horizontal spacing between image and text.    CENTER_SINGLE_LINE = true   # When true it will center the text (if it is a                                # single string and not an array) on the Y-axis.    DELETE_ON_TRANSFER  = :all  # Possible values:                                # :all, :active, :none    ACTION_ON_OPEN_MENU = :none # :hide, :del_all, :del_active, :none    #---------------------------------------------------------------------------    # Do not edit below this line    #---------------------------------------------------------------------------    attr_accessor :requires_refresh # Because Window_Base doesn't work correctly                                    # after being created by the DataManager.    attr_accessor :fade_direction    #---------------------------------------------------------------------------    # * Initialize    #   image       : may be nil to not display any image at all.    #   text        : Can either be a string or an array of strings. Each item    #                 in the array represents a single line of text.    #   display_time: is in frames.    #---------------------------------------------------------------------------       def initialize(text, image=nil, fade_speed=6, display_time=100)#~       conv_text = convert_escape_characters(text)      conv_text = text                  @requires_refresh = false      @image_str = image # for saving/loading      @fade_speed = fade_speed      @fade_direction = @fade_speed      @display_time = display_time            text_offset_left = OFFSET_X      if image        img = Cache.popup(image)        text_offset_left += img.width + IMAGE_TEXT_SPACING      end            # Size            measure_img = Bitmap.new(1,1)      if conv_text.kind_of?(Array)                txt_size = Rect.new(0, 0, 0, 0)        conv_text.each { |t|          measure = measure_img.text_size(t)          txt_size.width = [txt_size.width, measure.width].max          txt_size.height += PADDING if txt_size.height > 0          txt_size.height += measure.height        }      else        txt_size = measure_img.text_size(conv_text)      end      measure_img.dispose            width = txt_size.width + OFFSET_X * 2 + standard_padding * 2      width += img.width if img            height = txt_size.height + OFFSET_Y * 2 + standard_padding * 2      height = [height, img.height + OFFSET_Y * 2 + standard_padding * 2].max if img            x = Graphics.width / 2 - width / 2      y = LOCATION_Y      # [End of] Size            super(x,y,width,height)      self.contents_opacity = self.opacity = 0      #~       self.width = calculate_text_width(conv_text)            # Draw      if img        contents.blt(OFFSET_X, OFFSET_Y, img, Rect.new(0,0,img.width,img.height))        img.dispose      end            if conv_text.kind_of?(Array)        conv_text.each_with_index { |t, index|        h = text_size(t).height#~           draw_text_ex(text_offset_left, OFFSET_Y + (h + PADDING) * index, width, h, t)          draw_text_ex(text_offset_left, OFFSET_Y + (h + PADDING) * index, t)        }      else        if CENTER_SINGLE_LINE#~           draw_text_ex(text_offset_left, -12, width, height, conv_text)            draw_text_ex(text_offset_left, OFFSET_Y, conv_text)          else#~           draw_text_ex(text_offset_left, OFFSET_Y, width, text_size(conv_text).height, conv_text)          draw_text_ex(text_offset_left, OFFSET_Y, conv_text)        end      end            @text = conv_text # for saving/loading    end # initialize        #--------------------------------------------------------------------------    # * Draw Text with Control Characters    #--------------------------------------------------------------------------    def calculate_text_width(text)      reset_font_settings      conv_text = convert_escape_characters(text)      pos = {:x => x, :y => y, :new_x => x, :height => 0}      process_character(text.slice!(0, 1), conv_text, pos) until conv_text.empty?      return pos[:x]    end        #---------------------------------------------------------------------------    # * Convert Text (override)    # * This one is also compatible with arrays instead of just strings    #---------------------------------------------------------------------------         def convert_escape_characters(text)      if text.kind_of?(Array)        result = []        text.each { |t| result << super(t)}                return result      else        return super(text)              end    end    #---------------------------------------------------------------------------    # * Update    #---------------------------------------------------------------------------        def update      super      return if !self.visible # Do not update when hidden      if @display_time > 0 && @fade_direction == -@fade_speed        @display_time -= 1        @requires_refresh ? @requires_refresh = false : return      end            self.opacity += @fade_direction      self.contents_opacity = self.opacity            if self.opacity >= 255 # self.opacity does not need a correction to 255 because that is already performed by the RGSS framework.        @fade_direction = -@fade_speed              elsif self.opacity <= 0 && @fade_direction == -@fade_speed        Window_Popup.next_window      end    end    #---------------------------------------------------------------------------    # * To Data    # - For: saving    #---------------------------------------------------------------------------    def to_data      Popup_Data.new(@text,@image_str, @fade_speed, @fade_direction,                     @display_time, self.opacity)    end    #---------------------------------------------------------------------------    # * From Data (static)    # - For: loading    #---------------------------------------------------------------------------    def self.from_data(data)      result = Window_Popup.new(data.text, data.image_str, data.fade_speed,                                data.display_time)      result.fade_direction = data.fade_direction      result.opacity = data.opacity      return result    end        #---------------------------------------------------------------------------    # * Delete Active Window (static)    #---------------------------------------------------------------------------    def self.del_active_window      $popup_window.dispose if $popup_window      $popup_window = nil    end    #---------------------------------------------------------------------------    # * Delete All Windows (static)    #---------------------------------------------------------------------------    def self.del_all_windows      $popup_queue.each { |w| w.dispose}      $popup_queue = []      Nap::Window_Popup.del_active_window    end    #---------------------------------------------------------------------------    # * Next Window (static)    #---------------------------------------------------------------------------    def self.next_window      del_active_window      if $popup_queue.length > 0        $popup_window = $popup_queue.shift        $popup_window.visible = true      end    end  end # class Window_Popup < Window_Base  #===============================================================================  # Class Popup Data  #===============================================================================  Popup_Data = Struct.new:)text, :image_str, :fade_speed, :fade_direction,                          :display_time, :opacity)  #===============================================================================  # * Initialize Globals  #===============================================================================    $popup_window = nil  $popup_queue = []end # module Nap#===============================================================================# Game Player# For: DELETE_ON_TRANSFER#===============================================================================class Game_Player < Game_Character  alias nap_popup_reserve_transfer reserve_transfer  def reserve_transfer(map_id, x, y, d = 2)    case Nap::Window_Popup::DELETE_ON_TRANSFER    when :all      Nap::Window_Popup.del_all_windows    when :active      Nap::Window_Popup.next_window    when :none      # Do nothing    else      raise "Scene_Base.nap_popup_start (alias): Unknown DELETE_ON_SCENE_START value #{Nap::Window_Popup::DELETE_ON_SCENE_START}"    end        nap_popup_reserve_transfer(map_id, x, y, d)  end # reserve_transferend # class Game_Player < Game_Character#===============================================================================# Scene MenuBase# For: updating the active popup window#===============================================================================class Scene_Base  alias nap_popup_update update  def update    nap_popup_update    $popup_window.update if $popup_window && SceneManager.scene.is_a?(Scene_Map)  endend # class Scene_Base#===============================================================================# Scene MenuBase# For: ACTION_ON_OPEN_MENU part 1/2#===============================================================================class Scene_MenuBase < Scene_Base  alias nap_popup_terminate terminate  def terminate    $popup_window.visible = true if Nap::Window_Popup::ACTION_ON_OPEN_MENU == :hide && $popup_window    nap_popup_terminate      endend#===============================================================================# Scene Map# For: ACTION_ON_OPEN_MENU part 2/2#===============================================================================class Scene_Map < Scene_Base  alias nap_update_call_menu update_call_menu  def update_call_menu    if Input.trigger?:) && !$game_player.moving?      case Nap::Window_Popup::ACTION_ON_OPEN_MENU      when :none        # Do nothing      when :hide        $popup_window.visible = false if $popup_window      when :del_all        Nap::Window_Popup.del_all_windows      when :del_active        Nap::Window_Popup.next_window      else        raise "Scene_Map.nap_update_call_menu (alias): Unknown ACTION_ON_OPEN_MENU value #{Nap::Window_Popup::ACTION_ON_OPEN_MENU}"      end    end        nap_update_call_menu  endend#===============================================================================# Scene Title# For: clearing the queue if the user returned to the title screen.#===============================================================================class Scene_Title < Scene_Base  alias nap_popup_start start  def start    Nap::Window_Popup.del_all_windows if $popup_window    nap_popup_start  endend#===============================================================================# Scene Base# For: Preventing the popup window from showing in the Save Scene, Title Scene,#      etc.#===============================================================================class Scene_Base  alias nap_popup_post_start post_start  def post_start    nap_popup_post_start    $popup_window.visible = self.is_a?(Scene_Map) if $popup_window      endend#===============================================================================# Saving & Loading#===============================================================================#===============================================================================# Module DataManager# For: Saving & Loading#===============================================================================module DataManager  class << self    alias nap_popup_event_make_save_contents make_save_contents    alias nap_popup_event_extract_save_contents extract_save_contents  end   def self.make_save_contents    contents = nap_popup_event_make_save_contents    contents = contents.merge(make_popup_contents)    contents  end   def self.make_popup_contents    contents = {}        popups = []    popups << $popup_window.to_data if $popup_window    $popup_queue.each { |p|        popups << p.to_data if p != nil    }        contents[:nap_popup_window] = popups    contents  end   def self.extract_save_contents(contents)    nap_popup_event_extract_save_contents(contents)    extract_popup_contents(contents)  end   def self.extract_popup_contents(contents)    if contents[:nap_popup_window].length > 0      $popup_window = Nap::Window_Popup.from_data(contents[:nap_popup_window].shift)      $popup_window.requires_refresh = true    end    contents[:nap_popup_window].each { |data|      $popup_queue << Nap::Window_Popup.from_data(data)    }  endend#===============================================================================# Cache#===============================================================================module Cache  #--------------------------------------------------------------------------  # * Get Popup Graphic  #--------------------------------------------------------------------------  def self.popup(filename)        load_bitmap("Graphics/#{Nap::Window_Popup::FOLDER}/", filename)  endend#===============================================================================# Game Interpreter#===============================================================================class Game_Interpreter  #--------------------------------------------------------------------------  # * Popup Delete  #   Removes the active popup window  #--------------------------------------------------------------------------  def popup_delete    Nap::Window_Popup.next_window  end  #--------------------------------------------------------------------------  # * Popup Delete All  #   Removes the active and all queued popup windows  #--------------------------------------------------------------------------    def popup_delete_all    Nap::Window_Popup.del_all_windows  end  #--------------------------------------------------------------------------  # * Popup  #   Creates a new popup window  #--------------------------------------------------------------------------  def popup(text, image=nil, fade_speed=6, display_time=100)    if $popup_window      $popup_queue << Nap::Window_Popup.new(text, image, fade_speed, display_time)      $popup_queue.last.visible = false    else      $popup_window = Nap::Window_Popup.new(text, image, fade_speed, display_time)    end  endend#==============================================================================## ■ End of File##==============================================================================
I also tried creating a dummy window but that didn't really go as planned...

class Popup_Dummy < Window_Message attr_reader :text_width def initialize(txt) @text = txt self.class.superclass.superclass.initialize(-999999, -999999, Graphics.width, Graphics.height) # <<<<<<<<<<<<<<<<<<<<<< crash. Can't explicitly call initialize self.z = 200 self.openness = 0 create_all_windows create_back_bitmap create_back_sprite clear_instance_variables @show_fast = true # isntantly show text end def process_all_text open_and_wait @text = convert_escape_characters(@text) pos = {} new_page(@text, pos) process_character(@text.slice!(0, 1), @text, pos) until @text.empty? text_width = pos[:x] end def fiber_main # do nothing endendAnd then:

dummy = Popup_Dummy.new(text) text_size = dummy.text_width dummy.dispose
Another (failed) attempt:

################################################################################# measure tests################################################################################  def text_size2(str)    @dummy_bmp.text_size(str)  end  def calculate_text_width(text, new_line_x)        pos = {:x => new_line_x, :y => line_height, :new_x => new_line_x, :height => 0}    txt = text.clone    @dummy_bmp = Bitmap.new(1,1)#~     t = convert_escape_characters(t)    process_character2(txt.slice!(0, 1), txt, pos) until txt.empty?    @dummy_bmp.dispose    return pos  end  def process_character2(c, text, pos)    p c    case c    when "\n"   # New line      pos[:x] += pos[:new_x]      pos[:y] += line_height    when "\f"   # New page      pos[:x] = pos[:new_x]      pos[:y] = line_Height    when '\\'   # Control character      process_escape_character2(obtain_escape_code(text), text, pos)    else        # Normal character      pos[:x] += text_size2(c).width    end  end   def process_escape_character2(code, text, pos)    case code.upcase    when 'C'      return    when 'I'      pos[:x] += 24    when '{'      @temp.font.size += 8 if contents.font.size <= 64        when '}'      @temp.font.size -= 8 if contents.font.size >= 16    end        if $imported["YEA-MessageSystem"]      index = $1.to_i      case code.upcase      when 'II' # Item        pos[:x] += text_size2($data_items[index].name).width + 24      when 'IC' # Class        return unless $imported["YEA-ClassSystem"]        pos[:x] += text_size2($data_items[index].name).width + 24      when 'IW' # Weapon        pos[:x] += text_size2($data_weapons[index].name).width + 24      when 'IA' # Armor        pos[:x] += text_size2($data_armors[index].name).width + 24      when 'IS' # Skill        pos[:x] += text_size2($data_skills[index].name).width + 24      when 'IT' # State        pos[:x] += text_size2($data_states[index].name).width + 24      end    end  end################################################################################
Maybe I just need to do it differently:

- get total width from measuring the entire string using some temporary bitmap
- find control characters and remove that control-text-length from the total width
- then apply that control's real length to the total width from a huge case-switch.

def text_size2(str) @dummy_bmp.text_size(str) end def calculate_text_width(text, new_line_x) txt = text.clone @dummy_bmp = Bitmap.new(1,1) result = {:x => text_size2(txt), :y=>line_height} # run some regex to find all control **** real_text = txt.gsub(/\eII\[(\d+)\]/i) { $data_items[$1.to_i].name } result[:x] -= text_size2('\eII[0]') # goddamnit need original control-character length result[:x] += text_size2(real_text + 24) @dummy_bmp.dispose return result endSadly this crashes with some incredible error message that says nothing...

Latest Threads

Latest Profile Posts

Day 9 of giveaways! 8 prizes today :D
He mad, but he cute :kaopride:

Our latest feature is an interview with... me?!

People4_2 (Capelet off and on) added!

Just beat the last of us 2 last night and starting jedi: fallen order right now, both use unreal engine & when I say i knew 80% of jedi's buttons right away because they were the same buttons as TLOU2 its ridiculous, even the same narrow hallway crawl and barely-made-it jump they do. Unreal Engine is just big budget RPG Maker the way they make games nearly identical at its core lol.

Forum statistics

Threads
106,036
Messages
1,018,461
Members
137,821
Latest member
Capterson
Top