From adb65618d77f6570ff1a1bf55df2f4cf7efbb05e Mon Sep 17 00:00:00 2001 From: ficedula Date: Sun, 2 Jul 2023 21:11:22 +0100 Subject: [PATCH] Add pitch support for audio items Add focus item to fields Improve UISysten plugin to allow better tracking of initial load vs. change of selection Make debug options more accessible, layout labels more accessible Update Tolk plugin to support focus tracking --- Braver.Core/CoreTypes.cs | 2 +- Braver.Plugins/Field/FieldLocation.cs | 38 +++++- Braver.Plugins/Types.cs | 1 + Braver.Plugins/UI/UISystem.cs | 2 +- Braver.sln | 6 - Braver/Audio.cs | 14 ++- Braver/Braver.csproj | 2 +- Braver/Field/FieldScreen.cs | 118 +++++++++++++++--- Braver/Screen.cs | 8 +- Braver/UI/Layout/Debug.cs | 17 ++- Braver/UI/Layout/Layout.cs | 6 +- Braver/UI/Splash.cs | 2 +- BraverLauncher/MainWindow.xaml.cs | 2 +- Ficedula.FF7/Field/FieldFile.cs | 6 + .../Braver.Tolk/Braver.Tolk.csproj | 3 + .../Braver.Tolk/TolkPlugin.cs | 81 ++++++++---- PluginImplementations/Braver.Tolk/focus.ogg | Bin 0 -> 11991 bytes README.md | 16 +++ data/layout/debug.xml | 2 +- 19 files changed, 257 insertions(+), 69 deletions(-) create mode 100644 PluginImplementations/Braver.Tolk/focus.ogg diff --git a/Braver.Core/CoreTypes.cs b/Braver.Core/CoreTypes.cs index 3fef4ac..91041be 100644 --- a/Braver.Core/CoreTypes.cs +++ b/Braver.Core/CoreTypes.cs @@ -24,7 +24,7 @@ namespace Braver { } public interface IAudioItem : IDisposable { - void Play(float volume, float pan, bool loop); + void Play(float volume, float pan, bool loop, float pitch); void Pause(); void Resume(); void Stop(); diff --git a/Braver.Plugins/Field/FieldLocation.cs b/Braver.Plugins/Field/FieldLocation.cs index e03324d..b5c9e3a 100644 --- a/Braver.Plugins/Field/FieldLocation.cs +++ b/Braver.Plugins/Field/FieldLocation.cs @@ -4,6 +4,7 @@ // // SPDX-License-Identifier: EPL-2.0 +using Braver.Field; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; @@ -11,9 +12,44 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +namespace Braver.Field { + + public class FocusState { + public string TargetName { get; set; } + public Vector3 TargetPosition { get; set; } + public int WalkmeshDistance { get; set; } + } + + [Flags] + public enum FieldOptions { + None = 0, + PlayerControls = 0x1, + //LinesActive = 0x2, + MenuEnabled = 0x4, + CameraTracksPlayer = 0x8, + CameraIsAsyncScrolling = 0x10, + MusicLocked = 0x20, + UseMovieCam = 0x40, + GatewaysEnabled = 0x80, + ShowPlayerHand = 0x100, + + NoScripts = 0x1000, + + DEFAULT = PlayerControls | MenuEnabled | CameraTracksPlayer | UseMovieCam | GatewaysEnabled | ShowPlayerHand, + } + +} + namespace Braver.Plugins.Field { + public interface IField { + Vector3 PlayerPosition { get; } + FocusState GetFocusState(); + public FieldOptions Options { get; } + } + public interface IFieldLocation : IPluginInstance { - void Step(); + void Step(IField field); void EntityMoved(IFieldEntity entity, bool isRunning, Vector3 from, Vector3 to); + void FocusChanged(); } } diff --git a/Braver.Plugins/Types.cs b/Braver.Plugins/Types.cs index 2bcffec..4d4fb2f 100644 --- a/Braver.Plugins/Types.cs +++ b/Braver.Plugins/Types.cs @@ -28,6 +28,7 @@ namespace Braver.Plugins { public interface IScreen { string Description { get; } + bool HasFinishedLoading { get; } } public interface IFieldEntity { diff --git a/Braver.Plugins/UI/UISystem.cs b/Braver.Plugins/UI/UISystem.cs index 16b5afc..f956489 100644 --- a/Braver.Plugins/UI/UISystem.cs +++ b/Braver.Plugins/UI/UISystem.cs @@ -18,6 +18,6 @@ namespace Braver.Plugins.UI { void Dialog(string dialog); void Choices(IEnumerable choices, int selected); - void Menu(IEnumerable items, int selected); + void Menu(IEnumerable items, int selected, object container); } } diff --git a/Braver.sln b/Braver.sln index cd5a3e6..79e5d66 100644 --- a/Braver.sln +++ b/Braver.sln @@ -17,8 +17,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Braver.Core", "Braver.Core\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BraverBattleSim", "BraverBattleSim\BraverBattleSim.csproj", "{D2704960-29CC-4A4D-B725-73AC95C1E5F5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BraverSetup", "BraverSetup\BraverSetup.csproj", "{BC7A0739-2DEE-4396-BF05-9242D4833A5F}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{54809439-31F3-4C7E-B002-98BC2B476EB9}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig @@ -64,10 +62,6 @@ Global {D2704960-29CC-4A4D-B725-73AC95C1E5F5}.Debug|Any CPU.Build.0 = Debug|Any CPU {D2704960-29CC-4A4D-B725-73AC95C1E5F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {D2704960-29CC-4A4D-B725-73AC95C1E5F5}.Release|Any CPU.Build.0 = Release|Any CPU - {BC7A0739-2DEE-4396-BF05-9242D4833A5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BC7A0739-2DEE-4396-BF05-9242D4833A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BC7A0739-2DEE-4396-BF05-9242D4833A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BC7A0739-2DEE-4396-BF05-9242D4833A5F}.Release|Any CPU.Build.0 = Release|Any CPU {4C8A556C-F596-4F35-8E09-C79E5BEAFCEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4C8A556C-F596-4F35-8E09-C79E5BEAFCEB}.Debug|Any CPU.Build.0 = Debug|Any CPU {4C8A556C-F596-4F35-8E09-C79E5BEAFCEB}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Braver/Audio.cs b/Braver/Audio.cs index e74dbf1..39d6159 100644 --- a/Braver/Audio.cs +++ b/Braver/Audio.cs @@ -376,14 +376,19 @@ namespace Braver { private NAudio.Vorbis.VorbisWaveReader _vorbis; private VolumeSampleProvider _volume; private PanningSampleProvider _pan; + private SmbPitchShiftingSampleProvider _pitch; private bool _shouldLoop; public LoadedAudioItem(Stream s) { _waveOut = new WaveOut(); _waveOut.PlaybackStopped += _waveOut_PlaybackStopped; _vorbis = new NAudio.Vorbis.VorbisWaveReader(s, true); - _pan = new PanningSampleProvider(_volume = new VolumeSampleProvider(_vorbis)); - _waveOut.Init(_pan); + _pitch = new SmbPitchShiftingSampleProvider( + _pan = new PanningSampleProvider(_volume = new VolumeSampleProvider(_vorbis)) + ) { + PitchFactor = 1f + }; + _waveOut.Init(_pitch); } private void _waveOut_PlaybackStopped(object sender, StoppedEventArgs e) { @@ -405,10 +410,13 @@ namespace Braver { _waveOut.Resume(); } - public void Play(float volume, float pan, bool loop) { + public void Play(float volume, float pan, bool loop, float pitch) { + _vorbis.Position = 0; _shouldLoop = loop; _volume.Volume = volume; _pan.Pan = pan; + _pitch.PitchFactor = pitch; + System.Diagnostics.Debug.WriteLine($"AudioItem play vol {volume} pan {pan} pitch {pitch}"); _waveOut.Play(); } diff --git a/Braver/Braver.csproj b/Braver/Braver.csproj index 7013d00..0f95c7e 100644 --- a/Braver/Braver.csproj +++ b/Braver/Braver.csproj @@ -5,7 +5,7 @@ false false true - 0.1.1 + 0.1.2 app.manifest diff --git a/Braver/Field/FieldScreen.cs b/Braver/Field/FieldScreen.cs index 9eca09b..5492cab 100644 --- a/Braver/Field/FieldScreen.cs +++ b/Braver/Field/FieldScreen.cs @@ -39,26 +39,9 @@ namespace Braver.Field { } } - [Flags] - public enum FieldOptions { - None = 0, - PlayerControls = 0x1, - //LinesActive = 0x2, - MenuEnabled = 0x4, - CameraTracksPlayer = 0x8, - CameraIsAsyncScrolling = 0x10, - MusicLocked = 0x20, - UseMovieCam = 0x40, - GatewaysEnabled = 0x80, - ShowPlayerHand = 0x100, - - NoScripts = 0x1000, - - DEFAULT = PlayerControls | MenuEnabled | CameraTracksPlayer | UseMovieCam | GatewaysEnabled | ShowPlayerHand, - } - public class FieldScreen : Screen, Net.IListen, Net.IListen, - Net.IListen, Net.IListen { + Net.IListen, Net.IListen, + IField { private PerspView3D _view3D; private View2D _view2D; @@ -94,6 +77,17 @@ namespace Braver.Field { public TriggersAndGateways TriggersAndGateways { get; private set; } public Shake ShakeEffect { get; private set; } + private class Focusable { + public string Name { get; set; } + public Func Position { get; set; } + public Func WalkmeshTri { get; set; } + public Func Active { get; set; } + public object Source { get; set; } + } + + private List _focusables = new(); + private Focusable _currentFocus; + private EncounterTable[] _encounters; public FieldOptions Options { get; set; } = FieldOptions.DEFAULT; @@ -378,6 +372,29 @@ namespace Braver.Field { g.Net.Send(new Net.ScreenReadyMessage()); } Entity.DEBUG_OUT = false; + + foreach(var entity in Entities.Where(e => e.Model != null)) { + _focusables.Add(new Focusable { + Name = entity.Name, + Position = () => entity.Model.Translation, + WalkmeshTri = () => entity.WalkmeshTri, + Source = entity, + Active = () => entity.Model.Visible && (entity != Player) + }); + } + foreach(var gateway in TriggersAndGateways.Gateways) { + var middle = (gateway.V0.ToX() + gateway.V1.ToX()) * 0.5f; + var tri = FindWalkmeshForPosition(middle); + if (tri != null) { + _focusables.Add(new Focusable { + Name = "Exit " + TriggersAndGateways.Gateways.IndexOf(gateway), + Position = () => middle, + WalkmeshTri = () => tri.Value, + Source = gateway, + Active = () => true, + }); + } + } } private int _nextModelIndex = 0; @@ -482,7 +499,7 @@ namespace Braver.Field { entity.Model?.FrameStep(); } } - _plugins.Call(loc => loc.Step()); + _plugins.Call(loc => loc.Step(this)); _frame++; } @@ -619,6 +636,19 @@ namespace Braver.Field { (LineEvents.GoAway, 6), }; + private void SwitchFocus(int offset) { + _currentFocusState = null; + foreach (int i in Enumerable.Range(1, _focusables.Count - 1)) { + int newIndex = (_focusables.IndexOf(_currentFocus) + offset * i + _focusables.Count) % _focusables.Count; + var candidate = _focusables[newIndex]; + if (candidate.Active()) { + _currentFocus = candidate; + return; + } + } + _currentFocus = null; + } + public override void ProcessInput(InputState input) { base.ProcessInput(input); if (!(Game.Net is Net.Server)) return; @@ -639,6 +669,11 @@ namespace Braver.Field { ReportAllModelPositions(); } + if (input.IsJustDown(InputKey.PanLeft)) + SwitchFocus(-1); + if (input.IsJustDown(InputKey.PanRight)) + SwitchFocus(+1); + if (input.IsJustDown(InputKey.Debug5)) Game.PushScreen(new UI.Layout.LayoutScreen("FieldDebugger", parm: this)); @@ -1171,6 +1206,7 @@ namespace Braver.Field { case LeaveTriResult.Success: case LeaveTriResult.SlideNewTri: eMove.WalkmeshTri = newTri.Value; + _currentFocusState = null; //Could be a bit cleverer about when to invalidate this currentTri = _walkmesh[newTri.Value]; ClampToTriangle(ref newDest, currentTri); break; //Treat same as success, code below will move us accordingly @@ -1253,6 +1289,18 @@ namespace Braver.Field { } } + public int? FindWalkmeshForPosition(Vector3 position) { + foreach(int t in Enumerable.Range(0, _walkmesh.Count)) { + var height = HeightInTriangle(t, position.X, position.Y, false); + if (height != null) { + if (((height.Value - 5) <= position.Z) && (height.Value > (position.Z - 5))) //TODO - is this fudge factor needed, the right size, ...? + return t; + } + } + + return null; + } + public void DropToWalkmesh(Entity e, Vector2 position, int walkmeshTri, bool exceptOnFailure = true) { var tri = _walkmesh[walkmeshTri]; @@ -1263,6 +1311,7 @@ namespace Braver.Field { e.Model.Translation = new Vector3(position.X, position.Y, height.GetValueOrDefault()); e.WalkmeshTri = walkmeshTri; + _currentFocusState = null; //Could be a bit cleverer about when to invalidate this ReportDebugEntityPos(e); } @@ -1352,6 +1401,35 @@ namespace Braver.Field { public void Received(Net.FieldEntityModelMessage message) { Entities[message.EntityID].Model = FieldModels[message.ModelID]; } + + private FocusState _currentFocusState; + Vector3 IField.PlayerPosition => Player?.Model?.Translation ?? Vector3.Zero; + FocusState IField.GetFocusState() { + if ((_currentFocus != null) && (_currentFocusState == null) && (Player != null) && (_currentFocus.Source != Player)) { + Dictionary distance = new(); + distance[_walkmesh[Player.WalkmeshTri]] = 0; + Queue toConsider = new Queue(); + toConsider.Enqueue(_walkmesh[Player.WalkmeshTri]); + + while (toConsider.Any()) { + var tri = toConsider.Dequeue(); + foreach(var adjacent in tri.AdjacentTris()) { + var adj = _walkmesh[adjacent]; + if (!distance.ContainsKey(adj)) { + distance[adj] = distance[tri] + 1; + toConsider.Enqueue(adj); + } + } + } + + _currentFocusState = new FocusState { + TargetName = _currentFocus.Name, + TargetPosition = _currentFocus.Position(), + WalkmeshDistance = distance[_walkmesh[_currentFocus.WalkmeshTri()]], + }; + } + return _currentFocusState; + } } public class CachedField { diff --git a/Braver/Screen.cs b/Braver/Screen.cs index 2e90d39..c81241d 100644 --- a/Braver/Screen.cs +++ b/Braver/Screen.cs @@ -74,10 +74,13 @@ namespace Braver { public abstract string Description { get; } + bool IScreen.HasFinishedLoading => _frames > 0; + protected SpriteBatch _fxBatch; private Transition _transition; private Action _transitionAction; + private int _frames = 0; protected bool _readyToRender; protected Plugins.PluginInstances _plugins; @@ -96,7 +99,9 @@ namespace Braver { protected abstract void DoRender(); public virtual void Reactivated() { } - public virtual void Dispose() { } + public virtual void Dispose() { + _plugins?.Dispose(); + } public void FadeOut(Action then, int frames = 30) { _transition = new FadeTransition( @@ -126,6 +131,7 @@ namespace Braver { _transitionAction?.Invoke(); } } + _frames++; } public void Render() { diff --git a/Braver/UI/Layout/Debug.cs b/Braver/UI/Layout/Debug.cs index 2b54cf4..d366288 100644 --- a/Braver/UI/Layout/Debug.cs +++ b/Braver/UI/Layout/Debug.cs @@ -24,11 +24,17 @@ namespace Braver.UI.Layout { } private void Update() { - lNoFieldScripts.Color = Game.GameOptions.NoFieldScripts ? Color.White : Color.Gray; - lNoRandomBattles.Color = Game.GameOptions.NoRandomBattles ? Color.White : Color.Gray; - lSkipBattleMenu.Color = Game.GameOptions.SkipBattleMenu ? Color.White : Color.Gray; - lAutoSaveOnFieldEntry.Color = Game.GameOptions.AutoSaveOnFieldEntry ? Color.White : Color.Gray; - lSeparateSaveFiles.Color = Game.GameOptions.SeparateSaveFiles ? Color.White : Color.Gray; + + void DoLabel(Label L, bool option) { + L.Color = option ? Color.White : Color.Gray; + L.FocusDescription = L.Text + " " + (option ? "On" : "Off"); + } + + DoLabel(lNoFieldScripts, Game.GameOptions.NoFieldScripts); + DoLabel(lNoRandomBattles, Game.GameOptions.NoRandomBattles); + DoLabel(lSkipBattleMenu, Game.GameOptions.SkipBattleMenu); + DoLabel(lAutoSaveOnFieldEntry, Game.GameOptions.AutoSaveOnFieldEntry); + DoLabel(lSeparateSaveFiles, Game.GameOptions.SeparateSaveFiles); } public void LabelClick(Label L) { @@ -44,6 +50,7 @@ namespace Braver.UI.Layout { Game.GameOptions.SeparateSaveFiles = !Game.GameOptions.SeparateSaveFiles; Update(); + ChangeFocus(Focus); //to re-announce new state } public override bool ProcessInput(InputState input) { diff --git a/Braver/UI/Layout/Layout.cs b/Braver/UI/Layout/Layout.cs index 1d16ad5..533abe1 100644 --- a/Braver/UI/Layout/Layout.cs +++ b/Braver/UI/Layout/Layout.cs @@ -57,7 +57,7 @@ namespace Braver.UI.Layout { public virtual Action OnFocussed { get; set; } [XmlIgnore] - public virtual string Description => FocusDescription ?? "Unknown"; + public virtual string Description => FocusDescription ?? null; [XmlIgnore] public Container Parent { get; internal set; } @@ -227,7 +227,7 @@ namespace Braver.UI.Layout { public bool Enabled { get; set; } = true; [XmlIgnore] - public override string Description => Text ?? base.Description; + public override string Description => base.Description ?? Text; public override void Draw(LayoutModel model, UIBatch ui, int offsetX, int offsetY, Func getZ) { if (!string.IsNullOrWhiteSpace(Text)) @@ -351,7 +351,7 @@ namespace Braver.UI.Layout { Focus?.OnFocussed?.Invoke(); var group = FocusGroup.FocussableChildren().Select(child => child.Component).ToList(); Game.InvokeOnMainThread( - () => Game.UIPlugins.Call(ui => ui.Menu(group.Select(c => c.Description), group.IndexOf(f))), + () => Game.UIPlugins.Call(ui => ui.Menu(group.Select(c => c.Description), group.IndexOf(f), FocusGroup)), 1 ); //delay so initial announcement happens after loading has finished } diff --git a/Braver/UI/Splash.cs b/Braver/UI/Splash.cs index 90e95df..320a82a 100644 --- a/Braver/UI/Splash.cs +++ b/Braver/UI/Splash.cs @@ -34,7 +34,7 @@ namespace Braver.UI { } private void Announce() { - Game.UIPlugins.Call(ui => ui.Menu(_items, _menu)); + Game.UIPlugins.Call(ui => ui.Menu(_items, _menu, this)); } public override void Init(FGame g, GraphicsDevice graphics) { diff --git a/BraverLauncher/MainWindow.xaml.cs b/BraverLauncher/MainWindow.xaml.cs index 9881cde..d1f6c93 100644 --- a/BraverLauncher/MainWindow.xaml.cs +++ b/BraverLauncher/MainWindow.xaml.cs @@ -81,7 +81,7 @@ namespace BraverLauncher { txtMovies.Text = settings.GetValueOrDefault("Movies"); txtSave.Text = settings.GetValueOrDefault("Save"); if (txtSave.Text == ".") txtSave.Text = ""; - slMusicVolume.Value = double.Parse(settings.GetValueOrDefault("Options.MusicVolume") ?? "100"); + slMusicVolume.Value = double.Parse(settings.GetValueOrDefault("Options.MusicVolume") ?? "1") * 100; } } diff --git a/Ficedula.FF7/Field/FieldFile.cs b/Ficedula.FF7/Field/FieldFile.cs index 5168cf4..97a7b5c 100644 --- a/Ficedula.FF7/Field/FieldFile.cs +++ b/Ficedula.FF7/Field/FieldFile.cs @@ -230,6 +230,12 @@ namespace Ficedula.FF7.Field { yield return V1; yield return V2; } + + public IEnumerable AdjacentTris() { + if (V01Tri != null) yield return V01Tri.Value; + if (V12Tri != null) yield return V12Tri.Value; + if (V20Tri != null) yield return V20Tri.Value; + } } public class Walkmesh { diff --git a/PluginImplementations/Braver.Tolk/Braver.Tolk.csproj b/PluginImplementations/Braver.Tolk/Braver.Tolk.csproj index 3ffa75b..bdd3aef 100644 --- a/PluginImplementations/Braver.Tolk/Braver.Tolk.csproj +++ b/PluginImplementations/Braver.Tolk/Braver.Tolk.csproj @@ -12,6 +12,9 @@ + + PreserveNewest + PreserveNewest diff --git a/PluginImplementations/Braver.Tolk/TolkPlugin.cs b/PluginImplementations/Braver.Tolk/TolkPlugin.cs index 53fb689..e34f671 100644 --- a/PluginImplementations/Braver.Tolk/TolkPlugin.cs +++ b/PluginImplementations/Braver.Tolk/TolkPlugin.cs @@ -4,6 +4,7 @@ // // SPDX-License-Identifier: EPL-2.0 +using Braver.Field; using Braver.Plugins; using Braver.Plugins.Field; using Braver.Plugins.UI; @@ -15,6 +16,7 @@ namespace Braver.Tolk { public class TolkConfig { public bool EnableSAPI { get; set; } = true; public bool EnableFootsteps { get; set; } = true; + public bool EnableFocusTracking { get; set; } = true; } public class TolkPlugin : Plugin { @@ -29,14 +31,14 @@ namespace Braver.Tolk { if (t == typeof(UISystem)) return new TolkInstance(_config); else if (t == typeof(IFieldLocation)) - return new FootstepPlugin(_game); + return new FootstepFocusPlugin(_game, _config.EnableFootsteps, _config.EnableFocusTracking); else throw new NotSupportedException(); } public override IEnumerable GetPluginInstances() { yield return typeof(UISystem); - if (_config.EnableFootsteps) + if (_config.EnableFootsteps || _config.EnableFocusTracking) yield return typeof(IFieldLocation); } @@ -67,25 +69,33 @@ namespace Braver.Tolk { DavyKager.Tolk.Speak(dialog, false); } - public void Menu(IEnumerable items, int selected) { + private object _lastMenuContainer = null; + public void Menu(IEnumerable items, int selected, object container) { DavyKager.Tolk.Speak( $"Menu {items.ElementAtOrDefault(selected)}, {selected + 1} of {items.Count()}", - false + _lastMenuContainer == container ); + _lastMenuContainer = container; } } - public class FootstepPlugin : IFieldLocation, IDisposable { + public class FootstepFocusPlugin : IFieldLocation, IDisposable { - private IAudioItem _footsteps; + private IAudioItem _footsteps, _focusSound; private bool _playing = false; private Queue _positions = new(); private Vector3 _lastPosition; + private string _lastFocusName; - public FootstepPlugin(BGame game) { - _footsteps = game.Audio.LoadStream(typeof(TolkPlugin).FullName, "footsteps.ogg"); - _footsteps.Play(1f, 0f, true); - _footsteps.Pause(); + public FootstepFocusPlugin(BGame game, bool footsteps, bool focus) { + if (footsteps) { + _footsteps = game.Audio.LoadStream(typeof(TolkPlugin).FullName, "footsteps.ogg"); + _footsteps.Play(1f, 0f, true, 1f); + _footsteps.Pause(); + } + if (focus) { + _focusSound = game.Audio.LoadStream(typeof(TolkPlugin).FullName, "focus.ogg"); + } } public void EntityMoved(IFieldEntity entity, bool isRunning, Vector3 from, Vector3 to) { @@ -95,24 +105,47 @@ namespace Braver.Tolk { } } - public void Step() { - while (_positions.Count > 5) - _positions.Dequeue(); - _positions.Enqueue(_lastPosition); - bool shouldPlay = (_positions.First() - _positions.Last()).Length() > 13f; - if (shouldPlay != _playing) { - System.Diagnostics.Debug.WriteLine($"Tolk change!"); - if (shouldPlay) - _footsteps.Resume(); - else - _footsteps.Pause(); - _playing = shouldPlay; + private int _focusCountdown=180; + public void Step(IField field) { + + if (_footsteps != null) { + while (_positions.Count > 5) + _positions.Dequeue(); + _positions.Enqueue(_lastPosition); + bool shouldPlay = (_positions.First() - _positions.Last()).Length() > 13f; + if (shouldPlay != _playing) { + //System.Diagnostics.Debug.WriteLine($"Tolk change!"); + if (shouldPlay) + _footsteps.Resume(); + else + _footsteps.Pause(); + _playing = shouldPlay; + } + //System.Diagnostics.Debug.WriteLine($"Tolk step! {string.Join(",", _positions)}"); + } + + if ((_focusSound != null) && (--_focusCountdown == 0)) { + var focusState = field.GetFocusState(); + if (focusState != null) { + System.Diagnostics.Debug.WriteLine($"Focus at walkmesh distance {focusState.WalkmeshDistance}"); + if (field.Options.HasFlag(FieldOptions.PlayerControls)) + _focusSound.Play(1f, 0f, false, (float)Math.Pow(0.9, focusState.WalkmeshDistance)); + + if (_lastFocusName != focusState.TargetName) { + _lastFocusName = focusState.TargetName; + DavyKager.Tolk.Output("Focus " + _lastFocusName); + } + } + _focusCountdown = 180; } - System.Diagnostics.Debug.WriteLine($"Tolk step! {string.Join(",", _positions)}"); } public void Dispose() { - _footsteps.Dispose(); + _footsteps?.Dispose(); + } + + public void FocusChanged() { + _focusCountdown = 1; } } } \ No newline at end of file diff --git a/PluginImplementations/Braver.Tolk/focus.ogg b/PluginImplementations/Braver.Tolk/focus.ogg new file mode 100644 index 0000000000000000000000000000000000000000..0da91fa76b8ca06c41a0f5cbaa1e2dea762ec619 GIT binary patch literal 11991 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 JWCzU5{|C6Qaz6k7 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 61fb57e..0f4f8cb 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,19 @@ Braver is an open source reimplementation of the original FF7 game engine. More information is [available at the main website](https://braver.ficedula.co.uk/). + +# Getting Started + +After downloading the latest release, run BraverLauncher.exe; you can configure the necessary paths (e.g. where FF7 is installed) here and then click Launch Braver to run the game. + +# Plugins + +Braver comes with one plugin by default, which adds Tolk (text to speech) support to the game. Plugins are disabled by default so you will need to enable this plugin from within the configuration in BraverLauncher before it will activate. + +Once activated, the name of a screen is announced (e.g. which field location has loaded) when the game changes to a new screen; in a menu, the current menu item (and how many options there are) is announced; and when dialog is triggered, the dialog and any choices are announced. + +The Tolk plugin also has options to enable footstep sounds which wil play when the controllable character is moving, and a focus sound. When enabled, in the field screens you can cycle through focusable objects using the shoulder buttons (L1/R1 on the controller; the left/right square bracket keys on the keyboard). When a focusable object is selected, the name of the object is announced, and a tone plays every few seconds, higher pitched the closer you get to the object. + +Note that because the game wasn't designed for this, the names of the objects aren't necessarily very friendly, and it's possible to focus on an object that isn't actually reachable. Hopefully support for this will improve in future versions! + +The battle engine does not have any plugin support yet, so no Tolk output happens here yet. This will improve in a future version as well. diff --git a/data/layout/debug.xml b/data/layout/debug.xml index 4b4cd94..c4134e1 100644 --- a/data/layout/debug.xml +++ b/data/layout/debug.xml @@ -1,5 +1,5 @@ - + DEBUG OPTIONS