1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-16 12:35:22 +00:00

Added sound effects. Added audio to hero menu

This commit is contained in:
Tim Sarbin 2018-12-08 18:02:54 -05:00
parent b2b25db7de
commit 9c323fddf5
11 changed files with 174 additions and 20 deletions

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using OpenDiablo2.Common.Models;
namespace OpenDiablo2.Common.Interfaces
@ -10,5 +11,7 @@ namespace OpenDiablo2.Common.Interfaces
IEnumerable<MPQ> GetMPQs();
IEnumerable<String> GetTextFile(string fileName);
Stream GetStream(string fileName);
byte[] GetBytes(string fileName);
void GetBytesAsync(string fileName, Action<byte[]> callback);
}
}

View File

@ -3,10 +3,11 @@ using System.IO;
namespace OpenDiablo2.Common.Interfaces
{
public interface IMusicProvider : IDisposable
public interface ISoundProvider : IDisposable
{
void LoadSong(Stream data);
void PlaySong();
void StopSong();
void PlaySfx(byte[] data);
}
}

View File

@ -158,6 +158,27 @@ namespace OpenDiablo2.Common
public const string Experience = @"data\global\excel\experience.txt";
public const string CharStats = @"data\global\excel\charstats.txt";
// --- Music ---
public const string BGMTitle = @"data\global\music\introedit.wav";
public const string BGMTown1 = @"data\global\music\Act1\town1.wav";
// --- Sound Effects ---
public const string SFXButtonClick = @"data\global\sfx\Cursor\button.wav";
public const string SFXAmazonDeselect = @"data\global\sfx\Cursor\intro\amazon deselect.wav";
public const string SFXAmazonSelect = @"data\global\sfx\Cursor\intro\amazon select.wav";
public const string SFXAssassinDeselect = @"data\global\sfx\Cursor\intro\assassin deselect.wav";
public const string SFXAssassinSelect = @"data\global\sfx\Cursor\intro\assassin select.wav";
public const string SFXBarbarianDeselect = @"data\global\sfx\Cursor\intro\barbarian deselect.wav";
public const string SFXBarbarianSelect = @"data\global\sfx\Cursor\intro\barbarian select.wav";
public const string SFXDruidDeselect = @"data\global\sfx\Cursor\intro\druid deselect.wav";
public const string SFXDruidSelect = @"data\global\sfx\Cursor\intro\druid select.wav";
public const string SFXNecromancerDeselect = @"data\global\sfx\Cursor\intro\necromancer deselect.wav";
public const string SFXNecromancerSelect = @"data\global\sfx\Cursor\intro\necromancer select.wav";
public const string SFXPaladinDeselect = @"data\global\sfx\Cursor\intro\paladin deselect.wav";
public const string SFXPaladinSelect = @"data\global\sfx\Cursor\intro\paladin select.wav";
public const string SFXSorceressDeselect = @"data\global\sfx\Cursor\intro\sorceress deselect.wav";
public const string SFXSorceressSelect = @"data\global\sfx\Cursor\intro\sorceress select.wav";
public static string GeneratePathForItem(string spriteName)
{
return $@"data\global\items\{spriteName}.dc6";

View File

@ -51,6 +51,22 @@ namespace OpenDiablo2.Core
}
}
public byte[] GetBytes(string fileName)
{
var stream = GetStream(fileName);
var result = new byte[stream.Length];
stream.Read(result, 0, (int)stream.Length);
return result;
}
public void GetBytesAsync(string fileName, Action<byte[]> callback)
{
var stream = GetStream(fileName);
var result = new byte[stream.Length];
stream.Read(result, 0, (int)stream.Length);
callback(result);
}
public IEnumerable<MPQ> GetMPQs() => mpqs;
public Stream GetStream(string fileName)

View File

