Cleaned up music playback

This commit is contained in:
Eric Froemling 2020-04-01 01:22:26 -07:00
parent 6f3f1644b4
commit 2a2e64b7ab
36 changed files with 386 additions and 305 deletions

View File

@ -436,6 +436,7 @@
<w>domreg</w>
<w>domsg</w>
<w>dont</w>
<w>doplay</w>
<w>doraise</w>
<w>dosend</w>
<w>dosomething</w>
@ -1066,6 +1067,7 @@
<w>mtime</w>
<w>mtrans</w>
<w>mtvos</w>
<w>mtype</w>
<w>mult</w>
<w>multibytecodec</w>
<w>multikillcount</w>
@ -1074,6 +1076,8 @@
<w>multiteamendscreen</w>
<w>multiteamjoinscreen</w>
<w>multithreaded</w>
<w>musicinfo</w>
<w>musicinfos</w>
<w>musicplayer</w>
<w>musictype</w>
<w>musopen</w>
@ -1526,6 +1530,7 @@
<w>socketmodule</w>
<w>socketserver</w>
<w>somevar</w>
<w>soundtrackname</w>
<w>sourceimages</w>
<w>sourcelines</w>
<w>spacelen</w>

View File

@ -50,6 +50,9 @@
- The bs.Vector class is no more; in its place is a shiny new ba.Vec3 which is implemented internally in C++ so its nice and speedy. Will probably update certain things like vector node attrs to support this class in the future since it makes vector math nice and convenient.
- Ok you get the point..
### 1.4.151 (14371)
- Added Chinese-Traditional language and improved translations for others.
### 1.4.150 (14369)
- Telnet port can now be specified in the config
- Telnet socket no longer opens on headless build when telnet access is off (reduces DoS attack potential)

View File

