Merge branch 'sound-profiles' into 'master'

Selectable sound sets

See merge request core-developers/forge!5724
This commit is contained in:
Michael Kamensky
2021-11-03 10:08:09 +00:00
16 changed files with 102 additions and 20 deletions

View File

@@ -27,6 +27,8 @@ import org.apache.commons.lang3.tuple.Pair;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
@@ -262,6 +264,7 @@ public enum CSubmenuPreferences implements ICDoc {
initializeAutoUpdaterComboBox();
initializeMulliganRuleComboBox();
initializeAiProfilesComboBox();
initializeSoundSetsComboBox();
initializeStackAdditionsComboBox();
initializeLandPlayedComboBox();
initializeColorIdentityCombobox();
@@ -466,6 +469,20 @@ public enum CSubmenuPreferences implements ICDoc {
panel.setComboBox(comboBox, selectedItem);
}
private void initializeSoundSetsComboBox() {
final FPref userSetting = FPref.UI_CURRENT_SOUND_SET;
final FComboBoxPanel<String> panel = this.view.getSoundSetsComboBoxPanel();
final FComboBox<String> comboBox = createComboBox(SoundSystem.getAvailableSoundSets(), userSetting);
final String selectedItem = this.prefs.getPref(userSetting);
panel.setComboBox(comboBox, selectedItem);
comboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
SoundSystem.invalidateSoundCache();
}
});
}
private void initializeCardArtPreference() {
final String latestOpt = Localizer.getInstance().getMessage("latestArtOpt");
final String originalOpt = Localizer.getInstance().getMessage("originalArtOpt");

View File

@@ -128,6 +128,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
private final FComboBoxPanel<String> cbpCardArtFormat = new FComboBoxPanel<>(localizer.getMessage("cbpCardArtFormat")+":");
private final FComboBoxPanel<String> cbpCardArtPreference = new FComboBoxPanel<>(localizer.getMessage("lblPreferredArt")+":");
private final FComboBoxPanel<String> cbpMulliganRule = new FComboBoxPanel<>(localizer.getMessage("cbpMulliganRule")+":");
private final FComboBoxPanel<String> cbpSoundSets = new FComboBoxPanel<>(localizer.getMessage("cbpSoundSets")+":");
private final FComboBoxPanel<String> cbpAiProfiles = new FComboBoxPanel<>(localizer.getMessage("cbpAiProfiles")+":");
private final FComboBoxPanel<String> cbpStackAdditions = new FComboBoxPanel<>(localizer.getMessage("cbpStackAdditions")+":");
private final FComboBoxPanel<String> cbpLandPlayed = new FComboBoxPanel<>(localizer.getMessage("cbpLandPlayed")+":");
@@ -414,6 +415,9 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
pnlPrefs.add(cbEnableSounds, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlEnableSounds")), descriptionConstraints);
pnlPrefs.add(cbpSoundSets, comboBoxConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlpSoundSets")), descriptionConstraints);
pnlPrefs.add(cbEnableMusic, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlEnableMusic")), descriptionConstraints);
@@ -740,6 +744,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
return cbpMulliganRule;
}
public FComboBoxPanel<String> getSoundSetsComboBoxPanel() {
return cbpSoundSets;
}
public FComboBoxPanel<String> getAiProfilesComboBoxPanel() {
return cbpAiProfiles;
}

View File

@@ -40,9 +40,6 @@ import javax.sound.sampled.UnsupportedAudioFileException;
import com.google.common.io.Files;
import com.sipgate.mp3wav.Converter;
import forge.localinstance.properties.ForgeConstants;
/**
* SoundSystem - a simple sound playback system for Forge.
* Do not use directly. Instead, use the {@link forge.sound.SoundEffectType} enumeration.
@@ -64,7 +61,7 @@ public class AudioClip implements IAudioClip {
}
public static boolean fileExists(String fileName) {
File fSound = new File(ForgeConstants.SOUND_DIR, fileName);
File fSound = new File(SoundSystem.getSoundDirectory(), fileName);
return fSound.exists();
}
@@ -195,7 +192,7 @@ public class AudioClip implements IAudioClip {
}
private Clip createClip(String filename) {
File fSound = new File(ForgeConstants.SOUND_DIR, filename);
File fSound = new File(SoundSystem.getSoundDirectory(), filename);
if (!fSound.exists()) {
throw new IllegalArgumentException("Sound file " + fSound.toString() + " does not exist, cannot make a clip of it");
}

View File

@@ -21,10 +21,7 @@ import forge.screens.LoadingOverlay;
import forge.screens.match.MatchController;
import forge.screens.quest.QuestMenu;
import forge.screens.settings.GuiDownloader;
import forge.sound.AudioClip;
import forge.sound.AudioMusic;
import forge.sound.IAudioClip;
import forge.sound.IAudioMusic;
import forge.sound.*;
import forge.toolbox.FOptionPane;
import forge.toolbox.GuiChoose;
import forge.util.*;
@@ -272,7 +269,7 @@ public class GuiMobile implements IGuiBase {
@Override
public IAudioClip createAudioClip(final String filename) {
return AudioClip.createClip(ForgeConstants.SOUND_DIR + filename);
return AudioClip.createClip(SoundSystem.getSoundDirectory() + filename);
}
@Override

View File

@@ -612,6 +612,17 @@ public class SettingsPage extends TabPage<SettingsScreen> {
localizer.getMessage("nlVibrateAfterLongPress")),
6);
//Sound Options
lstSettings.addItem(new CustomSelectSetting(FPref.UI_CURRENT_SOUND_SET,
localizer.getMessage("cbpSoundSets"),
localizer.getMessage("nlpSoundSets"),
SoundSystem.getAvailableSoundSets()) {
@Override
public void valueChanged(String newValue) {
super.valueChanged(newValue);
SoundSystem.invalidateSoundCache();
}
},
7);
lstSettings.addItem(new CustomSelectSetting(FPref.UI_VOL_SOUNDS,
localizer.getMessage("cbAdjustSoundsVolume"),
localizer.getMessage("nlAdjustSoundsVolume"),

View File

@@ -1,6 +1,6 @@
#Add one announcement per line
Get in the discord if you aren't yet. https://discord.gg/3v9JCVr
The Throne of Eldraine quest world is the largest and most meticulously developed quest world to date, combining fascinating fairytales and interesting interactions. If you've never baked a pie into a pie, have you really played Magic?
Innistrad: Midnight Hunt (MID) and Innistrad: Midnight Hunt Commander (MIC) are 100% supported
Various improvements have been made to the desktop and mobile user interface to enhance the experience
Planar Conquest now features a new plane - Forgotten Realms, based on the AFR and AFC sets.
Forge now supports 100% of core and expansion set cards from Limited Edition Alpha to Innistrad: Midnight Hunt. This includes Equinox, Chaos Orb, and Falling Star.
Sound sets are now configurable - you can place your sound sets under "sound" in your Forge cache (as subfolders) and then select them with the "Sound set" option.
*** Android 7 & 8 support is now deprecated. Support will be dropped in an upcoming release. ***

View File

@@ -111,6 +111,7 @@ cbpGameLogEntryType=Spielberichtsumfang
cbpCloseAction=Beenden
cbpDefaultFontSize=Standard Schriftgröße
cbpCardArtFormat=Kartenbildformat
cbpSoundSets=Sound Set
cbpAiProfiles=KI Persönlichkeit
cbpStackAdditions=Nachricht bei Stapeländerung
cbpDisplayCurrentCardColors=Zeige detaillierte Kartenfarben
@@ -141,6 +142,7 @@ nlCompactMainMenu=Aktiviere, um im Seitenmenü platzsparend immer nur eine Menü
nlUseSentry=Aktiviere, um automatische Fehlerberichte an die Entwickler zu senden.
GamePlay=Spiel
nlpMulliganRule=Wähle die Version der Mulligan Regel
nlpSoundSets=Choose the sound set from the ones present in the "sound" folder in your Forge cache directory
nlpAiProfiles=Wähle die Spielweise deines KI-Gegners.
nlpStackAdditions=Wähle, wann du über Änderungen am Stapel benachrichtigt werden möchtest: Niemals, immer oder nur für durch andere Spieler ausgelöste Effekte und Fähigkeiten
nlAnte=Entscheidet, ob um einen Einsatz (Ante) gespielt wird.

View File

@@ -113,6 +113,7 @@ cbpCloseAction=Close Action
cbpDefaultFontSize=Default Font Size
cbpCardArtFormat=Card Art Format
cbpAiProfiles=AI Personality
cbpSoundSets=Sound Set
cbpStackAdditions=Stack effect notifications
cbpDisplayCurrentCardColors=Show Detailed Card Color
cbpAutoYieldMode=Auto-Yield
@@ -142,6 +143,7 @@ nlCompactMainMenu=Enable for a space efficient sidebar that displays only one me
nlUseSentry=When enabled, automatically submits bug reports to developers.
GamePlay=Gameplay
nlpMulliganRule=Choose the version of the Mulligan rule
nlpSoundSets=Choose the sound set from the ones present in the "sound" folder in your Forge cache directory
nlpAiProfiles=Choose your AI opponent
nlpStackAdditions=Choose when you want to get visual notifications for an effect added to the stack: Never, always, or only for the effects cast/activated by a AI player or triggered by any player
nlAnte=Determines whether or not the game is played for ante.

View File

@@ -112,6 +112,7 @@ cbpGameLogEntryType=Registro del juego
cbpCloseAction=Acción al cerrar
cbpDefaultFontSize=Tamaño de fuente predeterminado
cbpCardArtFormat=Card Art Format
cbpSoundSets=Sound Set
cbpAiProfiles=Personalidad de la IA
cbpStackAdditions=Efecto de la pila de notificaciones
cbpDisplayCurrentCardColors=Mostrar color de la carta
@@ -142,6 +143,7 @@ nlCompactMainMenu=Habilitar para una barra lateral eficiente en espacio que mues
nlUseSentry=Cuando está habilitado, envía automáticamente informes de errores a los desarrolladores.
GamePlay=Juego
nlpMulliganRule=Elige versión de reglas de mulligan
nlpSoundSets=Choose the sound set from the ones present in the "sound" folder in your Forge cache directory
nlpAiProfiles=Elige tu oponente de la IA
nlpStackAdditions=Elige cuándo quieres recibir notificaciones visuales para un efecto añadido a la pila: Nunca, siempre o sólo para los efectos lanzados/activados por un jugador IA o activados por cualquier jugador
nlAnte=Determina si el juego se juega con apuesta o no.

View File

@@ -111,6 +111,7 @@ cbpGameLogEntryType=Verbosità del registro di gioco
cbpCloseAction=Chiudi
cbpDefaultFontSize=Dimensione carattere predefinita
cbpCardArtFormat=Formato dell'Illustrazione delle carte
cbpSoundSets=Sound Set
cbpAiProfiles=Personalità dell''IA
cbpStackAdditions=Notifiche degli effetti in pila
cbpDisplayCurrentCardColors=Mostra colore scheda dettagliato
@@ -141,6 +142,7 @@ nlCompactMainMenu=Abilitare per una barra laterale efficiente in termini di spaz
nlUseSentry=Se abilitato, invia automaticamente segnalazioni di bug agli sviluppatori.
GamePlay=Gameplay
nlpMulliganRule=Scegli il tipo di Mulligan
nlpSoundSets=Choose the sound set from the ones present in the "sound" folder in your Forge cache directory
nlpAiProfiles=Scegli il tuo avversario (IA)
nlpStackAdditions=Scegli quando vuoi ricevere una notifica visiva di un effetto aggiunto alla pila: Mai, sempre, o solo per gli effetti lanciati/attivati da un giocatore IA o innescati da un qualsiasi giocatore
nlAnte=Determina se l''incontro è giocato o meno con la posta.

View File

@@ -112,6 +112,7 @@ cbpGameLogEntryType=ゲームログの詳細レベル
cbpCloseAction=閉じる時の動作
cbpDefaultFontSize=デフォルトのフォントサイズ
cbpCardArtFormat=カードのアートフォーマット
cbpSoundSets=Sound Set
cbpAiProfiles=AI の性格
cbpStackAdditions=スタック効果通知
cbpDisplayCurrentCardColors=詳細なカードの色を表示
@@ -142,6 +143,7 @@ nlCompactMainMenu=サイドバーに同時に一つのメニューグループ
nlUseSentry=有効にすると、バグレポートが開発者に自動的に送信されます。
GamePlay=ゲーム設定
nlpMulliganRule=マリガンルールを選択する。
nlpSoundSets=Choose the sound set from the ones present in the "sound" folder in your Forge cache directory
nlpAiProfiles=対戦相手 AI の性格を選択する。
nlpStackAdditions=スタックに追加された能力の視覚通知をいつ取得するかを選択しますNever[しない]、Always[常時]、またはAIプレーヤーによってキャスト/起動された能力、または任意のプレーヤーによって誘発された能力に対してのみ
nlAnte=ゲームでアンティ(賭け)ルールを適用するか選択する。

View File

@@ -112,6 +112,7 @@ cbpGameLogEntryType=游戏日志详细程度
cbpCloseAction=关闭动作
cbpDefaultFontSize=默认字体大小
cbpCardArtFormat=牌张插画格式
cbpSoundSets=Sound Set
cbpAiProfiles=AI强度
cbpStackAdditions=堆叠效应通知
cbpDisplayCurrentCardColors=显示卡牌颜色详情
@@ -142,6 +143,7 @@ nlCompactMainMenu=启用节省空间的侧边栏,一次只显示一个菜单
nlUseSentry=启用后,会自动向开发人员提交错误报告。
GamePlay=游戏
nlpMulliganRule=选择调度规则
nlpSoundSets=Choose the sound set from the ones present in the "sound" folder in your Forge cache directory
nlpAiProfiles=选择你的AI对手
nlpStackAdditions=选择何时因效应进入堆叠而提供视觉提醒从不总是任何由AI释放或起动以及由任何玩家触发的效应。
nlAnte=确定游戏是否使用赌注

View File

@@ -283,6 +283,7 @@ public final class ForgeConstants {
public static final String DB_DIR = CACHE_DIR + "db" + PATH_SEPARATOR;
public static final String FONTS_DIR = CACHE_DIR + "fonts" + PATH_SEPARATOR;
public static final String CACHE_SKINS_DIR = CACHE_DIR + "skins" + PATH_SEPARATOR;
public static final String CACHE_SOUND_DIR = CACHE_DIR + "sound" + PATH_SEPARATOR;
public static final String CACHE_TOKEN_PICS_DIR = PICS_DIR + "tokens" + PATH_SEPARATOR;
public static final String CACHE_ICON_PICS_DIR = PICS_DIR + "icons" + PATH_SEPARATOR;
public static final String CACHE_SYMBOLS_DIR = PICS_DIR + "symbols" + PATH_SEPARATOR;

View File

@@ -121,6 +121,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
UI_VOL_SOUNDS ("100"),
UI_VOL_MUSIC ("100"),
UI_ALT_SOUND_SYSTEM ("false"),
UI_CURRENT_SOUND_SET("Default"),
UI_CURRENT_AI_PROFILE ("Default"),
UI_CLONE_MODE_SOURCE ("false"),
UI_MATCH_IMAGE_VISIBLE ("true"),

View File

@@ -39,7 +39,6 @@ import forge.gui.events.IUiEventVisitor;
import forge.gui.events.UiEventAttackerDeclared;
import forge.gui.events.UiEventBlockerAssigned;
import forge.gui.events.UiEventNextGameDecision;
import forge.localinstance.properties.ForgeConstants;
import forge.util.TextUtil;
import forge.util.maps.MapOfLists;
@@ -331,7 +330,7 @@ public class EventVisualizer extends IGameEventVisitor.Base<SoundEffectType> imp
}
// Only proceed if the file actually exists
return new File(ForgeConstants.SOUND_DIR, effect).exists();
return new File(SoundSystem.getSoundDirectory(), effect).exists();
}

View File

@@ -1,8 +1,7 @@
package forge.sound;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.io.File;
import java.util.*;
import com.google.common.eventbus.Subscribe;
@@ -108,7 +107,7 @@ public class SoundSystem {
*/
public void play(final String resourceFileName, final boolean isSynchronized) {
if (isUsingAltSystem()) {
GuiBase.getInterface().startAltSoundSystem(ForgeConstants.SOUND_DIR + resourceFileName, isSynchronized);
GuiBase.getInterface().startAltSoundSystem(getSoundDirectory() + resourceFileName, isSynchronized);
}
else {
final IAudioClip snd = fetchResource(resourceFileName);
@@ -123,7 +122,7 @@ public class SoundSystem {
*/
public void play(final SoundEffectType type, final boolean isSynchronized) {
if (isUsingAltSystem()) {
GuiBase.getInterface().startAltSoundSystem(ForgeConstants.SOUND_DIR + type.getResourceFileName(), isSynchronized);
GuiBase.getInterface().startAltSoundSystem(getSoundDirectory() + type.getResourceFileName(), isSynchronized);
} else {
final IAudioClip snd = fetchResource(type);
if (!isSynchronized || snd.isDone()) {
@@ -245,4 +244,44 @@ public class SoundSystem {
currentTrack = null;
}
}
public static String[] getAvailableSoundSets()
{
final List<String> availableSets = new ArrayList<>();
final File dir = new File(ForgeConstants.CACHE_SOUND_DIR);
if (dir != null && dir.exists()) {
final String[] files = dir.list();
for (String fileName : files) {
String fullPath = ForgeConstants.CACHE_SOUND_DIR + fileName;
if (!fileName.equals("Default") && new File(fullPath).isDirectory()) {
availableSets.add(fileName);
}
}
}
Collections.sort(availableSets);
availableSets.add(0, "Default");
if (availableSets.size() == 1) {
// Default profile only - ensure that the preference is set accordingly
FModel.getPreferences().setPref(FPref.UI_CURRENT_SOUND_SET, "Default");
}
return availableSets.toArray(new String[availableSets.size()]);
}
public static String getSoundDirectory() {
String profileName = FModel.getPreferences().getPref(FPref.UI_CURRENT_SOUND_SET);
if (profileName.equals("Default")) {
return ForgeConstants.SOUND_DIR;
} else {
return ForgeConstants.CACHE_SOUND_DIR + profileName + ForgeConstants.PATH_SEPARATOR;
}
}
public static void invalidateSoundCache() {
loadedClips.clear();
loadedScriptClips.clear();
}
}