12-10-2025, 01:27 AM
(This post was last modified: 12-18-2025, 10:45 PM by kyonides.
Edit Reason: Update #4
)
KEnterText HC
by Kyonides
Introduction
Ever since HiddenChest version 1.1.98 was released, game developers are now able to implement a very simple text input interface in their games.
It heavily depends on the new additions to the Input module to make it work normally.
kyonides Wrote:Running HiddenChest version 1.1.99 is HIGHLY recommended here.
The CapsLock and Shift keys DO alter the outcome while typing a text.
You can delete characters with Backspace or Delete keys if you use my script.
You can count on the basic ASCII characters to enter a name or nickname or even a number. Hyphen -, period ., and space " " are recognized automatically.
WARNING: Accents will be ignored by default. Sorry about that.

Screenshots
RMXP
Code:
# * KEnterText HC for XP * #
# Scripter : Kyonides
# 2025-12-18
# $scene = KEnter::TextScene.new
module KEnter
class TextBox
REGEX_INT = /\-?[0-9]/
REGEX_DBL = /\-?[0-9]+[\.,]?[0-9]?/
def initialize(bw, bh, color)
@index = 0
@char_limit = 1
@number_mode = nil
@viewport = Viewport.new(0, 0, bw, bh)
bitmap = Bitmap.new(bw, bh)
bitmap.fill_rect(bitmap.rect, color)
@box = Sprite.new(@viewport)
@box.bitmap = bitmap
@bitmap = Bitmap.new(bw - 8, bh - 4)
@bitmap.font.size = 24
@bitmap.font.outline = true
@rect = @bitmap.rect
@text = Sprite.new(@viewport)
@text.bitmap = @bitmap
bitmap = Bitmap.new(20, 28)
bitmap.font.size = 24
bitmap.font.outline = true
bitmap.draw_text(bitmap.rect, "|")
@cursor = Sprite.new(@viewport)
@cursor.visible = false
@cursor.bitmap = bitmap
@sprites = [@text, @box, @cursor]
end
def move2(bx, by, n, lh)
@viewport.x = bx
@viewport.y = by + n * lh
@text.set_xy(4, 2)
@cursor.set_xy(8, 2)
end
def reset_cursor
@index = @chars.size - 1
set_cursor(@chars.join)
end
def chars=(ary)
@chars = ary
@index = ary.size - 1
refresh
set_cursor(@chars.join)
end
def move_back
@index -= 1 if @index > 0
text = @chars[0, @index] || [""]
set_cursor(text.join)
end
def move_forward
@index += 1 if @index < @chars.size - 1
text = @chars[0, @index] || [""]
set_cursor(text.join)
end
def process_char
char = Input.last_char
return unless char
case @number_mode
when :int
char = char[REGEX_INT]
when :dbl
char = char[REGEX_DBL]
else
if Input.press?(Input::SHIFT)
char = Input.capslock_state ? char.downcase : char.upcase
end
end
return unless char
@index += 1
if @chars.size == @index
@chars << char
else
@chars[@index, 0] = char
end
@chars.compact!
text = @chars[0, @index]
refresh
set_cursor(text.join)
end
def delete_prev_char
if @chars.size - 1 == @index
@chars.pop
@index = [@chars.size - 1, 0].max
text = @chars
else
@chars.delete_at(@index)
@index = [@index - 1, 0].max
text = @chars[0, @index] || [""]
end
refresh
set_cursor(text.join)
end
def delete_next_char
@chars.delete_at(@index + 1)
refresh
end
def refresh
@bitmap.clear
@bitmap.draw_text(@rect, @chars.join)
end
def set_cursor(text)
tw = @bitmap.text_width(text)
@cursor.x = tw + 8
end
def char_max?
@chars.size == @char_limit
end
def integer_only!
@number_mode = :int
end
def double_only!
@number_mode = :dbl
end
def include_all!
@number_mode = nil
end
def dispose
@sprites.each do |sprite|
sprite.bitmap.dispose
sprite.dispose
end
@sprites.clear
@viewport.dispose
end
attr_writer :char_limit
attr_reader :chars, :index, :cursor
end
class TextScene
CHAR_LIMIT = 18
NUM_LIMIT = 2
BACKDROP = "catacombs"
LABELS = ["Your Name", "Your Nickname", "Your Age"]
@@texts = Array.new(3) {[]}
def main
@stage = :main
@blink_timer = Graphics.frame_rate / 5
@blink_state = false
@index = 0
@text_sprites = []
black = Color.new(0, 0, 0)
@blue = Color.new(80, 80, 255)
custom = Color.new(255, 255, 40)
lbit = Bitmap.new(208, 180)
lbit.font.outline = true
3.times {|n| lbit.draw_text(16, 78 * n, 220, 24, LABELS[n]) }
@backdrop = Sprite.new
@backdrop.bitmap = RPG::Cache.title(BACKDROP)
@label_sprite = Sprite.new
@label_sprite.set_xy(16, 12)
@label_sprite.bitmap = lbit
make_box_text_sprites
cb = Bitmap.new(208, 4)
@main_cursor = Sprite.new
@main_cursor.set_xy(20, 38)
@main_cursor.bitmap = cb
cb.fill_rect(cb.rect, black)
cb.fill_rect(2, 1, 204, 2, custom)
@text_cursor = @text_sprites[0].cursor
Graphics.transition
while @stage
Graphics.update
Input.update
update
end
Graphics.freeze
cb.dispose
@main_cursor.dispose
@text_sprites.each_with_index do |box, n|
box.dispose
@@texts[n] = box.chars
end
@label_sprite.dispose
@backdrop.bitmap.dispose
@backdrop.dispose
end
def make_box_text_sprites
3.times do |n|
textbox = TextBox.new(n < 2 ? 228 : 116, 32, @blue)
textbox.move2(16, 48, n, 78)
textbox.chars = @@texts[n]
textbox.char_limit = n < 2 ? CHAR_LIMIT : NUM_LIMIT
textbox.integer_only! if n == 2
@text_sprites << textbox
end
end
def update
case @stage
when :main
update_index
when :text_input
update_text_input
end
end
def update_blink
@blink_timer -= 1
if @blink_timer == 0
@blink_timer = Graphics.frame_rate / 5
@blink_state = !@blink_state
@text_cursor.visible = @blink_state
end
end
def update_index
if Input.trigger?(Input::Escape)
$game_system.se_play($data_system.cancel_se)
$scene = Scene_Map.new
return @sprite = @stage = nil
elsif Input.trigger?(Input::UP)
set_index(-1)
return
elsif Input.trigger?(Input::DOWN)
set_index(1)
return
elsif Input.trigger?(Input::Enter) or Input.trigger?(Input::Return)
$game_system.se_play($data_system.decision_se)
Input.text_input = true
Input.update
@stage = :text_input
@sprite = @text_sprites[@index]
@blink_timer = Graphics.frame_rate / 5
@text_cursor.visible = @blink_state = true
end
end
def set_index(n)
$game_system.se_play($data_system.cursor_se)
@index = (@index + n) % 3
@main_cursor.y = 38 + @index * 78
@text_cursor = @text_sprites[@index].cursor
end
def update_text_input
update_blink
if Input.trigger?(Input::Left)
$game_system.se_play($data_system.cursor_se)
@sprite.move_back
return
elsif Input.trigger?(Input::Right)
$game_system.se_play($data_system.cursor_se)
@sprite.move_forward
return
elsif Input.trigger?(Input::Escape)
$game_system.se_play($data_system.cancel_se)
process_text_box_cancel
return
elsif Input.trigger?(Input::Enter) or Input.trigger?(Input::Return)
$game_system.se_play($data_system.decision_se)
Graphics.screenshot
process_text_box_cancel
return
elsif Input.repeat?(Input::Backspace)
$game_system.se_play($data_system.buzzer_se)
@sprite.delete_prev_char
return
elsif Input.repeat?(Input::Delete)
$game_system.se_play($data_system.buzzer_se)
@sprite.delete_next_char
return
elsif Input.trigger_any? and Input.last_key?
if @sprite.char_max?
$game_system.se_play($data_system.buzzer_se)
return
end
$game_system.se_play($data_system.equip_se)
@sprite.process_char
end
end
def process_text_box_cancel
Input.text_input = false
Input.clear_text_input
Input.update
@sprite.reset_cursor
@text_cursor.visible = @blink_state = false
@blink_timer = Graphics.frame_rate / 5
@stage = :main
end
end
endRMVX + RMVX ACE
Code:
# * KEnterText HC for VX + ACE * #
# Scripter : Kyonides
# 2025-12-18
# For VX:
# $scene = KEnter::TextScene.new
# For VX ACE:
# SceneManager.call(KEnter::TextScene)
module KEnter
class TextBox
REGEX_INT = /\-?[0-9]/
REGEX_DBL = /\-?[0-9]+[\.,]?[0-9]?/
def initialize(bw, bh, color)
@index = 0
@char_limit = 1
@number_mode = nil
@viewport = Viewport.new(0, 0, bw, bh)
bitmap = Bitmap.new(bw, bh)
bitmap.fill_rect(bitmap.rect, color)
@box = Sprite.new(@viewport)
@box.bitmap = bitmap
@bitmap = Bitmap.new(bw - 8, bh - 4)
@bitmap.font.size = 24
@bitmap.font.outline = true
@rect = @bitmap.rect
@text = Sprite.new(@viewport)
@text.bitmap = @bitmap
bitmap = Bitmap.new(20, 28)
bitmap.font.size = 24
bitmap.font.outline = true
bitmap.draw_text(bitmap.rect, "|")
@cursor = Sprite.new(@viewport)
@cursor.visible = false
@cursor.bitmap = bitmap
@sprites = [@text, @box, @cursor]
end
def move2(bx, by, n, lh)
@viewport.x = bx
@viewport.y = by + n * lh
@text.set_xy(4, 2)
@cursor.set_xy(8, 2)
end
def reset_cursor
@index = @chars.size - 1
set_cursor(@chars.join)
end
def chars=(ary)
@chars = ary
@index = ary.size - 1
refresh
set_cursor(@chars.join)
end
def move_back
@index -= 1 if @index > 0
text = @chars[0, @index] || [""]
set_cursor(text.join)
end
def move_forward
@index += 1 if @index < @chars.size - 1
text = @chars[0, @index] || [""]
set_cursor(text.join)
end
def process_char
char = Input.last_char
return unless char
case @number_mode
when :int
char = char[REGEX_INT]
when :dbl
char = char[REGEX_DBL]
else
if Input.press?(Input::SHIFT)
char = Input.capslock_state ? char.downcase : char.upcase
end
end
return unless char
@index += 1
if @chars.size == @index
@chars << char
else
@chars[@index, 0] = char
end
@chars.compact!
text = @chars[0, @index]
refresh
set_cursor(text.join)
end
def delete_prev_char
if @chars.size - 1 == @index
@chars.pop
@index = [@chars.size - 1, 0].max
text = @chars
else
@chars.delete_at(@index)
@index = [@index - 1, 0].max
text = @chars[0, @index] || [""]
end
refresh
set_cursor(text.join)
end
def delete_next_char
@chars.delete_at(@index + 1)
refresh
end
def refresh
@bitmap.clear
@bitmap.draw_text(@rect, @chars.join)
end
def set_cursor(text)
tw = @bitmap.text_width(text)
@cursor.x = tw + 8
end
def char_max?
@chars.size == @char_limit
end
def integer_only!
@number_mode = :int
end
def double_only!
@number_mode = :dbl
end
def include_all!
@number_mode = nil
end
def dispose
@sprites.each do |sprite|
sprite.bitmap.dispose
sprite.dispose
end
@sprites.clear
@viewport.dispose
end
attr_writer :char_limit
attr_reader :chars, :index, :cursor
end
class TextScene
CHAR_LIMIT = 18
BACKDROP = "catacombs"
LABELS = ["Your Name", "Your Nickname", "Your Age"]
@@texts = Array.new(3) {[]}
def main
@vx_ace = Game::RGSS_VERSION == 3
@stage = :main
@blink_timer = Graphics.frame_rate / 5
@blink_state = false
@index = 0
@text_sprites = []
black = Color.new(0, 0, 0)
@blue = Color.new(80, 80, 255)
custom = Color.new(255, 255, 40)
lbit = Bitmap.new(208, 180)
lbit.font.outline = true
3.times {|n| lbit.draw_text(16, 78 * n, 220, 24, LABELS[n]) }
@backdrop = Sprite.new
@backdrop.bitmap = Cache.system(BACKDROP)
@label_sprite = Sprite.new
@label_sprite.set_xy(16, 12)
@label_sprite.bitmap = lbit
make_box_text_sprites
cb = Bitmap.new(208, 4)
@main_cursor = Sprite.new
@main_cursor.set_xy(20, 38)
@main_cursor.bitmap = cb
cb.fill_rect(cb.rect, black)
cb.fill_rect(2, 1, 204, 2, custom)
@text_cursor = @text_sprites[0].cursor
Graphics.transition
while @stage
Graphics.update
Input.update
update
end
Graphics.freeze
cb.dispose
@main_cursor.dispose
@text_sprites.each_with_index do |box, n|
box.dispose
@@texts[n] = box.chars
end
@label_sprite.dispose
@backdrop.bitmap.dispose
@backdrop.dispose
end
def make_box_text_sprites
3.times do |n|
textbox = TextBox.new(n < 2 ? 228 : 116, 32, @blue)
textbox.move2(16, 48, n, 78)
textbox.chars = @@texts[n]
textbox.char_limit = n < 2 ? CHAR_LIMIT : NUM_LIMIT
textbox.integer_only! if n == 2
@text_sprites << textbox
end
end
def update
case @stage
when :main
update_index
when :text_input
update_text_input
end
end
def update_blink
@blink_timer -= 1
if @blink_timer == 0
@blink_timer = Graphics.frame_rate / 5
@blink_state = !@blink_state
@text_cursor.visible = @blink_state
end
end
def update_index
if Input.trigger?(Input::Escape)
Sound.play_cancel
$scene = Scene_Map.new
return @sprite = @stage = nil
elsif Input.trigger?(Input::UP)
set_index(-1)
return
elsif Input.trigger?(Input::DOWN)
set_index(1)
return
elsif Input.trigger?(Input::Enter) or Input.trigger?(Input::Return)
@vx_ace ? Sound.play_ok : Sound.play_decision
Input.text_input = true
Input.update
@stage = :text_input
@sprite = @text_sprites[@index]
@blink_timer = Graphics.frame_rate / 5
@text_cursor.visible = @blink_state = true
end
end
def set_index(n)
Sound.play_cursor
@index = (@index + n) % 3
@main_cursor.y = 38 + @index * 78
@text_cursor = @text_sprites[@index].cursor
end
def update_text_input
update_blink
if Input.trigger?(Input::Left)
$game_system.se_play($data_system.cursor_se)
@sprite.move_back
return
elsif Input.trigger?(Input::Right)
$game_system.se_play($data_system.cursor_se)
@sprite.move_forward
return
elsif Input.trigger?(Input::Escape)
Sound.play_cancel
process_text_box_cancel
return
elsif Input.trigger?(Input::Enter) or Input.trigger?(Input::Return)
@vx_ace ? Sound.play_ok : Sound.play_decision
Graphics.screenshot
process_text_box_cancel
return
elsif Input.repeat?(Input::Backspace)
Sound.play_buzzer
@sprite.delete_char
return
elsif Input.repeat?(Input::Delete)
$game_system.se_play($data_system.buzzer_se)
@sprite.delete_next_char
return
elsif Input.trigger_any? and Input.last_key?
if @sprite.char_max?
Sound.play_buzzer
return
end
Sound.play_equip
@sprite.process_char
end
end
def process_text_box_cancel
Input.text_input = false
Input.clear_text_input
Input.update
@sprite.reset_cursor
@text_cursor.visible = @blink_state = false
@blink_timer = Graphics.frame_rate / 5
@stage = :main
end
end
endTerms & Conditions
The same as HiddenChest's.
That's it!
"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]](https://www.save-point.org/images/userbars/SP1-Scripter.png)
![[Image: SP1-Writer.png]](https://www.save-point.org/images/userbars/SP1-Writer.png)
![[Image: SP1-Poet.png]](https://www.save-point.org/images/userbars/SP1-Poet.png)
![[Image: SP1-PixelArtist.png]](https://www.save-point.org/images/userbars/SP1-PixelArtist.png)
![[Image: SP1-Reporter.png]](https://i.postimg.cc/GmxWbHyL/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!
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
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]](https://www.save-point.org/images/userbars/SP1-Scripter.png)
![[Image: SP1-Writer.png]](https://www.save-point.org/images/userbars/SP1-Writer.png)
![[Image: SP1-Poet.png]](https://www.save-point.org/images/userbars/SP1-Poet.png)
![[Image: SP1-Reporter.png]](https://i.postimg.cc/GmxWbHyL/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!

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


![[Image: kentertext-hc001.jpg]](https://i.postimg.cc/Vkf1R9X7/kentertext-hc001.jpg)
![[Image: kentertext-hc002.jpg]](https://i.postimg.cc/VLPq0L1p/kentertext-hc002.jpg)
![[Image: kentertext-hc003.jpg]](https://i.postimg.cc/ncFwPDQp/kentertext-hc003.jpg)

game developers to easily use or reuse the KEnter::TextBox class in other contexts or menus. It now keeps track of everything related to the box and the custom text to be displayed there.