@ -34,7 +34,7 @@ NOTE: This file was autogenerated by gendummymodule; do not edit by hand.
"""
# (hash we can use to see if this file is out of date)
# SOURCES_HASH=107308741262112812748560676667362932520
# SOURCES_HASH=237130603961569354119127975507628873256
# I'm sorry Pylint. I know this file saddens you. Be strong.
# pylint: disable=useless-suppression
@ -2737,62 +2737,6 @@ def is_running_on_ouya() -> bool:
return bool()
def itunes_get_library_source() -> None:
"""itunes_get_library_source() -> None
(internal)
"""
return None
def itunes_get_playlists() -> List[str]:
"""itunes_get_playlists() -> List[str]
(internal)
"""
return ["blah", "blah2"]
def itunes_get_volume() -> int:
"""itunes_get_volume() -> int
(internal)
"""
return int()
def itunes_init() -> None:
"""itunes_init() -> None
(internal)
"""
return None
def itunes_play_playlist(playlist: str) -> bool:
"""itunes_play_playlist(playlist: str) -> bool
(internal)
"""
return bool()
def itunes_set_volume(volume: int) -> None:
"""itunes_set_volume(volume: int) -> None
(internal)
"""
return None
def itunes_stop() -> None:
"""itunes_stop() -> None
(internal)
"""
return None
def lock_all_input() -> None:
"""lock_all_input() -> None
@ -2825,6 +2769,62 @@ def log(message: str,
return None
def mac_music_app_get_library_source() -> None:
"""mac_music_app_get_library_source() -> None
(internal)
"""
return None
def mac_music_app_get_playlists() -> List[str]:
"""mac_music_app_get_playlists() -> List[str]
(internal)
"""
return ["blah", "blah2"]
def mac_music_app_get_volume() -> int:
"""mac_music_app_get_volume() -> int
(internal)
"""
return int()
def mac_music_app_init() -> None:
"""mac_music_app_init() -> None
(internal)
"""
return None
def mac_music_app_play_playlist(playlist: str) -> bool:
"""mac_music_app_play_playlist(playlist: str) -> bool
(internal)
"""
return bool()
def mac_music_app_set_volume(volume: int) -> None:
"""mac_music_app_set_volume(volume: int) -> None
(internal)
"""
return None
def mac_music_app_stop() -> None:
"""mac_music_app_stop() -> None
(internal)
"""
return None
def mark_config_dirty() -> None:
"""mark_config_dirty() -> None

View File

@ -78,7 +78,7 @@ from ba._messages import (OutOfBoundsMessage, DieMessage, StandMessage,
DroppedMessage, ShouldShatterMessage,
ImpactDamageMessage, FreezeMessage, ThawMessage,
HitMessage)
from ba._music import setmusic, MusicPlayer
from ba._music import setmusic, MusicPlayer, MusicType, MusicPlayMode
from ba._powerup import PowerupMessage, PowerupAcceptMessage
from ba._teambasesession import TeamBaseSession
from ba.ui import (OldWindow, UILocation, UILocationWindow, UIController,

View File

@ -25,7 +25,8 @@ import time
from typing import TYPE_CHECKING
import _ba
from ba import _activity
from ba._activity import Activity
from ba._music import setmusic, MusicType
if TYPE_CHECKING:
from typing import Any, Dict, Optional
@ -33,7 +34,7 @@ if TYPE_CHECKING:
from ba._lobby import JoinInfo
class EndSessionActivity(_activity.Activity):
class EndSessionActivity(Activity):
"""Special ba.Activity to fade out and end the current ba.Session."""
def __init__(self, settings: Dict[str, Any]):
@ -61,7 +62,7 @@ class EndSessionActivity(_activity.Activity):
call_after_ad(Call(_ba.new_host_session, MainMenuSession))
class JoiningActivity(_activity.Activity):
class JoiningActivity(Activity):
"""Standard activity for waiting for players to join.
It shows tips and other info and waits for all players to check ready.
@ -88,18 +89,17 @@ class JoiningActivity(_activity.Activity):
# pylint: disable=cyclic-import
from bastd.actor.tipstext import TipsText
from bastd.actor.background import Background
from ba import _music
super().on_transition_in()
self._background = Background(fade_time=0.5,
start_faded=True,
show_logo=True)
self._tips_text = TipsText()
_music.setmusic('CharSelect')
setmusic(MusicType.CHAR_SELECT)
self._join_info = self.session.lobby.create_join_info()
_ba.set_analytics_screen('Joining Screen')
class TransitionActivity(_activity.Activity):
class TransitionActivity(Activity):
"""A simple overlay fade out/in.
Useful as a bare minimum transition between two level based activities.
@ -132,7 +132,7 @@ class TransitionActivity(_activity.Activity):
_ba.timer(0.1, self.end)
class ScoreScreenActivity(_activity.Activity):
class ScoreScreenActivity(Activity):
"""A standard score screen that fades in and shows stuff for a while.
After a specified delay, player input is assigned to end the activity.
@ -151,7 +151,7 @@ class ScoreScreenActivity(_activity.Activity):
self._tips_text: Optional[ba.Actor] = None
self._kicked_off_server_shutdown = False
self._kicked_off_server_restart = False
self._default_music: Optional[str] = 'Scores'
self._default_music: Optional[MusicType] = MusicType.SCORES
self._default_show_tips = True
def on_player_join(self, player: ba.Player) -> None:
@ -168,14 +168,13 @@ class ScoreScreenActivity(_activity.Activity):
def on_transition_in(self) -> None:
from bastd.actor.tipstext import TipsText
from bastd.actor.background import Background
from ba import _music
super().on_transition_in()
self._background = Background(fade_time=0.5,
start_faded=False,
show_logo=True)
if self._default_show_tips:
self._tips_text = TipsText()
_music.setmusic(self._default_music)
setmusic(self._default_music)
def on_begin(self, custom_continue_message: ba.Lstr = None) -> None:
# FIXME: Unify args.

View File

@ -259,6 +259,7 @@ class App:
the single shared instance.
"""
# pylint: disable=too-many-statements
from ba._music import MusicPlayMode
# _test_https()
@ -270,26 +271,44 @@ class App:
# refreshed/etc.
self.fg_state = 0
# Environment stuff (pulling these out as attrs so we can type-check
# them).
# Environment stuff.
# (pulling these into attrs so we can type-check them)
env = _ba.env()
self._build_number: int = env['build_number']
assert isinstance(self._build_number, int)
self._config_file_path: str = env['config_file_path']
assert isinstance(self._config_file_path, str)
self._locale: str = env['locale']
assert isinstance(self._locale, str)
self._user_agent_string: str = env['user_agent_string']
assert isinstance(self._user_agent_string, str)
self._version: str = env['version']
assert isinstance(self._version, str)
self._debug_build: bool = env['debug_build']
assert isinstance(self._debug_build, bool)
self._test_build: bool = env['test_build']
assert isinstance(self._test_build, bool)
self._user_scripts_directory: str = env['user_scripts_directory']
assert isinstance(self._user_scripts_directory, str)
self._system_scripts_directory: str = env['system_scripts_directory']
assert isinstance(self._system_scripts_directory, str)
self._platform: str = env['platform']
assert isinstance(self._platform, str)
self._subplatform: str = env['subplatform']
assert isinstance(self._subplatform, str)
self._interface_type: str = env['interface_type']
assert isinstance(self._interface_type, str)
self._on_tv: bool = env['on_tv']
assert isinstance(self._on_tv, bool)
self._vr_mode: bool = env['vr_mode']
assert isinstance(self._vr_mode, bool)
self.protocol_version: int = env['protocol_version']
assert isinstance(self.protocol_version, int)
self.toolbar_test: bool = env['toolbar_test']
assert isinstance(self.toolbar_test, bool)
self.kiosk_mode: bool = env['kiosk_mode']
assert isinstance(self.kiosk_mode, bool)
# Misc.
self.default_language = self._get_default_language()
@ -339,12 +358,12 @@ class App:
# Music.
self.music: Optional[ba.Node] = None
self.music_mode: str = 'regular'
self.music_mode: ba.MusicPlayMode = MusicPlayMode.REGULAR
self.music_player: Optional[ba.MusicPlayer] = None
self.music_player_type: Optional[Type[ba.MusicPlayer]] = None
self.music_types: Dict[str, Optional[str]] = {
'regular': None,
'test': None
self.music_types: Dict[ba.MusicPlayMode, Optional[ba.MusicType]] = {
MusicPlayMode.REGULAR: None,
MusicPlayMode.TEST: None
}
# Language.
@ -443,15 +462,16 @@ class App:
cfg = self.config
# Set up our app delegate.
self.delegate = appdelegate.AppDelegate()
self.uicontroller = bsui.UIController()
_achievement.init_achievements()
spazappearance.register_appearances()
_campaign.init_campaigns()
if _ba.env()['platform'] == 'android':
self.music_player_type = _music.InternalMusicPlayer
elif _ba.env()['platform'] == 'mac' and hasattr(_ba, 'itunes_init'):
elif _ba.env()['platform'] == 'mac' and hasattr(
_ba, 'mac_music_app_init'):
self.music_player_type = _music.MacITunesMusicPlayer
# FIXME: This should not be hard-coded.
@ -550,6 +570,8 @@ class App:
launch_count = cfg.get('launchCount', 0)
launch_count += 1
# So we know how many times we've run the game at various
# version milestones.
for key in ('lc14173', 'lc14292'):
cfg.setdefault(key, launch_count)
@ -615,7 +637,6 @@ class App:
If there's a foreground host-activity that says it's pausable, tell it
to pause ..we now no longer pause if there are connected clients.
"""
# pylint: disable=cyclic-import
activity = _ba.get_foreground_host_activity()
if (activity is not None and activity.allow_pausing
and not _ba.have_connected_clients()):

View File

@ -308,7 +308,7 @@ class GameActivity(Activity):
# Music that should play in on_transition_in()
# (unless overridden by the map).
self._default_music: Optional[str] = None
self._default_music: Optional[ba.MusicType] = None
# Go ahead and get our map loading.
map_name: str

View File

@ -196,7 +196,7 @@ class Map(Actor):
return cls.name
@classmethod
def get_music_type(cls) -> Optional[str]:
def get_music_type(cls) -> Optional[ba.MusicType]:
"""Return a music-type string that should be played on this map.
If None is returned, default music will be used.

View File

@ -26,11 +26,44 @@ import os
import random
import threading
from typing import TYPE_CHECKING
from enum import Enum
import _ba
if TYPE_CHECKING:
from typing import Callable, Any, List, Optional, Dict, Union
from typing import Callable, Any, List, Optional, Dict, Union, Tuple
class MusicType(Enum):
"""Types of music available to play in-game."""
MENU = 'Menu'
VICTORY = 'Victory'
CHAR_SELECT = 'CharSelect'
RUN_AWAY = 'RunAway'
ONSLAUGHT = 'Onslaught'
KEEP_AWAY = 'Keep Away'
RACE = 'Race'
EPIC_RACE = 'Epic Race'
SCORES = 'Scores'
GRAND_ROMP = 'GrandRomp'
TO_THE_DEATH = 'ToTheDeath'
CHOSEN_ONE = 'Chosen One'
FORWARD_MARCH = 'ForwardMarch'
FLAG_CATCHER = 'FlagCatcher'
SURVIVAL = 'Survival'
EPIC = 'Epic'
SPORTS = 'Sports'
HOCKEY = 'Hockey'
FOOTBALL = 'Football'
FLYING = 'Flying'
SCARY = 'Scary'
MARCHING = 'Marching'
class MusicPlayMode(Enum):
"""Influences behavior when playing music."""
REGULAR = 'regular'
TEST = 'test'
class MusicPlayer:
@ -229,12 +262,13 @@ class InternalMusicPlayer(MusicPlayer):
# For internal music player.
# FIXME: this only applies to Android currently.
def get_valid_music_file_extensions() -> List[str]:
"""Return file extensions for types playable on this device."""
return ['mp3', 'ogg', 'm4a', 'wav', 'flac', 'mid']
class ITunesThread(threading.Thread):
class MacMusicAppThread(threading.Thread):
"""Thread which wrangles iTunes/Music.app playback"""
def __init__(self) -> None:
@ -246,16 +280,16 @@ class ITunesThread(threading.Thread):
self._orig_volume: Optional[int] = None
def run(self) -> None:
"""Run the iTunes/Music.app thread."""
"""Run the Music.app thread."""
from ba._general import Call
from ba._lang import Lstr
from ba._enums import TimeType
_ba.set_thread_name("BA_ITunesThread")
_ba.itunes_init()
_ba.set_thread_name("BA_MacMusicAppThread")
_ba.mac_music_app_init()
# It looks like launching iTunes here on 10.7/10.8 knocks us
# out of fullscreen; ick. That might be a bug, but for now we
# can work around it by reactivating ourself after.
# Let's mention to the user we're launching Music.app in case
# it causes any funny business (this used to background the app
# sometimes, though I think that is fixed now)
def do_print() -> None:
_ba.timer(1.0,
Call(_ba.screenmessage, Lstr(resource='usingItunesText'),
@ -265,12 +299,8 @@ class ITunesThread(threading.Thread):
_ba.pushcall(do_print, from_other_thread=True)
# Here we grab this to force the actual launch.
# Currently (on 10.8 at least) this is causing a switch
# away from our fullscreen window. to work around this we
# explicitly focus our main window to bring ourself back.
_ba.itunes_get_volume()
_ba.pushcall(_ba.focus_window, from_other_thread=True)
_ba.itunes_get_library_source()
_ba.mac_music_app_get_volume()
_ba.mac_music_app_get_library_source()
done = False
while not done:
self._commands_available.wait()
@ -307,8 +337,8 @@ class ITunesThread(threading.Thread):
if old_volume > 0.0 and volume == 0.0:
try:
assert self._orig_volume is not None
_ba.itunes_stop()
_ba.itunes_set_volume(self._orig_volume)
_ba.mac_music_app_stop()
_ba.mac_music_app_set_volume(self._orig_volume)
except Exception as exc:
print('Error stopping iTunes music:', exc)
elif self._volume > 0:
@ -316,8 +346,8 @@ class ITunesThread(threading.Thread):
# If volume was zero, store pre-playing volume and start
# playing.
if old_volume == 0.0:
self._orig_volume = _ba.itunes_get_volume()
self._update_itunes_volume()
self._orig_volume = _ba.mac_music_app_get_volume()
self._update_mac_music_app_volume()
if old_volume == 0.0:
self._play_current_playlist()
@ -341,7 +371,7 @@ class ITunesThread(threading.Thread):
self, target: Callable[[List[str]], None]) -> None:
from ba._general import Call
try:
playlists = _ba.itunes_get_playlists()
playlists = _ba.mac_music_app_get_playlists()
playlists = [
p for p in playlists if p not in [
'Music', 'Movies', 'TV Shows', 'Podcasts', 'iTunes\xa0U',
@ -360,8 +390,8 @@ class ITunesThread(threading.Thread):
if self._current_playlist is not None and self._volume > 0:
try:
assert self._orig_volume is not None
_ba.itunes_stop()
_ba.itunes_set_volume(self._orig_volume)
_ba.mac_music_app_stop()
_ba.mac_music_app_set_volume(self._orig_volume)
except Exception as exc:
print('Error stopping iTunes music:', exc)
self._current_playlist = None
@ -371,16 +401,16 @@ class ITunesThread(threading.Thread):
if self._current_playlist is not None and self._volume > 0:
try:
assert self._orig_volume is not None
_ba.itunes_stop()
_ba.itunes_set_volume(self._orig_volume)
_ba.mac_music_app_stop()
_ba.mac_music_app_set_volume(self._orig_volume)
except Exception as exc:
print('Error stopping iTunes music:', exc)
# Set our playlist and play it if our volume is up.
self._current_playlist = target
if self._volume > 0:
self._orig_volume = (_ba.itunes_get_volume())
self._update_itunes_volume()
self._orig_volume = (_ba.mac_music_app_get_volume())
self._update_mac_music_app_volume()
self._play_current_playlist()
def _handle_die_command(self) -> None:
@ -390,8 +420,8 @@ class ITunesThread(threading.Thread):
if self._current_playlist is not None and self._volume > 0:
try:
assert self._orig_volume is not None
_ba.itunes_stop()
_ba.itunes_set_volume(self._orig_volume)
_ba.mac_music_app_stop()
_ba.mac_music_app_set_volume(self._orig_volume)
except Exception as exc:
print('Error stopping iTunes music:', exc)
@ -400,7 +430,7 @@ class ITunesThread(threading.Thread):
from ba import _lang
from ba._general import Call
assert self._current_playlist is not None
if _ba.itunes_play_playlist(self._current_playlist):
if _ba.mac_music_app_play_playlist(self._current_playlist):
pass
else:
_ba.pushcall(Call(
@ -413,8 +443,9 @@ class ITunesThread(threading.Thread):
_error.print_exception(
f"error playing playlist {self._current_playlist}")
def _update_itunes_volume(self) -> None:
_ba.itunes_set_volume(max(0, min(100, int(100.0 * self._volume))))
def _update_mac_music_app_volume(self) -> None:
_ba.mac_music_app_set_volume(
max(0, min(100, int(100.0 * self._volume))))
class MacITunesMusicPlayer(MusicPlayer):
@ -425,7 +456,7 @@ class MacITunesMusicPlayer(MusicPlayer):
def __init__(self) -> None:
super().__init__()
self._thread = ITunesThread()
self._thread = MacMusicAppThread()
self._thread.start()
def on_select_entry(self, callback: Callable[[Any], None],
@ -479,7 +510,8 @@ def music_volume_changed(val: float) -> None:
app.music_player.set_volume(val)
def set_music_play_mode(mode: str, force_restart: bool = False) -> None:
def set_music_play_mode(mode: MusicPlayMode,
force_restart: bool = False) -> None:
"""Sets music play mode; used for soundtrack testing/etc."""
app = _ba.app
old_mode = app.music_mode
@ -490,8 +522,9 @@ def set_music_play_mode(mode: str, force_restart: bool = False) -> None:
# actually play anything until its requested.
# If we're switching *out* of test mode though
# we want to go back to whatever the normal song was.
if mode == 'regular':
do_play_music(app.music_types['regular'])
if mode is MusicPlayMode.REGULAR:
mtype = app.music_types[MusicPlayMode.REGULAR]
do_play_music(None if mtype is None else mtype.value)
def supports_soundtrack_entry_type(entry_type: str) -> bool:
@ -556,17 +589,11 @@ def get_soundtrack_entry_name(entry: Any) -> str:
return 'default'
def setmusic(musictype: Optional[str], continuous: bool = False) -> None:
def setmusic(musictype: Optional[MusicType], continuous: bool = False) -> None:
"""Set or stop the current music based on a string musictype.
category: Gameplay Functions
Current valid values for 'musictype': 'Menu', 'Victory', 'CharSelect',
'RunAway', 'Onslaught', 'Keep Away', 'Race', 'Epic Race', 'Scores',
'GrandRomp', 'ToTheDeath', 'Chosen One', 'ForwardMarch', 'FlagCatcher',
'Survival', 'Epic', 'Sports', 'Hockey', 'Football', 'Flying', 'Scary',
'Marching'.
This function will handle loading and playing sound media as necessary,
and also supports custom user soundtracks on specific platforms so the
user can override particular game music with their own.
@ -585,7 +612,7 @@ def setmusic(musictype: Optional[str], continuous: bool = False) -> None:
# not an actual sound node create.
gnode = _gameutils.sharedobj('globals')
gnode.music_continuous = continuous
gnode.music = '' if musictype is None else musictype
gnode.music = '' if musictype is None else musictype.value
gnode.music_count += 1
@ -595,27 +622,33 @@ def handle_app_resume() -> None:
do_play_music(None)
def do_play_music(musictype: Optional[str],
def do_play_music(musictype: Union[MusicType, str, None],
continuous: bool = False,
mode: str = 'regular',
testsoundtrack: Dict = None) -> None:
mode: MusicPlayMode = MusicPlayMode.REGULAR,
testsoundtrack: Dict[str, Any] = None) -> None:
"""Plays the requested music type/mode.
For most cases setmusic() is the proper call to use, which itself calls
this. Certain cases, however, such as soundtrack testing, may require
calling this directly.
"""
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
# We can be passed a MusicType or the string value of one.
if musictype is not None:
try:
musictype = MusicType(musictype)
except ValueError:
print(f"Invalid music type: '{musictype}'")
musictype = None
app = _ba.app
with _ba.Context('ui'):
# If they don't want to restart music and we're already
# playing what's requested, we're done.
if continuous and app.music_types[mode] == musictype:
if continuous and app.music_types[mode] is musictype:
return
app.music_types[mode] = musictype
cfg = app.config
# If the OS tells us there's currently music playing,
# all our operations default to playing nothing.
@ -632,127 +665,104 @@ def do_play_music(musictype: Optional[str],
# entry for this music-type, and if we have one, have the music-player
# play it. If not, we'll play game music ourself.
if musictype is not None and app.music_player_type is not None:
try:
soundtrack: Dict
if testsoundtrack is not None:
soundtrack = testsoundtrack
else:
soundtrack = cfg['Soundtracks'][cfg['Soundtrack']]
entry = soundtrack[musictype]
except Exception:
entry = None
if testsoundtrack is not None:
soundtrack = testsoundtrack
else:
soundtrack = _get_user_soundtrack()
entry = soundtrack.get(musictype.value)
else:
entry = None
# Go through music-player.
if entry is not None:
# Stop any existing internal music.
if app.music is not None:
app.music.delete()
app.music = None
# Play music-player music.
get_music_player().play(entry)
_play_music_player_music(entry)
# Handle via internal music.
else:
if musictype is not None:
loop = True
if musictype == 'Menu':
filename = 'menuMusic'
volume = 5.0
elif musictype == 'Victory':
filename = 'victoryMusic'
volume = 6.0
loop = False
elif musictype == 'CharSelect':
filename = 'charSelectMusic'
volume = 2.0
elif musictype == 'RunAway':
filename = 'runAwayMusic'
volume = 6.0
elif musictype == 'Onslaught':
filename = 'runAwayMusic'
volume = 6.0
elif musictype == 'Keep Away':
filename = 'runAwayMusic'
volume = 6.0
elif musictype == 'Race':
filename = 'runAwayMusic'
volume = 6.0
elif musictype == 'Epic Race':
filename = 'slowEpicMusic'
volume = 6.0
elif musictype == 'Scores':
filename = 'scoresEpicMusic'
volume = 3.0
loop = False
elif musictype == 'GrandRomp':
filename = 'grandRompMusic'
volume = 6.0
elif musictype == 'ToTheDeath':
filename = 'toTheDeathMusic'
volume = 6.0
elif musictype == 'Chosen One':
filename = 'survivalMusic'
volume = 4.0
elif musictype == 'ForwardMarch':
filename = 'forwardMarchMusic'
volume = 4.0
elif musictype == 'FlagCatcher':
filename = 'flagCatcherMusic'
volume = 6.0
elif musictype == 'Survival':
filename = 'survivalMusic'
volume = 4.0
elif musictype == 'Epic':
filename = 'slowEpicMusic'
volume = 6.0
elif musictype == 'Sports':
filename = 'sportsMusic'
volume = 4.0
elif musictype == 'Hockey':
filename = 'sportsMusic'
volume = 4.0
elif musictype == 'Football':
filename = 'sportsMusic'
volume = 4.0
elif musictype == 'Flying':
filename = 'flyingMusic'
volume = 4.0
elif musictype == 'Scary':
filename = 'scaryMusic'
volume = 4.0
elif musictype == 'Marching':
filename = 'whenJohnnyComesMarchingHomeMusic'
volume = 4.0
else:
print("Unknown music: '" + musictype + "'")
filename = 'flagCatcherMusic'
volume = 6.0
_play_internal_music(musictype)
# Stop any existing music-player playback.
if app.music_player is not None:
app.music_player.stop()
# Stop any existing internal music.
if app.music:
app.music.delete()
app.music = None
def _get_user_soundtrack() -> Dict[str, Any]:
"""Return current user soundtrack or empty dict otherwise."""
cfg = _ba.app.config
soundtrack: Dict[str, Any] = {}
soundtrackname = cfg.get('Soundtrack')
if soundtrackname is not None:
try:
soundtrack = cfg['Soundtracks'][soundtrackname]
except Exception as exc:
print(f"Error looking up user soundtrack: {exc}")
soundtrack = {}
return soundtrack
# Start up new internal music.
if musictype is not None:
# FIXME: Currently this won't start playing if we're paused
# since attr values don't get updated until
# node updates happen. :-(
# Update: hmm I don't think that's true anymore. Should check.
app.music = _ba.newnode(type='sound',
attrs={
'sound': _ba.getsound(filename),
'positional': False,
'music': True,
'volume': volume,
'loop': loop
})
def _play_music_player_music(entry: Any) -> None:
app = _ba.app
# Stop any existing internal music.
if app.music is not None:
app.music.delete()
app.music = None
# Do the thing.
get_music_player().play(entry)
def _play_internal_music(musictype: Optional[MusicType]) -> None:
app = _ba.app
# Stop any existing music-player playback.
if app.music_player is not None:
app.music_player.stop()
# Stop any existing internal music.
if app.music:
app.music.delete()
app.music = None
# Start up new internal music.
if musictype is not None:
# Filenames/volume/loop for our built-in music.
musicinfos: Dict[MusicType, Tuple[str, float, bool]] = {
MusicType.MENU: ('menuMusic', 5.0, True),
MusicType.VICTORY: ('victoryMusic', 6.0, False),
MusicType.CHAR_SELECT: ('charSelectMusic', 2.0, True),
MusicType.RUN_AWAY: ('runAwayMusic', 6.0, True),
MusicType.ONSLAUGHT: ('runAwayMusic', 6.0, True),
MusicType.KEEP_AWAY: ('runAwayMusic', 6.0, True),
MusicType.RACE: ('runAwayMusic', 6.0, True),
MusicType.EPIC_RACE: ('slowEpicMusic', 6.0, True),
MusicType.SCORES: ('scoresEpicMusic', 3.0, False),
MusicType.GRAND_ROMP: ('grandRompMusic', 6.0, True),
MusicType.TO_THE_DEATH: ('toTheDeathMusic', 6.0, True),
MusicType.CHOSEN_ONE: ('survivalMusic', 4.0, True),
MusicType.FORWARD_MARCH: ('forwardMarchMusic', 4.0, True),
MusicType.FLAG_CATCHER: ('flagCatcherMusic', 6.0, True),
MusicType.SURVIVAL: ('survivalMusic', 4.0, True),
MusicType.EPIC: ('slowEpicMusic', 6.0, True),
MusicType.SPORTS: ('sportsMusic', 4.0, True),
MusicType.HOCKEY: ('sportsMusic', 4.0, True),
MusicType.FOOTBALL: ('sportsMusic', 4.0, True),
MusicType.FLYING: ('flyingMusic', 4.0, True),
MusicType.SCARY: ('scaryMusic', 4.0, True),
MusicType.MARCHING:
('whenJohnnyComesMarchingHomeMusic', 4.0, True),
}
musicinfo = musicinfos.get(musictype)
if musicinfo is None:
print(f"Unknown music: '{musictype}'")
filename = 'flagCatcherMusic'
volume = 6.0
loop = True
else:
filename, volume, loop = musicinfo
app.music = _ba.newnode(type='sound',
attrs={
'sound': _ba.getsound(filename),
'positional': False,
'music': True,
'volume': volume,
'loop': loop
})

View File

@ -340,7 +340,7 @@ class TeamSeriesVictoryScoreScreenActivity(TeamsScoreScreenActivity):
# Make sure we don't stomp on the next activity's music choice.
if not self.is_transitioning_out():
ba.setmusic('Victory')
ba.setmusic(ba.MusicType.VICTORY)
def _show_winner(self, team: ba.Team) -> None:
from bastd.actor.image import Image

View File

@ -94,8 +94,8 @@ class AssaultGame(ba.TeamGameActivity):
return 'touch ${ARG1} flags', self.settings['Score to Win']
def on_transition_in(self) -> None:
self._default_music = ('Epic' if self.settings['Epic Mode'] else
'ForwardMarch')
self._default_music = (ba.MusicType.EPIC if self.settings['Epic Mode']
else ba.MusicType.FORWARD_MARCH)
super().on_transition_in()
def on_team_join(self, team: ba.Team) -> None:

View File

@ -135,8 +135,8 @@ class CaptureTheFlagGame(ba.TeamGameActivity):
return 'return ${ARG1} flags', self.settings['Score to Win']
def on_transition_in(self) -> None:
self._default_music = ('Epic' if self.settings['Epic Mode'] else
'FlagCatcher')
self._default_music = (ba.MusicType.EPIC if self.settings['Epic Mode']
else ba.MusicType.FLAG_CATCHER)
super().on_transition_in()
def on_team_join(self, team: ba.Team) -> None:

View File

@ -118,8 +118,8 @@ class ChosenOneGame(ba.TeamGameActivity):
return 'There can be only one.'
def on_transition_in(self) -> None:
self._default_music = ('Epic'
if self.settings['Epic Mode'] else 'Chosen One')
self._default_music = (ba.MusicType.EPIC if self.settings['Epic Mode']
else ba.MusicType.CHOSEN_ONE)
super().on_transition_in()
def on_team_join(self, team: ba.Team) -> None:

View File

@ -123,8 +123,8 @@ class ConquestGame(ba.TeamGameActivity):
return 'secure all ${ARG1} flags', len(self.map.flag_points)
def on_transition_in(self) -> None:
self._default_music = ('Epic'
if self.settings['Epic Mode'] else 'GrandRomp')
self._default_music = (ba.MusicType.EPIC if self.settings['Epic Mode']
else ba.MusicType.GRAND_ROMP)
super().on_transition_in()
def on_team_join(self, team: ba.Team) -> None:

View File

@ -115,8 +115,8 @@ class DeathMatchGame(ba.TeamGameActivity):
return 'kill ${ARG1} enemies', self._score_to_win
def on_transition_in(self) -> None:
self._default_music = ('Epic'
if self.settings['Epic Mode'] else 'ToTheDeath')
self._default_music = (ba.MusicType.EPIC if self.settings['Epic Mode']
else ba.MusicType.TO_THE_DEATH)
super().on_transition_in()
def on_team_join(self, team: ba.Team) -> None:

View File

@ -97,7 +97,7 @@ class EasterEggHuntGame(ba.TeamGameActivity):
# ..we can go ahead and set our music and whatnot.
def on_transition_in(self) -> None:
self._default_music = 'ForwardMarch'
self._default_music = ba.MusicType.FORWARD_MARCH
super().on_transition_in()
def on_team_join(self, team: ba.Team) -> None:

View File

@ -244,8 +244,8 @@ class EliminationGame(ba.TeamGameActivity):
self.session, ba.TeamsSession) else 'last one standing wins'
def on_transition_in(self) -> None:
self._default_music = ('Epic'
if self.settings['Epic Mode'] else 'Survival')
self._default_music = (ba.MusicType.EPIC if self.settings['Epic Mode']
else ba.MusicType.SURVIVAL)
super().on_transition_in()
self._start_time = ba.time()

View File

@ -146,7 +146,7 @@ class FootballTeamGame(ba.TeamGameActivity):
return 'score a touchdown'
def on_transition_in(self) -> None:
self._default_music = 'Football'
self._default_music = ba.MusicType.FOOTBALL
super().on_transition_in()
def on_begin(self) -> None:
@ -397,7 +397,7 @@ class FootballCoopGame(ba.CoopGameActivity):
self._flag: Optional[FootballFlag] = None
def on_transition_in(self) -> None:
self._default_music = 'Football'
self._default_music = ba.MusicType.FOOTBALL
super().on_transition_in()
self._scoreboard = Scoreboard()
self._flag_spawn_pos = self.map.get_flag_position(None)
@ -748,7 +748,7 @@ class FootballCoopGame(ba.CoopGameActivity):
if team is self._bot_team:
self.continue_or_end_game()
else:
ba.setmusic('Victory')
ba.setmusic(ba.MusicType.VICTORY)
# Completion achievements.
assert self._bot_team is not None

View File

@ -207,7 +207,7 @@ class HockeyGame(ba.TeamGameActivity):
return 'score ${ARG1} goals', self.settings['Score to Win']
def on_transition_in(self) -> None:
self._default_music = 'Hockey'
self._default_music = ba.MusicType.HOCKEY
super().on_transition_in()
def on_begin(self) -> None:

View File

@ -124,7 +124,7 @@ class KeepAwayGame(ba.TeamGameActivity):
self.settings['Hold Time'])
def on_transition_in(self) -> None:
self._default_music = 'Keep Away'
self._default_music = ba.MusicType.KEEP_AWAY
super().on_transition_in()
def on_team_join(self, team: ba.Team) -> None:

