commit ea14b22d13d2d1eb47b70e92f1e28a3715f3428e Author: Liza Carvelli Date: Wed Sep 29 16:36:27 2021 +0200 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6f3f18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vs/ +obj/ +dist/ +*.user \ No newline at end of file diff --git a/FishNotify.cs b/FishNotify.cs new file mode 100644 index 0000000..0eb2352 --- /dev/null +++ b/FishNotify.cs @@ -0,0 +1,164 @@ +using Dalamud.Data; +using Dalamud.Game.Network; +using Dalamud.Interface.Colors; +using Dalamud.IoC; +using Dalamud.Logging; +using Dalamud.Plugin; +using ImGuiNET; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace FishNotify +{ + public sealed class FishNotifyPlugin : IDalamudPlugin + { + public string Name => "FishNotify"; + + [PluginService] + [RequiredVersion("1.0")] + private DalamudPluginInterface PluginInterface { get; set; } + + [PluginService] + private GameNetwork Network { get; set; } + private bool settingsVisible; + private int expectedOpCode = -1; + + public FishNotifyPlugin() + { + Network!.NetworkMessage += OnNetworkMessage; + PluginInterface!.UiBuilder.Draw += OnDrawUI; + PluginInterface!.UiBuilder.OpenConfigUi += OnOpenConfigUi; + + var client = new HttpClient(); + client.GetStringAsync("https://raw.githubusercontent.com/karashiiro/FFXIVOpcodes/master/opcodes.min.json") + .ContinueWith(ExtractOpCode); + } + + public void Dispose() + { + Network.NetworkMessage -= OnNetworkMessage; + PluginInterface!.UiBuilder.Draw -= OnDrawUI; + PluginInterface.UiBuilder.OpenConfigUi -= OnOpenConfigUi; + } + + private void ExtractOpCode(Task task) + { + try + { + var regions = JsonConvert.DeserializeObject>(task.Result); + if (regions == null) + { + PluginLog.Warning("No regions found in opcode list"); + return; + } + + var region = regions.Find(r => r.Region == "Global"); + if (region == null || region.Lists == null) + { + PluginLog.Warning("No global region found in opcode list"); + return; + } + + if (!region.Lists.TryGetValue("ServerZoneIpcType", out List serverZoneIpcTypes)) + { + PluginLog.Warning("No ServerZoneIpcType in opcode list"); + return; + } + + var eventPlay = serverZoneIpcTypes.Find(opcode => opcode.Name == "EventPlay"); + if (eventPlay == null) + { + PluginLog.Warning("No EventPlay opcode in ServerZoneIpcType"); + return; + } + + expectedOpCode = eventPlay.Opcode; + PluginLog.Debug($"Found EventPlay opcode {expectedOpCode:X4}"); + } + catch (Exception e) + { + PluginLog.Error(e, "Could not download/extract opcodes: {}", e.Message); + } + } + + private void OnNetworkMessage(IntPtr dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction) + { + if (direction != NetworkMessageDirection.ZoneDown || opCode != expectedOpCode) + return; + + var data = new byte[32]; + Marshal.Copy(dataPtr, data, 0, data.Length); + + int eventId = BitConverter.ToInt32(data, 8); + short scene = BitConverter.ToInt16(data, 12); + int param5 = BitConverter.ToInt32(data, 28); + + // Fishing event? + if (eventId != 0x00150001) + return; + + // Fish hooked? + if (scene != 5) + return; + + switch (param5) + { + + case 0x124: + // light tug (!) + Sounds.PlaySound(Resources.Info); + break; + + case 0x125: + // medium tug (!!) + Sounds.PlaySound(Resources.Alert); + break; + + case 0x126: + // heavy tug (!!!) + Sounds.PlaySound(Resources.Alarm); + break; + + default: + Sounds.Stop(); + break; + } + } + + private void OnDrawUI() + { + if (!settingsVisible) + return; + + if (ImGui.Begin("FishNotify", ref this.settingsVisible, ImGuiWindowFlags.AlwaysAutoResize)) + { + if (expectedOpCode > -1) + ImGui.TextColored(ImGuiColors.HealerGreen, $"Status: OK, opcode = {expectedOpCode:X}"); + else + ImGui.TextColored(ImGuiColors.DalamudRed, "Status: No opcode :("); + } + ImGui.End(); + } + private void OnOpenConfigUi() + { + settingsVisible = !settingsVisible; + } + } + + public class OpcodeRegion + { + public string Version { get; set; } + public string Region { get; set; } + public Dictionary> Lists { get; set; } + } + + public class OpcodeList + { + public string Name { get; set; } + public ushort Opcode { get; set; } + } +} diff --git a/FishNotify.csproj b/FishNotify.csproj new file mode 100644 index 0000000..582dcfd --- /dev/null +++ b/FishNotify.csproj @@ -0,0 +1,87 @@ + + + + + + 1.0.0.0 + Plays a sound effect when a fish bites + + https://github.com/carvelli/Fish-Notify + Release + + + + net5.0-windows + x64 + enable + latest + true + false + false + dist + + + + + PreserveNewest + + + + + $(appdata)\XIVLauncher\addon\Hooks\dev\ + + + + none + false + + + + + + + $(DalamudLibPath)FFXIVClientStructs.dll + false + + + $(DalamudLibPath)Newtonsoft.Json.dll + false + + + $(DalamudLibPath)Dalamud.dll + false + + + $(DalamudLibPath)ImGui.NET.dll + false + + + $(DalamudLibPath)ImGuiScene.dll + false + + + $(DalamudLibPath)Lumina.dll + false + + + $(DalamudLibPath)Lumina.Excel.dll + false + + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/FishNotify.json b/FishNotify.json new file mode 100644 index 0000000..f8cfe11 --- /dev/null +++ b/FishNotify.json @@ -0,0 +1,12 @@ +{ + "Author": "Liza Carvelli", + "Name": "Fish Notify", + "Punchline": "Plays a sound when a fish is caught", + "Description": "Plays a sound when a fish is caught, depending on tug-strength", + "InternalName": "FishNotify", + "DalamudApiLevel": 4, + "Tags": [ + "fishing" + ], + "RepoUrl": "https://github.com/carvelli/Fish-Notify" +} diff --git a/FishNotify.sln b/FishNotify.sln new file mode 100644 index 0000000..6589cba --- /dev/null +++ b/FishNotify.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29709.97 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{DFE2B530-7D7B-41FD-B03C-8E11371610E3}") = "FishNotify", "FishNotify.csproj", "{3F91A6A9-5F97-4F6D-864C-DA066F94121A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3F91A6A9-5F97-4F6D-864C-DA066F94121A}.Release|x64.ActiveCfg = Release|x64 + {3F91A6A9-5F97-4F6D-864C-DA066F94121A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8BCE7152-3829-4975-A314-A29987BE3DE9} + EndGlobalSection +EndGlobal diff --git a/Resources.Designer.cs b/Resources.Designer.cs new file mode 100644 index 0000000..64ac113 --- /dev/null +++ b/Resources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace FishNotify { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FishNotify.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.IO.UnmanagedMemoryStream ähnlich wie System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream Alarm { + get { + return ResourceManager.GetStream("Alarm", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.IO.UnmanagedMemoryStream ähnlich wie System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream Alert { + get { + return ResourceManager.GetStream("Alert", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.IO.UnmanagedMemoryStream ähnlich wie System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream Info { + get { + return ResourceManager.GetStream("Info", resourceCulture); + } + } + } +} diff --git a/Resources.resx b/Resources.resx new file mode 100644 index 0000000..ad1a9e7 --- /dev/null +++ b/Resources.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Sounds\Alarm.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Sounds\Alert.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Sounds\Info.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Sounds.cs b/Sounds.cs new file mode 100644 index 0000000..ccbc5c7 --- /dev/null +++ b/Sounds.cs @@ -0,0 +1,30 @@ +using System.IO; +using System.Media; + +namespace FishNotify +{ + public class Sounds + { + private static readonly SoundPlayer player = new SoundPlayer(); + + public static void PlaySound(Stream input) + { + lock (player) + { + Stop(); + + player.Stream = input; + player.Play(); + } + } + + public static void Stop() + { + lock (player) + { + player.Stop(); + player.Stream = null; + } + } + } +} diff --git a/Sounds/Alarm.wav b/Sounds/Alarm.wav new file mode 100644 index 0000000..b54133c Binary files /dev/null and b/Sounds/Alarm.wav differ diff --git a/Sounds/Alert.wav b/Sounds/Alert.wav new file mode 100644 index 0000000..c138bac Binary files /dev/null and b/Sounds/Alert.wav differ diff --git a/Sounds/Info.wav b/Sounds/Info.wav new file mode 100644 index 0000000..9aa845a Binary files /dev/null and b/Sounds/Info.wav differ