06-22-2026, 02:44 AM
(This post was last modified: Yesterday, 06:54 PM by kyonides.
Edit Reason: Update #2
)
HiddenPlayList XP
by Kyonides
Introduction
Well, this one is quite easy to explain: it's a simple jukebox script.

Setup
Create 1 or more playlists, and name it "playlist1.txt" or something the like depending on the number of available playlists.
The file's contents should always follow this format:
Playlist Title
Custom Song Name > Filename
etc.
Here's an example:
Code:
ACE OGG + XP MIDI
Our Airship > Airship
Blood > 04_blood
Theme #4 > 015-Theme04
Battle #1 > 001-Battle01
Field #1 > 018-Field01
Battle #2 > 002-Battle02
Field #2 > 019-Field02
Battle #3 > 003-Battle03
Field #3 > 020-Field03
Battle #4 > 004-Battle04
Theme #6 > 017-Theme06NOTE: Always leave an empty line at the end of the file!
Script Call
Code:
$scene = HiddenPlay::Scene.newThe Script
Code:
# * HiddenPlayList XP * #
# Scripter : Kyonides Arkanthes
# 2026-06-30
# * REQUIRES HiddenChest v1.2.12 or later * #
# This scriptlet allows you to play a list of songs one after another.
# * Script Call * #
# $scene = HiddenPlay::Scene.new
module HiddenPlay
@menu_bgm = ["024-Town02", 50]
MAIN_BGM_CHANNEL = 3
LIST_BGM_CHANNEL = 1
class Song < RPG::AudioFile
DIR = "Audio/BGM/"
def play
Audio.bgms_play(@channel, DIR + @name, @volume, @pitch)
@seconds ||= Audio.bgms_seconds(@channel)
@playing = true
end
def pause
Audio.bgms_pause(@channel)
@playing = false
end
def resume
Audio.bgms_resume(@channel)
@playing = true
end
def toggle!
@playing ? pause : resume
end
attr_accessor :title, :channel
attr_reader :seconds
end
class List
def initialize
@name = "Playlist"
@files = []
end
attr_accessor :name, :files
end
extend self
attr_reader :menu_bgm
class BGMWindow < Window_Base
def initialize(wx, wy, ww, wh)
super
@width = ww - 32
@height = wh - 32
self.contents = Bitmap.new(@width, @height)
@name_rect = Rect.new(0, 0, @width, 32)
@time_rect = Rect.new(0, 40, @width, 32)
end
def calc_time(sec)
@bar.set_progress(sec)
@seconds = sec % 60
@minutes = sec / 60
self.contents.clear_rect(@time_rect)
end
def set(file)
@channel = file.channel
@name = file.title
@minutes = 0
@seconds = Audio.bgms_pos(@channel).round
total_seconds = file.seconds.floor
@sec = total_seconds % 60
@min = total_seconds / 60
self.contents.clear
self.contents.draw_text(@name_rect, @name, 1)
@total_time = sprintf("%02d:%02d", @min, @sec)
refresh
end
def refresh
self.contents.clear_rect(@time_rect)
text = sprintf("%02d:%02d", @minutes, @seconds)
self.contents.draw_text(@time_rect, text)
self.contents.draw_text(@time_rect, @total_time, 2)
end
def update
sec = Audio.bgms_pos(@channel).round
if @seconds != sec
calc_time(sec)
refresh
end
end
attr_writer :bar
end
class BarSprite < Sprite
BLACK = Color.new(0, 0, 0)
LIGHT = Color.new(192, 224, 255, 255)
def initialize(sx, sy, sw, sh)
super(nil)
self.x = sx
self.y = sy
@width = sw
@height = sh
reset
end
def reset
self.bitmap = Bitmap.new(@width, @height)
self.bitmap.fill_rect(:rect, BLACK)
end
def total=(sec)
self.visible = true
@total = sec
reset
end
def set_progress(sec)
rect = self.bitmap.rect.dup
percent = sec * 100 / @total
percent = 100 if percent == 0
rect.width = percent * @width / 100
self.bitmap.fill_rect(rect, LIGHT)
end
end
class Scene
SELECT_LIST = "Select a Playlist"
EMPTY_LIST = "Empty List"
MENU_BGM = Song.new(*HiddenPlay.menu_bgm)
MENU_BGM.channel = MAIN_BGM_CHANNEL
VOLUME = 40
def initialize
@loop_states = Audio.bgms_loop_all.dup
Audio.bgms_loop_set(1, false)
Audio.bgms_loop_set(2, false)
Audio.bgms_loop_set(3, true)
3.times {|n| Audio.bgms_fade(n + 1, 60) }
MENU_BGM.play
@font_outline = Font.default_outline
Font.default_outline = true
@lists = []
@files = Dir["playlist*.txt"].sort
@files.each do |file|
lines = File.readlines(file)
list = List.new
list.name = lines.shift.chomp
lines.each do |line|
texts = line.split(" > ")
audio = Song.new
audio.title = texts.shift
audio.name = texts.shift.chomp
audio.volume = VOLUME
audio.channel = LIST_BGM_CHANNEL
list.files << audio
end
@lists << list
end
@commands = @lists.map(&:name) || [EMPTY_LIST]
@playlist_empty = @lists.empty?
@list_index = @song_index = @timer = 0
@stop = true
@stage = :main
end
def main
start
Graphics.transition
while @stage
Graphics.update
Input.update
update
end
Graphics.freeze
terminate
end
def start
@help_window = Window_Help.new
@help_window.set_text(SELECT_LIST, 1)
@list_window = Window_Command.new(240, @commands)
@list_window.y = 64
ox = @list_window.width
ww = Graphics.width - ox
@bgm_window = BGMWindow.new(ox, 64, ww, 100)
@playtime_bar = BarSprite.new(ox + 16, 112, ww - 32, 4)
@playtime_bar.visible = false
@playtime_bar.z = 100
@bgm_window.bar = @playtime_bar
end
def update
case @stage
when :main
update_main
when :list
update_list
when :next
update_next
end
end
def update_main
@list_window.update
if Input.trigger?(:B)
$game_system.se_play($data_system.cancel_se)
Audio.bgms_stop_all
$scene = Scene_Map.new
return @stage = nil
elsif Input.trigger?(:C)
if @playlist_empty
$game_system.se_play($data_system.buzzer_se)
return
end
MENU_BGM.pause
n = @list_window.index
@list = @lists[n]
@files = @list.files
if @files.empty?
$game_system.se_play($data_system.buzzer_se)
@list = nil
return @files = nil
end
$game_system.se_play($data_system.decision_se)
@song_index = 0 if @list_index != n
@list_index = n
@help_window.set_text(@list.name, 1)
@list_window.active = false
@list_window.visible = false
titles = @files.map(&:title)
@song_window = Window_Command.new(240, titles)
@song_window.y = 64
@file_max = @files.size
play_file
end
end
def play_file
@song_window.index = @song_index
@file = @files[@song_index]
@file.play
@bgm_window.set(@file)
@playtime_bar.total = @file.seconds
@stage = :list
end
def update_next
@song_index = (@song_index + 1) % @file_max
play_file
@play_next = nil
end
def update_list
if Audio.bgms_stopped?(LIST_BGM_CHANNEL)
return @stage = :next
end
@bgm_window.update
@song_window.update
if Input.trigger?(:B)
$game_system.se_play($data_system.cancel_se)
Audio.bgms_stop(LIST_BGM_CHANNEL)
MENU_BGM.resume
@help_window.set_text(SELECT_LIST, 1)
@playtime_bar.visible = false
@bgm_window.contents.clear
@song_window.dispose
@list_window.active = true
@list_window.visible = true
return @stage = :main
elsif Input.trigger?(:C)
$game_system.se_play($data_system.decision_se)
if @song_window.index == @song_index
@file.toggle!
return
end
Audio.bgms_stop(LIST_BGM_CHANNEL)
@song_index = @song_window.index - 1
@stage = :next
end
end
def terminate
@loop_states.each {|n, state| Audio.bgms_loop_set(n, state) }
Font.default_outline = @font_outline
@playtime_bar.dispose
@bgm_window.dispose
@list_window.dispose
@help_window.dispose
$game_map.autoplay
end
end
end
class Game_System
def bgms_play(n, bgm)
@playing_bgms ||= {}
@playing_bgms[n] = bgm
if bgm != nil and bgm.name != ""
Audio.bgms_play(n, "Audio/BGM/" + bgm.name, bgm.volume, bgm.pitch)
else
Audio.bgms_stop(n)
end
Graphics.frame_reset
end
def bgms_stop(n)
Audio.bgms_stop(n)
end
def bgms_fade(n, time)
@playing_bgms ||= {}
@playing_bgms[n] = nil
Audio.bgms_fade(n, time * 1000)
end
def bgms_memorize(n)
@memorized_bgms ||= {}
@memorized_bgms[n] = @playing_bgm
end
def bgms_restore(n)
bgms_play(n, @memorized_bgm)
end
endTerms & Conditions
Under MIT License.
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