View File

@ -133,7 +133,7 @@ class KingOfTheHillGame(ba.TeamGameActivity):
self.settings['Hold Time'])
def on_transition_in(self) -> None:
self._default_music = 'Scary'
self._default_music = ba.MusicType.SCARY
super().on_transition_in()
def on_team_join(self, team: ba.Team) -> None:

View File

@ -91,8 +91,8 @@ class MeteorShowerGame(ba.TeamGameActivity):
# Called when our game is transitioning in but not ready to start;
# ..we can go ahead and set our music and whatnot.
def on_transition_in(self) -> None:
self._default_music = ('Epic'
if self.settings['Epic Mode'] else 'Survival')
self._default_music = (ba.MusicType.EPIC if self.settings['Epic Mode']
else ba.MusicType.SURVIVAL)
super().on_transition_in()
# Called when our game actually starts.

View File

@ -84,7 +84,7 @@ class NinjaFightGame(ba.TeamGameActivity):
# Called when our game is transitioning in but not ready to begin;
# we can go ahead and start creating stuff, playing music, etc.
def on_transition_in(self) -> None:
self._default_music = 'ToTheDeath'
self._default_music = ba.MusicType.TO_THE_DEATH
super().on_transition_in()
# Called when our game actually begins.

View File

@ -180,7 +180,7 @@ class OnslaughtGame(ba.CoopGameActivity):
'color': (0.3, 0.8, 0.3, 1.0),
'text': ''
}))
ba.setmusic('Onslaught')
ba.setmusic(ba.MusicType.ONSLAUGHT)
self._scoreboard = Scoreboard(label=ba.Lstr(resource='scoreText'),
score_split=0.5)
@ -769,7 +769,7 @@ class OnslaughtGame(ba.CoopGameActivity):
base_delay += 0.85
ba.playsound(self._winsound)
ba.cameraflash()
ba.setmusic('Victory')
ba.setmusic(ba.MusicType.VICTORY)
self._game_over = True
# Can't just pass delay to do_end because our extra bonuses