@ -10,7 +10,9 @@ namespace OpenDiablo2.Core.UI
{
private readonly IMouseInfoProvider mouseInfoProvider;
private readonly IRenderWindow renderWindow;
private readonly ISoundProvider musicProvider;
private readonly ButtonLayout buttonLayout;
private readonly byte[] sfxButtonClick;
public OnActivateDelegate OnActivate { get; set; }
public OnToggleDelegate OnToggle { get; set; }
@ -67,12 +69,15 @@ namespace OpenDiablo2.Core.UI
public Button(
ButtonLayout buttonLayout,
IRenderWindow renderWindow,
IMouseInfoProvider mouseInfoProvider
IMouseInfoProvider mouseInfoProvider,
ISoundProvider soundProvider,
IMPQProvider mpqProvider
)
{
this.buttonLayout = buttonLayout;
this.renderWindow = renderWindow;
this.mouseInfoProvider = mouseInfoProvider;
this.musicProvider = soundProvider;
font = renderWindow.LoadFont(ResourcePaths.FontExocet10, Palettes.Units);
label = renderWindow.CreateLabel(font);
@ -91,6 +96,8 @@ namespace OpenDiablo2.Core.UI
label.MaxWidth = buttonWidth - 8;
label.Alignment = Common.Enums.eTextAlign.Centered;
sfxButtonClick = mpqProvider.GetBytes(ResourcePaths.SFXButtonClick);
}
public bool Toggle()
@ -138,7 +145,7 @@ namespace OpenDiablo2.Core.UI
// The button is being pressed down
mouseInfoProvider.ReserveMouse = true;
active = true;
musicProvider.PlaySfx(sfxButtonClick);
}
else if (active && !mouseInfoProvider.LeftMouseDown)
{

View File

@ -13,7 +13,7 @@ namespace OpenDiablo2.SDL2_
log.Info("Configuring OpenDiablo2.Core service implementations.");
builder.RegisterType<SDL2RenderWindow>().AsImplementedInterfaces().SingleInstance();
builder.RegisterType<SDL2MusicPlayer>().AsImplementedInterfaces().SingleInstance();
builder.RegisterType<SDL2MusicProvider>().AsImplementedInterfaces().SingleInstance();
}
}
}

View File

@ -62,7 +62,7 @@
<Compile Include="SDL2Font.cs" />
<Compile Include="SDL2Label.cs" />
<Compile Include="SDL2MouseCursor.cs" />
<Compile Include="SDL2MusicPlayer.cs" />
<Compile Include="SDL2MusicProvider.cs" />
<Compile Include="SDL2RenderWindow.cs" />
<Compile Include="SDL2Sprite.cs" />
<Compile Include="SDL2Texture.cs" />

View File

@ -16,18 +16,20 @@
using System;
using System.IO;
using System.Threading;
using OpenDiablo2.Common.Interfaces;
using SDL2;
namespace OpenDiablo2.SDL2_
{
public sealed class SDL2MusicPlayer : IMusicProvider
public sealed class SDL2MusicProvider : ISoundProvider
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private IntPtr music = IntPtr.Zero;
private int musicChannel;
private byte[] musicBytes; // Cannot be local or GC will destory it with great anger
public SDL2MusicPlayer()
public SDL2MusicProvider()
{
if (SDL_mixer.Mix_OpenAudio(22050, SDL_mixer.MIX_DEFAULT_FORMAT, 2, 1024) < 0)
log.Error($"SDL_mixer could not initialize! SDL_mixer Error: {SDL.SDL_GetError()}");
@ -35,6 +37,9 @@ namespace OpenDiablo2.SDL2_
public void PlaySong()
{
if (music == IntPtr.Zero)
return;
musicChannel = SDL_mixer.Mix_PlayChannel(-1, music, 1);
}
@ -43,9 +48,14 @@ namespace OpenDiablo2.SDL2_
if (music != IntPtr.Zero)
StopSong();
var br = new BinaryReader(data);
var bytes = br.ReadBytes((int)(data.Length - data.Position));
music = SDL_mixer.Mix_QuickLoad_WAV(bytes);
musicBytes = new byte[data.Length - data.Position];
data.ReadAsync(musicBytes, 0, (int)(data.Length - data.Position));
// Wait until SOMETHING gets written out
while (musicBytes[8] == 0)
Thread.Sleep(1);
music = SDL_mixer.Mix_QuickLoad_WAV(musicBytes);
}
public void StopSong()
@ -54,6 +64,7 @@ namespace OpenDiablo2.SDL2_
return;
SDL_mixer.Mix_HaltChannel(musicChannel);
SDL_mixer.Mix_FreeChunk(music);
music = IntPtr.Zero;
}
public void Dispose()
@ -62,6 +73,10 @@ namespace OpenDiablo2.SDL2_
SDL_mixer.Mix_CloseAudio();
}
public void PlaySfx(byte[] data)
{
var sound = SDL_mixer.Mix_QuickLoad_WAV(data);
SDL_mixer.Mix_PlayChannel(-1, sound, 0);
}
}
}

View File

