├── .gitignore ├── Dalamud.FullscreenCutscenes ├── icon.png ├── packages.lock.json ├── Dalamud.FullscreenCutscenes.json ├── Configuration.cs ├── Dalamud.FullscreenCutscenes.csproj └── Plugin.cs ├── Dalamud.FullscreenCutscenes.sln └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | .idea/ 3 | obj/ 4 | bin/ 5 | *.user -------------------------------------------------------------------------------- /Dalamud.FullscreenCutscenes/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goaaats/Dalamud.FullscreenCutscenes/HEAD/Dalamud.FullscreenCutscenes/icon.png -------------------------------------------------------------------------------- /Dalamud.FullscreenCutscenes/packages.lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dependencies": { 4 | "net8.0-windows7.0": { 5 | "DalamudPackager": { 6 | "type": "Direct", 7 | "requested": "[2.1.13, )", 8 | "resolved": "2.1.13", 9 | "contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ==" 10 | } 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Dalamud.FullscreenCutscenes/Dalamud.FullscreenCutscenes.json: -------------------------------------------------------------------------------- 1 | { 2 | "Author": "goat", 3 | "Name": "Ultrawide Cutscenes", 4 | "Punchline": "Remove letterboxing from cutscenes on ultrawide monitors.", 5 | "Description": "This plugin removes the \"letterboxing\" bars when watching cutscenes on ultrawide monitors.\nBeware! You may see things that you are not supposed to be seeing, such as upcoming NPCs popping in or T-posing.\n\nThanks to aers for finding this.", 6 | "InternalName": "Dalamud.FullscreenCutscenes", 7 | "RepoUrl": "https://github.com/goaaats/Dalamud.FullscreenCutscenes", 8 | "ApplicableVersion": "any", 9 | "Tags": [ 10 | "ultrawide", 11 | "cutscene", 12 | "fullscreen" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Dalamud.FullscreenCutscenes/Configuration.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Configuration; 2 | using Dalamud.Plugin; 3 | using System; 4 | 5 | namespace Dalamud.FullscreenCutscenes 6 | { 7 | [Serializable] 8 | public class Configuration : IPluginConfiguration 9 | { 10 | public int Version { get; set; } = 0; 11 | 12 | public bool IsEnabled { get; set; } = true; 13 | 14 | // the below exist just to make saving less cumbersome 15 | 16 | [NonSerialized] 17 | private IDalamudPluginInterface? pluginInterface; 18 | 19 | public void Initialize(IDalamudPluginInterface pluginInterface) 20 | { 21 | this.pluginInterface = pluginInterface; 22 | } 23 | 24 | public void Save() 25 | { 26 | this.pluginInterface!.SavePluginConfig(this); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Dalamud.FullscreenCutscenes.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29709.97 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.FullscreenCutscenes", "Dalamud.FullscreenCutscenes\Dalamud.FullscreenCutscenes.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|x64.ActiveCfg = Debug|x64 15 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|x64.Build.0 = Debug|x64 16 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.ActiveCfg = Release|x64 17 | {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.Build.0 = Release|x64 18 | {4FEC9558-EB25-419F-B86E-51B8CFDA32B7}.Debug|x64.ActiveCfg = Debug|x64 19 | {4FEC9558-EB25-419F-B86E-51B8CFDA32B7}.Debug|x64.Build.0 = Debug|x64 20 | {4FEC9558-EB25-419F-B86E-51B8CFDA32B7}.Release|x64.ActiveCfg = Release|x64 21 | {4FEC9558-EB25-419F-B86E-51B8CFDA32B7}.Release|x64.Build.0 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | GlobalSection(ExtensibilityGlobals) = postSolution 27 | SolutionGuid = {B17E85B1-5F60-4440-9F9A-3DDE877E8CDF} 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /Dalamud.FullscreenCutscenes/Dalamud.FullscreenCutscenes.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1.0.0.3 7 | A sample plugin. 8 | 9 | https://github.com/goatcorp/SamplePlugin 10 | true 11 | 12 | 13 | 14 | net8.0-windows 15 | x64 16 | enable 17 | latest 18 | true 19 | false 20 | false 21 | 22 | 23 | 24 | $(appdata)\XIVLauncher\addon\Hooks\dev\ 25 | 26 | 27 | 28 | 29 | 30 | $(DalamudLibPath)FFXIVClientStructs.dll 31 | false 32 | 33 | 34 | $(DalamudLibPath)Newtonsoft.Json.dll 35 | false 36 | 37 | 38 | $(DalamudLibPath)Dalamud.dll 39 | false 40 | 41 | 42 | $(DalamudLibPath)ImGui.NET.dll 43 | false 44 | 45 | 46 | $(DalamudLibPath)ImGuiScene.dll 47 | false 48 | 49 | 50 | $(DalamudLibPath)Lumina.dll 51 | false 52 | 53 | 54 | $(DalamudLibPath)Lumina.Excel.dll 55 | false 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SamplePlugin 2 | 3 | Simple example plugin for Dalamud. 4 | 5 | This is not designed to be the simplest possible example, but it is also not designed to cover everything you might want to do. For more detailed questions, come ask in [the Discord](https://discord.gg/3NMcUV5). 6 | 7 | ## Main Points 8 | 9 | * Simple functional plugin 10 | * Slash command 11 | * Main UI 12 | * Settings UI 13 | * Image loading 14 | * Plugin json 15 | * Simple, slightly-improved plugin configuration handling 16 | * Project organization 17 | * Copies all necessary plugin files to the output directory 18 | * Does not copy dependencies that are provided by dalamud 19 | * Output directory can be zipped directly and have exactly what is required 20 | * Hides data files from visual studio to reduce clutter 21 | * Also allows having data files in different paths than VS would usually allow if done in the IDE directly 22 | 23 | 24 | The intention is less that any of this is used directly in other projects, and more to show how similar things can be done. 25 | 26 | ## To Use 27 | ### Building 28 | 29 | 1. Open up `SamplePlugin.sln` in your C# editor of choice (likely [Visual Studio 2022](https://visualstudio.microsoft.com) or [JetBrains Rider](https://www.jetbrains.com/rider/)). 30 | 2. Build the solution. By default, this will build a `Debug` build, but you can switch to `Release` in your IDE. 31 | 3. The resulting plugin can be found at `SamplePlugin/obj/x64/Debug/SamplePlugin.dll` (or `Release` if appropriate.) 32 | 33 | ### Activating in-game 34 | 35 | 1. Launch the game and use `/xlsettings` in chat or `xlsettings` in the Dalamud Console to open up the Dalamud settings. 36 | * In here, go to `Experimental`, and add the full path to the `SamplePlugin.dll` to the list of Dev Plugin Locations. 37 | 2. Next, use `/xlplugins` (chat) or `xlplugins` (console) to open up the Plugin Installer. 38 | * In here, go to `Dev Tools > Installed Dev Plugins`, and the `SamplePlugin` should be visible. Enable it. 39 | 3. You should now be able to use `/pmycommand` (chat) or `pmycommand` (console)! 40 | 41 | Note that you only need to add it to the Dev Plugin Locations once (Step 1); it is preserved afterwards. You can disable, enable, or load your plugin on startup through the Plugin Installer. 42 | 43 | ### Reconfiguring for your own uses 44 | 45 | Basically, just replace all references to `SamplePlugin` in all of the files and filenames with your desired name. You'll figure it out 😁 46 | 47 | Dalamud will load the JSON file (by default, `Data/SamplePlugin.json`) next to your DLL and use it for metadata, including the description for your plugin in the Plugin Installer. Make sure to update this with information relevant to _your_ plugin! -------------------------------------------------------------------------------- /Dalamud.FullscreenCutscenes/Plugin.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Dalamud.Game.Command; 3 | using Dalamud.Plugin; 4 | using Dalamud.Game; 5 | using Dalamud.Game.ClientState.Conditions; 6 | using Dalamud.Hooking; 7 | using Dalamud.Plugin.Services; 8 | 9 | namespace Dalamud.FullscreenCutscenes 10 | { 11 | public sealed class Plugin : IDalamudPlugin 12 | { 13 | public string Name => "Ultrawide Cutscenes"; 14 | 15 | private const string commandName = "/pcutscenes"; 16 | 17 | private delegate nint UpdateLetterboxingDelegate(nint thisPtr); 18 | 19 | private Hook? updateLetterboxingHook; 20 | 21 | private IDalamudPluginInterface PluginInterface { get; init; } 22 | private ICommandManager CommandManager { get; init; } 23 | private Configuration Configuration { get; init; } 24 | private ICondition Condition { get; init; } 25 | public Plugin( 26 | IDalamudPluginInterface pluginInterface, 27 | ICommandManager commandManager, 28 | ISigScanner targetScanner, 29 | IGameInteropProvider gameInteropProvider, 30 | ICondition condition) 31 | { 32 | this.PluginInterface = pluginInterface; 33 | this.CommandManager = commandManager; 34 | this.Condition = condition; 35 | 36 | this.Configuration = this.PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); 37 | this.Configuration.Initialize(this.PluginInterface); 38 | 39 | // you might normally want to embed resources and load them from the manifest stream 40 | //this.PluginUi = new PluginUI(this.Configuration, goatImage); 41 | 42 | this.CommandManager.AddHandler(commandName, new CommandInfo(OnCommand) 43 | { 44 | HelpMessage = "A useful message to display in /xlhelp" 45 | }); 46 | 47 | if (targetScanner.TryScanText("E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ??", out var ptr)) 48 | { 49 | this.updateLetterboxingHook = gameInteropProvider.HookFromAddress(ptr, UpdateLetterboxingDetour); 50 | this.updateLetterboxingHook.Enable(); 51 | } 52 | 53 | //this.PluginInterface.UiBuilder.Draw += DrawUI; 54 | //this.PluginInterface.UiBuilder.OpenConfigUi += DrawConfigUI; 55 | } 56 | 57 | private unsafe nint UpdateLetterboxingDetour(nint thisptr) 58 | { 59 | bool isWatchingCutscene = Condition[ConditionFlag.OccupiedInCutSceneEvent] || 60 | Condition[ConditionFlag.WatchingCutscene78]; 61 | if (this.Configuration.IsEnabled && isWatchingCutscene) 62 | { 63 | SomeConfig* config = (SomeConfig*) thisptr; 64 | config->ShouldLetterBox &= ~(1 << 5); 65 | } 66 | 67 | return this.updateLetterboxingHook!.Original(thisptr); 68 | } 69 | 70 | public void Dispose() 71 | { 72 | this.updateLetterboxingHook?.Disable(); 73 | this.CommandManager.RemoveHandler(commandName); 74 | } 75 | 76 | private void OnCommand(string command, string args) 77 | { 78 | if (!string.IsNullOrWhiteSpace(args) && bool.TryParse(args, out var val)) 79 | { 80 | this.Configuration.IsEnabled = val; 81 | } 82 | else 83 | { 84 | this.Configuration.IsEnabled = !this.Configuration.IsEnabled; 85 | } 86 | 87 | this.Configuration.Save(); 88 | } 89 | 90 | [StructLayout(LayoutKind.Explicit)] 91 | public partial struct SomeConfig 92 | { 93 | [FieldOffset(0x40)] public int ShouldLetterBox; 94 | } 95 | } 96 | } --------------------------------------------------------------------------------