View File

@ -172,8 +172,9 @@ class RaceGame(ba.TeamGameActivity):
return 'run 1 lap'
def on_transition_in(self) -> None:
self._default_music = ('Epic Race'
if self.settings['Epic Mode'] else 'Race')
self._default_music = (ba.MusicType.EPIC_RACE
if self.settings['Epic Mode'] else
ba.MusicType.RACE)
super().on_transition_in()
pts = self.map.get_def_points('race_point')

View File

@ -130,7 +130,7 @@ class RunaroundGame(ba.CoopGameActivity):
self._wave_update_timer: Optional[ba.Timer] = None
def on_transition_in(self) -> None:
self._default_music = 'Marching'
self._default_music = ba.MusicType.MARCHING
super().on_transition_in()
self._scoreboard = Scoreboard(label=ba.Lstr(resource='scoreText'),
score_split=0.5)
@ -630,7 +630,7 @@ class RunaroundGame(ba.CoopGameActivity):
base_delay += 0.85
ba.playsound(self._winsound)
ba.cameraflash()
ba.setmusic('Victory')
ba.setmusic(ba.MusicType.VICTORY)
self._game_over = True
ba.timer(base_delay, ba.Call(self.do_end, 'victory'))
return

View File

@ -81,7 +81,7 @@ class TargetPracticeGame(ba.TeamGameActivity):
self._countdown: Optional[OnScreenCountdown] = None
def on_transition_in(self) -> None:
self._default_music = 'ForwardMarch'
self._default_music = ba.MusicType.FORWARD_MARCH
super().on_transition_in()
def on_team_join(self, team: ba.Team) -> None:

