KInfoMenu XP
#1
KInfoMenu XP

by Kyonides

Introduction

The script allows you to create some sort of tutorial menu with lots of options using a GUI similar to my other script FancyChoices XP.
As the Gamer game developer you can define what texts and pictures you will display on screen. You can also play SE if deemed necessary.

Nope, this is NOT a Plug & Play script at all, guys! Confused

If you want to customize it, you will have to edit several CONSTANTS in the KInfoMenu module first.

All texts will be shown on top of the option boxes or on a typical message window. This also means that your typical note tags like \n[1] or \c[2] will work normally. Winking

The pictures will be shown on the center of the screen right when its corresponding text message gets displayed there.

Understanding the Options Feature

There are 3 different levels of options, namely the Main Choices plus the first and second subcategories. Those subcategories depend on Hashes to let you customize its contents.

Option labels are simple to understand, they are just a list of texts:
Code:
SUB_CAT1["Combat"]  = ["Allies", "Enemies", "FF Menu", "Section 4", "Section 5", "Section 6", "Section 7"]

"Combat" is a key that can be found in the Main Choices list:
Code:
MAIN_CHOICES    = ["Combat", "Controls", "Concepts"]

So both subcategories should use keys that already exist on the previous subcategory or the main choices list.

The text hashes sport 3 keys and their respective values. Those keys are :text, :se and :pic and that's it. Winking

You can leave the :pic and :se hashes empty if you need to, but you can add custom pictures or SFX at any given time. Just follow this example:

Code:
SUB_CAT1_TEXTS["Enemies"] = {
  :se => { 1 => "Battle1", 2 => "Battle2" },
  :pic => { 1 => "093-Monster07", 2 => "099-Monster13" }, 
  :text => [
    "You're not given a chance to think twice.",
    "All mobs definitely hate you.",
    "They'll sweep the floor with your death body",
    "while laughing maniacally.",
    "Please don't challenge them unless you know",
    "very well what you're doing.",
    "You've been warned!",
  ] }

So you need to pick a CONSTANT, then call its option key, and make it contain a { Hash }.
The :se and :pic keys use page indexes as their hash keys, but you can skip pages at will.
The :text hash has a very interesting feature that you should know about from the very beginning. If you plan to display less than 4 lines of text on a given Book page, and you expect another page to show up next after pressing the OK button, you will have to add empty Strings "" and a comma , per empty line. And this is mandatory. Angry

A Friendly Reminder

If a key like "Section 4" does not exist either in SUB_CAT1_TEXTS or SUB_CAT2_TEXTS, the script will skip the whole enchilada to prevent it from crashing.

NOTES

The comments include an explanation that claims that calling the custom scene is optional. Happy with a sweat Well, that is only true if you pretend to customize your main menu to include an option to open the KInfoMenu scene. If you are planning to use any NPC as an interface for this tutorial menu, you will have to make that script call at all costs. Laughing

I don't care if there are other ways to achieve the same goal. I just wanted to create this special menu, and that's it! Tongue sticking out

Screenshots

Several Snapshots

The Script

Code:
# * KInfoMenu XP * #
#   Scripter : Kyonides Arkanthes
#   v1.2.1 - 2025-09-03
#   Not a Plug & Play Script!

# This scriptlet lets you setup a simple tutorial menu.

# NOTES on Message Window's Contents:

# For the message window, empty lines are equally as important as normal ones.
# They are used as fillers, and this matters a lot whenever you're trying to
# display multiple pages on screen. If you ever skip them, you'll soon notice
# that certain lines of text do appear sooner than expected.

# * Optional Script Call * #
# $scene = KInfoMenu::Scene.new

module KInfoMenu
  WIDTH            = 640
  HEIGHT           = 480
  BACKDROP         = "Roman City"
  TITLE_FILENAME   = "title480"