@ -14,6 +14,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using OpenDiablo2.Common;
using OpenDiablo2.Common.Attributes;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
@ -43,6 +44,8 @@ namespace OpenDiablo2.Scenes
IMouseInfoProvider mouseInfoProvider,
IItemManager itemManager,
ISessionManager sessionManager,
ISoundProvider soundProvider,
IMPQProvider mpqProvider,
IGameHUD gameHUD
)
{
@ -53,6 +56,10 @@ namespace OpenDiablo2.Scenes
this.sessionManager = sessionManager;
this.gameHUD = gameHUD;
// TODO: Dynamic based on actual location
soundProvider.StopSong();
soundProvider.LoadSong(mpqProvider.GetStream(ResourcePaths.BGMTown1));
soundProvider.PlaySong();
//var item = itemManager.getItem("hdm");
}

View File

@ -40,7 +40,7 @@ namespace OpenDiablo2.Scenes
IRenderWindow renderWindow,
ISceneManager sceneManager,
IResourceManager resourceManager,
IMusicProvider musicProvider,
ISoundProvider soundProvider,
IMPQProvider mpqProvider,
Func<eButtonType, IButton> createButton,
Func<eSceneType, IScene> getScene // Temporary until SDL load functions are sped up
@ -89,8 +89,8 @@ namespace OpenDiablo2.Scenes
getScene(scenesToLoad[i]);
}
musicProvider.LoadSong(mpqProvider.GetStream("data\\global\\music\\introedit.wav"));
musicProvider.PlaySong();
soundProvider.LoadSong(mpqProvider.GetStream(ResourcePaths.BGMTitle));
soundProvider.PlaySong();
}
private void OnVisitWebsiteClicked()

View File