View File

@ -98,7 +98,7 @@ class TheLastStandGame(ba.CoopGameActivity):
def on_transition_in(self) -> None:
from bastd.actor.scoreboard import Scoreboard
self._default_music = 'Epic'
self._default_music = ba.MusicType.EPIC
super().on_transition_in()
ba.timer(1.3, ba.Call(ba.playsound, self._new_wave_sound))
self._scoreboard = Scoreboard(label=ba.Lstr(resource='scoreText'),

View File

@ -822,7 +822,7 @@ class MainMenuActivity(ba.Activity):
with ba.Context(self):
_preload1()
ba.timer(0.5, lambda: ba.setmusic('Menu'))
ba.timer(0.5, lambda: ba.setmusic(ba.MusicType.MENU))
def _preload1() -> None:

View File

@ -1216,8 +1216,8 @@ class HappyThoughts(ba.Map):
return data
@classmethod
def get_music_type(cls) -> str:
return 'Flying'
def get_music_type(cls) -> ba.MusicType:
return ba.MusicType.FLYING
def __init__(self) -> None:
super().__init__(vr_overlay_offset=(0, -3.7, 2.5))

View File

@ -234,7 +234,7 @@ class TutorialActivity(ba.Activity):
def on_transition_in(self) -> None:
super().on_transition_in()
ba.setmusic('CharSelect', continuous=True)
ba.setmusic(ba.MusicType.CHAR_SELECT, continuous=True)
self._map = self._map_type()
def on_begin(self) -> None:

View File

@ -188,7 +188,7 @@ class SoundtrackBrowserWindow(ba.OldWindow):
ba.widget(edit=btn,
left_widget=_ba.get_special_widget('back_button'))
# keep our lock images up to date/etc.
# Keep our lock images up to date/etc.
self._update_timer = ba.Timer(1.0,
ba.WeakCall(self._update),
timetype=ba.TimeType.REAL,
@ -282,10 +282,10 @@ class SoundtrackBrowserWindow(ba.OldWindow):
else:
sdtk = cfg['Soundtracks'][self._selected_soundtrack]
# find a valid dup name that doesn't exist
# Find a valid dup name that doesn't exist.
test_index = 1
copy_text = ba.Lstr(resource='copyOfText').evaluate()
# get just 'Copy' or whatnot
# Get just 'Copy' or whatnot.
copy_word = copy_text.replace('${NAME}', '').strip()
base_name = self._get_soundtrack_display_name(
self._selected_soundtrack).evaluate()
@ -294,7 +294,7 @@ class SoundtrackBrowserWindow(ba.OldWindow):
assert isinstance(base_name, bytes)
base_name = base_name.decode('utf-8')
# if it looks like a copy, strip digits and spaces off the end
# If it looks like a copy, strip digits and spaces off the end.
if copy_word in base_name:
while base_name[-1].isdigit() or base_name[-1] == ' ':
base_name = base_name[:-1]
@ -320,14 +320,14 @@ class SoundtrackBrowserWindow(ba.OldWindow):
cfg = ba.app.config
current_soundtrack = cfg.setdefault('Soundtrack', '__default__')
# if it varies from current, commit and play
# If it varies from current, commit and play.
if current_soundtrack != name and self._allow_changing_soundtracks:
ba.playsound(ba.getsound('gunCocking'))
cfg['Soundtrack'] = self._selected_soundtrack
cfg.commit()
# just play whats already playing.. this'll grab it from the
# new soundtrack
do_play_music(ba.app.music_types['regular'])
# Just play whats already playing.. this'll grab it from the
# new soundtrack.
do_play_music(ba.app.music_types[ba.MusicPlayMode.REGULAR])
def _back(self) -> None:
# pylint: disable=cyclic-import

View File

@ -280,7 +280,7 @@ class SoundtrackEditWindow(ba.OldWindow):
size=(50, 32),
label=ba.Lstr(resource=self._r + '.testText'),
text_scale=0.6,
on_activate_call=ba.Call(self._test, song_type),
on_activate_call=ba.Call(self._test, ba.MusicType(song_type)),
up_widget=prev_test_button
if prev_test_button is not None else self._text_field)
if prev_test_button is not None:
@ -330,7 +330,7 @@ class SoundtrackEditWindow(ba.OldWindow):
ba.Call(self._restore_editor, state, song_type), entry,
selection_target_name).get_root_widget())
def _test(self, song_type: str) -> None:
def _test(self, song_type: ba.MusicType) -> None:
from ba.internal import set_music_play_mode, do_play_music
# Warn if volume is zero.
@ -339,8 +339,10 @@ class SoundtrackEditWindow(ba.OldWindow):
ba.screenmessage(ba.Lstr(resource=self._r +
'.musicVolumeZeroWarning'),
color=(1, 0.5, 0))
set_music_play_mode('test')
do_play_music(song_type, mode='test', testsoundtrack=self._soundtrack)
set_music_play_mode(ba.MusicPlayMode.TEST)
do_play_music(song_type,
mode=ba.MusicPlayMode.TEST,
testsoundtrack=self._soundtrack)
def _get_entry_button_display_name(self,
entry: Any) -> Union[str, ba.Lstr]:
@ -369,7 +371,7 @@ class SoundtrackEditWindow(ba.OldWindow):
from ba.internal import set_music_play_mode
from bastd.ui.soundtrack import browser as stb
# Resets music back to normal.
set_music_play_mode('regular')
set_music_play_mode(ba.MusicPlayMode.REGULAR)
ba.containerwidget(edit=self._root_widget, transition='out_right')
ba.app.main_menu_window = (stb.SoundtrackBrowserWindow(
transition='in_left').get_root_widget())
@ -411,7 +413,7 @@ class SoundtrackEditWindow(ba.OldWindow):
ba.containerwidget(edit=self._root_widget, transition='out_right')
# Resets music back to normal.
set_music_play_mode('regular', force_restart=True)
set_music_play_mode(ba.MusicPlayMode.REGULAR, force_restart=True)
ba.app.main_menu_window = (stb.SoundtrackBrowserWindow(
transition='in_left').get_root_widget())