# BGM Options: "" for no song, "Some Name" for any specific BGM
  TUTORIAL_BGM     = "062-Slow05"
  TEXT_ALIGN_X     = :center
  CHOICE_ALIGN_X   = :center
  CHOICE_TOP_Y     = 76
  CHOICE_MARGIN_Y  = 16
  CHOICE_ROW_MAX   = 6
  MAIN_CHOICE_FILE = "box280"
  SUB_CHOICE_FILE  = "box240"
  CHOICE_CURSOR    = "cursor_shield"
  MAIN_TITLE       = "Tutorial Menu"
  MAIN_CHOICES     = ["Combat", "Controls", "Concepts"]
  SHOW_WINDOW_BACK = true # true or false only!
  WINDOW_POSITION  = 2 # 0, 1 or 2 only!
# - Message Window's Contents - #
  INTRO_TEXTS = ["Welcome to the Great Hero's Tutorial!",
                 "I'm your valorous host \c[2]\n[1]\c[0].",
                 "Nice to meet you!"]
# - The Don't Touch These Lines Section - #
  MAIN_CHO_COLS  = 1
  SUB_CAT1       = {}
  SUB_CAT2       = {}
  SUB_CAT1_TEXTS = {}
  SUB_CAT2_TEXTS = {}
  SUB_CAT1.default       = []
  SUB_CAT2.default       = []
  SUB_CAT1_TEXTS.default = {}
  SUB_CAT2_TEXTS.default = {}
# - End of Section - #
  SUB_CAT1["Combat"]   = ["Allies", "Enemies", "FF Menu", "Section 4",
                          "Section 5", "Section 6", "Section 7"]
  SUB_CAT1["Controls"] = ["Decision", "Cancel"]
  SUB_CAT1["Concepts"] = ["Concepts 1", "Concepts 2", "Concepts 3"]
  SUB_CAT1_TEXTS["Allies"] = {
  :se => {},
  :pic => {}, 
  :text => [
    "Most of the time your allies will be very useless.",
    "Just keep them alive to avoid making very large",
    "donations to your local priest to heal those",
    "reckless companions after every single battle."
  ] }
  SUB_CAT1_TEXTS["Enemies"] = {
  :se => { 1 => "Battle1", 2 => "Battle2" },
  :pic => { 1 => "093-Monster07", 2 => "099-Monster13" }, 
  :text => [
    "You're not given a chance to think twice.",
    "All mobs definitely hate you.",
    "They'll sweep the floor with your death body",
    "while laughing maniacally.",
    "Please don't challenge them unless you know",
    "very well what you're doing.",
    "You've been warned!",
  ] }
  SUB_CAT1_TEXTS["FF Menu"] = {
  :se => {},
  :pic => { 1 => "FF9_MenuBar old" },
  :text => [
    "This tutorial uses an old fashioned FF9 menubar.",
    "We don't know when it will be replaced by a new",
    "one. The game developer has repeatedly refused",
    "to address the issue on social media."
  ] }
  SUB_CAT2["Concepts 1"] = ["Concept 1", "Concept 2", "Concept 3"]
  SUB_CAT2_TEXTS["Concept 1"] = {
  :se => {},
  :pic => {}, 
  :text => [
    "You've chosen the Proof of Concept #1!",
    "Hurray..."
  ] }

class TextBox
  def initialize(pos, total, vp=nil)
    @index = pos
    @max = total
    @row_max = CHOICE_ROW_MAX
    @col_max = (@max / @row_max) + 1
    @viewport = vp
    @backdrop = Sprite.new(vp)
    @label = Sprite.new(vp)
    @label.z = 50
  end

  def set_image(filename)
    @filename = filename
    set_bitmap
    set_text_bitmap
    reset_alignment
  end

  def set_bitmap
    bitmap = RPG::Cache.picture(@filename)
    @width = bitmap.width
    @height = bitmap.height
    @rect = bitmap.rect
    @col_width = 640
    @center_x = (@col_width - @width) / 2
    @backdrop.bitmap = bitmap
  end

  def set_align_x
    case TEXT_ALIGN_X
    when :left
      0
    when :center
      1
    when :right
      2
    else
      TEXT_ALIGN_X
    end
  end

  def set_text_bitmap
    @align_x = set_align_x
    @label.bitmap = Bitmap.new(@width, @height)
  end

  def set_title_xy
    @backdrop.x = @center_x
    @backdrop.y = 8
  end

  def set_align_xy
    h = @height + CHOICE_MARGIN_Y
    if @max < @row_max
      @backdrop.x = @center_x
      @backdrop.y = CHOICE_TOP_Y + @index * h
    else
      n = @index % @col_max
      cw = @col_width / 2
      @backdrop.x = n * cw + (cw - @width) / 2
      @backdrop.y = CHOICE_TOP_Y + @index / @col_max * h
    end
  end

  def reset_alignment
    @index ? set_align_xy : set_title_xy
    @label.x = @backdrop.x
    @label.y = @backdrop.y
  end

  def x
    @backdrop.x
  end

  def y
    @backdrop.y
  end

  def set_text(text)
    @text = text
    bit = @label.bitmap
    bit.clear
    bit.draw_text(@rect, text, @align_x)
  end

  def dispose
    @label.bitmap.dispose
    @label.dispose
    @backdrop.bitmap.dispose
    @backdrop.dispose
  end
  attr_reader :height, :text
