CampBuddy/Camp.Buddy v2.2.1/Camp_Buddy-2.2.1-pc/renpy/common/00musicroom.rpy
2025-03-03 23:00:33 +01:00

529 lines
16 KiB
Text

# Copyright 2004-2019 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# This file contains the code to implement Ren'Py's music room function,
# which consists of the ability to set up a playlist (the aforementioned
# "music room"), and then a series of actions that let the player
# navigate through the music room.
init -1500 python:
@renpy.pure
class __MusicRoomPlay(Action, FieldEquality):
"""
The action returned by MusicRoom.Play when called with a file.
"""
identity_fields = [ "mr" ]
equality_fields = [ "filename" ]
def __init__(self, mr, filename):
self.mr = mr
self.filename = filename
self.selected = self.get_selected()
def __call__(self):
self.mr.play(self.filename, 0)
def get_sensitive(self):
return self.mr.is_unlocked(self.filename)
def get_selected(self):
return renpy.music.get_playing(self.mr.channel) == self.filename
def periodic(self, st):
if self.selected != self.get_selected():
self.selected = self.get_selected()
renpy.restart_interaction()
self.mr.periodic(st)
return .1
@renpy.pure
class __MusicRoomRandomPlay(Action, FieldEquality):
"""
The action returned by MusicRoom.RandomPlay
"""
identity_fields = [ "mr" ]
def __init__(self, mr):
self.mr = mr
def __call__(self):
playlist = self.mr.unlocked_playlist()
if not playlist:
return
self.mr.shuffled = None
self.mr.play(renpy.random.choice(playlist), 0)
@renpy.pure
class __MusicRoomTogglePlay(Action, FieldEquality):
"""
The action returned by MusicRoom.TogglePlay
"""
identity_fields = [ "mr" ]
def __init__(self, mr):
self.mr = mr
def __call__(self):
if renpy.music.get_playing(self.mr.channel):
return self.mr.stop()
self.mr.shuffled = None
return self.mr.play()
def get_selected(self):
return renpy.music.get_playing(self.mr.channel) is not None
@renpy.pure
class __MusicRoomStop(Action, FieldEquality):
"""
The action returned by MusicRoom.Stop.
"""
identity_fields = [ "mr" ]
def __init__(self, mr):
self.mr = mr
self.selected = self.get_selected()
def __call__(self):
self.mr.stop()
def get_selected(self):
return renpy.music.get_playing() is None
def periodic(self, st):
if self.selected != self.get_selected():
self.selected = self.get_selected()
renpy.restart_interaction()
self.mr.periodic(st)
return .1
class MusicRoom(object):
"""
:doc: music_room class
A music room that contains a series of songs that can be unlocked
by the user, and actions that can play entries from the list in
order.
"""
loop = False
loop_compat = False
def __init__(self, channel="music", fadeout=0.0, fadein=0.0, loop=True, single_track=False, shuffle=False, stop_action=None):
"""
`channel`
The channel that this music room will operate on.
`fadeout`
The number of seconds it takes to fade out the old
music when changing tracks.
`fadein`
The number of seconds it takes to fade in the new
music when changing tracks.
`loop`
Determines if playback will loop or stop when it reaches
the end of the playlist.
`single_track`
If true, only a single track will play. If loop is true,
that track will loop. Otherwise, playback will stop when the
track finishes.
`shuffle`
If true, the tracks are shuffled, and played in the shuffled
order. If false, the tracks are played in the order they're
added to the MusicRoom.
`stop_action`
An action to run when the music has stopped.
Single_track and shuffle conflict with each other. Only one should
be true at a time. (Actions that set single_track and shuffle
enforce this.)
"""
self.channel = channel
self.fadeout = fadeout
self.fadein = fadein
# A map from track name (or "" for stopped) to the appropriate
# action.
self.action = { "" : stop_action }
# The last track playing, or "" if we've been stopped.
self.last_playing = None
# The list of strings giving the titles of songs that make up the
# playlist.
self.playlist = [ ]
# A shuffled copy of the playlist. (Created on demand when we
# need it.)
self.shuffled = None
# A set of filenames, so we can quickly check if a valid filename
# has been provided.
self.filenames = set()
# The set of songs that are always unlocked.
self.always_unlocked = set()
# Should we loop a single track rather than advancing to the next
# track?
self.loop = loop
# Should we play a single track?
self.single_track = single_track
# Should we shuffle the playlist?
if self.single_track:
self.shuffle = False
else:
self.shuffle = shuffle
# In older versions, loop would loop a single trak.
if self.loop_compat and loop:
self.single_track = True
# The last shown time for the music room.
self.st = -1
def periodic(self, st):
if st == self.st:
return
elif st < self.st:
self.last_playing = None
self.st = st
current_playing = renpy.music.get_playing(self.channel)
if current_playing is None:
current_playing = ""
if self.last_playing != current_playing:
action = self.action.get(current_playing, None)
renpy.run_action(action)
self.last_playing = current_playing
def add(self, filename, always_unlocked=False, action=None):
"""
:doc: music_room method
Adds the music file `filename` to this music room. The music room
will play unlocked files in the order that they are added to the
room.
`always_unlocked`
If true, the music file will be always unlocked. This allows
the file to show up in the music room before it has been
played in the game.
`action`
This is a action or the list of actions. these are called when this
file is played.
For example, These actions is used to change a screen or background, description
by the playing file.
"""
self.playlist.append(filename)
self.filenames.add(filename)
if action:
self.action[filename] = action
if always_unlocked:
self.always_unlocked.add(filename)
def is_unlocked(self, filename):
"""
:doc: music_room method
Returns true if the filename has been unlocked (or is always
unlocked), and false if it is still locked.
"""
if filename in self.always_unlocked:
return True
return renpy.seen_audio(filename)
def unlocked_playlist(self, filename=None):
"""
Returns a list of filenames in the playlist that have been
unlocked.
"""
if self.shuffle:
if self.shuffled is None or (filename and self.shuffled[0] != filename):
import random
self.shuffled = list(self.playlist)
random.shuffle(self.shuffled)
if filename in self.shuffled:
self.shuffled.remove(filename)
self.shuffled.insert(0, filename)
playlist = self.shuffled
else:
self.shuffled = None
playlist = self.playlist
return [ i for i in playlist if self.is_unlocked(i) ]
def play(self, filename=None, offset=0, queue=False):
"""
Starts the music room playing. The file we start playing with is
selected in two steps.
If `filename` is an unlocked file, we start by playing it.
Otherwise, we start by playing the currently playing file, and if
that doesn't exist or isn't unlocked, we start with the first file.
We then apply `offset`. If `offset` is positive, we advance that many
files, otherwise we go back that many files.
If `queue` is true, the music is queued. Otherwise, it is played
immediately.
"""
playlist = self.unlocked_playlist(filename)
if not playlist:
return
if filename is None:
filename = renpy.music.get_playing(channel=self.channel)
try:
idx = playlist.index(filename)
except ValueError:
idx = 0
idx = (idx + offset) % len(playlist)
if self.single_track:
playlist = [ playlist[idx] ]
elif self.loop:
playlist = playlist[idx:] + playlist[:idx]
else:
playlist = playlist[idx:]
if queue:
renpy.music.queue(playlist, channel=self.channel, loop=self.loop)
else:
renpy.music.play(playlist, channel=self.channel, fadeout=self.fadeout, fadein=self.fadein, loop=self.loop)
def queue_if_playing(self):
"""
If the music is not playing, do nothing.
Otherwise, redo the queue such that we have the right tracks
queued up.
"""
filename = renpy.music.get_playing(channel=self.channel)
if filename is None:
return
if self.single_track:
self.play(None, offset=0, queue=True)
else:
self.play(None, offset=1, queue=True)
def stop(self):
"""
Stops the music from playing.
"""
renpy.music.stop(channel=self.channel, fadeout=self.fadeout)
def next(self):
"""
Plays the next file in the playlist.
"""
filename = renpy.music.get_playing(channel=self.channel)
if filename is None:
return self.play(None, 0)
else:
return self.play(None, 1)
def previous(self):
"""
Plays the previous file in the playlist.
"""
return self.play(None, -1)
def Play(self, filename=None):
"""
:doc: music_room method
This action causes the music room to start playing. If `filename` is given, that
file begins playing. Otherwise, the currently playing file starts
over (if it's unlocked), or the first file starts playing.
If `filename` is given, buttons with this action will be insensitive
while `filename` is locked, and will be selected when `filename`
is playing.
"""
if filename is None:
return self.play
if filename not in self.filenames:
raise Exception("{0!r} is not a filename registered with this music room.".format(filename))
return __MusicRoomPlay(self, filename)
def RandomPlay(self):
"""
:doc: music_room method
This action causes the music room to start playing a randomly selected unlocked
music track.
"""
return __MusicRoomRandomPlay(self)
def TogglePlay(self):
"""
:doc: music_room method
If no music is currently playing, this action starts playing the first
unlocked track. Otherwise, stops the currently playing music.
This button is selected when any music is playing.
"""
return __MusicRoomTogglePlay(self)
def Stop(self):
"""
:doc: music_room method
This action stops the music.
"""
return __MusicRoomStop(self)
def Next(self):
"""
:doc: music_room method
An action that causes the music room to play the next unlocked file
in the playlist.
"""
return self.next
def Previous(self):
"""
:doc: music_room method
An action that causes the music room to play the previous unlocked
file in the playlist.
"""
return self.previous
def SetLoop(self, value):
"""
:doc: music_room method
This action sets the value of the loop property.
"""
return [ SetField(self, "loop", value), self.queue_if_playing ]
def SetSingleTrack(self, value):
"""
:doc: music_room method
This action sets the value of the single_track property.
"""
if value:
return [SelectedIf(self.single_track), SetField(self, "single_track", value), SetField(self, "shuffle", False), self.queue_if_playing ]
else:
return [SelectedIf(not self.single_track), SetField(self, "single_track", value), self.queue_if_playing ]
def SetShuffle(self, value):
"""
:doc: music_room method
This action sets the value of the shuffle property.
"""
if value:
return [SelectedIf(self.shuffle), SetField(self, "shuffle", value), SetField(self, "single_track", False), self.queue_if_playing ]
else:
return [SelectedIf(not self.shuffle), SetField(self, "shuffle", value), self.queue_if_playing ]
def ToggleLoop(self):
"""
:doc: music_room method
This action toggles the value of the loop property.
"""
return [ ToggleField(self, "loop"), self.queue_if_playing ]
def ToggleSingleTrack(self):
"""
:doc: music_room method
This action toggles the value of the single_track property.
"""
return [SelectedIf(self.single_track), ToggleField(self, "single_track"), SetField(self, "shuffle", False), self.queue_if_playing ]
def ToggleShuffle(self):
"""
:doc: music_room method
This action toggles the value of the shuffle property.
"""
return [SelectedIf(self.shuffle), ToggleField(self, "shuffle"), SetField(self, "single_track", False), self.queue_if_playing ]