View File

@ -51,11 +51,12 @@ class SoundtrackEntryTypeSelectWindow(ba.OldWindow):
spacing = 80
do_default = True
do_itunes_playlist = supports_soundtrack_entry_type('iTunesPlaylist')
do_mac_music_app_playlist = supports_soundtrack_entry_type(
'iTunesPlaylist')
do_music_file = supports_soundtrack_entry_type('musicFile')
do_music_folder = supports_soundtrack_entry_type('musicFolder')
if do_itunes_playlist:
if do_mac_music_app_playlist:
self._height += spacing
if do_music_file:
self._height += spacing
@ -108,13 +109,13 @@ class SoundtrackEntryTypeSelectWindow(ba.OldWindow):
ba.containerwidget(edit=self._root_widget, selected_child=btn)
v -= spacing
if do_itunes_playlist:
if do_mac_music_app_playlist:
btn = ba.buttonwidget(
parent=self._root_widget,
size=(self._width - 100, 60),
position=(50, v),
label=ba.Lstr(resource=self._r + '.useITunesPlaylistText'),
on_activate_call=self._on_itunes_playlist_press,
on_activate_call=self._on_mac_music_app_playlist_press,
icon=None)
if current_entry_type == 'iTunesPlaylist':
ba.containerwidget(edit=self._root_widget, selected_child=btn)
@ -145,7 +146,7 @@ class SoundtrackEntryTypeSelectWindow(ba.OldWindow):
ba.containerwidget(edit=self._root_widget, selected_child=btn)
v -= spacing
def _on_itunes_playlist_press(self) -> None:
def _on_mac_music_app_playlist_press(self) -> None:
from ba.internal import (get_soundtrack_entry_type,
get_soundtrack_entry_name)
from bastd.ui.soundtrack import itunes

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2020-03-31 for Ballistica version 1.5.0 build 20001</em></h4>
<h4><em>last updated on 2020-04-01 for Ballistica version 1.5.0 build 20001</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr>
@ -174,7 +174,7 @@
<li><a href="#class_ba_WidgetNotFoundError">ba.WidgetNotFoundError</a></li>
</ul>
</ul>
<h4><a name="class_category_Misc">Misc</a></h4>
<h4><a name="class_category_Misc_Classes">Misc Classes</a></h4>
<ul>
<li><a href="#class_ba_Achievement">ba.Achievement</a></li>
<li><a href="#class_ba_AppDelegate">ba.AppDelegate</a></li>
@ -184,10 +184,12 @@
<li><a href="#class_ba_DependencySet">ba.DependencySet</a></li>
<li><a href="#class_ba_Lobby">ba.Lobby</a></li>
<li><a href="#class_ba_MusicPlayer">ba.MusicPlayer</a></li>
<li><a href="#class_ba_MusicPlayMode">ba.MusicPlayMode</a></li>
<li><a href="#class_ba_MusicType">ba.MusicType</a></li>
<li><a href="#class_ba_OldWindow">ba.OldWindow</a></li>
<li><a href="#class_ba_UIController">ba.UIController</a></li>
</ul>
<h4><a name="function_category_Misc">Misc</a></h4>
<h4><a name="function_category_Misc_Functions">Misc Functions</a></h4>
<ul>
<li><a href="#function_ba_show_damage_count">ba.show_damage_count()</a></li>
</ul>
@ -2806,7 +2808,7 @@ as far from these players as possible.</p>
</dd>
<dt><h4><a name="method_ba_Map__get_music_type">get_music_type()</a></dt></h4><dd>
<h5><span><em>&lt;class method&gt;</span></em></h5>
<p><span>get_music_type() -&gt; Optional[str] </span></p>
<p><span>get_music_type() -&gt; Optional[<a href="#class_ba_MusicType">ba.MusicType</a>] </span></p>
<p>Return a music-type string that should be played on this map.</p>
@ -3146,6 +3148,46 @@ signify that the default soundtrack should be used..</p>
</dd>
</dl>
<hr>
<h2><strong><a name="class_ba_MusicPlayMode">ba.MusicPlayMode</a></strong></h3>
<p>inherits from: enum.Enum</p>
<p>Influences behavior when playing music.</p>
<h3>Values:</h3>
<ul>
<li>REGULAR</li>
<li>TEST</li>
</ul>
<hr>
<h2><strong><a name="class_ba_MusicType">ba.MusicType</a></strong></h3>
<p>inherits from: enum.Enum</p>
<p>Types of music available to play in-game.</p>
<h3>Values:</h3>
<ul>
<li>MENU</li>
<li>VICTORY</li>
<li>CHAR_SELECT</li>
<li>RUN_AWAY</li>
<li>ONSLAUGHT</li>
<li>KEEP_AWAY</li>
<li>RACE</li>
<li>EPIC_RACE</li>
<li>SCORES</li>
<li>GRAND_ROMP</li>
<li>TO_THE_DEATH</li>
<li>CHOSEN_ONE</li>
<li>FORWARD_MARCH</li>
<li>FLAG_CATCHER</li>
<li>SURVIVAL</li>
<li>EPIC</li>
<li>SPORTS</li>
<li>HOCKEY</li>
<li>FOOTBALL</li>
<li>FLYING</li>
<li>SCARY</li>
<li>MARCHING</li>
</ul>
<hr>
<h2><strong><a name="class_ba_Node">ba.Node</a></strong></h3>
<p><em>&lt;top level class&gt;</em>
</p>
@ -5627,18 +5669,12 @@ are applied to the Widget.</p>
<hr>
<h2><strong><a name="function_ba_setmusic">ba.setmusic()</a></strong></h3>
<p><span>setmusic(musictype: Optional[str], continuous: bool = False) -&gt; None</span></p>
<p><span>setmusic(musictype: Optional[MusicType], continuous: bool = False) -&gt; None</span></p>
<p>Set or stop the current music based on a string musictype.</p>
<p>Category: <a href="#function_category_Gameplay_Functions">Gameplay Functions</a></p>
<p>Current valid values for 'musictype': 'Menu', 'Victory', 'CharSelect',
'RunAway', 'Onslaught', 'Keep Away', 'Race', 'Epic Race', 'Scores',
'GrandRomp', 'ToTheDeath', 'Chosen One', 'ForwardMarch', 'FlagCatcher',
'Survival', 'Epic', 'Sports', 'Hockey', 'Football', 'Flying', 'Scary',
'Marching'.</p>
<p>This function will handle loading and playing sound media as necessary,
and also supports custom user soundtracks on specific platforms so the
user can override particular game music with their own.</p>

View File

@ -105,6 +105,9 @@ class App:
self._check_sync_states()
self._find_sources_and_headers('src/ballistica')
# FIXME: It might make more sense to have some of these checks
# run via 'make check' rather than here through 'make update'.
self._check_source_files()
self._check_headers()