end

class Spriteset
  def initialize
    create_variables
    create_backdrop
    create_viewports
    create_title
    create_picture
    create_main_choices
    create_cursors
    update_box(0)
    @sprites = @cursors + [@backdrop, @picture]
  end

  def create_variables
    @index = 0
    @section_index = 0
    @subsection_index = 0
    @sound = RPG::SE.new
    @cursors = []
    @sections = []
    @subsections = []
    @main_boxes = []
    @section_boxes = []
    @subsection_boxes = []
    @section_viewports = []
    @subsection_viewports = []
  end

  def create_backdrop
    @backdrop = Sprite.new
    @backdrop.bitmap = RPG::Cache.title(BACKDROP)
  end

  def create_viewports
    @viewport_rect = [0, 0, WIDTH, HEIGHT]
    @main_viewport = Viewport.new(*@viewport_rect)
    @picture_viewport = Viewport.new(*@viewport_rect)
    @picture_viewport.z = 1000
  end

  def create_title
    @title = TextBox.new(nil, 0, nil)
    @title.set_image(TITLE_FILENAME)
    @title.set_text(MAIN_TITLE)
  end

  def create_picture
    @picture = Sprite.new(@picture_viewport)
    @picture.y = 48
    @picture.visible = false
    @picture.bitmap = Bitmap.new(WIDTH, HEIGHT - 96)
  end

  def create_main_choices
    filename = MAIN_CHOICE_FILE
    total = MAIN_CHOICES.size
    MAIN_CHOICES.each_with_index do |choice, n|
      box = TextBox.new(n, total, @main_viewport)
      box.set_image(filename)
      box.set_text(choice)
      @main_boxes << box
    end
    @item_max = @main_boxes.size
  end

  def create_cursors
    box = @main_boxes[@index]
    @main_cursor = Sprite.new(@main_viewport)
    @main_cursor.z = 100
    @main_cursor.bitmap = RPG::Cache.icon(CHOICE_CURSOR)
    @main_cursor.x = box.x + 4
    @offset_y = (box.height - @main_cursor.bitmap.height) / 2
    @main_cursor.y = box.y + @offset_y
    @section_cursor = Sprite.new
    @section_cursor.visible = false
    @section_cursor.z = 100
    @section_cursor.bitmap = RPG::Cache.icon(CHOICE_CURSOR)
    @subsection_cursor = Sprite.new
    @subsection_cursor.visible = false
    @subsection_cursor.z = 100
    @subsection_cursor.bitmap = RPG::Cache.icon(CHOICE_CURSOR)
    @cursors << @main_cursor << @section_cursor << @subsection_cursor
  end

  def update_main
    if Input.trigger?(Input::UP)
      $game_system.se_play($data_system.cursor_se)
      update_box(-1)
      return
    elsif Input.trigger?(Input::DOWN)
      $game_system.se_play($data_system.cursor_se)
      update_box(1)
    end
  end

  def update_box(n)
    @index = (@index + n) % @item_max
    box = @main_boxes[@index]
    @section_title = box.text
    @main_cursor.x = box.x + 4
    @main_cursor.y = box.y + @offset_y
  end

  def show_main
    @title.set_text(MAIN_TITLE)
    @main_viewport.visible = true
  end

  def hide_main
    @main_viewport.visible = false
  end

  def find_section_viewport
    @section_viewports[@index] ||= Viewport.new(*@viewport_rect)
  end

  def find_subsections
    @sections[@index] ||= SUB_CAT1[@section_title].dup
  end

  def find_section_boxes
    @section_boxes[@index] ||= []
  end

  def setup_section
    @section_viewport = find_section_viewport
    @section_list = find_subsections
    @current_boxes = find_section_boxes
    create_section if @current_boxes.empty?
    reset_section
    update_section_box(0)
    @title.set_text(@section_title)
    @section_viewport.visible = true
  end

  def create_section
    return if @section_list.empty?
    filename = SUB_CHOICE_FILE
    total = @section_list.size
    @section_list.each_with_index do |choice, n|
      box = TextBox.new(n, total, @section_viewport)
      box.set_image(filename)
      box.set_text(choice)
      @current_boxes << box
    end
  end

  def reset_section
    @section_max = @current_boxes.size
    @col_max = (@section_max / CHOICE_ROW_MAX) + 1
    @section_index = 0
    box = @current_boxes[@section_index]
    @section_oy1 = (box.height - @section_cursor.bitmap.height) / 2
    @section_cursor.visible = box ? true : false
  end

  def show_section
    @title.set_text(@section_title)
    @section_viewport.visible = true
    @section_cursor.visible = true
  end

  def hide_section(clear)
    @section_cursor.visible = false
    @section_viewport.visible = false
    return unless clear
    @section_viewport = @current_boxes = nil
    @section_index = 0
  end

  def update_section
    if Input.trigger?(Input::UP)
      $game_system.se_play($data_system.cursor_se)
      update_section_box(-@col_max)
      return
    elsif Input.trigger?(Input::DOWN)
      $game_system.se_play($data_system.cursor_se)
      update_section_box(@col_max)
      return
    end
    return if @col_max == 1
    if Input.trigger?(Input::LEFT)
      $game_system.se_play($data_system.cursor_se)
      update_section_box(-1)
      return
    elsif Input.trigger?(Input::RIGHT)
      $game_system.se_play($data_system.cursor_se)
      update_section_box(1)
    end
  end

  def update_section_box(n)
    @section_index = (@section_index + n) % @section_max
    box = @current_boxes[@section_index]
    @subsection_title1 = box.text
    @section_cursor.x = box.x + 4
    @section_cursor.y = box.y + @section_oy1
  end

  def set_pictures(pictures)
    @page_index = 1
    @pictures = pictures
    show_picture
  end

  def show_picture
    picture = @pictures[@page_index]
    return unless picture
    @picture.bitmap.dispose
    @picture.bitmap = bit = RPG::Cache.picture(picture)
    @picture.x = (WIDTH - bit.width) / 2
    @picture.y = (HEIGHT - bit.height - 128) / 2
    @picture.visible = true
  end

  def set_sfx(sfx)
    @sfx = sfx
    play_sound
  end

  def play_sound
    @sound.name = @sfx[@page_index] || ""
    $game_system.se_play(@sound)
  end

  def find_subsection_viewport
    @subsection_viewports[@section_index] ||= Viewport.new(*@viewport_rect)
  end

  def find_subsections_lvl2
    @subsections[@section_index] ||= SUB_CAT2[@subsection_title1].dup
  end

  def find_subsection_boxes
    @subsection_boxes[@section_index] ||= []
  end

  def setup_subsection
    @subsection_viewport = find_subsection_viewport
    @subsection_list = find_subsections_lvl2
    @current_subboxes = find_subsection_boxes
    create_subsection if @current_subboxes.empty?
    reset_subsection
    update_subsection_box(0)
    box = @current_subboxes[@subsection_index]
    @subsection_title2 = box ? box.text : ""
    @title.set_text(@subsection_title1)
    @subsection_viewport.visible = true
  end

  def create_subsection
    return if @subsection_list.empty?
    filename = SUB_CHOICE_FILE
    total = @subsection_list.size
    @subsection_list.each_with_index do |choice, n|
      box = TextBox.new(n, total, @subsection_viewport)
      box.set_image(filename)
      box.set_text(choice)
      @current_subboxes << box
    end
  end

  def reset_subsection
    @subsection_max = @current_subboxes.size
    @subcol_max = (@subsection_max / CHOICE_ROW_MAX) + 1
    @subsection_index = 0
    box = @current_subboxes[@subsection_index]
    @section_oy2 = (box.height - @subsection_cursor.bitmap.height) / 2
    @subsection_cursor.visible = box ? true : false
  end

  def hide_subsection
    @subsection_cursor.visible = false
    @subsection_viewport.visible = false
    @subsection_viewport = @current_subboxes = nil
    @subsection_index = 0
  end

  def update_subsection
    if Input.trigger?(Input::UP)
      $game_system.se_play($data_system.cursor_se)
      update_subsection_box(-@subcol_max)
      return
    elsif Input.trigger?(Input::DOWN)
      $game_system.se_play($data_system.cursor_se)
      update_subsection_box(@subcol_max)
      return
    end
    return if @subcol_max == 1
    if Input.trigger?(Input::LEFT)
      $game_system.se_play($data_system.cursor_se)
      update_subsection_box(-1)
      return
    elsif Input.trigger?(Input::RIGHT)
      $game_system.se_play($data_system.cursor_se)
      update_subsection_box(1)
    end
  end

  def update_subsection_box(n)
    @subsection_index = (@subsection_index + n) % @subsection_max
    box = @current_subboxes[@subsection_index]
    @subsection_title2 = box.text
    @subsection_cursor.x = box.x + 4
    @subsection_cursor.y = box.y + @section_oy2
  end

  def hide_picture
    @picture.bitmap.dispose
    @picture.bitmap = Bitmap.new(WIDTH, HEIGHT - 96)
    @picture.visible = false
  end

  def update_extras
    return unless $game_temp.new_message_page
    $game_temp.new_message_page = nil
    @page_index += 1
    show_picture
    play_sound
  end

  def dispose
    @sprites.each do |sprite|
      sprite.bitmap.dispose
      sprite.dispose
    end
    @subsection_boxes.each do |boxes|
      next unless boxes
      boxes.each {|box| box.dispose }
    end
    @section_boxes.each do |boxes|
      next unless boxes
      boxes.each {|box| box.dispose }
    end
    @main_boxes.each {|box| box.dispose }
    @title.dispose
    @section_viewports.each {|vp| vp.dispose if vp }
    @picture_viewport.dispose
    @main_viewport.dispose
    @section_boxes.clear
    @main_boxes.clear
    @section_viewports.clear
  end
  attr_reader :subsection_title1, :subsection_title2
