HiddenPlayList XP
#1
HiddenPlayList XP

by Kyonides

Introduction

Well, this one is quite easy to explain: it's a simple jukebox script. Grinning

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-Theme06

NOTE: Always leave an empty line at the end of the file!

Script Call

Code:
$scene = HiddenPlay::Scene.new

The 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
end

Terms & Conditions

Under MIT License.
That's it! Tongue sticking out
"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
#2
Script Update

The latest release of HiddenPlayList XP now allows you to watch a timer telling you how many seconds have been played so far. There were other minor changes that altered the way the menu behaves internally, but they're not relevant for the end user / player.

I also included some instructions on the main post about how to create your custom playlist(s). Winking
"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: 5 Guest(s)