@ -37,6 +37,7 @@ namespace OpenDiablo2.Scenes
private readonly ITextDictionary textDictionary;
private readonly IKeyboardInfoProvider keyboardInfoProvider;
private readonly IGameState gameState;
private readonly ISoundProvider soundProvider;
private bool showEntryUi = false;
private eHero? selectedHero = null;
@ -50,14 +51,20 @@ namespace OpenDiablo2.Scenes
private readonly ITextBox characterNameTextBox;
private readonly Dictionary<eHero, HeroRenderInfo> heroRenderInfo = new Dictionary<eHero, HeroRenderInfo>();
private byte[] sfxAmazonSelect, sfxAmazonDeselect, sfxAssassinSelect, sfxAssassinDeselect, sfxBarbarianSelect, sfxBarbarianDeselect,
sfxDruidSelect, sfxDruidDeselect, sfxNecromancerSelect, sfxNecromancerDeselect, sfxPaladinSelect, sfxPaladinDeselect,
sfxSorceressSelect, sfxSorceressDeselect;
public SelectHeroClass(
IRenderWindow renderWindow,
IMouseInfoProvider mouseInfoProvider,
ISceneManager sceneManager,
ISoundProvider soundProvider,
Func<eButtonType, IButton> createButton,
Func<ITextBox> createTextBox,
ITextDictionary textDictionary,
IKeyboardInfoProvider keyboardInfoProvider,
IMPQProvider mpqProvider,
IGameState gameState
)
{
@ -66,6 +73,7 @@ namespace OpenDiablo2.Scenes
this.sceneManager = sceneManager;
this.textDictionary = textDictionary;
this.keyboardInfoProvider = keyboardInfoProvider;
this.soundProvider = soundProvider;
this.gameState = gameState;
@ -83,7 +91,7 @@ namespace OpenDiablo2.Scenes
SelectedSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelectBarbarianSelected, Palettes.Fechar, new Point(400, 330)),
BackWalkSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelectBarbarianBackWalk, Palettes.Fechar, new Point(400, 330)),
SelectionBounds = new Rectangle(364, 201, 90, 170),
ForwardWalkTimeMs = 3000,
ForwardWalkTimeMs = 2500,
BackWalkTimeMs = 1000
};
@ -99,7 +107,7 @@ namespace OpenDiablo2.Scenes
BackWalkSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelecSorceressBackWalk, Palettes.Fechar, new Point(626, 352)),
BackWalkSpriteOverlay = renderWindow.LoadSprite(ResourcePaths.CharacterSelecSorceressBackWalkOverlay, Palettes.Fechar, new Point(626, 352)),
SelectionBounds = new Rectangle(580, 240, 65, 160),
ForwardWalkTimeMs = 3000,
ForwardWalkTimeMs = 2300,
BackWalkTimeMs = 1200
};
heroRenderInfo[eHero.Sorceress].SelectedSpriteOverlay.Blend = true;
@ -136,7 +144,7 @@ namespace OpenDiablo2.Scenes
SelectedSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelecPaladinSelected, Palettes.Fechar, new Point(521, 338)),
BackWalkSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelecPaladinBackWalk, Palettes.Fechar, new Point(521, 338)),
SelectionBounds = new Rectangle(490, 210, 65, 180),
ForwardWalkTimeMs = 4000,
ForwardWalkTimeMs = 3400,
BackWalkTimeMs = 1300
};
@ -150,7 +158,7 @@ namespace OpenDiablo2.Scenes
SelectedSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelecAmazonSelected, Palettes.Fechar, new Point(100, 339)),
BackWalkSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelecAmazonBackWalk, Palettes.Fechar, new Point(100, 339)),
SelectionBounds = new Rectangle(70, 220, 55, 200),
ForwardWalkTimeMs = 2600,
ForwardWalkTimeMs = 2200,
BackWalkTimeMs = 1500
};
@ -163,7 +171,7 @@ namespace OpenDiablo2.Scenes
SelectedSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelectAssassinSelected, Palettes.Fechar, new Point(231, 365)),
BackWalkSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelectAssassinBackWalk, Palettes.Fechar, new Point(231, 365)),
SelectionBounds = new Rectangle(175, 235, 50, 180),
ForwardWalkTimeMs = 3000,
ForwardWalkTimeMs = 3800,
BackWalkTimeMs = 1500
};
@ -176,7 +184,7 @@ namespace OpenDiablo2.Scenes
SelectedSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelectDruidSelected, Palettes.Fechar, new Point(720, 370)),
BackWalkSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelectDruidBackWalk, Palettes.Fechar, new Point(720, 370)),
SelectionBounds = new Rectangle(680, 220, 70, 195),
ForwardWalkTimeMs = 3000,
ForwardWalkTimeMs = 4800,
BackWalkTimeMs = 1500
};
@ -216,6 +224,21 @@ namespace OpenDiablo2.Scenes
characterNameTextBox.Text = "";
characterNameTextBox.Location = new Point(320, 493);
mpqProvider.GetBytesAsync(ResourcePaths.SFXAmazonSelect, x => sfxAmazonSelect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXAmazonDeselect, x => sfxAmazonDeselect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXAssassinSelect, x => sfxAssassinSelect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXAssassinDeselect, x => sfxAssassinDeselect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXBarbarianSelect, x => sfxBarbarianSelect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXBarbarianDeselect, x => sfxBarbarianDeselect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXDruidSelect, x => sfxDruidSelect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXDruidDeselect, x => sfxDruidDeselect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXNecromancerSelect, x => sfxNecromancerSelect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXNecromancerDeselect, x => sfxNecromancerDeselect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXPaladinSelect, x => sfxPaladinSelect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXPaladinDeselect, x => sfxPaladinDeselect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXSorceressSelect, x => sfxSorceressSelect = x);
mpqProvider.GetBytesAsync(ResourcePaths.SFXSorceressDeselect, x => sfxSorceressDeselect = x);
}
private void OnOkclicked()
@ -432,6 +455,7 @@ namespace OpenDiablo2.Scenes
selectedHero = hero;
UpdateHeroText();
PlayHeroSelected(hero);
return;
}
@ -446,6 +470,66 @@ namespace OpenDiablo2.Scenes
}
private void PlayHeroSelected(eHero hero)
{
switch (hero)
{
case eHero.Barbarian:
soundProvider.PlaySfx(sfxBarbarianSelect);
break;
case eHero.Necromancer:
soundProvider.PlaySfx(sfxNecromancerSelect);
break;
case eHero.Paladin:
soundProvider.PlaySfx(sfxPaladinSelect);
break;
case eHero.Assassin:
soundProvider.PlaySfx(sfxAssassinSelect);
break;
case eHero.Sorceress:
soundProvider.PlaySfx(sfxSorceressSelect);
break;
case eHero.Amazon:
soundProvider.PlaySfx(sfxAmazonSelect);
break;
case eHero.Druid:
soundProvider.PlaySfx(sfxDruidSelect);
break;
default:
break;
}
}
private void PlayHeroDeselected(eHero hero)
{
switch (hero)
{
case eHero.Barbarian:
soundProvider.PlaySfx(sfxBarbarianDeselect);
break;
case eHero.Necromancer:
soundProvider.PlaySfx(sfxNecromancerDeselect);
break;
case eHero.Paladin:
soundProvider.PlaySfx(sfxPaladinDeselect);
break;
case eHero.Assassin:
soundProvider.PlaySfx(sfxAssassinDeselect);
break;
case eHero.Sorceress:
soundProvider.PlaySfx(sfxSorceressDeselect);
break;
case eHero.Amazon:
soundProvider.PlaySfx(sfxAmazonDeselect);
break;
case eHero.Druid:
soundProvider.PlaySfx(sfxDruidDeselect);
break;
default:
break;
}
}
private void SetDescLabels(string descKey)
{
var heroDesc = textDictionary.Translate(descKey);