end

class Scene
  def initialize(index=nil)
    @main_menu_index = index
    @map_bgm = $game_system.playing_bgm || RPG::BGM.new
    @bgm = RPG::BGM.new
    return if TUTORIAL_BGM.empty?
    @bgm.name = TUTORIAL_BGM
    $game_system.bgm_play(@bgm)
  end

  def main
    start
    Graphics.transition
    update_basic while @stage
    Graphics.freeze
    terminate
  end

  def start
    @stage = :message
    @next_stage = :main
    @spriteset = Spriteset.new
    @message_window = Window_Message.new
    $game_system.setup_message
    $game_temp.set_tutorial_texts(INTRO_TEXTS)
  end

  def update_basic
    Graphics.update
    Input.update
    update
  end

  def close_message
    if $game_temp.next_lines
      $game_temp.set_next_page
      @spriteset.update_extras
    else
      @message_window.clear_contents
      @spriteset.hide_picture
      @stage = @next_stage
    end
  end

  def update
    case @stage
    when :message
      update_message
    when :main
      update_main
    when :section
      update_section
    when :subsection
      update_subsection
    end
  end

  def update_message
    @message_window.update
    while $game_temp.message_window_showing
      Graphics.update
      Input.update
      @message_window.update
    end
    close_message
  end

  def update_main
    @spriteset.update_main
    if Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      if @main_menu_index
        $scene = Scene_Menu.new(@main_menu_index)
      else
        $scene = Scene_Map.new
      end
      return @stage = nil
    elsif Input.trigger?(Input::C)
      $game_system.se_play($data_system.decision_se)
      @spriteset.hide_main
      @spriteset.setup_section
      @stage = :section
    end
  end

  def update_section
    @spriteset.update_section
    if Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      @spriteset.hide_section(true)
      @spriteset.show_main
      return @stage = :main
    elsif Input.trigger?(Input::C)
      $game_system.se_play($data_system.decision_se)
      key = @spriteset.subsection_title1
      if SUB_CAT2[key].any?
        show_subsection
      else
        show_section_message(key)
      end
    end
  end

  def show_subsection
    @spriteset.hide_section(nil)
    @spriteset.setup_subsection
    @stage = :subsection
  end

  def show_section_message(key)
    data = SUB_CAT1_TEXTS[key].dup
    return if data.empty?
    message = data[:text]
    pictures = data[:pic]
    sfx = data[:se]
    $game_system.setup_message
    $game_temp.set_tutorial_texts(message)
    @spriteset.set_pictures(pictures)
    @spriteset.set_sfx(sfx)
    @next_stage = :section
    @stage = :message
  end

  def update_subsection
    @spriteset.update_subsection
    if Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      @spriteset.hide_subsection
      @spriteset.show_section
      return @stage = :section
    elsif Input.trigger?(Input::C)
      $game_system.se_play($data_system.decision_se)
      key = @spriteset.subsection_title2
      data = SUB_CAT2_TEXTS[key]
      return if data.empty?
      message = data[:text]
      pictures = data[:pic]
      sfx = data[:se]
      $game_system.setup_message
      $game_temp.set_tutorial_texts(message)
      @spriteset.set_pictures(pictures)
      @spriteset.set_sfx(sfx)
      @next_stage = :subsection
      @stage = :message
    end
  end

  def terminate
    @message_window.dispose
    @spriteset.dispose
    $game_system.reset_message
    return if TUTORIAL_BGM.empty?
    if $game_map.autoplay_bgm?
      $game_map.autoplay
    elsif @map_bgm
      $game_system.bgm_play(@map_bgm)
    end
  end
end

end

class Game_Temp
  def set_tutorial_texts(texts)
    texts = texts.dup
    if texts.size > 4
      @next_lines = texts[4..-1]
      texts = texts[0..3]
    else
      @next_lines = nil
    end
    texts = texts.join("\n")
    texts.gsub!("\e", "\\c[")
    texts.gsub!(/(.)\^/) { "#{$1}\\^" }
    texts.gsub!(/([^\\])\|/) { "#{$1}\\|" }
    texts.gsub!(/(\-|,|;|:|!)\./) { "#{$1}\\." }
    texts.gsub!(/[\b]/i, "\\b")
    texts.gsub!(/([^\\])i\[([0-9]+)\]/i) { "#{$1}\\i[#{$2}]" }
    texts.gsub!(/\n\[([0-9]+)\]/i) { "\\n[#{$1}]" }
    @message_text = texts
  end

  def set_next_page
    @new_message_page = true
    set_tutorial_texts(@next_lines)
  end
  attr_accessor :new_message_page
  attr_reader :next_lines
end

class Game_System
  def setup_message
    @message_position = KInfoMenu::WINDOW_POSITION
    @message_frame = KInfoMenu::SHOW_WINDOW_BACK ? 0 : 1
  end

  def reset_message
    @message_position = 2
    @message_frame = 0
  end
end

class Game_Map
  def autoplay_bgm?
    @map.autoplay_bgm
  end
end

class Window_Message
  def clear_contents
    @gold_window.close if @gold_window
    self.pause = false
    self.index = -1
    self.visible = false
  end
end

Terms & Conditions

Free as in [Image: beer.gif] beer for non-commercial games. [Image: gamer.gif]
Include my nickname in your game credits.
That's it! [Image: tongue.gif]
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9

Maranatha!

The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

My Original Stories (available in English and Spanish)

List of Compiled Binary Executables I have published...
HiddenChest & Roole

Give me a free copy of your completed game if you include at least 3 of my scripts! Laughing + Tongue sticking out

Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Reply




Users browsing this thread: 1 Guest(s)