├── README.md ├── res ├── motd.txt ├── readme_images │ ├── About.png │ ├── Commands.png │ ├── Features.png │ ├── top-logo.png │ ├── Contributing.png │ ├── Installation.png │ ├── Prerequisites.png │ ├── adding_repo.png │ ├── arrowhead-up.png │ ├── Getting Started.png │ ├── experimental_tab.png │ └── Using XIVSlothCombo.png ├── plugin │ ├── XIVSlothComboX.png │ └── github-mark-white.png └── team_sloth_images │ ├── Members │ ├── Aki.png │ ├── k-kz.png │ ├── Augporto.jpg │ ├── Tartarga.png │ ├── grimgal.png │ ├── Taurenkey.png │ ├── damolitionn.png │ ├── Genesis-Nova.png │ └── ele-starshade.png │ └── team-sloth.png ├── XIVSlothComboX ├── Global.cs ├── bin │ └── x64 │ │ └── Debug │ │ ├── XIVSlothComboX.dll │ │ └── XIVSlothComboX.pdb ├── Combos │ ├── JobHelpers │ │ ├── Enums │ │ │ └── OpenerState.cs │ │ ├── DNCOpenerLogic.cs │ │ └── SCH.cs │ ├── PvE │ │ ├── DOH.cs │ │ ├── ADV.cs │ │ ├── Content │ │ │ ├── Variant.cs │ │ │ ├── Bozja.cs │ │ │ └── RaidBuff.cs │ │ └── DOL.cs │ └── PvP │ │ ├── PLDPVP.cs │ │ ├── SCHPVP.cs │ │ ├── BRDPVP.cs │ │ ├── SGEPVP.cs │ │ ├── WHMPVP.cs │ │ ├── WARPVP.cs │ │ ├── RDMPVP.cs │ │ ├── VPRPVP.cs │ │ ├── DRKPVP.cs │ │ ├── MNKPVP.cs │ │ ├── MCHPVP.cs │ │ ├── DNCPVP.cs │ │ ├── GNBPVP.cs │ │ ├── ASTPVP.cs │ │ ├── BLMPVP.cs │ │ ├── PCTPVP.cs │ │ ├── SMNPvP.cs │ │ ├── SAMPVP.cs │ │ └── DRGPVP.cs ├── Attributes │ ├── PvPCustomComboAttribute.cs │ ├── HoverInfoAttribute.cs │ ├── BozjaAttribute.cs │ ├── EurekaAttribute.cs │ ├── VariantAttribute.cs │ ├── ParentComboAttribute.cs │ ├── ConflictingCombosAttribute.cs │ ├── BozjaParentAttribute.cs │ ├── EurekaParentAttribute.cs │ ├── VariantParentAttribute.cs │ ├── ReplaceSkillAttribute.cs │ └── BlueInactiveAttribute.cs ├── stylecop.json ├── Extensions │ ├── ListExtensions.cs │ ├── BattleCharaExtensions.cs │ ├── Presets.cs │ ├── SeStringUtils.cs │ └── UIntExtensions.cs ├── Window │ ├── help │ │ ├── ImGuiIDGenerator.cs │ │ └── GroupWrapper.cs │ ├── Tabs │ │ ├── AiFaDianWindows.cs │ │ ├── AboutUs.cs │ │ └── TestFeatures.cs │ ├── Messages │ │ └── Messages.cs │ ├── Icons.cs │ ├── ImGUIHelper.cs │ └── TargetHelper.cs ├── XIVSlothComboX.json ├── 自动类 │ ├── Throttler.cs │ ├── ActionManagerEx.cs │ └── AutoItem.cs ├── Core │ ├── Countdown.cs │ ├── PluginAddressResolver.cs │ ├── HookAddress.cs │ └── Presets.cs ├── Services │ ├── BlueMageService.cs │ ├── PartyTargetingService.cs │ ├── IconManager.cs │ └── Service.cs ├── CustomComboNS │ ├── Functions │ │ ├── Movement.cs │ │ ├── Resource.cs │ │ ├── Timer.cs │ │ ├── Party.cs │ │ ├── PlayerCharacter.cs │ │ ├── Misc.cs │ │ └── Cooldown.cs │ └── CustomCombo.cs ├── Data │ ├── RepoCheck.cs │ ├── CustomTimeline.cs │ ├── CooldownData.cs │ ├── CustomComboCache.cs │ └── TmpGauge.cs └── GlobalSuppressions.cs ├── release ├── ECommons.dll ├── XIVSlothComboX.dll ├── XIVSlothComboX.pdb ├── XIVSlothComboX │ └── latest.zip ├── images │ └── XIVSlothComboX.png ├── XIVSlothComboX.json ├── XIVSlothComboX.deps.json └── pluginmaster.json ├── CN_tools ├── 技能名英汉对应表.xlsx ├── 替换表20230428.xlsx ├── 导入excel.py ├── 导出excel.py └── 替换技能名.py ├── .editorconfig ├── .gitignore ├── .gitmodules ├── stylecop.json ├── XIVSlothCombo.sln └── .github └── workflows └── build.yml /README.md: -------------------------------------------------------------------------------- 1 | API12版本 2 | -------------------------------------------------------------------------------- /res/motd.txt: -------------------------------------------------------------------------------- 1 | Welcome to XIVSlothComboX v3.0.18.1! -------------------------------------------------------------------------------- /XIVSlothComboX/Global.cs: -------------------------------------------------------------------------------- 1 | global using static XIVSlothComboX.XIVSlothComboX; -------------------------------------------------------------------------------- /release/ECommons.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/release/ECommons.dll -------------------------------------------------------------------------------- /CN_tools/技能名英汉对应表.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/CN_tools/技能名英汉对应表.xlsx -------------------------------------------------------------------------------- /CN_tools/替换表20230428.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/CN_tools/替换表20230428.xlsx -------------------------------------------------------------------------------- /release/XIVSlothComboX.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/release/XIVSlothComboX.dll -------------------------------------------------------------------------------- /release/XIVSlothComboX.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/release/XIVSlothComboX.pdb -------------------------------------------------------------------------------- /res/readme_images/About.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/About.png -------------------------------------------------------------------------------- /res/plugin/XIVSlothComboX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/plugin/XIVSlothComboX.png -------------------------------------------------------------------------------- /res/readme_images/Commands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/Commands.png -------------------------------------------------------------------------------- /res/readme_images/Features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/Features.png -------------------------------------------------------------------------------- /res/readme_images/top-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/top-logo.png -------------------------------------------------------------------------------- /res/plugin/github-mark-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/plugin/github-mark-white.png -------------------------------------------------------------------------------- /release/XIVSlothComboX/latest.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/release/XIVSlothComboX/latest.zip -------------------------------------------------------------------------------- /release/images/XIVSlothComboX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/release/images/XIVSlothComboX.png -------------------------------------------------------------------------------- /res/readme_images/Contributing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/Contributing.png -------------------------------------------------------------------------------- /res/readme_images/Installation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/Installation.png -------------------------------------------------------------------------------- /res/readme_images/Prerequisites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/Prerequisites.png -------------------------------------------------------------------------------- /res/readme_images/adding_repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/adding_repo.png -------------------------------------------------------------------------------- /res/readme_images/arrowhead-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/arrowhead-up.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS8602: Dereference of a possibly null reference. 4 | dotnet_diagnostic.CS8602.severity = none 5 | -------------------------------------------------------------------------------- /res/readme_images/Getting Started.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/Getting Started.png -------------------------------------------------------------------------------- /res/team_sloth_images/Members/Aki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/team_sloth_images/Members/Aki.png -------------------------------------------------------------------------------- /res/team_sloth_images/team-sloth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/team_sloth_images/team-sloth.png -------------------------------------------------------------------------------- /res/readme_images/experimental_tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/experimental_tab.png -------------------------------------------------------------------------------- /res/team_sloth_images/Members/k-kz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/team_sloth_images/Members/k-kz.png -------------------------------------------------------------------------------- /res/readme_images/Using XIVSlothCombo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/readme_images/Using XIVSlothCombo.png -------------------------------------------------------------------------------- /res/team_sloth_images/Members/Augporto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/team_sloth_images/Members/Augporto.jpg -------------------------------------------------------------------------------- /res/team_sloth_images/Members/Tartarga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/team_sloth_images/Members/Tartarga.png -------------------------------------------------------------------------------- /res/team_sloth_images/Members/grimgal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/team_sloth_images/Members/grimgal.png -------------------------------------------------------------------------------- /res/team_sloth_images/Members/Taurenkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/team_sloth_images/Members/Taurenkey.png -------------------------------------------------------------------------------- /res/team_sloth_images/Members/damolitionn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/team_sloth_images/Members/damolitionn.png -------------------------------------------------------------------------------- /XIVSlothComboX/bin/x64/Debug/XIVSlothComboX.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/XIVSlothComboX/bin/x64/Debug/XIVSlothComboX.dll -------------------------------------------------------------------------------- /XIVSlothComboX/bin/x64/Debug/XIVSlothComboX.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/XIVSlothComboX/bin/x64/Debug/XIVSlothComboX.pdb -------------------------------------------------------------------------------- /res/team_sloth_images/Members/Genesis-Nova.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/team_sloth_images/Members/Genesis-Nova.png -------------------------------------------------------------------------------- /res/team_sloth_images/Members/ele-starshade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/44451516/XIVSlothCombo/HEAD/res/team_sloth_images/Members/ele-starshade.png -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/JobHelpers/Enums/OpenerState.cs: -------------------------------------------------------------------------------- 1 | namespace XIVSlothComboX.Combos.JobHelpers.Enums 2 | { 3 | internal enum OpenerState 4 | { 5 | PrePull, 6 | InOpener, 7 | OpenerFinished, 8 | FailedOpener 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/PvPCustomComboAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XIVSlothComboX.Attributes 4 | { 5 | /// Attribute designating secret combos. 6 | [AttributeUsage(AttributeTargets.Field)] 7 | internal class PvPCustomComboAttribute : Attribute 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | *.v2 3 | .idea 4 | *.user 5 | XIVSlothCombo/obj/ 6 | XIVSlothComboX/obj/ 7 | .XIVSlothComboX/bin/ 8 | /XIVSlothComboX/bin/x64/ 9 | /XIVSlothComboX/bin/x64/Debug 10 | CN_tools/action.txt 11 | CN_tools/actionCn.txt 12 | CN_tools/status.txt 13 | CN_tools/statusCN.txt 14 | XIVSlothCombo.sln.DotSettings.user 15 | XIVSlothCombo.sln.DotSettings -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ECommons"] 2 | path = ECommons 3 | url = https://github.com/NightmareXIV/ECommons.git 4 | [submodule "FFXIVClientStructs"] 5 | path = FFXIVClientStructs 6 | url = https://github.com/aers/FFXIVClientStructs.git 7 | [submodule "lib/FFXIVClientStructs"] 8 | path = lib/FFXIVClientStructs 9 | url = https://github.com/aers/FFXIVClientStructs.git -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/HoverInfoAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XIVSlothComboX.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Field)] 6 | public class HoverInfoAttribute : Attribute 7 | { 8 | internal HoverInfoAttribute(string hoverText) => HoverText = hoverText; 9 | public string HoverText { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/BozjaAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace XIVSlothComboX.Attributes 8 | { 9 | /// Attribute designating Bozja combos. 10 | [AttributeUsage(AttributeTargets.Field)] 11 | internal class BozjaAttribute : Attribute 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/EurekaAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace XIVSlothComboX.Attributes 8 | { 9 | /// Attribute designating Eureka combos. 10 | [AttributeUsage(AttributeTargets.Field)] 11 | internal class EurekaAttribute : Attribute 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/VariantAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace XIVSlothComboX.Attributes 8 | { 9 | /// Attribute designating variant combos. 10 | [AttributeUsage(AttributeTargets.Field)] 11 | internal class VariantAttribute : Attribute 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CN_tools/导入excel.py: -------------------------------------------------------------------------------- 1 | import re 2 | import openpyxl 3 | 4 | # 读取txt文件 5 | with open("CustomComboPreset.txt", "r") as f: 6 | txt_content = f.read() 7 | 8 | # 使用正则表达式提取被""包裹起来的语句 9 | pattern = re.compile(r'"([^"]*)"') 10 | extracted_sentences = pattern.findall(txt_content) 11 | 12 | # 将提取出的语句存储到xlsx文件的第一列 13 | wb = openpyxl.Workbook() 14 | sheet = wb.active 15 | 16 | for i in range(len(extracted_sentences)): 17 | sheet.cell(row=i+1, column=1, value=extracted_sentences[i]) 18 | 19 | wb.save("output.xlsx") 20 | -------------------------------------------------------------------------------- /stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "orderingRules": { 5 | "systemUsingDirectivesFirst": true, 6 | "usingDirectivesPlacement": "outsideNamespace", 7 | "blankLinesBetweenUsingGroups": "require" 8 | }, 9 | "maintainabilityRules": { 10 | "topLevelTypes": [ "class", "interface", "struct", "enum" ] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvE/DOH.cs: -------------------------------------------------------------------------------- 1 | namespace XIVSlothComboX.Combos.PvE 2 | { 3 | internal static class DOH 4 | { 5 | public const byte ClassID = 0; 6 | public const byte JobID = 50; 7 | 8 | public const uint 9 | Placeholder = 0; 10 | 11 | public static class Buffs 12 | { 13 | public const ushort 14 | Placeholder = 0; 15 | } 16 | 17 | public static class Debuffs 18 | { 19 | public const ushort 20 | Placeholder = 0; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /XIVSlothComboX/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "orderingRules": { 5 | "systemUsingDirectivesFirst": true, 6 | "usingDirectivesPlacement": "outsideNamespace", 7 | "blankLinesBetweenUsingGroups": "require" 8 | }, 9 | "maintainabilityRules": { 10 | "topLevelTypes": [ "class", "interface", "struct", "enum" ] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /XIVSlothComboX/Extensions/ListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace XIVSlothComboX.Extensions 5 | { 6 | internal static class ListExtensions 7 | { 8 | private static readonly Random rng = new(); 9 | 10 | public static void Shuffle(this IList list) 11 | { 12 | int n = list.Count; 13 | while (n > 1) 14 | { 15 | n--; 16 | int k = rng.Next(n + 1); 17 | (list[n], list[k]) = (list[k], list[n]); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /XIVSlothComboX/Window/help/ImGuiIDGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace XIVSlothComboX.Window.help; 4 | 5 | public static class ImGuiIDGenerator 6 | { 7 | public static int Id; 8 | 9 | public static void Reset() 10 | { 11 | Id = 1000000; 12 | } 13 | 14 | public static string GetID() 15 | { 16 | return $"XIVSlothComboX_Number_{Id++}"; 17 | } 18 | 19 | public static string GetID(object obj,[CallerMemberName]string callerMemberName="", [CallerLineNumber]int lineNum = 0) 20 | { 21 | return $"XIVSlothComboX_{obj.GetHashCode()}_{callerMemberName}_{lineNum}"; 22 | } 23 | } -------------------------------------------------------------------------------- /XIVSlothComboX/XIVSlothComboX.json: -------------------------------------------------------------------------------- 1 | { 2 | "Author": "XIVSlothComboX", 3 | "Name": "XIVSlothComboX", 4 | "InternalName": "XIVSlothComboX", 5 | "DalamudApiLevel": 14, 6 | "Punchline": "Condenses combos and mutually exclusive abilities onto a single button - and then some.", 7 | "Description": "Condenses combos and mutually exclusive abilities onto a single button - and then some.", 8 | "RepoUrl": "https://github.com/44451516/XIVSlothCombo", 9 | "IconUrl": "https://i.imgur.com/iEcxM81.png", 10 | "ImageUrls": [ "https://i.imgur.com/iEcxM81.png" ], 11 | "Changelog": "API14", 12 | "Tags": [ 13 | "combo" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvE/ADV.cs: -------------------------------------------------------------------------------- 1 | namespace XIVSlothComboX.Combos.PvE 2 | { 3 | internal static class ADV 4 | { 5 | public const byte ClassID = 0; 6 | public const byte JobID = 0; 7 | 8 | public const uint 9 | LucidDreaming = 1204; 10 | 11 | public static class Buffs 12 | { 13 | public const ushort 14 | Placeholder = 0; 15 | } 16 | 17 | public static class Debuffs 18 | { 19 | public const ushort 20 | Placeholder = 0; 21 | } 22 | 23 | public static class Levels 24 | { 25 | public const byte 26 | Placeholder = 0; 27 | } 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /release/XIVSlothComboX.json: -------------------------------------------------------------------------------- 1 | { 2 | "Author": "XIVSlothComboX", 3 | "Name": "XIVSlothComboX", 4 | "InternalName": "XIVSlothComboX", 5 | "AssemblyVersion": "3.3.2.3", 6 | "Description": "XIVCombo for lazy players", 7 | "ApplicableVersion": "any", 8 | "RepoUrl": "https://github.com/44451516/XIVSlothCombo", 9 | "Tags": [ 10 | "combo" 11 | ], 12 | "DalamudApiLevel": 14, 13 | "LoadRequiredState": 0, 14 | "LoadSync": false, 15 | "CanUnloadAsync": false, 16 | "LoadPriority": 0, 17 | "ImageUrls": [ 18 | "https://i.imgur.com/iEcxM81.png" 19 | ], 20 | "IconUrl": "https://i.imgur.com/iEcxM81.png", 21 | "Punchline": "Condenses combos and mutually exclusive abilities onto a single button - and then some.", 22 | "Changelog": "API14", 23 | "AcceptsFeedback": true 24 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/ParentComboAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using XIVSlothComboX.Combos; 3 | 4 | namespace XIVSlothComboX.Attributes 5 | { 6 | /// Attribute documenting required combo relationships. 7 | [AttributeUsage(AttributeTargets.Field)] 8 | internal class ParentComboAttribute : Attribute 9 | { 10 | /// Initializes a new instance of the class. 11 | /// Presets that conflict with the given combo. 12 | internal ParentComboAttribute(CustomComboPreset parentPreset) => ParentPreset = parentPreset; 13 | 14 | /// Gets the display name. 15 | public CustomComboPreset ParentPreset { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /XIVSlothComboX/自动类/Throttler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XIVSlothComboX.自动类 4 | { 5 | internal static class Throttler 6 | { 7 | static long NextCommandAt = 0; 8 | internal static bool Throttle(int ms) 9 | { 10 | if(Environment.TickCount64 > NextCommandAt) 11 | { 12 | if (ms > 0) 13 | { 14 | NextCommandAt = Environment.TickCount64 + ms; 15 | } 16 | return true; 17 | } 18 | return false; 19 | } 20 | 21 | internal static void Rethrottle(int ms) 22 | { 23 | if (NextCommandAt - Environment.TickCount64 < ms) 24 | { 25 | NextCommandAt = Environment.TickCount64 + ms; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /XIVSlothComboX/Core/Countdown.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using FFXIVClientStructs.FFXIV.Client.System.Framework; 3 | using FFXIVClientStructs.FFXIV.Client.UI.Agent; 4 | 5 | namespace XIVSlothComboX.Core 6 | { 7 | [StructLayout(LayoutKind.Explicit)] 8 | public unsafe struct Countdown 9 | { 10 | [FieldOffset(0x28)] public float Timer; 11 | [FieldOffset(0x38)] public byte Active; 12 | [FieldOffset(0x3C)] public uint Initiator; 13 | 14 | public static unsafe Countdown* Instance => (Countdown*)Framework.Instance()->GetUIModule()->GetAgentModule()->GetAgentByInternalId(AgentId.CountDownSettingDialog); 15 | 16 | public static float? TimeRemaining() 17 | { 18 | var inst = Instance; 19 | return inst->Active != 0 ? inst->Timer : null; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/ConflictingCombosAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using XIVSlothComboX.Combos; 3 | 4 | namespace XIVSlothComboX.Attributes 5 | { 6 | /// Attribute documenting conflicting presets for each combo. 7 | [AttributeUsage(AttributeTargets.Field)] 8 | internal class ConflictingCombosAttribute : Attribute 9 | { 10 | /// Initializes a new instance of the class. 11 | /// Presets that conflict with the given combo. 12 | internal ConflictingCombosAttribute(params CustomComboPreset[] conflictingPresets) => ConflictingPresets = conflictingPresets; 13 | 14 | /// Gets the display name. 15 | public CustomComboPreset[] ConflictingPresets { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /XIVSlothComboX/Window/Tabs/AiFaDianWindows.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Interface.Colors; 2 | using Dalamud.Utility; 3 | using Dalamud.Bindings.ImGui; 4 | 5 | namespace XIVSlothComboX.Window.Tabs 6 | { 7 | internal class AiFaDianWindows : ConfigWindow 8 | { 9 | internal new static void Draw() 10 | { 11 | ImGui.TextColored(ImGuiColors.ParsedGreen, $"如果你认可我的工作, 可以给我买杯蜜雪冰城,每一份支持都是在传递善意\n注: 捐赠均为【无偿】性质, 我无法因为捐赠给出任何承诺或回报, 请务必三思而后行"); 12 | ImGui.TextColored(ImGuiColors.DPSRed, $"如果你认可我的工作, 可以给我买杯蜜雪冰城,每一份支持都是在传递善意\n注: 捐赠均为【无偿】性质, 我无法因为捐赠给出任何承诺或回报, 请务必三思而后行"); 13 | ImGui.TextColored(ImGuiColors.ParsedGreen, $"如果你认可我的工作, 可以给我买杯蜜雪冰城,每一份支持都是在传递善意\n注: 捐赠均为【无偿】性质, 我无法因为捐赠给出任何承诺或回报, 请务必三思而后行"); 14 | 15 | if (ImGui.Button("爱发电")) 16 | { 17 | Util.OpenLink("https://afdian.com/a/a_44451516"); 18 | } 19 | 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Core/PluginAddressResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Dalamud.Plugin.Services; 3 | using FFXIVClientStructs.FFXIV.Client.Game; 4 | using XIVSlothComboX.Services; 5 | 6 | namespace XIVSlothComboX.Core 7 | { 8 | /// Plugin address resolver. 9 | internal class PluginAddressResolver 10 | { 11 | /// Gets the address of fpIsIconReplacable. 12 | public IntPtr IsActionIdReplaceable { get; private set; } 13 | 14 | /// 15 | public unsafe void Setup(ISigScanner scanner) 16 | { 17 | 18 | IsActionIdReplaceable = scanner.ScanText(HookAddress.ActionIdReplaceable); 19 | 20 | Service.PluginLog.Verbose("===== X I V S L O T H C O M B O ====="); 21 | 22 | Service.PluginLog.Debug($"{nameof(IsActionIdReplaceable)} 0x{IsActionIdReplaceable:X}"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/BozjaParentAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using XIVSlothComboX.Combos; 7 | 8 | namespace XIVSlothComboX.Attributes 9 | { 10 | /// Attribute documenting required combo relationships. 11 | [AttributeUsage(AttributeTargets.Field)] 12 | internal class BozjaParentAttribute : Attribute 13 | { 14 | /// Initializes a new instance of the class. 15 | /// Presets that require the given combo to be enabled. 16 | internal BozjaParentAttribute(params CustomComboPreset[] parentPresets) => ParentPresets = parentPresets; 17 | 18 | /// Gets the display name. 19 | public CustomComboPreset[] ParentPresets { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/EurekaParentAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using XIVSlothComboX.Combos; 7 | 8 | namespace XIVSlothComboX.Attributes 9 | { 10 | /// Attribute documenting required combo relationships. 11 | [AttributeUsage(AttributeTargets.Field)] 12 | internal class EurekaParentAttribute : Attribute 13 | { 14 | /// Initializes a new instance of the class. 15 | /// Presets that require the given combo to be enabled. 16 | internal EurekaParentAttribute(params CustomComboPreset[] parentPresets) => ParentPresets = parentPresets; 17 | 18 | /// Gets the display name. 19 | public CustomComboPreset[] ParentPresets { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /XIVSlothComboX/Window/help/GroupWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Dalamud.Bindings.ImGui; 3 | 4 | namespace XIVSlothComboX.Window.help 5 | { 6 | public struct GroupWrapper : IDisposable 7 | { 8 | public GroupWrapper() 9 | { 10 | ImGui.PushID(ImGuiIDGenerator.GetID()); 11 | ImGui.BeginGroup(); 12 | } 13 | 14 | public void Dispose() 15 | { 16 | ImGui.EndGroup(); 17 | ImGui.PopID(); 18 | } 19 | } 20 | 21 | public struct IndentGroupWrapper : IDisposable 22 | { 23 | public IndentGroupWrapper() 24 | { 25 | ImGui.PushID(ImGuiIDGenerator.GetID()); 26 | ImGui.BeginGroup(); 27 | ImGui.Indent(); 28 | } 29 | 30 | public void Dispose() 31 | { 32 | ImGui.Unindent(); 33 | ImGui.EndGroup(); 34 | ImGui.PopID(); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/VariantParentAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using XIVSlothComboX.Combos; 7 | 8 | namespace XIVSlothComboX.Attributes 9 | { 10 | /// Attribute documenting required combo relationships. 11 | [AttributeUsage(AttributeTargets.Field)] 12 | internal class VariantParentAttribute : Attribute 13 | { 14 | /// Initializes a new instance of the class. 15 | /// Presets that require the given combo to be enabled. 16 | internal VariantParentAttribute(params CustomComboPreset[] parentPresets) => ParentPresets = parentPresets; 17 | 18 | /// Gets the display name. 19 | public CustomComboPreset[] ParentPresets { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CN_tools/导出excel.py: -------------------------------------------------------------------------------- 1 | import openpyxl 2 | 3 | # 读取 xlsx 文件 4 | workbook = openpyxl.load_workbook('替换表20230428.xlsx') 5 | worksheet = workbook.active 6 | 7 | # 构建替换字典 8 | replace_dict = {} 9 | for row in worksheet.iter_rows(values_only=True): 10 | replace_dict[row[0]] = row[1] 11 | 12 | # 读取 txt 文件 13 | with open('output1.txt', 'r',encoding='utf-8') as f: 14 | content = f.read() 15 | 16 | # 替换文本 17 | for key, value in replace_dict.items(): 18 | print(f"Replacing '{key}' with '{value}'") 19 | if key is not None and value is not None: 20 | content = content.replace('"' + key + '"', '"' + value + '"') 21 | content = content.replace(' ' + key + ' ', ' ' + value + ' ') 22 | content = content.replace('\n' + key + '\n', '\n' + value + '\n') 23 | else: 24 | print(f"Skipping {key} -> {value} because it contains None") 25 | continue 26 | 27 | # 输出替换后的 txt 文件 28 | with open('output.txt', 'w',encoding='utf-8') as f: 29 | f.write(content) 30 | -------------------------------------------------------------------------------- /XIVSlothComboX/自动类/ActionManagerEx.cs: -------------------------------------------------------------------------------- 1 | using ECommons.DalamudServices; 2 | using FFXIVClientStructs.FFXIV.Client.Game; 3 | using FFXIVClientStructs.FFXIV.Client.UI; 4 | 5 | namespace Artisan.GameInterop; 6 | 7 | // wrapper around in-game action manager that provides nicer api + use-action hooking 8 | public static unsafe class ActionManagerEx 9 | { 10 | public static float AnimationLock => ((float*)ActionManager.Instance())[2]; 11 | 12 | public static bool CanUseAction(ActionType actionType, uint actionID) => ActionManager.Instance()->GetActionStatus(actionType, actionID) == 0; 13 | 14 | 15 | 16 | 17 | 18 | public static bool UseItem(uint ItemId) => ActionManager.Instance()->UseAction(ActionType.Item, ItemId, extraParam: 65535); 19 | public static bool UseRepair() => ActionManager.Instance()->UseAction(ActionType.GeneralAction, 6); 20 | public static bool UseMateriaExtraction() => ActionManager.Instance()->UseAction(ActionType.GeneralAction, 14); 21 | } 22 | -------------------------------------------------------------------------------- /XIVSlothComboX/Services/BlueMageService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using FFXIVClientStructs.FFXIV.Client.Game; 6 | using FFXIVClientStructs.FFXIV.Client.UI; 7 | 8 | namespace XIVSlothComboX.Services 9 | { 10 | internal unsafe static class BlueMageService 11 | { 12 | public static void PopulateBLUSpells() 13 | { 14 | var prevList = Service.Configuration.ActiveBLUSpells.ToList(); 15 | Service.Configuration.ActiveBLUSpells.Clear(); 16 | 17 | for (int i = 0; i <= 24; i++) 18 | { 19 | var id = ActionManager.Instance()->GetActiveBlueMageActionInSlot(i); 20 | if (id != 0) 21 | Service.Configuration.ActiveBLUSpells.Add(id); 22 | } 23 | 24 | if (Service.Configuration.ActiveBLUSpells.Except(prevList).Any()) 25 | Service.Configuration.Save(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /XIVSlothComboX/CustomComboNS/Functions/Movement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FFXIVClientStructs.FFXIV.Client.UI.Agent; 3 | using XIVSlothComboX.Services; 4 | 5 | namespace XIVSlothComboX.CustomComboNS.Functions 6 | { 7 | internal abstract partial class CustomComboFunctions 8 | { 9 | 10 | private static DateTime? movementStarted; 11 | 12 | 13 | 14 | public static unsafe bool IsMoving() 15 | { 16 | bool isMoving = AgentMap.Instance() is not null && AgentMap.Instance()->IsPlayerMoving; 17 | 18 | if (isMoving && movementStarted is null) 19 | movementStarted = DateTime.Now; 20 | 21 | if (!isMoving) 22 | movementStarted = null; 23 | 24 | return isMoving && (TimeMoving.TotalMilliseconds / 1000f) >= Service.Configuration.MovementLeeway; 25 | } 26 | 27 | public static TimeSpan TimeMoving => movementStarted is null ? TimeSpan.Zero : (DateTime.Now - movementStarted.Value); 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Extensions/BattleCharaExtensions.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.Objects.Types; 2 | using FFXIVClientStructs.FFXIV.Client.Game.Character; 3 | 4 | namespace XIVSlothComboX.Extensions 5 | { 6 | internal static class BattleCharaExtensions 7 | { 8 | 9 | public unsafe static uint RawShieldValue(this IBattleChara chara) 10 | { 11 | FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara* baseVal = (FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara*)chara.Address; 12 | var value = baseVal->Character.CharacterData.ShieldValue; 13 | var rawValue = chara.CurrentHp / 100 * value; 14 | 15 | return rawValue; 16 | } 17 | 18 | public unsafe static byte ShieldPercentage(this IBattleChara chara) 19 | { 20 | BattleChara* baseVal = (BattleChara*)chara.Address; 21 | var value = baseVal->Character.CharacterData.ShieldValue; 22 | return value; 23 | } 24 | 25 | public static bool HasShield(this IBattleChara chara) => chara.RawShieldValue() > 0; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /release/XIVSlothComboX.deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeTarget": { 3 | "name": ".NETCoreApp,Version=v10.0", 4 | "signature": "" 5 | }, 6 | "compilationOptions": {}, 7 | "targets": { 8 | ".NETCoreApp,Version=v10.0": { 9 | "XIVSlothComboX/3.3.2.3": { 10 | "dependencies": { 11 | "ECommons": "3.1.0.1" 12 | }, 13 | "runtime": { 14 | "XIVSlothComboX.dll": {} 15 | } 16 | }, 17 | "ECommons/3.1.0.1": { 18 | "runtime": { 19 | "lib/net10.0-windows7.0/ECommons.dll": { 20 | "assemblyVersion": "3.1.0.1", 21 | "fileVersion": "3.1.0.1" 22 | } 23 | } 24 | } 25 | } 26 | }, 27 | "libraries": { 28 | "XIVSlothComboX/3.3.2.3": { 29 | "type": "project", 30 | "serviceable": false, 31 | "sha512": "" 32 | }, 33 | "ECommons/3.1.0.1": { 34 | "type": "package", 35 | "serviceable": true, 36 | "sha512": "sha512-Z91kOvDEyLk3nDxLrA+Jl6+6vH8TUoaWjp3RkXrv5dVuDrWBCQp3qnYg/qOB241ZAwLGQIuHPn8KWwi3LltOyg==", 37 | "path": "ecommons/3.1.0.1", 38 | "hashPath": "ecommons.3.1.0.1.nupkg.sha512" 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Services/PartyTargetingService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Dalamud.Game.ClientState.Objects.Types; 3 | using ECommons.DalamudServices; 4 | using FFXIVClientStructs.FFXIV.Client.Game.Object; 5 | using FFXIVClientStructs.FFXIV.Client.System.Framework; 6 | using XIVSlothComboX.Core; 7 | 8 | namespace XIVSlothComboX.Services 9 | { 10 | public static unsafe class PartyTargetingService 11 | { 12 | private static readonly IntPtr pronounModule = (IntPtr)Framework.Instance()->GetUIModule()->GetPronounModule(); 13 | public static GameObject* UITarget => (GameObject*)*(IntPtr*)(pronounModule + 0x290); 14 | 15 | public static ulong GetObjectID(GameObject* o) 16 | { 17 | var id = o->GetGameObjectId(); 18 | return id.ObjectId; 19 | } 20 | 21 | private static readonly delegate* unmanaged getGameObjectFromPronounID = (delegate* unmanaged) 22 | Svc.SigScanner.ScanText(HookAddress.GetGameObjectFromPronounID); 23 | public static GameObject* GetGameObjectFromPronounID(uint id) => getGameObjectFromPronounID(pronounModule, id); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /XIVSlothComboX/CustomComboNS/Functions/Resource.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.JobGauge.Types; 2 | using XIVSlothComboX.Data; 3 | using XIVSlothComboX.Services; 4 | 5 | namespace XIVSlothComboX.CustomComboNS.Functions 6 | { 7 | internal abstract partial class CustomComboFunctions 8 | { 9 | /// Gets the Resource Cost of the action. 10 | /// Action ID to check. 11 | /// 12 | public static int GetResourceCost(uint actionID) => CustomComboCache.GetResourceCost(actionID); 13 | 14 | /// Gets the Resource Type of the action. 15 | /// Action ID to check. 16 | /// 17 | public static bool IsResourceTypeNormal(uint actionID) => CustomComboCache.GetResourceCost(actionID) is >= 100 or 0; 18 | 19 | /// Get a job gauge. 20 | /// Type of job gauge. 21 | /// The job gauge. 22 | public static T GetJobGauge() where T : JobGaugeBase => Service.ComboCache.GetJobGauge(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/ReplaceSkillAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using XIVSlothComboX.Data; 4 | 5 | namespace XIVSlothComboX.Attributes 6 | { 7 | /// Attribute documenting which skill each preset replace. 8 | [AttributeUsage(AttributeTargets.Field)] 9 | public class ReplaceSkillAttribute : Attribute 10 | { 11 | /// List of each action the feature replaces. Initializes a new instance of the class. 12 | /// List of actions the preset replaces. 13 | internal ReplaceSkillAttribute(params uint[] actionIDs) 14 | { 15 | foreach (uint id in actionIDs) 16 | { 17 | if (ActionWatching.ActionSheet.TryGetValue(id, out var action) ) 18 | { 19 | ActionNames.Add($"{action.Name}"); 20 | ActionIcons.Add(action.Icon); 21 | } 22 | } 23 | } 24 | 25 | internal List ActionNames { get; set; } = []; 26 | 27 | internal List ActionIcons { get; set; } = []; 28 | } 29 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Window/Messages/Messages.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Interface.Colors; 2 | using Dalamud.Bindings.ImGui; 3 | using XIVSlothComboX.Services; 4 | 5 | namespace XIVSlothComboX.Window.MessagesNS 6 | { 7 | internal static class Messages 8 | { 9 | internal static bool PrintBLUMessage(string jobName) 10 | { 11 | if (jobName == Attributes.CustomComboInfoAttribute.JobIDToName(36)) //Blue Mage ID 12 | { 13 | if (Service.Configuration.ActiveBLUSpells.Count == 0) 14 | { 15 | ImGui.Text("Please open the Blue Magic Spellbook to populate your active spells and enable features."); 16 | return false; 17 | } 18 | 19 | else 20 | { 21 | ImGui.TextColored(ImGuiColors.ParsedPink, $"Please note that even if you do not have all the required spells active, you may still use these features.\nAny spells you do not have active will be skipped over so if a feature is not working as intended then\nplease try and enable more required spells."); 22 | } 23 | } 24 | 25 | return true; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /release/pluginmaster.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Author": "Team Sloth", 4 | "Name": "XIVSlothComboX", 5 | "Punchline": "Condenses combos and mutually exclusive abilities onto a single button - and then some.", 6 | "Description": "Condenses combos and mutually exclusive abilities onto a single button - and then some.", 7 | "InternalName": "XIVSlothComboX", 8 | "AssemblyVersion": "3.2.2.3", 9 | "Changelog": "更新内容看更新日志", 10 | "RepoUrl": "https://github.com/44451516/XIVSlothCombo/", 11 | "ApplicableVersion": "any", 12 | "DalamudApiLevel": 14, 13 | "LoadPriority": 0, 14 | "IconUrl": "https://raw.githubusercontent.com/44451516/XIVSlothCombo/main/res/plugin/xivslothcombo.png", 15 | "DownloadLinkInstall": "https://raw.githubusercontent.com/44451516/XIVSlothCombo/CN/release/XIVSlothComboX/latest.zip", 16 | "IsHide": false, 17 | "IsTestingExclusive": false, 18 | "DownloadLinkTesting": "https://raw.githubusercontent.com/44451516/XIVSlothCombo/CN/release/XIVSlothComboX/latest.zip", 19 | "DownloadLinkUpdate": "https://raw.githubusercontent.com/44451516/XIVSlothCombo/CN/release/XIVSlothComboX/latest.zip", 20 | "DownloadCount": 0, 21 | "LastUpdated": "1724608643" 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /XIVSlothComboX/Attributes/BlueInactiveAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using XIVSlothComboX.Services; 4 | 5 | namespace XIVSlothComboX.Attributes 6 | { 7 | /// Attribute documenting which skill the feature uses the user does not have active currently. 8 | [AttributeUsage(AttributeTargets.Field)] 9 | public class BlueInactiveAttribute : Attribute 10 | { 11 | /// List of each action the feature uses the user does not have active. Initializes a new instance of the class. 12 | /// List of actions the preset replaces. 13 | internal BlueInactiveAttribute(params uint[] actionIDs) 14 | { 15 | if (Service.Configuration is null) 16 | return; 17 | 18 | NoneSet = true; 19 | foreach (uint id in actionIDs) 20 | { 21 | if (Service.Configuration.ActiveBLUSpells.Contains(id)) 22 | { 23 | NoneSet = false; 24 | continue; 25 | } 26 | 27 | Actions.Add(id); 28 | } 29 | } 30 | 31 | internal List Actions { get; set; } = []; 32 | internal bool NoneSet { get; set; } = false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /XIVSlothComboX/Data/RepoCheck.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using XIVSlothComboX.Services; 9 | 10 | namespace XIVSlothComboX.Data 11 | { 12 | public class RepoCheck 13 | { 14 | public string? InstalledFromUrl { get; set; } 15 | } 16 | 17 | public static class RepoCheckFunctions 18 | { 19 | public static RepoCheck? FetchCurrentRepo() 20 | { 21 | FileInfo? f = Service.Interface.AssemblyLocation; 22 | var manifest = Path.Join(f.DirectoryName, "XIVSlothComboX.json"); 23 | 24 | if (File.Exists(manifest)) 25 | { 26 | RepoCheck? repo = JsonConvert.DeserializeObject(File.ReadAllText(manifest)); 27 | return repo; 28 | } 29 | else 30 | { 31 | return null; 32 | } 33 | 34 | } 35 | 36 | public static bool IsFromSlothRepo() 37 | { 38 | RepoCheck? repo = FetchCurrentRepo(); 39 | if (repo is null) return false; 40 | 41 | if (repo.InstalledFromUrl is null) return false; 42 | 43 | if (repo.InstalledFromUrl == "https://raw.githubusercontent.com/Nik-Potokar/MyDalamudPlugins/main/pluginmaster.json") 44 | return true; 45 | else 46 | return false; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvE/Content/Variant.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.Services; 2 | 3 | namespace XIVSlothComboX.Combos.PvE.Content 4 | { 5 | internal static class Variant 6 | { 7 | public const uint 8 | VariantUltimatum = 29730, 9 | VariantRaise = 29731, 10 | VariantRaise2 = 29734; 11 | //1069 = The Sil'dihn Subterrane 12 | //1137 = Mount Rokkon 13 | //1176 = Aloalo Island 14 | public static uint VariantCure => Service.ClientState.TerritoryType switch 15 | { 16 | 1069 => 29729, 17 | 1137 or 1176 => 33862, 18 | _ => 0 19 | }; 20 | 21 | public static uint VariantSpiritDart => Service.ClientState.TerritoryType switch 22 | { 23 | 1069 => 29732, 24 | 1137 or 1176 => 33863, 25 | _ => 0 26 | }; 27 | 28 | public static uint VariantRampart => Service.ClientState.TerritoryType switch 29 | { 30 | 1069 => 29733, 31 | 1137 or 1176 => 33864, 32 | _ => 0 33 | }; 34 | 35 | public static class Buffs 36 | { 37 | public const ushort 38 | EmnityUp = 3358, 39 | VulnDown = 3360, 40 | Rehabilitation = 3367, 41 | DamageBarrier = 3405; 42 | } 43 | 44 | public static class Debuffs 45 | { 46 | public const ushort 47 | SustainedDamage = 3359; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/PLDPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class PLDPvP 6 | { 7 | public const byte JobID = 19; 8 | 9 | public const uint 10 | FastBlade = 29058, 11 | RiotBlade = 29059, 12 | RoyalAuthority = 29060, 13 | ShieldBash = 29064, 14 | Confiteor = 29070; 15 | 16 | internal class Debuffs 17 | { 18 | internal const ushort 19 | Stun = 1343; 20 | } 21 | 22 | internal class PLDPvP_Burst : CustomCombo 23 | { 24 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.PLDPvP_Burst; 25 | 26 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 27 | { 28 | if (actionID is FastBlade or RiotBlade or RoyalAuthority) 29 | { 30 | if (IsEnabled(CustomComboPreset.PLDPvP_ShieldBash) && 31 | InCombat() && IsOffCooldown(ShieldBash) && CanWeave(actionID)) 32 | return ShieldBash; 33 | 34 | if (IsEnabled(CustomComboPreset.PLDPvP_Confiteor)) 35 | { 36 | if (IsOffCooldown(Confiteor)) 37 | return Confiteor; 38 | } 39 | } 40 | 41 | return actionID; 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /XIVSlothComboX/CustomComboNS/Functions/Timer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Timers; 3 | 4 | namespace XIVSlothComboX.CustomComboNS.Functions 5 | { 6 | internal abstract partial class CustomComboFunctions 7 | { 8 | internal bool restartCombatTimer = true; 9 | internal static TimeSpan combatDuration = new(); 10 | internal DateTime combatStart; 11 | internal DateTime combatEnd; 12 | internal Timer? combatTimer; 13 | 14 | /// Called by the timer in the constructor to keep track of combat duration. 15 | internal void UpdateCombatTimer() 16 | { 17 | if (InCombat()) 18 | { 19 | if (restartCombatTimer) 20 | { 21 | restartCombatTimer = false; 22 | combatStart = DateTime.Now; 23 | } 24 | 25 | combatEnd = DateTime.Now; 26 | combatDuration = combatEnd - combatStart; 27 | } 28 | else 29 | { 30 | restartCombatTimer = true; 31 | combatDuration = TimeSpan.Zero; 32 | } 33 | 34 | 35 | } 36 | 37 | /// Tells the elapsed time since the combat started. 38 | /// Combat time in seconds. 39 | public static TimeSpan CombatEngageDuration() => combatDuration; 40 | 41 | protected void StartTimer() 42 | { 43 | // combatTimer = new Timer(100); // in milliseconds 44 | // combatTimer.Elapsed += UpdateCombatTimer; 45 | // combatTimer.Start(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /XIVSlothComboX/CustomComboNS/Functions/Party.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Dalamud.Game.ClientState.Objects.Types; 3 | using Dalamud.Game.ClientState.Party; 4 | using Dalamud.Plugin.Services; 5 | using ECommons.DalamudServices; 6 | using XIVSlothComboX.Services; 7 | 8 | namespace XIVSlothComboX.CustomComboNS.Functions 9 | { 10 | internal abstract partial class CustomComboFunctions 11 | { 12 | /// Checks if player is in a party 13 | public static bool IsInParty() => (Svc.Party.PartyId > 0); 14 | 15 | /// Gets the party list 16 | /// Current party list. 17 | public static IPartyList GetPartyMembers() => Svc.Party; 18 | 19 | public unsafe static IGameObject? GetPartySlot(int slot) 20 | { 21 | try 22 | { 23 | var o = slot switch 24 | { 25 | 1 => GetTarget(TargetType.Self), 26 | 2 => GetTarget(TargetType.P2), 27 | 3 => GetTarget(TargetType.P3), 28 | 4 => GetTarget(TargetType.P4), 29 | 5 => GetTarget(TargetType.P5), 30 | 6 => GetTarget(TargetType.P6), 31 | 7 => GetTarget(TargetType.P7), 32 | 8 => GetTarget(TargetType.P8), 33 | _ => GetTarget(TargetType.Self), 34 | }; 35 | ulong i = PartyTargetingService.GetObjectID(o); 36 | return Svc.Objects.Where(x => x.GameObjectId == i).Any() 37 | ? Svc.Objects.Where(x => x.GameObjectId == i).First() 38 | : null; 39 | } 40 | 41 | catch 42 | { 43 | return null; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /XIVSlothComboX/Data/CustomTimeline.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Numerics; 4 | 5 | namespace XIVSlothComboX.Data; 6 | 7 | public class CustomTimeline 8 | { 9 | public int Index; 10 | 11 | public bool Enable; 12 | 13 | public uint JobId; 14 | 15 | public string Name; 16 | 17 | public List ActionList=new List(); 18 | 19 | public override bool Equals(object? obj) 20 | { 21 | if (ReferenceEquals(obj, this)) return true; 22 | if (ReferenceEquals(obj, null)) return false; 23 | if (obj is CustomTimeline tCustomTimeline) 24 | { 25 | return JobId == tCustomTimeline.JobId && Name == tCustomTimeline.Name; 26 | } 27 | 28 | return false; 29 | } 30 | 31 | public override int GetHashCode() 32 | { 33 | return HashCode.Combine(JobId, Name); 34 | } 35 | } 36 | 37 | public class CustomAction 38 | { 39 | public uint ActionId; 40 | 41 | public double UseTimeStart; 42 | public double UseTimeEnd; 43 | 44 | /// 45 | /// 1自己 2-8为小队其他队友 46 | /// todo 9为血量百分比最低 47 | /// 48 | public int TargetType; 49 | 50 | public byte CustomActionType = Data.CustomType.序列; 51 | public byte CustomActionTypeSub = Data.CustomTypeSub.默认; 52 | 53 | public Vector3 Vector3 = new(); 54 | } 55 | 56 | internal static class CustomType 57 | { 58 | internal const byte 59 | 序列 = 1, 60 | 时间 = 2, 61 | 药品 = 3, 62 | 地面 = 4, 63 | 强制插入 = 5; 64 | } 65 | 66 | 67 | internal static class CustomTypeSub 68 | { 69 | internal const byte 70 | 长期替换 = 1, 71 | 能力技窗口 = 2, 72 | 能力技窗口后半 = 3, 73 | //自动 强制 不管是不是GCD 74 | 强制 = 4, 75 | //0自动,默认再能力技窗口 76 | 默认 = 0; 77 | } 78 | -------------------------------------------------------------------------------- /CN_tools/替换技能名.py: -------------------------------------------------------------------------------- 1 | 2 | # import openpyxl 3 | # import re 4 | 5 | # # 读取 xlsx 文件 6 | # workbook = openpyxl.load_workbook('技能名data1.xlsx') 7 | # worksheet = workbook.active 8 | 9 | # # 构建替换字典 10 | # replace_dict = {} 11 | # for row in worksheet.iter_rows(values_only=True): 12 | # replace_dict[row[0]] = row[1] 13 | 14 | # # 读取 txt 文件 15 | # with open('CustomComboPreset.txt', 'r',encoding="utf8") as f: 16 | # content = f.read() 17 | 18 | # # 替换文本 19 | # for key, value in replace_dict.items(): 20 | # print(f"Replacing '{key}' with '{value}'") 21 | # if key is not None and value is not None: 22 | # # 使用正则表达式匹配被""包裹的语句 23 | # pattern = r'"([^"]*?(' + key + r')[^"]*)"' # 在匹配表达式中使用非贪婪模式匹配最小的语句 24 | # content = re.sub(pattern, lambda match: match.group(0).replace(match.group(2), value), content) 25 | # else: 26 | # print(f"Skipping {key} -> {value} because it contains None") 27 | # continue 28 | 29 | # # 输出替换后的 txt 文件 30 | # with open('output.txt', 'w',encoding="utf8") as f: 31 | # f.write(content) 32 | import openpyxl 33 | import re 34 | 35 | # 读取xlsx表格 36 | wb = openpyxl.load_workbook('技能名data1.xlsx') 37 | ws = wb.active 38 | 39 | # 将第一列作为替换词,第二列作为被替换词 40 | replace_dict = {} 41 | for row in ws.iter_rows(min_row=2, values_only=True): 42 | key = row[0] 43 | value = row[1] 44 | if key is not None and value is not None: 45 | replace_dict[key] = value 46 | 47 | # 读取txt文件 48 | with open('CustomComboPreset.txt', 'r', encoding='utf-8') as f: 49 | content = f.read() 50 | 51 | # 匹配被""包裹的子语句并替换 52 | pattern = r'"(?:\\.|[^"\\])*"' # 正则表达式匹配被""包裹的子语句 53 | matches = re.findall(pattern, content) 54 | for match in matches: 55 | print("1") 56 | for key, value in replace_dict.items(): 57 | if match.strip('"') == key: 58 | content = content.replace(match, '"' + value + '"') 59 | 60 | # 输出替换后的txt文件 61 | with open('modified.txt', 'w', encoding='utf-8') as f: 62 | f.write(content) 63 | -------------------------------------------------------------------------------- /XIVSlothCombo.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32228.343 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XIVSlothComboX", "XIVSlothComboX\XIVSlothComboX.csproj", "{619DF476-7225-4783-95A5-D29A8816EE66}" 7 | EndProject 8 | 9 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Github Actions", "Github Actions", "{D3F73229-7C85-4017-B8C9-B7668022A4F0}" 10 | ProjectSection(SolutionItems) = preProject 11 | .github\workflows\build.yml = .github\workflows\build.yml 12 | EndProjectSection 13 | EndProject 14 | 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ECommons", "ECommons\ECommons\ECommons.csproj", "{3E8E720E-9990-4202-9035-12F54C78CF47}" 16 | EndProject 17 | 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|x64 = Debug|x64 21 | #GitBuild|x64 = GitBuild|x64 22 | Release|x64 = Release|x64 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {619DF476-7225-4783-95A5-D29A8816EE66}.Debug|x64.ActiveCfg = Debug|x64 26 | {619DF476-7225-4783-95A5-D29A8816EE66}.Debug|x64.Build.0 = Debug|x64 27 | {619DF476-7225-4783-95A5-D29A8816EE66}.Release|x64.ActiveCfg = Release|x64 28 | {619DF476-7225-4783-95A5-D29A8816EE66}.Release|x64.Build.0 = Release|x64 29 | {3E8E720E-9990-4202-9035-12F54C78CF47}.Debug|x64.ActiveCfg = Debug|x64 30 | {3E8E720E-9990-4202-9035-12F54C78CF47}.Debug|x64.Build.0 = Debug|x64 31 | {3E8E720E-9990-4202-9035-12F54C78CF47}.Release|x64.ActiveCfg = Release|x64 32 | {3E8E720E-9990-4202-9035-12F54C78CF47}.Release|x64.Build.0 = Release|x64 33 | EndGlobalSection 34 | GlobalSection(SolutionProperties) = preSolution 35 | HideSolutionNode = FALSE 36 | EndGlobalSection 37 | GlobalSection(ExtensibilityGlobals) = postSolution 38 | SolutionGuid = {37674B88-2038-4C63-A979-84404391773A} 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvE/Content/Bozja.cs: -------------------------------------------------------------------------------- 1 | namespace XIVSlothComboX.Combos.PvE.Content 2 | { 3 | internal static class Bozja 4 | { 5 | public const uint 6 | LostBanish3 = 20702, 7 | FontOfMagic = 20715, 8 | FontOfPower = 20717, 9 | BannerOfNobleEnds = 20720, 10 | BannerOfHonoredSacrifice = 20721, 11 | LostCure = 20726, 12 | LostCure2 = 20727, 13 | LostCure3 = 20728, 14 | LostCure4 = 20729, 15 | LostIncense = 20731, 16 | LostFlareStar = 22352, 17 | LostRendArmor = 22353, 18 | LostSeraphStrike = 22354, 19 | LostAethershield = 22355, 20 | LostDervish = 22356, 21 | LostBurst = 23909, 22 | LostRampage = 23910, 23 | LostChainspell = 23913, 24 | LostAssassination = 23914, 25 | LostExcellence = 23919, 26 | LostBloodRage = 23921; 27 | 28 | public static class Buffs 29 | { 30 | public const ushort 31 | BeastEssence = 2324, 32 | BannerOfNobleEnds = 2326, 33 | BannerOfHonoredSacrifice = 2327, 34 | FontOfMagic = 2332, 35 | LostBravery2 = 2341, 36 | FontOfPower = 2346, 37 | PureElder = 2435, 38 | PureFiendhunter = 2437, 39 | PureIndomitable = 2438, 40 | PureDivine = 2439, 41 | LostAethershield = 2443, 42 | LostDervish = 2444, 43 | ClericStance = 2484, 44 | LostChainspell = 2560, 45 | LostExcellence = 2564, 46 | LostBloodRage = 2566; 47 | } 48 | 49 | public static class Debuffs 50 | { 51 | public const ushort 52 | LostFlareStar = 2440, 53 | LostRendArmor = 2441; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /XIVSlothComboX/Core/HookAddress.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.Game; 2 | 3 | namespace XIVSlothComboX.Core; 4 | 5 | internal class HookAddress 6 | { 7 | // public const string ActionIdReplaceable = "E8 ?? ?? ?? ?? 84 C0 74 4C 8B D3"; 8 | //7.01-7.05 9 | //public const string ActionIdReplaceable = "40 53 48 83 EC 20 8B D9 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0 74 1B"; 10 | //7.2 11 | // 12 | public const string ActionIdReplaceable = "40 53 48 83 EC 20 8B D9 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0 74 1F"; 13 | // public const string ActionIdReplaceable = "E8 ?? ?? ?? ?? 84 C0 74 67 8B D3"; 14 | 15 | // public const string ReceiveActionEffect = "E8 ?? ?? ?? ?? 48 8B 8D F0 03 00 00"; 16 | public static nint ReceiveActionEffect = FFXIVClientStructs.FFXIV.Client.Game.Character.ActionEffectHandler.Addresses.Receive.Value; 17 | // public const string ReceiveActionEffect = "40 55 56 57 41 54 41 55 41 56 48 8D AC 24"; 18 | 19 | 20 | 21 | // public const string SendAction = "E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? F3 0F 10 3D ?? ?? ?? ?? 48 8D 4D BF"; 22 | //ECommons/Hooks/SendAction.cs 23 | public const string SendAction = "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B E9 41 0F B7 D9"; 24 | 25 | // public const string GetGameObjectFromObjectID = "E8 ?? ?? ?? ?? 44 0F B6 C3 48 8B D0"; 26 | public const string GetGameObjectFromPronounID = "E8 ?? ?? ?? ?? 48 8B D8 48 85 C0 0F 85 ?? ?? ?? ?? 8D 4F DD"; 27 | 28 | // public const string UseActionLocation = "E8 ?? ?? ?? ?? 41 3A C5 0F 85 ?? ?? ?? ??"; 29 | // public const string UseActionLocation = "E8 ?? ?? ?? ?? 40 3A C7 0F 85 ?? ?? ?? ??"; 30 | public static nint UseActionLocation = FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Addresses.UseActionLocation.Value; 31 | 32 | public const string GetAdjustedActionId = "E8 ?? ?? ?? ?? 89 03 8B 03"; 33 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvE/Content/RaidBuff.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS.Functions; 2 | 3 | namespace XIVSlothComboX.Combos.PvE; 4 | 5 | public class RaidBuff 6 | { 7 | internal const ushort 8 | 9 | // Heals 10 | // Heals 11 | 强化药 = 49, 12 | 灼热之光 = 2703, 13 | 连环计 = 1221, 14 | 星空 = 3685, 15 | 占卜 = 1878, 16 | 义结金兰 = 1185, 17 | 战斗连祷 = 786, 18 | 19 | //忍者夺取 20 | 攻其不备 = 3254, 21 | 罐毒之术 = 3849, 22 | 受伤加重 = 638, 23 | 技巧舞步结束TechnicalFinish = 1822, 24 | 战斗之声 = 141, 25 | 留空 = 0; 26 | 27 | 28 | public static bool 爆发期() 29 | { 30 | if (CustomComboFunctions.HasEffect(强化药) && CustomComboFunctions.GetBuffRemainingTime(强化药) < 25f) 31 | { 32 | return true; 33 | } 34 | 35 | if (CustomComboFunctions.HasEffectAny(灼热之光)) 36 | { 37 | return true; 38 | } 39 | 40 | if (CustomComboFunctions.HasEffectAny(技巧舞步结束TechnicalFinish)) 41 | { 42 | return true; 43 | } 44 | 45 | if (CustomComboFunctions.HasEffectAny(星空)) 46 | return true; 47 | 48 | if (CustomComboFunctions.HasEffectAny(占卜)) 49 | return true; 50 | 51 | if (CustomComboFunctions.HasEffectAny(义结金兰)) 52 | return true; 53 | 54 | if (CustomComboFunctions.HasEffectAny(战斗连祷)) 55 | return true; 56 | 57 | if (CustomComboFunctions.HasEffectAny(战斗之声)) 58 | return true; 59 | 60 | 61 | if (CustomComboFunctions.FindTargetEffectAny(攻其不备) is not null) 62 | { 63 | return true; 64 | } 65 | 66 | if (CustomComboFunctions.FindTargetEffectAny(连环计) is not null) 67 | { 68 | return true; 69 | } 70 | 71 | if (CustomComboFunctions.FindTargetEffectAny(罐毒之术) is not null) 72 | { 73 | return true; 74 | } 75 | 76 | return false; 77 | } 78 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/SCHPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class SCHPvP 6 | { 7 | public const byte JobID = 28; 8 | 9 | public const uint 10 | Broil = 29231, 11 | Aqloquilum = 29232, 12 | Biolysis = 29233, 13 | DeploymentTactics = 29234, 14 | Expedient = 29236; 15 | 16 | internal class Buffs 17 | { 18 | internal const ushort 19 | Recitation = 3094; 20 | } 21 | internal class Debuffs 22 | { 23 | internal const ushort 24 | Biolysis = 3089, 25 | Biolytic = 3090; 26 | } 27 | 28 | internal class SCHPvP_Burst : CustomCombo 29 | { 30 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.SCHPvP_Burst; 31 | 32 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 33 | { 34 | if (actionID is Broil && InCombat()) 35 | { 36 | // Uses Expedient when available and target isn't affected with Biolysis 37 | if (IsEnabled(CustomComboPreset.SCHPvP_Expedient) && IsOffCooldown(Expedient) && !TargetHasEffect(Debuffs.Biolysis)) 38 | return Expedient; 39 | 40 | // Uses Biolysis under Recitation, or on cooldown when option active 41 | if ((IsEnabled(CustomComboPreset.SCHPvP_Biolysis) && IsOffCooldown(Biolysis)) || (HasEffect(Buffs.Recitation) && IsOffCooldown(Biolysis))) 42 | return Biolysis; 43 | 44 | // Uses Deployment Tactics when available 45 | if (IsEnabled(CustomComboPreset.SCHPvP_DeploymentTactics) && GetRemainingCharges(DeploymentTactics) > 1) 46 | return DeploymentTactics; 47 | } 48 | 49 | return actionID; 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Data/CooldownData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FFXIVClientStructs.FFXIV.Client.Game; 3 | 4 | namespace XIVSlothComboX.Data 5 | { 6 | internal class CooldownData 7 | { 8 | /// Gets a value indicating whether the action is on cooldown. 9 | public bool IsCooldown 10 | { 11 | get { return CooldownRemaining > 0; } 12 | } 13 | 14 | /// Gets the action ID on cooldown. 15 | public uint ActionID; 16 | 17 | /// Gets the elapsed cooldown time. 18 | public unsafe float CooldownElapsed => ActionManager.Instance()->GetRecastTimeElapsed(ActionType.Action, ActionID); 19 | 20 | /// Gets the total cooldown time. 21 | public unsafe float CooldownTotal => (ActionManager.GetAdjustedRecastTime(ActionType.Action, ActionID) / 1000f) * MaxCharges; 22 | 23 | /// Gets the cooldown time remaining. 24 | public unsafe float CooldownRemaining => CooldownElapsed == 0 ? 0 : Math.Max(0, CooldownTotal - CooldownElapsed); 25 | 26 | /// Gets the maximum number of charges for an action at the current level. 27 | /// Number of charges. 28 | public ushort MaxCharges => ActionManager.GetMaxCharges(ActionID, 0); 29 | 30 | /// Gets a value indicating whether the action has charges, not charges available. 31 | public bool HasCharges => MaxCharges > 1; 32 | 33 | 34 | public float 单次计时器 => CooldownTotal / MaxCharges; 35 | 36 | /// Gets the remaining number of charges for an action. 37 | public unsafe uint RemainingCharges 38 | { 39 | get 40 | { 41 | if (MaxCharges == 1) 42 | return CooldownRemaining == 0 ? 1 : 0u; 43 | 44 | return ActionManager.Instance()->GetCurrentCharges(ActionID); 45 | } 46 | } 47 | 48 | /// Gets the cooldown time remaining until the next charge. 49 | public float ChargeCooldownRemaining 50 | { 51 | get { return CooldownRemaining % (CooldownTotal / MaxCharges); } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/BRDPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class BRDPvP 6 | { 7 | public const byte ClassID = 5; 8 | public const byte JobID = 23; 9 | 10 | public const uint 11 | PowerfulShot = 29391, 12 | ApexArrow = 29393, 13 | SilentNocturne = 29395, 14 | EmpyrealArrow = 29398, 15 | RepellingShot = 29399, 16 | WardensPaean = 29400, 17 | PitchPerfect = 29392, 18 | BlastArrow = 29394; 19 | 20 | public static class Buffs 21 | { 22 | public const ushort 23 | FrontlinersMarch = 3138, 24 | FrontlinersForte = 3140, 25 | Repertoire = 3137, 26 | BlastArrowReady = 3142; 27 | } 28 | 29 | internal class BRDPvP_BurstMode : CustomCombo 30 | { 31 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.BRDPvP_BurstMode; 32 | 33 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 34 | { 35 | 36 | if (actionID == PowerfulShot) 37 | { 38 | var canWeave = CanWeave(actionID, 0.5); 39 | 40 | if (canWeave) 41 | { 42 | if (GetCooldown(EmpyrealArrow).RemainingCharges == 3) 43 | return OriginalHook(EmpyrealArrow); 44 | 45 | if (!GetCooldown(SilentNocturne).IsCooldown) 46 | return OriginalHook(SilentNocturne); 47 | } 48 | 49 | if (HasEffect(Buffs.BlastArrowReady)) 50 | return OriginalHook(BlastArrow); 51 | 52 | if (HasEffect(Buffs.Repertoire)) 53 | return OriginalHook(PowerfulShot); 54 | 55 | if (!GetCooldown(ApexArrow).IsCooldown) 56 | return OriginalHook(ApexArrow); 57 | 58 | return OriginalHook(PowerfulShot); 59 | } 60 | 61 | return actionID; 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Extensions/Presets.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Utility; 2 | using System.Collections.Generic; 3 | using XIVSlothComboX.Attributes; 4 | using XIVSlothComboX.Combos; 5 | 6 | namespace XIVSlothComboX.Extensions 7 | { 8 | internal static class PresetExtensions 9 | { 10 | internal static Dictionary replaceSkillCache = new(); 11 | internal static Dictionary comboInfoCache = new(); 12 | internal static Dictionary hoverInfoCache = new(); 13 | 14 | /// Retrieves the for the preset if it exists. 15 | internal static ReplaceSkillAttribute? GetReplaceAttribute(this CustomComboPreset preset) 16 | { 17 | if (replaceSkillCache.TryGetValue(preset, out var replaceSkillAttribute)) 18 | { 19 | return replaceSkillAttribute; 20 | } 21 | 22 | ReplaceSkillAttribute? att = preset.GetAttribute(); 23 | return att != null && replaceSkillCache.TryAdd(preset, att) ? replaceSkillCache[preset] : null; 24 | } 25 | 26 | /// Retrieves the for the preset if it exists. 27 | internal static CustomComboInfoAttribute? GetComboAttribute(this CustomComboPreset preset) 28 | { 29 | if (comboInfoCache.TryGetValue(preset, out var customComboInfoAttribute)) 30 | { 31 | return customComboInfoAttribute; 32 | } 33 | 34 | CustomComboInfoAttribute? att = preset.GetAttribute(); 35 | return att != null && comboInfoCache.TryAdd(preset, att) ? comboInfoCache[preset] : null; 36 | 37 | } 38 | 39 | /// Retrieves the for the preset if it exists. 40 | internal static HoverInfoAttribute? GetHoverAttribute(this CustomComboPreset preset) 41 | { 42 | if (hoverInfoCache.TryGetValue(preset, out var hoverInfoAttribute)) 43 | { 44 | return hoverInfoAttribute; 45 | } 46 | 47 | HoverInfoAttribute? att = preset.GetAttribute(); 48 | return att != null && hoverInfoCache.TryAdd(preset, att) ? hoverInfoCache[preset] : null; 49 | 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/SGEPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class SGEPvP 6 | { 7 | internal const uint 8 | Dosis = 29256, 9 | Phlegma = 29259, 10 | Pneuma = 29260, 11 | Eukrasia = 29258, 12 | Icarus = 29261, 13 | Toxikon = 29262, 14 | Kardia = 29264, 15 | EukrasianDosis = 29257, 16 | Toxicon2 = 29263; 17 | 18 | internal class Debuffs 19 | { 20 | internal const ushort 21 | EukrasianDosis = 3108, 22 | Toxicon = 3113; 23 | } 24 | 25 | internal class Buffs 26 | { 27 | internal const ushort 28 | Kardia = 2871, 29 | Kardion = 2872, 30 | Eukrasia = 3107, 31 | Addersting = 3115, 32 | Haima = 3110, 33 | Haimatinon = 3111; 34 | } 35 | 36 | internal class SGEPvP_BurstMode : CustomCombo 37 | { 38 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.SGEPvP_BurstMode; 39 | 40 | protected override uint Invoke(uint actionID, uint lastComboActionID, float comboTime, byte level) 41 | { 42 | if (actionID == Dosis) 43 | { 44 | 45 | if (!HasEffectAny(Buffs.Kardia)) 46 | return Kardia; 47 | 48 | if (IsEnabled(CustomComboPreset.SGEPvP_BurstMode_Pneuma) && !GetCooldown(Pneuma).IsCooldown) 49 | return Pneuma; 50 | 51 | if (InMeleeRange() && !HasEffect(Buffs.Eukrasia) && GetCooldown(Phlegma).RemainingCharges > 0) 52 | return Phlegma; 53 | 54 | if (HasEffect(Buffs.Addersting) && !HasEffect(Buffs.Eukrasia)) 55 | return Toxicon2; 56 | 57 | if (!TargetHasEffectAny(Debuffs.EukrasianDosis) && GetCooldown(Eukrasia).RemainingCharges > 0 && !HasEffect(Buffs.Eukrasia)) 58 | return Eukrasia; 59 | 60 | if (HasEffect(Buffs.Eukrasia)) 61 | return OriginalHook(Dosis); 62 | 63 | if (!TargetHasEffect(Debuffs.Toxicon) && GetCooldown(Toxikon).RemainingCharges > 0) 64 | return OriginalHook(Toxikon); 65 | 66 | } 67 | return actionID; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | # Put your personal access token in a repository secret named PAT for cross-repository access 4 | 5 | on: 6 | workflow_dispatch: 7 | push: 8 | branches: 9 | - API11 10 | 11 | env: 12 | # PUBLIC_NAME: XIVSlothComboX 13 | SOLUTION_NAME: XIVSlothCombo 14 | INTERNAL_NAME: XIVSlothComboX 15 | RELEASE_DIR: release\XIVSlothComboX 16 | PERSONAL_PLUGIN_REPO: 44451516/MyDalamudPlugins 17 | PR_PLUGIN_REPO: 44451516/MyDalamudPlugins 18 | 19 | jobs: 20 | build: 21 | runs-on: windows-latest 22 | outputs: 23 | timestamp: ${{ steps.timestamp.outputs.timestamp }} 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | with: 28 | submodules: recursive 29 | - name: Setup MSBuild 30 | uses: microsoft/setup-msbuild@v2 31 | - name: Download Dalamud 32 | run: | 33 | Invoke-WebRequest -Uri https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -OutFile latest.zip 34 | Expand-Archive -Force latest.zip "$env:AppData\XIVLauncher\addon\Hooks\dev\" 35 | - name: Build 36 | run: | 37 | dotnet restore -r win ${{ env.SOLUTION_NAME }}.sln 38 | dotnet build --configuration Release 39 | env: 40 | DOTNET_CLI_TELEMETRY_OPTOUT: true 41 | - name: Set Timestamp 42 | id: timestamp 43 | run: | 44 | $timestamp = Get-Date -Format "yyyy-MM-dd-HH-mm-ss" 45 | echo "timestamp=$timestamp" >> $env:GITHUB_OUTPUT 46 | - name: Create latest.zip 47 | run: | 48 | Compress-Archive -Path "${{ env.RELEASE_DIR }}\*" -DestinationPath "${{ env.RELEASE_DIR }}\latest.zip" -Force 49 | - uses: actions/upload-artifact@v4 50 | with: 51 | name: "XIVSlothComboX-7.1-${{ steps.timestamp.outputs.timestamp }}" 52 | path: ${{ env.RELEASE_DIR }} 53 | if-no-files-found: error 54 | 55 | deploy: 56 | needs: build 57 | if: "contains(toJSON(github.event.commits.*.message), '[PUSH]')" 58 | runs-on: ubuntu-latest 59 | steps: 60 | - uses: actions/checkout@v4 61 | with: 62 | repository: ${{ env.PERSONAL_PLUGIN_REPO }} 63 | token: ${{ secrets.XIVCOMBO_TOKEN }} 64 | - uses: actions/download-artifact@v4 65 | with: 66 | name: "XIVSlothComboX-7.1-${{ needs.build.outputs.timestamp }}" 67 | path: plugins/${{ env.INTERNAL_NAME }} 68 | - uses: EndBug/add-and-commit@v7 69 | with: 70 | add: --all 71 | author_name: GitHub Action 72 | author_email: github-actions[bot]@users.noreply.github.com 73 | message: Update ${{ env.INTERNAL_NAME }} 74 | -------------------------------------------------------------------------------- /XIVSlothComboX/CustomComboNS/Functions/PlayerCharacter.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.Conditions; 2 | using Dalamud.Game.ClientState.Objects.SubKinds; 3 | using ECommons.DalamudServices; 4 | using FFXIVClientStructs.FFXIV.Client.Game.UI; 5 | using Lumina.Excel.Sheets; 6 | using XIVSlothComboX.Services; 7 | using GameMain = FFXIVClientStructs.FFXIV.Client.Game.GameMain; 8 | 9 | namespace XIVSlothComboX.CustomComboNS.Functions 10 | { 11 | internal abstract partial class CustomComboFunctions 12 | { 13 | /// Gets the player or null. 14 | public static IPlayerCharacter? LocalPlayer => Service.ObjectTable.LocalPlayer; 15 | 16 | /// Find if the player has a certain condition. 17 | /// Condition flag. 18 | /// A value indicating whether the player is in the condition. 19 | public static bool HasCondition(ConditionFlag flag) => Service.Condition[flag]; 20 | 21 | /// Find if the player is in combat. 22 | /// A value indicating whether the player is in combat. 23 | public static bool InCombat() => Service.Condition[ConditionFlag.InCombat]; 24 | 25 | /// Find if the player has a pet present. 26 | /// A value indicating whether the player has a pet (fairy/carbuncle) present. 27 | public static bool HasPetPresent() => Service.BuddyList.PetBuddy!=null; 28 | 29 | /// Find if the player has a companion (chocobo) present. 30 | /// A value indicating whether the player has a companion (chocobo). 31 | public static bool HasCompanionPresent() => Service.BuddyList.CompanionBuddy !=null ; 32 | 33 | /// Checks if the player is in a PVP enabled zone. 34 | /// A value indicating whether the player is in a PVP enabled zone. 35 | public static bool InPvP() => GameMain.IsInPvPArea() || GameMain.IsInPvPInstance(); 36 | 37 | 38 | /// Checks if the player has completed the required job quest for the ability. 39 | /// A value indicating a quest has been completed for a job action. 40 | public static unsafe bool IsActionUnlocked(uint id) 41 | { 42 | var unlockLink = Svc.Data.GetExcelSheet().GetRow(id).UnlockLink.RowId; 43 | if (unlockLink == 0) return true; 44 | return UIState.Instance()->IsUnlockLinkUnlockedOrQuestCompleted(unlockLink); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /XIVSlothComboX/Window/Icons.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Interface.Internal; 2 | using ECommons.DalamudServices; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Dalamud.Interface.Textures; 9 | using Dalamud.Interface.Textures.TextureWraps; 10 | using Dalamud.Utility; 11 | using Lumina.Data.Files; 12 | using XIVSlothComboX.Services; 13 | 14 | namespace XIVSlothComboX.Window 15 | { 16 | internal static class Icons 17 | { 18 | public static Dictionary CachedModdedIcons = new(); 19 | 20 | public static IDalamudTextureWrap? GetJobIcon(uint jobId) 21 | { 22 | { 23 | if (jobId == 0) 24 | { 25 | var icon = GetTextureFromIconId(62571 ); 26 | return icon; 27 | } 28 | else if (jobId == 51) 29 | { 30 | var icon = GetTextureFromIconId(62116); 31 | return icon; 32 | } 33 | else 34 | { 35 | var icon = GetTextureFromIconId(62100 + jobId); 36 | return icon; 37 | } 38 | } 39 | } 40 | 41 | private static string ResolvePath(string path) => Svc.TextureSubstitution.GetSubstitutedPath(path); 42 | 43 | public static IDalamudTextureWrap? GetTextureFromIconId(uint iconId, uint stackCount = 0, bool hdIcon = true) 44 | { 45 | GameIconLookup lookup = new GameIconLookup(iconId + stackCount, false, hdIcon); 46 | string path = Svc.Texture.GetIconPath(lookup); 47 | string resolvePath = ResolvePath(path); 48 | 49 | var wrap = Svc.Texture.GetFromFile(resolvePath); 50 | if (wrap.TryGetWrap(out var icon, out _)) 51 | return icon; 52 | 53 | try 54 | { 55 | if (CachedModdedIcons.ContainsKey(iconId)) 56 | return CachedModdedIcons[iconId]; 57 | var tex = Svc.Data.GameData.GetFileFromDisk(resolvePath); 58 | var output = Svc.Texture.CreateFromRaw(RawImageSpecification.Rgba32(tex.Header.Width, tex.Header.Width), tex.GetRgbaImageData()); 59 | if (output != null) 60 | { 61 | CachedModdedIcons[iconId] = output; 62 | return output; 63 | } 64 | } 65 | catch 66 | { 67 | } 68 | 69 | 70 | return Svc.Texture.GetFromGame(path).GetWrapOrDefault(); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/WHMPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class WHMPvP 6 | { 7 | public const byte JobID = 24; 8 | 9 | public const uint 10 | Glare = 29223, 11 | Cure2 = 29224, 12 | Cure3 = 29225, 13 | AfflatusMisery = 29226, 14 | Aquaveil = 29227, 15 | MiracleOfNature = 29228, 16 | SeraphStrike = 29229; 17 | 18 | internal class Buffs 19 | { 20 | internal const ushort 21 | Cure3Ready = 3083; 22 | } 23 | 24 | internal class WHMPvP_Burst : CustomCombo 25 | { 26 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.WHMPvP_Burst; 27 | 28 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 29 | { 30 | if (actionID is Glare) 31 | { 32 | if (!TargetHasEffectAny(PvPCommon.Buffs.Guard)) 33 | { 34 | if (IsEnabled(CustomComboPreset.WHMPvP_Afflatus_Misery) && IsOffCooldown(AfflatusMisery)) 35 | return AfflatusMisery; 36 | 37 | if (CanWeave(actionID)) 38 | { 39 | if (IsEnabled(CustomComboPreset.WHMPvP_Mirace_of_Nature) && IsOffCooldown(MiracleOfNature)) 40 | return MiracleOfNature; 41 | 42 | if (IsEnabled(CustomComboPreset.WHMPvP_Seraph_Strike) && IsOffCooldown(SeraphStrike)) 43 | return SeraphStrike; 44 | } 45 | } 46 | } 47 | 48 | return actionID; 49 | } 50 | } 51 | internal class WHMPvP_Aquaveil : CustomCombo 52 | { 53 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.WHMPvP_Aquaveil; 54 | 55 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 56 | { 57 | if (actionID is Cure2 && IsOffCooldown(Aquaveil)) 58 | return Aquaveil; 59 | 60 | return actionID; 61 | } 62 | } 63 | 64 | internal class WHMPvP_Cure3 : CustomCombo 65 | { 66 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.WHMPvP_Cure3; 67 | 68 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 69 | { 70 | if (actionID is Cure2 && HasEffect(Buffs.Cure3Ready)) 71 | return Cure3; 72 | 73 | return actionID; 74 | } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/JobHelpers/DNCOpenerLogic.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.JobGauge.Types; 2 | using FFXIVClientStructs.FFXIV.Client.Game; 3 | using XIVSlothComboX.Combos.PvE; 4 | using XIVSlothComboX.Core; 5 | using XIVSlothComboX.CustomComboNS.Functions; 6 | using XIVSlothComboX.Extensions; 7 | 8 | namespace XIVSlothComboX.Combos.JobHelpers 9 | { 10 | internal class DNCOpenerLogic : PvE.DNC 11 | { 12 | 13 | private static uint OpenerLevel => 90; 14 | public bool DoFullOpener(ref uint actionID) 15 | { 16 | 17 | if (!CustomComboFunctions.LevelChecked(标准舞步StandardStep)) 18 | return false; 19 | 20 | if (CustomComboFunctions.InCombat()) 21 | { 22 | return false; 23 | } 24 | 25 | 26 | if (!CustomComboFunctions.InCombat()) 27 | { 28 | 29 | DNCGauge? gauge = CustomComboFunctions.GetJobGauge(); 30 | if (CustomComboFunctions.HasEffect(Buffs.标准舞步预备StandardStep)) 31 | { 32 | if (gauge.CompletedSteps < 2) 33 | { 34 | actionID = gauge.NextStep; 35 | if (actionID.ActionReady()) 36 | { 37 | unsafe 38 | { 39 | ActionManager.Instance()->UseAction(ActionType.Action, actionID); 40 | } 41 | } 42 | } 43 | } 44 | 45 | 46 | 47 | if (Countdown.TimeRemaining() <= 15) 48 | { 49 | if (标准舞步StandardStep.ActionReady()) 50 | { 51 | actionID = 标准舞步StandardStep; 52 | unsafe 53 | { 54 | ActionManager.Instance()->UseAction(ActionType.Action, actionID); 55 | } 56 | } 57 | } 58 | 59 | if (Countdown.TimeRemaining() is > 0.3f and < 0.5f) 60 | { 61 | if (双色标准舞步结束StandardFinish2.ActionReady()) 62 | { 63 | actionID = 双色标准舞步结束StandardFinish2; 64 | unsafe 65 | { 66 | ActionManager.Instance()->UseAction(ActionType.Action, 双色标准舞步结束StandardFinish2); 67 | } 68 | } 69 | } 70 | 71 | if (标准舞步StandardStep.ActionReady()) 72 | { 73 | actionID = 标准舞步StandardStep; 74 | return true; 75 | } 76 | 77 | return false; 78 | } 79 | 80 | return false; 81 | } 82 | } 83 | 84 | 85 | 86 | } 87 | -------------------------------------------------------------------------------- /XIVSlothComboX/Window/Tabs/AboutUs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Dalamud.Interface.Colors; 3 | using Dalamud.Utility; 4 | using Dalamud.Bindings.ImGui; 5 | using System.Numerics; 6 | using System.Reflection; 7 | 8 | namespace XIVSlothComboX.Window.Tabs 9 | { 10 | internal class AboutUs : ConfigWindow 11 | { 12 | public static Version version = null!; 13 | 14 | public const string Ban = "[XIVSlothComboX]请不要去卫月频道交流此插件,包括提BUG"; 15 | 16 | internal static new void Draw() 17 | { 18 | 19 | PvEFeatures.HasToOpenJob = true; 20 | version ??= Assembly.GetExecutingAssembly().GetName().Version!; 21 | 22 | PvEFeatures.HasToOpenJob = true; 23 | 24 | ImGui.BeginChild("About", new Vector2(0, 0), true); 25 | 26 | ImGui.TextColored(ImGuiColors.ParsedGreen, $"XIVSlothComboX - v{version}"); 27 | // ImGui.Spacing(); 28 | // ImGui.Spacing(); 29 | // ImGui.Spacing(); 30 | // ImGui.TextWrapped("Brought to you by: \nAki, k-kz, ele-starshade, damolitionn, Taurenkey, Augporto, grimgal, Genesis-Nova, Tartarga and many other contributors!"); 31 | // ImGui.Spacing(); 32 | // ImGui.Spacing(); 33 | // ImGui.Spacing(); 34 | // ImGui.PushStyleColor(ImGuiCol.Button, ImGuiColors.ParsedPurple); 35 | // ImGui.PushStyleColor(ImGuiCol.ButtonHovered, ImGuiColors.HealerGreen); 36 | 37 | 38 | if (ImGui.Button("有问题来这里提")) 39 | { 40 | Util.OpenLink("https://docs.qq.com/sheet/DT0JZdlVHaklSSkN6&tab=cr8tma"); 41 | // Util.OpenLink("https://github.com/44451516/XIVSlothCombo/issues"); 42 | } 43 | 44 | // ImGui.PopStyleColor(); 45 | // ImGui.PopStyleColor(); 46 | 47 | 48 | if (ImGui.Button("交流群")) 49 | { 50 | // Util.OpenLink("https://qm.qq.com/q/eBaUVYbKpi"); 51 | // 点击链接加入群聊【XIVSlothCombo汉化】:https://qm.qq.com/q/5UdSTydlfi 52 | Util.OpenLink("https://qm.qq.com/q/5UdSTydlfi"); 53 | } 54 | 55 | ImGui.Spacing(); 56 | ImGui.Spacing(); 57 | ImGui.Spacing(); 58 | ImGui.Spacing(); 59 | // ImGui.TextColored(ImGuiColors.ParsedGreen, $"由于写的汉化脚本不算智能,部分区域未匹配到而没有汉化。"); 60 | // ImGui.TextColored(ImGuiColors.ParsedGreen, $"如果你要是从某个咸鱼小铺买的,恭喜你,上大当了,请立刻退款差评免得被人嘲笑。"); 61 | // ImGui.TextColored(ImGuiColors.ParsedGreen, $"温馨提醒:怂就别开,但开别怂,少开点挂。"); 62 | ImGui.Spacing(); 63 | ImGui.Spacing(); 64 | ImGui.Spacing(); 65 | ImGui.TextColored(ImGuiColors.DPSRed, $"{Ban}"); 66 | ImGui.TextColored(ImGuiColors.DPSRed, $"{Ban}"); 67 | ImGui.TextColored(ImGuiColors.DPSRed, $"{Ban}"); 68 | ImGui.EndChild(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /XIVSlothComboX/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | // General 9 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines", Scope = "namespaceanddescendants", Target = "~N:XIVSlothComboXPlugin")] 10 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions", Scope = "namespaceanddescendants", Target = "~N:XIVSlothComboXPlugin")] 11 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too", Scope = "namespaceanddescendants", Target = "~N:XIVSlothComboXPlugin")] 12 | [assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:XIVSlothComboXPlugin")] 13 | [assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:XIVSlothComboXPlugin")] 14 | [assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:XIVSlothComboXPlugin")] 15 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:XIVSlothComboXPlugin")] 16 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")] 17 | 18 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Self explanatory.", Scope = "type", Target = "~T:XIVSlothComboXPlugin.CustomComboPreset")] 19 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Self explanatory.", Scope = "namespaceanddescendants", Target = "~N:XIVSlothComboXPlugin.Combos")] 20 | [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Group jobs together.", Scope = "namespaceanddescendants", Target = "~N:XIVSlothComboXPlugin.Combos")] 21 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1132:Do not combine fields", Justification = "Not necessary.", Scope = "namespaceanddescendants", Target = "~N:XIVSlothComboXPlugin.Combos")] 22 | -------------------------------------------------------------------------------- /XIVSlothComboX/Window/ImGUIHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using FFXIVClientStructs.FFXIV.Common.Math; 4 | using Dalamud.Bindings.ImGui; 5 | using XIVSlothComboX.Window.help; 6 | 7 | namespace XIVSlothComboX.Window; 8 | 9 | public class ImGUIHelper 10 | { 11 | public static void DrawSimpleList(string title, IList list, Func addCallback, Func drawCallback) 12 | { 13 | using (new GroupWrapper()) 14 | { 15 | ImGui.Text(title); 16 | using (new IndentGroupWrapper()) 17 | { 18 | ImGui.Text($"Count: {list.Count}"); 19 | ImGui.SameLine(); 20 | if (ImGui.Button("Add", new Vector2(60, 20))) 21 | { 22 | list.Add(addCallback.Invoke()); 23 | return; 24 | } 25 | 26 | var removeIndex = -1; 27 | var swapIndexA = -1; 28 | var swapIndexB = -1; 29 | for (int i = 0; i < list.Count; i++) 30 | { 31 | using (new GroupWrapper()) 32 | { 33 | ImGui.Text($"Index: {i}"); 34 | ImGui.SameLine(); 35 | var value = list[i]; 36 | list[i] = drawCallback.Invoke(value); 37 | 38 | if (i > 0) 39 | { 40 | ImGui.SameLine(); 41 | if (ImGui.Button("上移")) 42 | { 43 | swapIndexA = i; 44 | swapIndexB = i - 1; 45 | } 46 | } 47 | 48 | if (i < list.Count - 1) 49 | { 50 | ImGui.SameLine(); 51 | if (ImGui.Button("下移")) 52 | { 53 | swapIndexA = i; 54 | swapIndexB = i + 1; 55 | } 56 | } 57 | 58 | ImGui.SameLine(); 59 | if (ImGui.Button("X")) 60 | { 61 | removeIndex = i; 62 | } 63 | } 64 | } 65 | 66 | if (removeIndex >= 0) 67 | list.RemoveAt(removeIndex); 68 | 69 | if (swapIndexA != -1) 70 | { 71 | (list[swapIndexA], list[swapIndexB]) = (list[swapIndexB], list[swapIndexA]); 72 | } 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Extensions/SeStringUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using Dalamud.Game.Text.SeStringHandling; 6 | using Dalamud.Game.Text.SeStringHandling.Payloads; 7 | 8 | namespace XIVSlothComboX.Extensions; 9 | 10 | public static class SeStringUtils 11 | { 12 | public static IntPtr emptyPtr; 13 | 14 | public static void Initialize() 15 | { 16 | emptyPtr = SeStringToPtr(Text("")); 17 | } 18 | 19 | public static void Dispose() 20 | { 21 | } 22 | 23 | public static SeString SeStringFromPtr(IntPtr seStringPtr) 24 | { 25 | byte b; 26 | var offset = 0; 27 | 28 | unsafe 29 | { 30 | while ((b = *(byte*)(seStringPtr + offset)) != 0) 31 | { 32 | offset++; 33 | } 34 | } 35 | 36 | var bytes = new byte[offset]; 37 | Marshal.Copy(seStringPtr, bytes, 0, offset); 38 | 39 | return SeString.Parse(bytes); 40 | } 41 | 42 | public static IntPtr SeStringToPtr(SeString seString) 43 | { 44 | var bytes = seString.Encode(); 45 | var pointer = Marshal.AllocHGlobal(bytes.Length + 1); 46 | Marshal.Copy(bytes, 0, pointer, bytes.Length); 47 | Marshal.WriteByte(pointer, bytes.Length, 0); 48 | 49 | return pointer; 50 | } 51 | 52 | public static void FreePtr(IntPtr seStringPtr) 53 | { 54 | if (seStringPtr != emptyPtr) 55 | { 56 | Marshal.FreeHGlobal(seStringPtr); 57 | } 58 | } 59 | 60 | public static Byte[] NameText(string rawText) 61 | { 62 | var b = new byte[32]; 63 | var Length = Math.Min(Encoding.UTF8.GetBytes(rawText.ToCharArray()).Length, b.Length); 64 | Array.Copy(Encoding.UTF8.GetBytes(rawText.ToCharArray()), b, Length); 65 | return b; 66 | } 67 | 68 | 69 | 70 | public static SeString Text(string rawText) 71 | { 72 | var seString = new SeString(new List()); 73 | seString.Append(new TextPayload(rawText)); 74 | 75 | return seString; 76 | } 77 | 78 | public static SeString Text(string text, ushort color) 79 | { 80 | var seString = new SeString(new List()); 81 | seString.Append(new UIForegroundPayload(color)); 82 | seString.Append(new TextPayload(text)); 83 | seString.Append(UIForegroundPayload.UIForegroundOff); 84 | 85 | return seString; 86 | } 87 | 88 | public static SeString Icon(BitmapFontIcon icon, string? prefix) 89 | { 90 | var seString = new SeString(new List()); 91 | 92 | if (prefix != null) 93 | { 94 | seString.Append(new TextPayload(prefix)); 95 | } 96 | 97 | seString.Append(new IconPayload(icon)); 98 | 99 | return seString; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/WARPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | using XIVSlothComboX.CustomComboNS.Functions; 3 | 4 | namespace XIVSlothComboX.Combos.PvP 5 | { 6 | internal static class WARPvP 7 | { 8 | public const byte ClassID = 3; 9 | public const byte JobID = 21; 10 | internal const uint 11 | HeavySwing = 29074, 12 | Maim = 29075, 13 | StormsPath = 29076, 14 | PrimalRend = 29084, 15 | Onslaught = 29079, 16 | Orogeny = 29080, 17 | Blota = 29081, 18 | Bloodwhetting = 29082; 19 | 20 | internal class Buffs 21 | { 22 | internal const ushort 23 | NascentChaos = 1992, 24 | InnerRelease = 1303; 25 | } 26 | 27 | public static class Config 28 | { 29 | public static UserInt 30 | WARPVP_BlotaTiming = new("WARPVP_BlotaTiming"); 31 | 32 | } 33 | internal class WARPvP_BurstMode : CustomCombo 34 | { 35 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.WARPvP_BurstMode; 36 | 37 | protected override uint Invoke(uint actionID, uint lastComboActionID, float comboTime, byte level) 38 | { 39 | if (actionID is HeavySwing or Maim or StormsPath) 40 | { 41 | var canWeave = CanWeave(actionID); 42 | 43 | if (!GetCooldown(Bloodwhetting).IsCooldown && (IsEnabled(CustomComboPreset.WARPvP_BurstMode_Bloodwhetting) || canWeave)) 44 | return OriginalHook(Bloodwhetting); 45 | 46 | if (!InMeleeRange() && IsOffCooldown(Blota) && !TargetHasEffectAny(PvPCommon.Debuffs.Stun) && IsEnabled(CustomComboPreset.WARPvP_BurstMode_Blota) && Config.WARPVP_BlotaTiming == 0 && IsOffCooldown(PrimalRend)) 47 | return OriginalHook(Blota); 48 | 49 | if (IsEnabled(CustomComboPreset.WARPvP_BurstMode_PrimalRend) && IsOffCooldown(PrimalRend)) 50 | return OriginalHook(PrimalRend); 51 | 52 | if (!InMeleeRange() && IsOffCooldown(Blota) && !TargetHasEffectAny(PvPCommon.Debuffs.Stun) && IsEnabled(CustomComboPreset.WARPvP_BurstMode_Blota) && Config.WARPVP_BlotaTiming == 1 && IsOnCooldown(PrimalRend)) 53 | return OriginalHook(Blota); 54 | 55 | if (!GetCooldown(Onslaught).IsCooldown && canWeave) 56 | return OriginalHook(Onslaught); 57 | 58 | if (InMeleeRange()) 59 | { 60 | if (HasEffect(Buffs.NascentChaos)) 61 | return OriginalHook(Bloodwhetting); 62 | 63 | if (!GetCooldown(Orogeny).IsCooldown && canWeave) 64 | return OriginalHook(Orogeny); 65 | } 66 | } 67 | return actionID; 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/RDMPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class RDMPvP 6 | { 7 | public const byte JobID = 35; 8 | 9 | public const uint 10 | Verstone = 29683, 11 | EnchantedRiposte = 29689, 12 | Resolution = 29695, 13 | MagickBarrier = 29697, 14 | CorpsACorps = 29699, 15 | Displacement = 29700, 16 | Veraero3 = 29684, 17 | Verholy = 29685, 18 | Verfire = 29686, 19 | Verthunder3 = 29687, 20 | Verflare = 29688, 21 | EnchantedZwerchhau = 29690, 22 | EnchantedRedoublement = 29691, 23 | Frazzle = 29698, 24 | WhiteShift = 29703, 25 | BlackShift = 29702, 26 | SouthernCross = 29704; 27 | 28 | public static class Buffs 29 | { 30 | public const ushort 31 | WhiteShift = 3245, 32 | BlackShift = 3246, 33 | Dualcast = 1393, 34 | EnchantedRiposte = 3234, 35 | EnchantedRedoublement = 3236, 36 | EnchantedZwerchhau = 3235, 37 | VermilionRadiance = 3233, 38 | MagickBarrier = 3240; 39 | } 40 | 41 | internal class RDMPvP_BurstMode : CustomCombo 42 | { 43 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.RDMPvP_BurstMode; 44 | 45 | protected override uint Invoke(uint actionID, uint lastComboActionID, float comboTime, byte level) 46 | { 47 | if (actionID is Verstone or Verfire) 48 | { 49 | 50 | if (!GetCooldown(Frazzle).IsCooldown && HasEffect(Buffs.BlackShift) && IsNotEnabled(CustomComboPreset.RDMPvP_FrazzleOption)) 51 | return OriginalHook(Frazzle); 52 | 53 | if (!GetCooldown(Resolution).IsCooldown) 54 | return OriginalHook(Resolution); 55 | 56 | if (!InMeleeRange() && GetCooldown(CorpsACorps).RemainingCharges > 0 && !GetCooldown(EnchantedRiposte).IsCooldown) 57 | return OriginalHook(CorpsACorps); 58 | 59 | if (InMeleeRange()) 60 | { 61 | if (!GetCooldown(EnchantedRiposte).IsCooldown || lastComboActionID == EnchantedRiposte || lastComboActionID == EnchantedZwerchhau) 62 | return OriginalHook(EnchantedRiposte); 63 | 64 | if (lastComboActionID == EnchantedRedoublement && GetCooldown(Displacement).RemainingCharges > 0) 65 | return OriginalHook(Displacement); 66 | } 67 | 68 | if (HasEffect(Buffs.VermilionRadiance)) 69 | return OriginalHook(EnchantedRiposte); 70 | 71 | return OriginalHook(Verstone); 72 | } 73 | 74 | return actionID; 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/VPRPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class VPRPvP 6 | { 7 | public const byte JobID = 41; 8 | 9 | internal const uint 10 | SteelFangs = 39157, 11 | HuntersSting = 39159, 12 | BarbarousSlice = 39161, 13 | PiercingFangs = 39158, 14 | SwiftskinsSting = 39160, 15 | RavenousBite = 39163, 16 | HuntersSnap = 39166, 17 | SwiftskinsCoil = 39167, 18 | UncoiledFury = 39168, 19 | SerpentsTail = 39183, 20 | FirstGeneration = 39169, 21 | SecondGeneration = 39170, 22 | ThirdGeneration = 39171, 23 | FourthGeneration = 39172, 24 | Ouroboros = 39173; 25 | 26 | internal class VPRPvP_Burst : CustomCombo 27 | { 28 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.VPRPvP_Burst; 29 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 30 | { 31 | #region Variables 32 | bool canWeave = CanWeave(OriginalHook(actionID)); 33 | bool isSerpentsTailPrimed = OriginalHook(SerpentsTail) != SerpentsTail; 34 | bool inGenerationsCombo = OriginalHook(actionID) is FirstGeneration or SecondGeneration or ThirdGeneration or FourthGeneration; 35 | bool inMeleeRange = GetTargetDistance() <= 5; 36 | #endregion 37 | 38 | if (actionID is SteelFangs or HuntersSting or BarbarousSlice or PiercingFangs or SwiftskinsSting or RavenousBite) 39 | { 40 | if (HasTarget()) 41 | { 42 | // Serpent's Tail 43 | if (isSerpentsTailPrimed && canWeave) 44 | return OriginalHook(SerpentsTail); 45 | 46 | if (inMeleeRange) 47 | { 48 | // Ouroboros 49 | if (OriginalHook(HuntersSnap) == Ouroboros && !inGenerationsCombo) 50 | return OriginalHook(HuntersSnap); 51 | 52 | // Reawakened 53 | if (inGenerationsCombo) 54 | return OriginalHook(actionID); 55 | } 56 | 57 | // Uncoiled Fury 58 | if (IsOffCooldown(UncoiledFury) && (!inMeleeRange || (!HasCharges(HuntersSnap) && OriginalHook(HuntersSnap) != SwiftskinsCoil))) 59 | return OriginalHook(UncoiledFury); 60 | 61 | // Hunter's Snap / Swiftskin's Coil 62 | if ((HasCharges(HuntersSnap) && OriginalHook(HuntersSnap) != Ouroboros) || OriginalHook(HuntersSnap) == SwiftskinsCoil) 63 | return OriginalHook(HuntersSnap); 64 | } 65 | } 66 | 67 | return actionID; 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /XIVSlothComboX/CustomComboNS/Functions/Misc.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using XIVSlothComboX.Combos; 3 | using XIVSlothComboX.Combos.PvE; 4 | using XIVSlothComboX.Core; 5 | using XIVSlothComboX.Services; 6 | 7 | namespace XIVSlothComboX.CustomComboNS.Functions 8 | { 9 | internal abstract partial class CustomComboFunctions 10 | { 11 | /// Determine if the given preset is enabled. 12 | /// Preset to check. 13 | /// A value indicating whether the preset is enabled. 14 | public static bool IsEnabled(CustomComboPreset preset) => (int)preset < 100 || PresetStorage.IsEnabled(preset); 15 | 16 | /// Determine if the given preset is not enabled. 17 | /// Preset to check. 18 | /// A value indicating whether the preset is not enabled. 19 | public static bool IsNotEnabled(CustomComboPreset preset) => !IsEnabled(preset); 20 | 21 | public class JobIDs 22 | { 23 | // Job IDs ClassIDs (no jobstone) (Lancer, Pugilist, etc) 24 | public static readonly List Melee = new() 25 | { 26 | DRG.JobID, DRG.ClassID, 27 | MNK.JobID, MNK.ClassID, 28 | NIN.JobID, NIN.ClassID, 29 | RPR.JobID, 30 | SAM.JobID, 31 | VPR.JobID 32 | }; 33 | 34 | public static readonly List Ranged = new() 35 | { 36 | BLM.JobID, BLM.ClassID, 37 | BRD.JobID, BRD.ClassID, 38 | SMN.JobID, SMN.ClassID, 39 | MCH.JobID, 40 | RDM.JobID, 41 | DNC.JobID, 42 | BLU.JobID, 43 | PCT.JobID 44 | }; 45 | 46 | public static readonly List Tank = new() 47 | { 48 | PLD.JobID, PLD.ClassID, 49 | WAR.JobID, WAR.ClassID, 50 | DRK.JobID, 51 | GNB.JobID 52 | }; 53 | 54 | public static readonly List Healer = new() 55 | { 56 | WHM.JobID, WHM.ClassID, 57 | SCH.JobID, 58 | AST.JobID, 59 | SGE.JobID 60 | }; 61 | 62 | public static byte JobToClass(uint jobID) 63 | { 64 | return jobID switch 65 | { 66 | ADV.JobID => ADV.ClassID, 67 | BLM.JobID => BLM.ClassID, 68 | BRD.JobID => BRD.ClassID, 69 | DRG.JobID => DRG.ClassID, 70 | MNK.JobID => MNK.ClassID, 71 | NIN.JobID => NIN.ClassID, 72 | PLD.JobID => PLD.ClassID, 73 | SCH.JobID => SCH.ClassID, 74 | SMN.JobID => SMN.ClassID, 75 | WAR.JobID => WAR.ClassID, 76 | WHM.JobID => WHM.ClassID, 77 | _ => 0xFF, 78 | }; 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/JobHelpers/SCH.cs: -------------------------------------------------------------------------------- 1 | using static XIVSlothComboX.Combos.PvE.SCH; 2 | using static XIVSlothComboX.CustomComboNS.Functions.CustomComboFunctions; 3 | 4 | namespace XIVSlothComboX.Combos.JobHelpers 5 | { 6 | internal class SCHHelper 7 | { 8 | public static int GetMatchingConfigST(int i, out uint action, out bool enabled) 9 | { 10 | var healTarget = GetHealTarget(Config.SCH_ST_Heal_Adv && Config.SCH_ST_Heal_UIMouseOver); 11 | 12 | switch (i) 13 | { 14 | case 0: 15 | action = Lustrate; 16 | enabled = IsEnabled(CustomComboPreset.SCH_ST_Heal_Lustrate) && Gauge.HasAetherflow(); 17 | return Config.SCH_ST_Heal_LustrateOption; 18 | case 1: 19 | action = Excogitation; 20 | enabled = IsEnabled(CustomComboPreset.SCH_ST_Heal_Excogitation) && (Gauge.HasAetherflow() || HasEffect(Buffs.Recitation)); 21 | return Config.SCH_ST_Heal_ExcogitationOption; 22 | case 2: 23 | action = Protraction; 24 | enabled = IsEnabled(CustomComboPreset.SCH_ST_Heal_Protraction); 25 | return Config.SCH_ST_Heal_ProtractionOption; 26 | } 27 | 28 | enabled = false; 29 | action = 0; 30 | return 0; 31 | } 32 | 33 | //public static int GetMatchingConfigAoE(int i, out uint action, out bool enabled) 34 | //{ 35 | // switch (i) 36 | // { 37 | // case 0: 38 | // action = PvE.SGE.Kerachole; 39 | // enabled = IsEnabled(CustomComboPreset.SGE_AoE_Heal_Kerachole) && (!PvE.SGE.Config.SGE_AoE_Heal_KeracholeTrait || (PvE.SGE.Config.SGE_AoE_Heal_KeracholeTrait && TraitLevelChecked(PvE.SGE.Traits.EnhancedKerachole))) && PvE.SGE.Gauge.HasAddersgall(); 40 | // return 0; 41 | // case 1: 42 | // action = PvE.SGE.Ixochole; 43 | // enabled = IsEnabled(CustomComboPreset.SGE_AoE_Heal_Ixochole) && PvE.SGE.Gauge.HasAddersgall(); 44 | // return 0; 45 | // case 2: 46 | // action = OriginalHook(PvE.SGE.Physis); 47 | // enabled = IsEnabled(CustomComboPreset.SGE_AoE_Heal_Physis); 48 | // return 0; 49 | // case 3: 50 | // action = PvE.SGE.Holos; 51 | // enabled = IsEnabled(CustomComboPreset.SGE_AoE_Heal_Holos); 52 | // return 0; 53 | // case 4: 54 | // action = PvE.SGE.Panhaima; 55 | // enabled = IsEnabled(CustomComboPreset.SGE_AoE_Heal_Panhaima); 56 | // return 0; 57 | // case 5: 58 | // action = PvE.SGE.Pepsis; 59 | // enabled = IsEnabled(CustomComboPreset.SGE_AoE_Heal_Pepsis) && FindEffect(PvE.SGE.Buffs.EukrasianPrognosis) is not null; 60 | // return 0; 61 | // case 6: 62 | // action = PvE.SGE.Philosophia; 63 | // enabled = IsEnabled(CustomComboPreset.SGE_AoE_Heal_Philosophia); 64 | // return 0; 65 | // } 66 | 67 | // enabled = false; 68 | // action = 0; 69 | // return 0; 70 | //} 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/DRKPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal class DRKPvP 6 | { 7 | public const uint 8 | HardSlash = 29085, 9 | SyphonStrike = 29086, 10 | Souleater = 29087, 11 | Quietus = 29737, 12 | Shadowbringer = 29091, 13 | Plunge = 29092, 14 | BlackestNight = 29093, 15 | SaltedEarth = 29094, 16 | Bloodspiller = 29088, 17 | SaltAndDarkness = 29095; 18 | 19 | public class Buffs 20 | { 21 | public const ushort 22 | Blackblood = 3033, 23 | BlackestNight = 1038, 24 | SaltedEarthDMG = 3036, 25 | SaltedEarthDEF = 3037, 26 | DarkArts = 3034, 27 | UndeadRedemption = 3039; 28 | } 29 | 30 | public class Config 31 | { 32 | public const string 33 | ShadowbringerThreshold = nameof(ShadowbringerThreshold); 34 | 35 | } 36 | 37 | internal class DRKPvP_BurstMode : CustomCombo 38 | { 39 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.DRKPvP_Burst; 40 | 41 | protected override uint Invoke(uint actionID, uint lastComboActionID, float comboTime, byte level) 42 | { 43 | if (actionID is HardSlash or SyphonStrike or Souleater) 44 | { 45 | bool canWeave = CanWeave(HardSlash); 46 | int shadowBringerThreshold = GetOptionValue(Config.ShadowbringerThreshold); 47 | 48 | 49 | if (IsEnabled(CustomComboPreset.DRKPvP_Plunge) && HasTarget() && ((!InMeleeRange()) || (InMeleeRange() && IsEnabled(CustomComboPreset.DRKPvP_PlungeMelee))) && ActionReady(Plunge)) 50 | return OriginalHook(Plunge); 51 | 52 | if (canWeave) 53 | { 54 | if (ActionReady(BlackestNight)) 55 | return OriginalHook(BlackestNight); 56 | 57 | if (ActionReady(SaltedEarth)) 58 | return OriginalHook(SaltedEarth); 59 | 60 | if (HasEffect(Buffs.SaltedEarthDMG) && ActionReady(SaltAndDarkness)) 61 | return OriginalHook(SaltAndDarkness); 62 | 63 | if (!HasEffect(Buffs.Blackblood) && (HasEffect(Buffs.DarkArts) || PlayerHealthPercentageHp() >= shadowBringerThreshold)) 64 | return OriginalHook(Shadowbringer); 65 | } 66 | 67 | if (InMeleeRange()) 68 | { 69 | if (ActionReady(Quietus)) 70 | return OriginalHook(Quietus); 71 | 72 | if (comboTime > 1f) 73 | { 74 | if (lastComboActionID == HardSlash) 75 | return OriginalHook(SyphonStrike); 76 | 77 | if (lastComboActionID == SyphonStrike) 78 | return OriginalHook(Souleater); 79 | } 80 | 81 | return OriginalHook(HardSlash); 82 | } 83 | } 84 | 85 | return actionID; 86 | } 87 | } 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/MNKPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class MNKPvP 6 | { 7 | public const byte ClassID = 2; 8 | public const byte JobID = 20; 9 | 10 | public const uint 11 | PhantomRushCombo = 55, 12 | Bootshine = 29472, 13 | TrueStrike = 29473, 14 | SnapPunch = 29474, 15 | DragonKick = 29475, 16 | TwinSnakes = 29476, 17 | Demolish = 29477, 18 | PhantomRush = 29478, 19 | SixSidedStar = 29479, 20 | Enlightenment = 29480, 21 | RisingPhoenix = 29481, 22 | RiddleOfEarth = 29482, 23 | ThunderClap = 29484, 24 | EarthsReply = 29483, 25 | Meteordrive = 29485; 26 | 27 | public static class Buffs 28 | { 29 | public const ushort 30 | WindResonance = 2007, 31 | FireResonance = 3170, 32 | EarthResonance = 3171; 33 | } 34 | 35 | public static class Debuffs 36 | { 37 | public const ushort 38 | PressurePoint = 3172; 39 | } 40 | 41 | internal class MNKPvP_Burst : CustomCombo 42 | { 43 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.MNKPvP_Burst; 44 | 45 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 46 | { 47 | if (actionID is Bootshine or TrueStrike or SnapPunch or DragonKick or TwinSnakes or Demolish or PhantomRush) 48 | { 49 | 50 | if (!TargetHasEffectAny(PvPCommon.Buffs.Guard)) 51 | { 52 | if (IsEnabled(CustomComboPreset.MNKPvP_Burst_RiddleOfEarth) && IsOffCooldown(RiddleOfEarth) && PlayerHealthPercentageHp() <= 95) 53 | return OriginalHook(RiddleOfEarth); 54 | 55 | if (IsEnabled(CustomComboPreset.MNKPvP_Burst_Thunderclap) && !HasEffect(Buffs.WindResonance) && GetRemainingCharges(ThunderClap) > 0) 56 | return OriginalHook(ThunderClap); 57 | 58 | if (CanWeave(actionID)) 59 | { 60 | if (IsOffCooldown(SixSidedStar)) 61 | return OriginalHook(SixSidedStar); 62 | 63 | if (IsEnabled(CustomComboPreset.MNKPvP_Burst_RiddleOfEarth) && HasEffect(Buffs.EarthResonance) && GetBuffRemainingTime(Buffs.EarthResonance) < 6) 64 | return OriginalHook(EarthsReply); 65 | 66 | if (GetRemainingCharges(RisingPhoenix) > 0 && !HasEffect(Buffs.FireResonance) && (lastComboMove is Demolish || IsOffCooldown(Enlightenment))) 67 | return OriginalHook(RisingPhoenix); 68 | } 69 | 70 | if (HasEffect(Buffs.FireResonance)) 71 | { 72 | if (lastComboMove is Demolish) 73 | return OriginalHook(PhantomRush); 74 | 75 | if (IsOffCooldown(Enlightenment)) 76 | return OriginalHook(Enlightenment); 77 | } 78 | } 79 | } 80 | 81 | return actionID; 82 | } 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/MCHPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class MCHPvP 6 | { 7 | public const byte JobID = 31; 8 | 9 | public const uint 10 | BlastCharge = 29402, 11 | HeatBlast = 29403, 12 | Scattergun = 29404, 13 | Drill = 29405, 14 | BioBlaster = 29406, 15 | AirAnchor = 29407, 16 | ChainSaw = 29408, 17 | Wildfire = 29409, 18 | BishopTurret = 29412, 19 | AetherMortar = 29413, 20 | Analysis = 29414, 21 | MarksmanSpite = 29415; 22 | 23 | public static class Buffs 24 | { 25 | public const ushort 26 | Heat = 3148, 27 | Overheated = 3149, 28 | DrillPrimed = 3150, 29 | BioblasterPrimed = 3151, 30 | AirAnchorPrimed = 3152, 31 | ChainSawPrimed = 3153, 32 | Analysis = 3158; 33 | } 34 | 35 | public static class Debuffs 36 | { 37 | public const ushort 38 | Wildfire = 1323; 39 | } 40 | 41 | internal class MCHPvP_BurstMode : CustomCombo 42 | { 43 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.MCHPvP_BurstMode; 44 | 45 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 46 | { 47 | if (actionID == BlastCharge) 48 | { 49 | var canWeave = CanWeave(actionID); 50 | var analysisStacks = GetRemainingCharges(Analysis); 51 | var bigDamageStacks = GetRemainingCharges(OriginalHook(Drill)); 52 | var overheated = HasEffect(Buffs.Overheated); 53 | 54 | if (canWeave && HasEffect(Buffs.Overheated) && IsOffCooldown(Wildfire)) 55 | return OriginalHook(Wildfire); 56 | 57 | if (overheated) 58 | return OriginalHook(HeatBlast); 59 | 60 | if ((HasEffect(Buffs.DrillPrimed) || 61 | (HasEffect(Buffs.ChainSawPrimed) && !IsEnabled(CustomComboPreset.MCHPvP_BurstMode_AltAnalysis)) || 62 | (HasEffect(Buffs.AirAnchorPrimed) && IsEnabled(CustomComboPreset.MCHPvP_BurstMode_AltAnalysis))) && 63 | !HasEffect(Buffs.Analysis) && analysisStacks > 0 && (!IsEnabled(CustomComboPreset.MCHPvP_BurstMode_AltDrill) 64 | || IsOnCooldown(Wildfire)) && !canWeave && !overheated && bigDamageStacks > 0) 65 | return OriginalHook(Analysis); 66 | 67 | if (bigDamageStacks > 0) 68 | { 69 | if (HasEffect(Buffs.DrillPrimed)) 70 | return OriginalHook(Drill); 71 | 72 | if (HasEffect(Buffs.BioblasterPrimed) && GetTargetDistance() <= 12) 73 | return OriginalHook(BioBlaster); 74 | 75 | if (HasEffect(Buffs.AirAnchorPrimed)) 76 | return OriginalHook(AirAnchor); 77 | 78 | if (HasEffect(Buffs.ChainSawPrimed)) 79 | return OriginalHook(ChainSaw); 80 | } 81 | } 82 | 83 | return actionID; 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /XIVSlothComboX/自动类/AutoItem.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using FFXIVClientStructs.FFXIV.Client.Game; 3 | using XIVSlothComboX.Services; 4 | 5 | namespace XIVSlothComboX.自动类; 6 | 7 | public class AutoItem 8 | { 9 | public static unsafe void Useitem(uint itemId) 10 | { 11 | uint a4 = 65535; 12 | 13 | //检查有没有HQ 14 | if (InventoryManager.Instance()->GetInventoryItemCount(itemId, true) > 0) 15 | { 16 | if (ActionManager.Instance()->GetActionStatus(ActionType.Item, itemId + 1000000) == 0) 17 | { 18 | ActionManager.Instance()->UseAction(ActionType.Item, itemId + 1000000, Service.ObjectTable.LocalPlayer.GameObjectId, a4); 19 | return; 20 | } 21 | } 22 | 23 | if (InventoryManager.Instance()->GetInventoryItemCount(itemId) > 0) 24 | { 25 | if (ActionManager.Instance()->GetActionStatus(ActionType.Item, itemId) == 0) 26 | { 27 | ActionManager.Instance()->UseAction(ActionType.Item, itemId, Service.ObjectTable.LocalPlayer.GameObjectId, a4); 28 | } 29 | } 30 | } 31 | 32 | public static void 自动吃食物(uint requiredItem, bool requiredItemHQ) 33 | { 34 | var LocalPlayer = Service.ObjectTable.LocalPlayer; 35 | if (LocalPlayer == null) 36 | { 37 | return; 38 | } 39 | 40 | var hasBuff = LocalPlayer.StatusList.FirstOrDefault(x => x.StatusId == 48 & x.RemainingTime > 10f); 41 | 42 | if (hasBuff == null) 43 | { 44 | if (ConsumableChecker.HasItem(requiredItem, requiredItemHQ)) 45 | { 46 | ConsumableChecker.UseItem(requiredItem, requiredItemHQ); 47 | } 48 | } 49 | } 50 | 51 | public static unsafe void 自动精炼药() 52 | { 53 | var LocalPlayer = Service.ObjectTable.LocalPlayer; 54 | if (LocalPlayer == null) 55 | { 56 | return; 57 | } 58 | 59 | bool 是否有食物buff = false; 60 | 61 | foreach (var statuse in LocalPlayer.StatusList) 62 | { 63 | if (statuse.StatusId == 49) 64 | { 65 | 是否有食物buff = true; 66 | } 67 | } 68 | 69 | if (是否有食物buff == false) 70 | { 71 | 72 | const uint 极精炼药 = 27960; 73 | const uint 强精炼药 = 19885; 74 | const uint 精炼药 = 7059; 75 | 76 | 77 | uint itemId = 0; 78 | 79 | if (InventoryManager.Instance()->GetInventoryItemCount(精炼药, true) > 0) 80 | { 81 | itemId = 精炼药; 82 | } 83 | 84 | if (InventoryManager.Instance()->GetInventoryItemCount(精炼药, false) > 0) 85 | { 86 | itemId = 精炼药; 87 | } 88 | 89 | 90 | if (InventoryManager.Instance()->GetInventoryItemCount(强精炼药, true) > 0) 91 | { 92 | itemId = 强精炼药; 93 | } 94 | 95 | if (InventoryManager.Instance()->GetInventoryItemCount(强精炼药, false) > 0) 96 | { 97 | itemId = 强精炼药; 98 | } 99 | 100 | 101 | //极精炼药HQ 102 | if (InventoryManager.Instance()->GetInventoryItemCount(极精炼药, true) > 0) 103 | { 104 | itemId = 极精炼药; 105 | } 106 | 107 | ///极精炼药NQ 108 | if (InventoryManager.Instance()->GetInventoryItemCount(极精炼药, false) > 0) 109 | { 110 | itemId = 极精炼药; 111 | } 112 | 113 | 114 | if (itemId > 0) 115 | { 116 | Useitem(itemId); 117 | } 118 | 119 | 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/DNCPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.Combos.PvE; 2 | using XIVSlothComboX.Core; 3 | using XIVSlothComboX.CustomComboNS; 4 | 5 | namespace XIVSlothComboX.Combos.PvP 6 | { 7 | internal static class DNCPvP 8 | { 9 | public const byte JobID = 38; 10 | 11 | internal const uint 12 | FountainCombo = 54, 13 | Cascade = 29416, 14 | Fountain = 29417, 15 | ReverseCascade = 29418, 16 | Fountainfall = 29419, 17 | SaberDance = 29420, 18 | StarfallDance = 29421, 19 | HoningDance = 29422, 20 | HoningOvation = 29470, 21 | FanDance = 29428, 22 | CuringWaltz = 29429, 23 | EnAvant = 29430, 24 | ClosedPosition = 29431, 25 | Contradance = 29432; 26 | 27 | internal class Buffs 28 | { 29 | internal const ushort 30 | EnAvant = 2048, 31 | FanDance = 2052, 32 | Bladecatcher = 3159, 33 | FlourishingSaberDance = 3160, 34 | StarfallDance = 3161, 35 | HoningDance = 3162, 36 | Acclaim = 3163, 37 | HoningOvation = 3164; 38 | } 39 | public static class Config 40 | { 41 | public const string 42 | DNCPvP_WaltzThreshold = "DNCWaltzThreshold"; 43 | } 44 | 45 | internal class DNCPvP_BurstMode : CustomCombo 46 | { 47 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.DNCPvP_BurstMode; 48 | 49 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 50 | { 51 | if (actionID is Cascade or Fountain or ReverseCascade or Fountainfall) 52 | { 53 | bool starfallDanceReady = !GetCooldown(StarfallDance).IsCooldown; 54 | bool starfallDance = HasEffect(Buffs.StarfallDance); 55 | bool curingWaltzReady = !GetCooldown(CuringWaltz).IsCooldown; 56 | bool honingDanceReady = !GetCooldown(HoningDance).IsCooldown; 57 | var acclaimStacks = GetBuffStacks(Buffs.Acclaim); 58 | bool canWeave = CanWeave(actionID); 59 | var distance = GetTargetDistance(); 60 | var HPThreshold = PluginConfiguration.GetCustomIntValue(Config.DNCPvP_WaltzThreshold); 61 | var HP = PlayerHealthPercentageHp(); 62 | 63 | // Honing Dance Option 64 | if (IsEnabled(CustomComboPreset.DNCPvP_BurstMode_HoningDance) && honingDanceReady && HasTarget() && distance <= 5) 65 | { 66 | if (HasEffect(Buffs.Acclaim) && acclaimStacks < 4) 67 | return WHM.Assize; 68 | 69 | return HoningDance; 70 | } 71 | 72 | if (canWeave) 73 | { 74 | // Curing Waltz Option 75 | if (IsEnabled(CustomComboPreset.DNCPvP_BurstMode_CuringWaltz) && curingWaltzReady && HP <= HPThreshold) 76 | return OriginalHook(CuringWaltz); 77 | 78 | // Fan Dance weave 79 | if (IsOffCooldown(FanDance) && distance < 13) // 2y below max to avoid waste 80 | return OriginalHook(FanDance); 81 | } 82 | 83 | // Starfall Dance 84 | if (!starfallDance && starfallDanceReady && distance < 20) // 5y below max to avoid waste 85 | return OriginalHook(StarfallDance); 86 | } 87 | 88 | return actionID; 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /XIVSlothComboX/Window/TargetHelper.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Interface.Utility; 2 | using ECommons.DalamudServices; 3 | using FFXIVClientStructs.FFXIV.Client.UI; 4 | using FFXIVClientStructs.FFXIV.Component.GUI; 5 | using Dalamud.Bindings.ImGui; 6 | using System; 7 | using System.Numerics; 8 | using System.Runtime.InteropServices; 9 | using XIVSlothComboX.CustomComboNS.Functions; 10 | using XIVSlothComboX.Services; 11 | 12 | 13 | namespace XIVSlothComboX.Window; 14 | 15 | internal class TargetHelper : Dalamud.Interface.Windowing.Window 16 | { 17 | internal TargetHelper() : base("###SlothComboTargeteHelper", ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.AlwaysUseWindowPadding | ImGuiWindowFlags.AlwaysAutoResize, true) 18 | { 19 | this.IsOpen = true; 20 | this.RespectCloseHotkey = false; 21 | } 22 | 23 | internal unsafe void DrawTargetHelper() 24 | { 25 | if (Combos.JobHelpers.AST.AST_QuickTargetCards.SelectedRandomMember is not null) 26 | { 27 | IntPtr partyPTR = Svc.GameGui.GetAddonByName("_PartyList", 1); 28 | if (partyPTR == IntPtr.Zero) 29 | return; 30 | 31 | AddonPartyList plist = Marshal.PtrToStructure(partyPTR); 32 | if (!plist.IsVisible) return; 33 | 34 | for (int i = 1; i <= 8; i++) 35 | { 36 | if (CustomComboFunctions.GetPartySlot(i) is null) continue; 37 | if (CustomComboFunctions.GetPartySlot(i).GameObjectId == Combos.JobHelpers.AST.AST_QuickTargetCards.SelectedRandomMember.GameObjectId) 38 | { 39 | var member = i switch 40 | { 41 | 1 => plist.PartyMembers[0].TargetGlow, 42 | 2 => plist.PartyMembers[1].TargetGlow, 43 | 3 => plist.PartyMembers[2].TargetGlow, 44 | 4 => plist.PartyMembers[3].TargetGlow, 45 | 5 => plist.PartyMembers[4].TargetGlow, 46 | 6 => plist.PartyMembers[5].TargetGlow, 47 | 7 => plist.PartyMembers[6].TargetGlow, 48 | 8 => plist.PartyMembers[7].TargetGlow, 49 | _ => plist.PartyMembers[0].TargetGlow, 50 | }; 51 | 52 | DrawOutline(member->AtkResNode.PrevSiblingNode); 53 | 54 | } 55 | } 56 | } 57 | } 58 | 59 | private unsafe void DrawOutline(AtkResNode* node) 60 | { 61 | var position = GetNodePosition(node); 62 | var scale = GetNodeScale(node); 63 | var size = new Vector2(node->Width, node->Height) * scale; 64 | 65 | position += ImGuiHelpers.MainViewport.Pos; 66 | 67 | var colour = Service.Configuration.TargetHighlightColor; 68 | ImGui.GetForegroundDrawList(ImGuiHelpers.MainViewport).AddRect(position, position + size, ImGui.GetColorU32(colour), 0, ImDrawFlags.RoundCornersAll, 2); 69 | } 70 | public unsafe Vector2 GetNodePosition(AtkResNode* node) 71 | { 72 | var pos = new Vector2(node->X, node->Y); 73 | var par = node->ParentNode; 74 | while (par != null) 75 | { 76 | pos *= new Vector2(par->ScaleX, par->ScaleY); 77 | pos += new Vector2(par->X, par->Y); 78 | par = par->ParentNode; 79 | } 80 | 81 | return pos; 82 | } 83 | 84 | public unsafe Vector2 GetNodeScale(AtkResNode* node) 85 | { 86 | if (node == null) return new Vector2(1, 1); 87 | var scale = new Vector2(node->ScaleX, node->ScaleY); 88 | while (node->ParentNode != null) 89 | { 90 | node = node->ParentNode; 91 | scale *= new Vector2(node->ScaleX, node->ScaleY); 92 | } 93 | 94 | return scale; 95 | } 96 | 97 | public override void Draw() => DrawTargetHelper(); 98 | } -------------------------------------------------------------------------------- /XIVSlothComboX/CustomComboNS/Functions/Cooldown.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FFXIVClientStructs.FFXIV.Client.Game; 3 | using XIVSlothComboX.Data; 4 | using XIVSlothComboX.Services; 5 | 6 | namespace XIVSlothComboX.CustomComboNS.Functions 7 | { 8 | internal abstract partial class CustomComboFunctions 9 | { 10 | /// Gets the cooldown data for an action. 11 | /// Action ID to check. 12 | /// Cooldown data. 13 | public static CooldownData GetCooldown(uint actionID) => Service.ComboCache.GetCooldown(actionID); 14 | 15 | /// Gets the cooldown total remaining time. 16 | /// Action ID to check. 17 | /// Total remaining time of the cooldown. 18 | public static float GetCooldownRemainingTime(uint actionID) => Service.ComboCache.GetCooldown(actionID).CooldownRemaining; 19 | 20 | /// Gets the cooldown remaining time for the next charge. 21 | /// Action ID to check. 22 | /// Remaining time for the next charge of the cooldown. 23 | public static float GetCooldownChargeRemainingTime(uint actionID) => Service.ComboCache.GetCooldown(actionID).ChargeCooldownRemaining; 24 | 25 | /// Gets the elapsed cooldown time. 26 | /// Action ID to check 27 | /// Time passed since action went on cooldown. 28 | public static float GetCooldownElapsed(uint actionID) => Service.ComboCache.GetCooldown(actionID).CooldownElapsed; 29 | 30 | /// Gets a value indicating whether an action is on cooldown. 31 | /// Action ID to check. 32 | /// True or false. 33 | public static bool IsOnCooldown(uint actionID) => GetCooldown(actionID).IsCooldown; 34 | 35 | /// Gets a value indicating whether an action is off cooldown. 36 | /// Action ID to check. 37 | /// True or false. 38 | public static bool IsOffCooldown(uint actionID) => !GetCooldown(actionID).IsCooldown; 39 | 40 | /// Check if the Cooldown was just used. 41 | /// Action ID to check. 42 | /// Variance of how long to check the elapsed cooldown 43 | /// True or false. 44 | public static bool JustUsed(uint actionID, float variance = 3f) => GetMaxCharges(actionID) == 0 ? IsOnCooldown(actionID) && GetCooldownElapsed(actionID) <= variance : ActionWatching.ChargeTimestamps.ContainsKey(actionID) ? (Environment.TickCount64 - ActionWatching.ChargeTimestamps[actionID]) / 1000f <= variance : false; 45 | 46 | /// Gets a value indicating whether an action has any available charges. 47 | /// Action ID to check. 48 | /// True or false. 49 | public static bool HasCharges(uint actionID) => GetCooldown(actionID).RemainingCharges > 0; 50 | 51 | /// Get the current number of charges remaining for an action. 52 | /// Action ID to check. 53 | /// Number of charges. 54 | public static uint GetRemainingCharges(uint actionID) => GetCooldown(actionID).RemainingCharges; 55 | 56 | /// Get the maximum number of charges for an action. 57 | /// Action ID to check. 58 | /// Number of charges. 59 | public static ushort GetMaxCharges(uint actionID) => GetCooldown(actionID).MaxCharges; 60 | 61 | /// Get if an action is enabled. 62 | /// Action ID to check 63 | /// If the action is currently enabled. 64 | public unsafe static bool IsEnabled(uint actionID) => ActionManager.Instance()->GetActionStatus(ActionType.Action, actionID) == 0; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /XIVSlothComboX/Extensions/UIntExtensions.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.Combos.PvE; 2 | using XIVSlothComboX.CustomComboNS.Functions; 3 | using XIVSlothComboX.Data; 4 | 5 | namespace XIVSlothComboX.Extensions 6 | { 7 | internal static class UIntExtensions 8 | { 9 | internal static bool LevelChecked(this uint value) => CustomComboFunctions.LevelChecked(value); 10 | 11 | internal static bool TraitLevelChecked(this uint value) => CustomComboFunctions.TraitLevelChecked(value); 12 | 13 | internal static bool ActionReady(this uint value) => CustomComboFunctions.ActionReady(value); 14 | internal static uint OriginalHook(this uint value) => CustomComboFunctions.OriginalHook(value); 15 | 16 | internal static string ActionName(this uint value) => ActionWatching.GetActionName(value); 17 | 18 | 19 | internal static bool GCDActionReady(this uint value) 20 | { 21 | if (!CustomComboFunctions.LevelChecked(value)) 22 | { 23 | return false; 24 | } 25 | 26 | var gcdCooldownRemainingTime = CustomComboFunctions.GetCooldownRemainingTime(WAR.Tomahawk); 27 | var cooldownRemainingTime = CustomComboFunctions.GetCooldownRemainingTime(value); 28 | 29 | if (CustomComboFunctions.GetCooldownRemainingTime(value) <= 0 || cooldownRemainingTime - gcdCooldownRemainingTime <= 0) 30 | { 31 | return true; 32 | } 33 | 34 | if (CustomComboFunctions.GetMaxCharges(value) > 1) 35 | { 36 | if (cooldownRemainingTime - gcdCooldownRemainingTime <= CustomComboFunctions.GetCooldown(value).单次计时器 + 0.5f) 37 | { 38 | return true; 39 | } 40 | } 41 | 42 | 43 | return false; 44 | } 45 | 46 | 47 | 48 | internal static bool GCDActionReady(this uint value, uint gcdActionId) 49 | { 50 | if (!CustomComboFunctions.LevelChecked(value)) 51 | { 52 | return false; 53 | } 54 | 55 | if (CustomComboFunctions.GetCooldownRemainingTime(value) <= 0 56 | || (CustomComboFunctions.GetCooldownRemainingTime(value) <= CustomComboFunctions.GetCooldownRemainingTime(gcdActionId) 57 | && CustomComboFunctions.GetCooldownRemainingTime(gcdActionId) < 1.5f)) 58 | { 59 | return true; 60 | } 61 | return false; 62 | } 63 | 64 | 65 | internal static bool GCDActionReady(this uint value, uint gcdActionId, float RemainingTime) 66 | { 67 | if (!CustomComboFunctions.LevelChecked(value)) 68 | { 69 | return false; 70 | } 71 | 72 | if (CustomComboFunctions.GetCooldownRemainingTime(value) <= 0 73 | || (CustomComboFunctions.GetCooldownRemainingTime(value) <= CustomComboFunctions.GetCooldownRemainingTime(gcdActionId) 74 | && CustomComboFunctions.GetCooldownRemainingTime(gcdActionId) < RemainingTime)) 75 | { 76 | return true; 77 | } 78 | return false; 79 | } 80 | 81 | /// 82 | /// 能力技好之前的最后一个gcd 83 | /// 84 | /// 85 | /// 86 | /// 87 | internal static bool GCDActionPreReady(this uint value, uint ActionId) 88 | { 89 | if (!CustomComboFunctions.LevelChecked(value)) 90 | { 91 | return false; 92 | } 93 | 94 | if ((CustomComboFunctions.GetCooldownRemainingTime(ActionId) > 0 && CustomComboFunctions.GetCooldownRemainingTime(ActionId) < 2.5f) 95 | && CustomComboFunctions.GetCooldownRemainingTime(value) < 2f 96 | && CustomComboFunctions.GetCooldownRemainingTime(value) < CustomComboFunctions.GetCooldownRemainingTime(ActionId)) 97 | { 98 | return true; 99 | } 100 | return false; 101 | } 102 | } 103 | 104 | internal static class UShortExtensions 105 | { 106 | internal static string StatusName(this ushort value) => ActionWatching.GetStatusName(value); 107 | } 108 | } -------------------------------------------------------------------------------- /XIVSlothComboX/CustomComboNS/CustomCombo.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Utility; 2 | using FFXIVClientStructs.FFXIV.Client.Game; 3 | using XIVSlothComboX.Attributes; 4 | using XIVSlothComboX.Combos; 5 | using XIVSlothComboX.Combos.PvE; 6 | using XIVSlothComboX.CustomComboNS.Functions; 7 | using XIVSlothComboX.Services; 8 | 9 | namespace XIVSlothComboX.CustomComboNS 10 | { 11 | /// Base class for each combo. 12 | internal abstract partial class CustomCombo : CustomComboFunctions 13 | { 14 | /// Initializes a new instance of the class. 15 | protected CustomCombo() 16 | { 17 | CustomComboInfoAttribute? presetInfo = Preset.GetAttribute(); 18 | JobID = presetInfo.JobID; 19 | ClassID = JobID switch 20 | { 21 | ADV.JobID => ADV.ClassID, 22 | BLM.JobID => BLM.ClassID, 23 | BRD.JobID => BRD.ClassID, 24 | DRG.JobID => DRG.ClassID, 25 | MNK.JobID => MNK.ClassID, 26 | NIN.JobID => NIN.ClassID, 27 | PLD.JobID => PLD.ClassID, 28 | SCH.JobID => SCH.ClassID, 29 | SMN.JobID => SMN.ClassID, 30 | WAR.JobID => WAR.ClassID, 31 | WHM.JobID => WHM.ClassID, 32 | _ => 0xFF, 33 | }; 34 | 35 | // InitCustomTimeline(); 36 | 37 | // StartTimer(); 38 | } 39 | 40 | /// Gets the preset associated with this combo. 41 | protected internal abstract CustomComboPreset Preset { get; } 42 | 43 | /// Gets the class ID associated with this combo. 44 | protected byte ClassID { get; } 45 | 46 | /// Gets the job ID associated with this combo. 47 | protected byte JobID { get; } 48 | 49 | 50 | 51 | /// Performs various checks then attempts to invoke the combo. 52 | /// Starting action ID. 53 | /// Player level. 54 | /// Last combo action ID. 55 | /// Combo timer. 56 | /// Replacement action ID. 57 | /// True if the action has changed, otherwise false. 58 | 59 | public unsafe bool TryInvoke(uint actionID, byte level, uint lastComboMove, float comboTime, out uint newActionID) 60 | { 61 | newActionID = 0; 62 | 63 | if (!Service.ClientState.IsPvP && ActionManager.Instance()->QueuedActionType == ActionType.Action && ActionManager.Instance()->QueuedActionId != actionID) 64 | return false; 65 | 66 | 67 | UpdateCombatTimer(); 68 | 69 | if (!IsEnabled(Preset)) 70 | return false; 71 | 72 | 73 | uint classJobID = LocalPlayer!.ClassJob.RowId; 74 | 75 | if (classJobID is >= 8 and <= 15) 76 | classJobID = DOH.JobID; 77 | 78 | if (classJobID is >= 16 and <= 18) 79 | classJobID = DOL.JobID; 80 | 81 | if (JobID != ADV.JobID && ClassID != ADV.ClassID && 82 | JobID != classJobID && ClassID != classJobID) 83 | return false; 84 | 85 | uint resultingActionID = Invoke(actionID, lastComboMove, comboTime, level); 86 | 87 | 88 | if (resultingActionID == 0 || actionID == resultingActionID) 89 | return false; 90 | 91 | newActionID = resultingActionID; 92 | 93 | 94 | return true; 95 | } 96 | 97 | /// Invokes the combo. 98 | /// Starting action ID. 99 | /// Last combo action. 100 | /// Current combo time. 101 | /// Current player level. 102 | /// The replacement action ID. 103 | protected abstract uint Invoke(uint actionID, uint lastComboActionID, float comboTime, byte level); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/GNBPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class GNBPvP 6 | { 7 | 8 | public const uint 9 | KeenEdge = 29098, 10 | BrutalShell = 29099, 11 | SolidBarrel = 29100, 12 | GnashingFang = 29102, 13 | SavageClaw = 29103, 14 | WickedTalon = 29104, 15 | DoubleDown = 29105, 16 | Continuation = 29106, 17 | JugularRip = 29108, 18 | AbdomenTear = 29109, 19 | EyeGouge = 29110, 20 | RoughDivide = 29123, 21 | DrawAndJunction = 29124, 22 | JunctionedCast = 29125; 23 | 24 | internal class Debuffs 25 | { 26 | internal const ushort 27 | Stun = 1343; 28 | } 29 | 30 | internal class Buffs 31 | { 32 | internal const ushort 33 | ReadyToRip = 2002, 34 | ReadyToTear = 2003, 35 | ReadyToGouge = 2004, 36 | ReadyToBlast = 3041, 37 | NoMercy = 3042, 38 | PowderBarrel = 3043, 39 | JunctionTank = 3044, 40 | JunctionDPS = 3045, 41 | JunctionHealer = 3046; 42 | } 43 | 44 | internal class GNBPvP_Burst : CustomCombo 45 | { 46 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.GNBPvP_Burst; 47 | 48 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 49 | { 50 | if (actionID is KeenEdge or BrutalShell or SolidBarrel) 51 | { 52 | // Buff Effects 53 | if (CanWeave(actionID)) 54 | { 55 | if (IsEnabled(CustomComboPreset.GNBPvP_ST_Continuation) && 56 | (HasEffect(Buffs.ReadyToBlast) || HasEffect(Buffs.ReadyToRip) || HasEffect(Buffs.ReadyToTear) || HasEffect(Buffs.ReadyToGouge))) 57 | return OriginalHook(Continuation); 58 | 59 | if ((IsEnabled(CustomComboPreset.GNBPvP_JunctionDPS) && HasEffect(Buffs.JunctionDPS) && HasEffect(Buffs.NoMercy) && IsOffCooldown(JunctionedCast)) || 60 | (IsEnabled(CustomComboPreset.GNBPvP_JunctionHealer) && HasEffect(Buffs.JunctionHealer) && IsOffCooldown(JunctionedCast)) || 61 | (IsEnabled(CustomComboPreset.GNBPvP_JunctionTank) && HasEffect(Buffs.JunctionTank) && IsOffCooldown(JunctionedCast))) 62 | return OriginalHook(JunctionedCast); 63 | 64 | if (IsEnabled(CustomComboPreset.GNBPvP_DrawAndJunction) && IsOffCooldown(DrawAndJunction) && !HasEffect(Buffs.PowderBarrel) && !HasEffect(Buffs.ReadyToBlast)) 65 | return DrawAndJunction; 66 | 67 | if (IsEnabled(CustomComboPreset.GNBPvP_RoughDivide) && HasEffect(Buffs.NoMercy) && 68 | GetBuffRemainingTime(Buffs.NoMercy) <= 1.5f && GetBuffRemainingTime(Buffs.NoMercy) > 0 && 69 | GetRemainingCharges(RoughDivide) == 1) 70 | return RoughDivide; 71 | } 72 | 73 | // Gnashing Fang 74 | if (IsEnabled(CustomComboPreset.GNBPvP_DoubleDown) && HasEffect(Buffs.NoMercy) && IsOffCooldown(DoubleDown)) 75 | return DoubleDown; 76 | 77 | if ((IsEnabled(CustomComboPreset.GNBPvP_ST_GnashingFang) && IsOffCooldown(GnashingFang) && HasEffect(Buffs.NoMercy)) || 78 | WasLastWeaponskill(GnashingFang) || WasLastWeaponskill(JugularRip) || WasLastWeaponskill(SavageClaw)) 79 | return OriginalHook(GnashingFang); 80 | } 81 | 82 | return actionID; 83 | } 84 | } 85 | internal class GNBPvP_GnashingFang : CustomCombo 86 | { 87 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.GNBPvP_GnashingFang; 88 | 89 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) => 90 | actionID is GnashingFang && 91 | CanWeave(actionID) && (HasEffect(Buffs.ReadyToRip) || HasEffect(Buffs.ReadyToTear) || HasEffect(Buffs.ReadyToGouge)) 92 | ? OriginalHook(Continuation) 93 | : actionID; 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Services/IconManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Dalamud.Game; 6 | using Dalamud.Interface.Textures; 7 | using Dalamud.Interface.Textures.TextureWraps; 8 | using Lumina.Data.Files; 9 | using Action = Lumina.Excel.Sheets.Action; 10 | 11 | namespace XIVSlothComboX.Services; 12 | 13 | using Action = Action; 14 | 15 | 16 | public class IconManager : IDisposable { 17 | private bool disposed; 18 | private readonly Dictionary<(uint, bool), IDalamudTextureWrap> iconTextures = new(); 19 | private readonly Dictionary actionCustomIcons = new() { 20 | 21 | }; 22 | 23 | 24 | 25 | 26 | public IconManager() { 27 | 28 | } 29 | 30 | public void Dispose() { 31 | disposed = true; 32 | var c = 0; 33 | foreach (var texture in iconTextures.Values.Where(texture => texture != null)) { 34 | c++; 35 | texture.Dispose(); 36 | } 37 | 38 | iconTextures.Clear(); 39 | 40 | } 41 | 42 | private void LoadIconTexture(uint iconId, bool hq = false) { 43 | Task.Run(() => { 44 | try { 45 | 46 | var gameIconLookup = new GameIconLookup(iconId); 47 | 48 | var dalamudTextureWrap = Service.TextureProvider.GetFromGameIcon(gameIconLookup).RentAsync().Result; 49 | 50 | 51 | var tex = dalamudTextureWrap; 52 | 53 | if (tex.Handle != IntPtr.Zero) { 54 | this.iconTextures[(iconId, hq)] = tex; 55 | } else { 56 | tex.Dispose(); 57 | } 58 | } catch (Exception ex) { 59 | } 60 | }); 61 | } 62 | 63 | public TexFile GetIcon(uint iconId, bool hq = false) => this.GetIcon(Service.DataManager.Language, iconId, hq); 64 | 65 | /// 66 | /// Get a containing the icon with the given ID, of the given language. 67 | /// 68 | /// The requested language. 69 | /// The icon ID. 70 | /// The containing the icon. 71 | public TexFile GetIcon(ClientLanguage iconLanguage, uint iconId, bool hq = false) 72 | { 73 | string type; 74 | switch (iconLanguage) 75 | { 76 | case ClientLanguage.Japanese: 77 | type = "ja/"; 78 | break; 79 | case ClientLanguage.English: 80 | type = "en/"; 81 | break; 82 | case ClientLanguage.German: 83 | type = "de/"; 84 | break; 85 | case ClientLanguage.French: 86 | type = "fr/"; 87 | break; 88 | 89 | default: 90 | type = "/"; 91 | break; 92 | } 93 | return this.GetIcon(type, iconId, hq); 94 | } 95 | 96 | public TexFile GetIcon(string type, uint iconId, bool hq = false) 97 | { 98 | if (type == null) 99 | type = string.Empty; 100 | if (type.Length > 0 && !type.EndsWith("/")) 101 | type += "/"; 102 | 103 | var formatStr = $"ui/icon/{{0:D3}}000/{(hq?"hq/":"")}{{1}}{{2:D6}}.tex"; 104 | TexFile file = Service.DataManager.GetFile(string.Format(formatStr, (object) (iconId / 1000), (object) type, (object) iconId)); 105 | return file != null || type.Length <= 0 ? file : Service.DataManager.GetFile(string.Format(formatStr, (object) (iconId / 1000), (object) string.Empty, (object) iconId)); 106 | } 107 | 108 | 109 | public IDalamudTextureWrap? GetActionIcon(Action action) { 110 | return GetIconTexture(actionCustomIcons.ContainsKey(action.RowId) ? actionCustomIcons[action.RowId] : action.Icon); 111 | } 112 | 113 | public ushort GetActionIconId(Action action) { 114 | return actionCustomIcons.ContainsKey(action.RowId) ? actionCustomIcons[action.RowId] : action.Icon; 115 | } 116 | 117 | public IDalamudTextureWrap? GetIconTexture(uint iconId, bool hq = false) { 118 | if (this.disposed) 119 | return null; 120 | if (this.iconTextures.ContainsKey((iconId, hq))) 121 | return this.iconTextures[(iconId, hq)]; 122 | this.iconTextures.Add((iconId, hq), null); 123 | LoadIconTexture(iconId, hq); 124 | return this.iconTextures[(iconId, hq)]; 125 | } 126 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/ASTPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class ASTPvP 6 | { 7 | internal const byte JobID = 33; 8 | 9 | internal const uint 10 | Malefic = 29242, 11 | AspectedBenefic = 29243, 12 | Gravity = 29244, 13 | DoubleCast = 29245, 14 | DoubleMalefic = 29246, 15 | NocturnalBenefic = 29247, 16 | DoubleGravity = 29248, 17 | Draw = 29249, 18 | Macrocosmos = 29253, 19 | Microcosmos = 29254; 20 | 21 | internal class Buffs 22 | { 23 | internal const ushort 24 | BalanceDrawn = 3101, 25 | BoleDrawn = 3403, 26 | ArrowDrawn = 3404, 27 | Arrow = 3402, 28 | Balance = 1338, 29 | Bole = 1339; 30 | } 31 | 32 | internal class ASTPvP_Burst : CustomCombo 33 | { 34 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.ASTPvP_Burst; 35 | 36 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 37 | { 38 | if (actionID is Malefic) 39 | { 40 | // Out of combat Draw 41 | if (IsOffCooldown(Draw) && !InCombat()) 42 | return Draw; 43 | 44 | // Malefic to initiate combat 45 | if (!InCombat() && 46 | (IsOffCooldown(Draw) || HasEffect(Buffs.BoleDrawn) || HasEffect(Buffs.ArrowDrawn))) 47 | return Malefic; 48 | 49 | // Post-Draw Malefic 50 | if (lastComboMove == Draw && !CanWeave(actionID)) 51 | return Malefic; 52 | 53 | // Play "The Balance" before a Gravity/Double + Macro burst 54 | if (HasCharges(DoubleCast) && IsOffCooldown(Gravity) && IsOffCooldown(Macrocosmos) && HasEffect(Buffs.BalanceDrawn)) 55 | return OriginalHook(Draw); 56 | 57 | if (!TargetHasEffectAny(PvPCommon.Buffs.Guard)) 58 | { 59 | if (lastComboMove == DoubleGravity && IsOffCooldown(Macrocosmos)) 60 | return Macrocosmos; 61 | 62 | if (lastComboMove == Gravity && HasCharges(DoubleCast)) 63 | return DoubleGravity; 64 | 65 | if (IsOffCooldown(Gravity)) 66 | return Gravity; 67 | 68 | if (lastComboMove == Malefic && (GetRemainingCharges(DoubleCast) > 1 || 69 | GetCooldownRemainingTime(Gravity) > 7.5f) && CanWeave(actionID)) 70 | return DoubleMalefic; 71 | } 72 | 73 | // Card waste prevention 74 | if (((GetBuffRemainingTime(Buffs.BalanceDrawn) < 3) || 75 | (GetBuffRemainingTime(Buffs.BoleDrawn) < 3) || 76 | (GetBuffRemainingTime(Buffs.ArrowDrawn) < 3)) && 77 | CanWeave(actionID)) 78 | return OriginalHook(Draw); 79 | 80 | // Generic Draw 81 | if (IsOffCooldown(Draw) && CanWeave(actionID)) 82 | return Draw; 83 | 84 | // Generic Play outside of necessary holding 85 | if (CanWeave(actionID) && GetCooldownRemainingTime(Macrocosmos) > 7.5f && (IsOffCooldown(Draw) || 86 | HasEffect(Buffs.BalanceDrawn) || HasEffect(Buffs.BoleDrawn) || HasEffect(Buffs.ArrowDrawn))) 87 | return OriginalHook(Draw); 88 | } 89 | 90 | return actionID; 91 | } 92 | 93 | internal class ASTPvP_Heal : CustomCombo 94 | { 95 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.ASTPvP_Heal; 96 | 97 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 98 | { 99 | if (actionID is AspectedBenefic && CanWeave(actionID) && 100 | lastComboMove == AspectedBenefic && 101 | HasCharges(DoubleCast)) 102 | return OriginalHook(DoubleCast); 103 | 104 | return actionID; 105 | } 106 | } 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/BLMPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class BLMPvP 6 | { 7 | public const uint 8 | Fire = 29649, 9 | Blizzard = 29653, 10 | Burst = 29657, 11 | Paradox = 29663, 12 | NightWing = 29659, 13 | AetherialManipulation = 29660, 14 | Superflare = 29661, 15 | Fire4 = 29650, 16 | Flare = 29651, 17 | Blizzard4 = 29654, 18 | Freeze = 29655, 19 | Foul = 29371; 20 | 21 | public static class Buffs 22 | { 23 | public const ushort 24 | AstralFire2 = 3212, 25 | AstralFire3 = 3213, 26 | UmbralIce2 = 3214, 27 | UmbralIce3 = 3215, 28 | Burst = 3221, 29 | SoulResonance = 3222, 30 | Polyglot = 3169; 31 | } 32 | 33 | public static class Debuffs 34 | { 35 | public const ushort 36 | AstralWarmth = 3216, 37 | UmbralFreeze = 3217, 38 | Burns = 3218, 39 | DeepFreeze = 3219; 40 | } 41 | 42 | internal class BLMPvP_BurstMode : CustomCombo 43 | { 44 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.BLMPvP_BurstMode; 45 | 46 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 47 | { 48 | if (actionID is Fire or Fire4 or Flare) 49 | { 50 | bool canWeave = CanSpellWeave(actionID); 51 | 52 | if (HasEffect(Buffs.Polyglot)) 53 | return Foul; 54 | 55 | if (IsEnabled(CustomComboPreset.BLMPvP_BurstMode_AetherialManip) && 56 | GetCooldown(AetherialManipulation).RemainingCharges > 0 && 57 | !InMeleeRange() && IsOffCooldown(Burst) && canWeave) 58 | return AetherialManipulation; 59 | 60 | if (InMeleeRange() && 61 | IsOffCooldown(Burst)) 62 | return Burst; 63 | 64 | if (!TargetHasEffect(Debuffs.AstralWarmth)) 65 | return OriginalHook(Fire); 66 | 67 | if (FindTargetEffect(Debuffs.AstralWarmth).Param < 3 && 68 | IsOffCooldown(Paradox)) 69 | return Paradox; 70 | 71 | if (IsEnabled(CustomComboPreset.BLMPvP_BurstMode_NightWing) && 72 | IsOffCooldown(NightWing)) 73 | return NightWing; 74 | 75 | if (FindTargetEffect(Debuffs.AstralWarmth).Param == 3 && 76 | GetCooldown(Superflare).RemainingCharges > 0 && 77 | !TargetHasEffect(Debuffs.Burns)) 78 | return Superflare; 79 | 80 | } 81 | 82 | if (actionID is Blizzard or Blizzard4 or Freeze) 83 | { 84 | bool canWeave = CanSpellWeave(actionID); 85 | 86 | if (HasEffect(Buffs.Polyglot)) 87 | return Foul; 88 | 89 | if (IsEnabled(CustomComboPreset.BLMPvP_BurstMode_AetherialManip) && 90 | GetCooldown(AetherialManipulation).RemainingCharges > 0 && 91 | !InMeleeRange() && 92 | IsOffCooldown(Burst) && 93 | canWeave) 94 | return AetherialManipulation; 95 | 96 | if (InMeleeRange() && 97 | IsOffCooldown(Burst)) 98 | return Burst; 99 | 100 | if (!TargetHasEffect(Debuffs.UmbralFreeze)) 101 | return OriginalHook(Blizzard); 102 | 103 | if (FindTargetEffect(Debuffs.UmbralFreeze).Param < 3 && 104 | IsOffCooldown(Paradox)) 105 | return Paradox; 106 | 107 | if (IsEnabled(CustomComboPreset.BLMPvP_BurstMode_NightWing) && 108 | IsOffCooldown(NightWing)) 109 | return NightWing; 110 | 111 | if (FindTargetEffect(Debuffs.UmbralFreeze).Param == 3 && 112 | GetCooldown(Superflare).RemainingCharges > 0 && 113 | !TargetHasEffect(Debuffs.DeepFreeze)) 114 | return Superflare; 115 | } 116 | 117 | return actionID; 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/PCTPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | using XIVSlothComboX.CustomComboNS.Functions; 3 | 4 | namespace XIVSlothComboX.Combos.PvP 5 | { 6 | internal static class PCTPvP 7 | { 8 | public const byte JobID = 42; 9 | 10 | internal const uint 11 | FireInRed = 39191, 12 | AeroInGreen = 39192, 13 | WaterInBlue = 39193, 14 | HolyInWhite = 39198, 15 | CreatureMotif = 39204, 16 | LivingMuse = 39209, 17 | TemperaCoat = 39211, 18 | SubtractivePalette = 39213, 19 | StarPrism = 39216, 20 | MogOfTheAges = 39782; 21 | 22 | internal class Buffs 23 | { 24 | internal const ushort 25 | PomMotif = 4105, 26 | WingMotif = 4106, 27 | ClawMotif = 4107, 28 | MawMotif = 4108, 29 | TemperaCoat = 4114, 30 | Starstruck = 4118, 31 | MooglePortrait = 4103, 32 | MadeenPortrait = 4104, 33 | SubtractivePalette = 4102; 34 | } 35 | 36 | internal class Config 37 | { 38 | internal static UserInt 39 | PCTPvP_BurstHP = new("PCTPvP_BurstHP", 100), 40 | PCTPvP_TemperaHP = new("PCTPvP_TemperaHP", 50); 41 | } 42 | 43 | internal class PCTPvP_Burst : CustomCombo 44 | { 45 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.PCTPvP_Burst; 46 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 47 | { 48 | #region Variables 49 | bool isMoving = IsMoving(); 50 | bool hasTarget = HasTarget(); 51 | bool hasStarPrism = HasEffect(Buffs.Starstruck); 52 | bool targetHasGuard = TargetHasEffectAny(PvPCommon.Buffs.Guard); 53 | bool hasSubtractivePalette = HasEffect(Buffs.SubtractivePalette); 54 | bool hasPortrait = HasEffect(Buffs.MooglePortrait) || HasEffect(Buffs.MadeenPortrait); 55 | bool isStarPrismExpiring = HasEffect(Buffs.Starstruck) && GetBuffRemainingTime(Buffs.Starstruck) <= 3; 56 | bool isTemperaCoatExpiring = HasEffect(Buffs.TemperaCoat) && GetBuffRemainingTime(Buffs.TemperaCoat) <= 3; 57 | bool hasMotifDrawn = HasEffect(Buffs.PomMotif) || HasEffect(Buffs.WingMotif) || HasEffect(Buffs.ClawMotif) || HasEffect(Buffs.MawMotif); 58 | bool isBurstControlled = IsNotEnabled(CustomComboPreset.PCTPvP_BurstControl) || (IsEnabled(CustomComboPreset.PCTPvP_BurstControl) && GetTargetHPPercent() < Config.PCTPvP_BurstHP); 59 | #endregion 60 | 61 | if (actionID is FireInRed or AeroInGreen or WaterInBlue) 62 | { 63 | // Tempera Coat / Tempera Grassa 64 | if (IsEnabled(CustomComboPreset.PCTPvP_TemperaCoat) && ((IsOffCooldown(TemperaCoat) && 65 | InCombat() && PlayerHealthPercentageHp() < Config.PCTPvP_TemperaHP) || isTemperaCoatExpiring)) 66 | return OriginalHook(TemperaCoat); 67 | 68 | if (hasTarget && !targetHasGuard) 69 | { 70 | // Star Prism 71 | if (hasStarPrism && (isBurstControlled || isStarPrismExpiring)) 72 | return StarPrism; 73 | 74 | // Moogle / Madeen Portrait 75 | if (hasPortrait && isBurstControlled) 76 | return OriginalHook(MogOfTheAges); 77 | 78 | // Living Muse 79 | if (hasMotifDrawn && HasCharges(OriginalHook(LivingMuse)) && isBurstControlled) 80 | return OriginalHook(LivingMuse); 81 | 82 | // Holy in White / Comet in Black 83 | if (HasCharges(OriginalHook(HolyInWhite)) && isBurstControlled) 84 | return OriginalHook(HolyInWhite); 85 | } 86 | 87 | // Creature Motif 88 | if (!hasMotifDrawn && !IsMoving()) 89 | return OriginalHook(CreatureMotif); 90 | 91 | // Subtractive Palette 92 | if (IsEnabled(CustomComboPreset.PCTPvP_SubtractivePalette) && IsOffCooldown(OriginalHook(SubtractivePalette)) && 93 | hasTarget && ((IsMoving() && hasSubtractivePalette) || (!IsMoving() && !hasSubtractivePalette))) 94 | return OriginalHook(SubtractivePalette); 95 | } 96 | 97 | return actionID; 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Services/Service.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using Dalamud.Interface.Windowing; 5 | using Dalamud.IoC; 6 | using Dalamud.Plugin; 7 | using Dalamud.Plugin.Services; 8 | using XIVSlothComboX.Core; 9 | using XIVSlothComboX.Data; 10 | using XIVSlothComboX.Window; 11 | 12 | namespace XIVSlothComboX.Services 13 | { 14 | /// Dalamud and plugin services. 15 | internal class Service 16 | { 17 | 18 | 19 | 20 | /// Gets or sets the plugin address resolver. 21 | internal static PluginAddressResolver Address { get; set; } = null!; 22 | 23 | /// Gets the Dalamud buddy list. 24 | [PluginService] 25 | internal static IBuddyList BuddyList { get; private set; } = null!; 26 | 27 | /// Gets the Dalamud chat gui. 28 | [PluginService] 29 | internal static IChatGui ChatGui { get; private set; } = null!; 30 | 31 | /// Gets the Dalamud client state. 32 | [PluginService] 33 | internal static IClientState ClientState { get; private set; } = null!; 34 | 35 | /// Gets or sets the plugin caching mechanism. 36 | internal static CustomComboCache ComboCache { get; set; } = null!; 37 | 38 | /// Gets the Dalamud command manager. 39 | [PluginService] 40 | internal static ICommandManager CommandManager { get; private set; } = null!; 41 | 42 | /// Gets the Dalamud condition. 43 | [PluginService] 44 | internal static ICondition Condition { get; private set; } = null!; 45 | 46 | /// Gets or sets the plugin configuration. 47 | internal static PluginConfiguration Configuration { get; set; } = null!; 48 | 49 | /// Gets the Dalamud data manager. 50 | [PluginService] 51 | internal static IDataManager DataManager { get; private set; } = null!; 52 | 53 | /// Gets the Dalamud framework manager. 54 | [PluginService] 55 | internal static IFramework Framework { get; private set; } = null!; 56 | 57 | /// Handles the in-game UI. 58 | [PluginService] 59 | internal static IGameGui GameGui { get; private set; } = null!; 60 | 61 | /// Gets or sets the plugin icon replacer. 62 | internal static IconReplacer IconReplacer { get; set; } = null!; 63 | 64 | /// Gets the Dalamud plugin interface. 65 | [PluginService] 66 | internal static IDalamudPluginInterface Interface { get; private set; } = null!; 67 | 68 | /// Gets the Dalamud job gauges. 69 | [PluginService] 70 | internal static IJobGauges JobGauges { get; private set; } = null!; 71 | 72 | /// Gets the Dalamud object table. 73 | [PluginService] 74 | internal static IObjectTable ObjectTable { get; private set; } = null!; 75 | 76 | /// Returns the Plugin Folder location 77 | public static string PluginFolder 78 | { 79 | get 80 | { 81 | string codeBase = Assembly.GetExecutingAssembly().Location; 82 | UriBuilder uri = new(codeBase); 83 | string path = Uri.UnescapeDataString(uri.Path); 84 | return Path.GetDirectoryName(path)!; 85 | } 86 | } 87 | 88 | /// Gets the Dalamud party list. 89 | [PluginService] 90 | internal static IPartyList PartyList { get; private set; } = null!; 91 | 92 | /// Facilitates searching for memory signatures. 93 | [PluginService] 94 | internal static ISigScanner SigScanner { get; private set; } = null!; 95 | 96 | /// Gets the Dalamud target manager. 97 | [PluginService] 98 | internal static ITargetManager TargetManager { get; private set; } = null!; 99 | 100 | [PluginService] 101 | internal static IGameInteropProvider GameInteropProvider { get; private set; } = null!; 102 | 103 | [PluginService] 104 | internal static IPluginLog PluginLog { get; private set; } = null!; 105 | [PluginService] 106 | internal static ITextureProvider TextureProvider { get; private set; } = null!; 107 | 108 | internal static WindowSystem WindowSystem { get; set; } = null!; 109 | 110 | internal static IconManager IconManager { get; set; } = null!; 111 | [PluginService] 112 | internal static IDutyState DutyState { get; private set; } = null!; 113 | 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/SMNPvP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.Core; 2 | using XIVSlothComboX.CustomComboNS; 3 | 4 | namespace XIVSlothComboX.Combos.PvP 5 | { 6 | internal static class SMNPvP 7 | { 8 | public const byte ClassID = 26; 9 | public const byte JobID = 27; 10 | 11 | internal const uint 12 | Ruin3 = 29664, 13 | AstralImpulse = 29665, 14 | FountainOfFire = 29666, 15 | CrimsonCyclone = 29667, 16 | CrimsonStrike = 29668, 17 | Slipstream = 29669, 18 | RadiantAegis = 29670, 19 | MountainBuster = 29671, 20 | Fester = 29672, 21 | EnkindleBahamut = 29674, 22 | Megaflare = 29675, // unused 23 | Wyrmwave = 29676, // unused 24 | AkhMorn = 29677, // unused 25 | EnkindlePhoenix = 29679, 26 | ScarletFlame = 29681, // unused 27 | Revelation = 29682; // unused 28 | 29 | public static class Config 30 | { 31 | public const string 32 | SMNPvP_RadiantAegisThreshold = "SMNPvP_RadiantAegisThreshold"; 33 | public const string 34 | SMNPvP_FesterThreshold = "SMNPvP_FesterThreshold"; 35 | } 36 | 37 | internal class SMNPvP_BurstMode : CustomCombo 38 | { 39 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.SMNPvP_BurstMode; 40 | 41 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 42 | { 43 | if (actionID is Ruin3) 44 | { 45 | #region Types 46 | bool canWeave = CanWeave(actionID); 47 | bool bahamutBurst = OriginalHook(Ruin3) is AstralImpulse; 48 | bool phoenixBurst = OriginalHook(Ruin3) is FountainOfFire; 49 | double playerHP = PlayerHealthPercentageHp(); 50 | double targetHP = GetTargetHPPercent(); 51 | bool enemyGuarded = TargetHasEffectAny(PvPCommon.Buffs.Guard); 52 | bool canBind = !TargetHasEffectAny(PvPCommon.Debuffs.Bind); 53 | int radiantThreshold = PluginConfiguration.GetCustomIntValue(Config.SMNPvP_RadiantAegisThreshold); 54 | int festerThreshold = PluginConfiguration.GetCustomIntValue(Config.SMNPvP_FesterThreshold); 55 | #endregion 56 | 57 | if (canWeave) 58 | { 59 | // Radiant Aegis 60 | if (IsEnabled(CustomComboPreset.SMNPvP_BurstMode_RadiantAegis) && 61 | IsOffCooldown(RadiantAegis) && playerHP <= radiantThreshold) 62 | return RadiantAegis; 63 | 64 | // Fester 65 | if (HasCharges(Fester) && targetHP <= festerThreshold && !enemyGuarded && 66 | !(phoenixBurst || bahamutBurst)) // Lazy method for correct (?) priority 67 | return Fester; 68 | } 69 | 70 | // Phoenix & Bahamut bursts 71 | if (phoenixBurst || bahamutBurst) 72 | { 73 | if (!enemyGuarded && canWeave) 74 | { 75 | if (IsOffCooldown(EnkindlePhoenix) && phoenixBurst) 76 | return EnkindlePhoenix; 77 | if (IsOffCooldown(EnkindleBahamut) && bahamutBurst) 78 | return EnkindleBahamut; 79 | if (HasCharges(Fester) && targetHP <= festerThreshold) 80 | return Fester; 81 | if (IsOffCooldown(MountainBuster)) 82 | return MountainBuster; 83 | } 84 | } 85 | 86 | // Titan 87 | if (IsOffCooldown(MountainBuster) && canWeave && canBind && !enemyGuarded) 88 | return MountainBuster; 89 | 90 | // Ifrit 91 | if (!enemyGuarded) 92 | { 93 | if (OriginalHook(CrimsonCyclone) is CrimsonStrike) 94 | return CrimsonStrike; 95 | if (IsOffCooldown(CrimsonCyclone) && InMeleeRange()) 96 | return CrimsonCyclone; 97 | } 98 | 99 | // Garuda 100 | if (IsOffCooldown(Slipstream)) 101 | return Slipstream; 102 | } 103 | 104 | return actionID; 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /XIVSlothComboX/Core/Presets.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Utility; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using XIVSlothComboX.Attributes; 8 | using XIVSlothComboX.Combos; 9 | using XIVSlothComboX.Services; 10 | 11 | namespace XIVSlothComboX.Core 12 | { 13 | internal static class PresetStorage 14 | { 15 | private static HashSet? PvPCombos; 16 | private static HashSet? VariantCombos; 17 | private static HashSet? BozjaCombos; 18 | private static HashSet? EurekaCombos; 19 | private static Dictionary? ConflictingCombos; 20 | private static Dictionary? ParentCombos; // child: parent 21 | 22 | public static void Init() 23 | { 24 | // Secret combos 25 | PvPCombos = Enum.GetValues() 26 | .Where(preset => preset.GetAttribute() != default) 27 | .ToHashSet(); 28 | 29 | VariantCombos = Enum.GetValues() 30 | .Where(preset => preset.GetAttribute() != default) 31 | .ToHashSet(); 32 | 33 | BozjaCombos = Enum.GetValues() 34 | .Where(preset => preset.GetAttribute() != default) 35 | .ToHashSet(); 36 | 37 | EurekaCombos = Enum.GetValues() 38 | .Where(preset => preset.GetAttribute() != default) 39 | .ToHashSet(); 40 | 41 | // Conflicting combos 42 | ConflictingCombos = Enum.GetValues() 43 | .ToDictionary( 44 | preset => preset, 45 | preset => preset.GetAttribute()?.ConflictingPresets ?? Array.Empty()); 46 | 47 | // Parent combos 48 | ParentCombos = Enum.GetValues() 49 | .ToDictionary( 50 | preset => preset, 51 | preset => preset.GetAttribute()?.ParentPreset); 52 | } 53 | 54 | 55 | /// Gets a value indicating whether a preset is enabled. 56 | /// Preset to check. 57 | /// The boolean representation. 58 | public static bool IsEnabled(CustomComboPreset preset) => Service.Configuration.EnabledActions.Contains(preset); 59 | 60 | /// Gets a value indicating whether a preset is secret. 61 | /// Preset to check. 62 | /// The boolean representation. 63 | public static bool IsPvP(CustomComboPreset preset) => PvPCombos.Contains(preset); 64 | 65 | /// Gets a value indicating whether a preset is secret. 66 | /// Preset to check. 67 | /// The boolean representation. 68 | public static bool IsVariant(CustomComboPreset preset) => VariantCombos.Contains(preset); 69 | 70 | /// Gets a value indicating whether a preset is secret. 71 | /// Preset to check. 72 | /// The boolean representation. 73 | public static bool IsBozja(CustomComboPreset preset) => BozjaCombos.Contains(preset); 74 | 75 | /// Gets a value indicating whether a preset is secret. 76 | /// Preset to check. 77 | /// The boolean representation. 78 | public static bool IsEureka(CustomComboPreset preset) => EurekaCombos.Contains(preset); 79 | 80 | /// Gets the parent combo preset if it exists, or null. 81 | /// Preset to check. 82 | /// The parent preset. 83 | public static CustomComboPreset? GetParent(CustomComboPreset preset) => ParentCombos[preset]; 84 | 85 | /// Gets an array of conflicting combo presets. 86 | /// Preset to check. 87 | /// The conflicting presets. 88 | public static CustomComboPreset[] GetConflicts(CustomComboPreset preset) => ConflictingCombos[preset]; 89 | 90 | /// Gets the full list of conflicted combos. 91 | public static List GetAllConflicts() => ConflictingCombos.Keys.ToList(); 92 | 93 | /// Get all the info from conflicted combos. 94 | public static List GetAllConflictOriginals() => ConflictingCombos.Values.ToList(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/SAMPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.Combos.PvE; 2 | using XIVSlothComboX.Core; 3 | using XIVSlothComboX.CustomComboNS; 4 | 5 | namespace XIVSlothComboX.Combos.PvP 6 | { 7 | internal static class SAMPvP 8 | { 9 | public const byte JobID = 34; 10 | 11 | public const uint 12 | KashakCombo = 58, 13 | Yukikaze = 29523, 14 | Gekko = 29524, 15 | Kasha = 29525, 16 | Hyosetsu = 29526, 17 | Mangetsu = 29527, 18 | Oka = 29528, 19 | OgiNamikiri = 29530, 20 | Soten = 29532, 21 | Chiten = 29533, 22 | Mineuchi = 29535, 23 | MeikyoShisui = 29536, 24 | Midare = 29529, 25 | Kaeshi = 29531, 26 | Zantetsuken = 29537; 27 | 28 | public static class Buffs 29 | { 30 | public const ushort 31 | Kaiten = 3201, 32 | Midare = 3203; 33 | } 34 | 35 | public static class Debuffs 36 | { 37 | public const ushort 38 | Kuzushi = 3202; 39 | } 40 | 41 | public static class Config 42 | { 43 | public const string 44 | SAMPvP_SotenCharges = "SamSotenCharges", 45 | SAMPvP_SotenHP = "SamSotenHP"; 46 | 47 | } 48 | 49 | internal class SAMPvP_BurstMode : CustomCombo 50 | { 51 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.SAMPvP_BurstMode; 52 | 53 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 54 | { 55 | var sotenCharges = PluginConfiguration.GetCustomIntValue(Config.SAMPvP_SotenCharges); 56 | 57 | if ((IsNotEnabled(CustomComboPreset.SAMPvP_BurstMode_MainCombo) && actionID == MeikyoShisui) || 58 | (IsEnabled(CustomComboPreset.SAMPvP_BurstMode_MainCombo) && actionID is Yukikaze or Gekko or Kasha or Hyosetsu or Oka or Mangetsu)) 59 | { 60 | 61 | if (!TargetHasEffectAny(PvPCommon.Buffs.Guard)) 62 | { 63 | if (IsOffCooldown(MeikyoShisui)) 64 | return OriginalHook(MeikyoShisui); 65 | 66 | if (IsEnabled(CustomComboPreset.SAMPvP_BurstMode_Chiten) && IsOffCooldown(Chiten) && InCombat() && PlayerHealthPercentageHp() <= 95) 67 | return OriginalHook(Chiten); 68 | 69 | if (GetCooldownRemainingTime(Soten) < 1 && CanWeave(Yukikaze)) 70 | return OriginalHook(Soten); 71 | 72 | if (OriginalHook(MeikyoShisui) == Midare && !IsMoving()) 73 | return OriginalHook(MeikyoShisui); 74 | 75 | if (IsEnabled(CustomComboPreset.SAMPvP_BurstMode_Stun) && IsOffCooldown(Mineuchi)) 76 | return OriginalHook(Mineuchi); 77 | 78 | if (IsOffCooldown(OgiNamikiri) && !IsMoving()) 79 | return OriginalHook(OgiNamikiri); 80 | 81 | if (GetRemainingCharges(Soten) > sotenCharges && CanWeave(Yukikaze)) 82 | return OriginalHook(Soten); 83 | 84 | if (OriginalHook(OgiNamikiri) == Kaeshi) 85 | return OriginalHook(OgiNamikiri); 86 | } 87 | } 88 | 89 | return actionID; 90 | } 91 | } 92 | 93 | internal class SAMPvP_KashaFeatures : CustomCombo 94 | { 95 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.SAMPvP_KashaFeatures; 96 | 97 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 98 | { 99 | var SamSotenHP = PluginConfiguration.GetCustomIntValue(Config.SAMPvP_SotenHP); 100 | 101 | if (actionID is Yukikaze or Gekko or Kasha or Hyosetsu or Mangetsu or Oka) 102 | { 103 | if (!InMeleeRange()) 104 | { 105 | if (IsEnabled(CustomComboPreset.SAMPvP_KashaFeatures_GapCloser) && GetRemainingCharges(Soten) > 0 && GetTargetHPPercent() <= SamSotenHP) 106 | return OriginalHook(Soten); 107 | 108 | if (IsEnabled(CustomComboPreset.SAMPvP_KashaFeatures_AoEMeleeProtection) && !IsOriginal(Yukikaze) && !HasEffect(Buffs.Midare) && IsOnCooldown(MeikyoShisui) && IsOnCooldown(OgiNamikiri) && OriginalHook(OgiNamikiri) != Kaeshi) 109 | return SAM.Yukikaze; 110 | } 111 | } 112 | 113 | return actionID; 114 | } 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Window/Tabs/TestFeatures.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Dalamud; 7 | using Dalamud.Logging; 8 | using Dalamud.Utility; 9 | using FFXIVClientStructs.FFXIV.Client.Game; 10 | using Dalamud.Bindings.ImGui; 11 | using XIVSlothComboX.Attributes; 12 | using XIVSlothComboX.Extensions; 13 | using XIVSlothComboX.Services; 14 | 15 | namespace XIVSlothComboX.Window.Tabs 16 | { 17 | internal class TestFeatures : ConfigWindow 18 | { 19 | internal static new void Draw() 20 | { 21 | // ImGui.BeginChild("main", new Vector2(0, 0), true); 22 | ImGui.Text("此选项卡的功能在测试中..."); 23 | 24 | 25 | #region 假名 26 | 27 | bool isFakeName = Service.Configuration.isFakeName; 28 | 29 | if (ImGui.Checkbox("是否使用假名", ref isFakeName)) 30 | { 31 | Service.Configuration.isFakeName = isFakeName; 32 | Service.Configuration.Save(); 33 | function(); 34 | } 35 | ImGui.PushItemWidth(100); 36 | string FakeName = Service.Configuration.FakeName; 37 | bool inputChangedeth = false; 38 | inputChangedeth |= ImGui.InputText("假名", ref FakeName, 20); 39 | 40 | if (inputChangedeth) 41 | { 42 | Service.Configuration.FakeName = FakeName; 43 | Service.Configuration.Save(); 44 | function(); 45 | } 46 | 47 | #endregion 48 | 49 | ImGui.EndChild(); 50 | } 51 | 52 | private static CancellationTokenSource? tokenSource = new(); 53 | private static CancellationToken cancellationToken; 54 | 55 | public static void function() 56 | { 57 | if (Service.ObjectTable.LocalPlayer == null) 58 | { 59 | tokenSource?.Cancel(); 60 | return; 61 | } 62 | 63 | bool isFakeName = Service.Configuration.isFakeName; 64 | 65 | // PluginLog.Error("我执行了2"+isFakeName); 66 | 67 | if (isFakeName == false) 68 | { 69 | tokenSource?.Cancel(); 70 | return; 71 | } 72 | 73 | string fakeName = Service.Configuration.FakeName.Trim(); 74 | if (fakeName.IsNullOrEmpty()) 75 | { 76 | tokenSource?.Cancel(); 77 | return; 78 | } 79 | 80 | { 81 | tokenSource = new CancellationTokenSource(); 82 | cancellationToken = tokenSource.Token; 83 | 84 | //??? 85 | SafeMemory.WriteBytes(Service.SigScanner.Module.BaseAddress + 0x2180739, 86 | SeStringUtils.NameText(Service.Configuration.FakeName.Trim())); 87 | SafeMemory.WriteBytes(Service.SigScanner.Module.BaseAddress + 0x21601B4, 88 | SeStringUtils.NameText(Service.Configuration.FakeName.Trim())); 89 | SafeMemory.WriteBytes(Service.SigScanner.Module.BaseAddress + 0x2163914, 90 | SeStringUtils.NameText(Service.Configuration.FakeName.Trim())); 91 | } 92 | 93 | 94 | 95 | // PluginLog.Error("我执行了1"); 96 | { 97 | Task.Run(async delegate 98 | { 99 | while (!cancellationToken.IsCancellationRequested) 100 | { 101 | // PluginLog.Error("我执行了1"); 102 | if (Service.ClientState.IsLoggedIn) 103 | { 104 | if (Service.ObjectTable.LocalPlayer != null) 105 | { 106 | unsafe 107 | { 108 | // PluginLog.Error("我执行了"); 109 | FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* Struct = 110 | (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Service.ObjectTable.LocalPlayer.Address; 111 | 112 | 113 | SafeMemory.WriteBytes((IntPtr)Struct->Name.GetPinnableReference(), SeStringUtils.NameText(fakeName)); 114 | 115 | } 116 | 117 | } 118 | 119 | } 120 | await Task.Delay(TimeSpan.FromMilliseconds(5000)); 121 | } 122 | 123 | 124 | }, 125 | cancellationToken); 126 | 127 | } 128 | 129 | 130 | } 131 | 132 | internal static void Dispose() 133 | { 134 | tokenSource.Cancel(); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvP/DRGPVP.cs: -------------------------------------------------------------------------------- 1 | using XIVSlothComboX.CustomComboNS; 2 | 3 | namespace XIVSlothComboX.Combos.PvP 4 | { 5 | internal static class DRGPvP 6 | { 7 | public const byte ClassID = 4; 8 | public const byte JobID = 22; 9 | 10 | public const uint 11 | WheelingThrustCombo = 56, 12 | RaidenThrust = 29486, 13 | FangAndClaw = 29487, 14 | WheelingThrust = 29488, 15 | ChaoticSpring = 29490, 16 | Geirskogul = 29491, 17 | HighJump = 29493, 18 | ElusiveJump = 29494, 19 | WyrmwindThrust = 29495, 20 | HorridRoar = 29496, 21 | HeavensThrust = 29489, 22 | Nastrond = 29492, 23 | Purify = 29056, 24 | Guard = 29054; 25 | 26 | 27 | public static class Buffs 28 | { 29 | public const ushort 30 | FirstmindsFocus = 3178, 31 | LifeOfTheDragon = 3177, 32 | Heavensent = 3176; 33 | 34 | 35 | } 36 | internal static class Config 37 | { 38 | internal const string 39 | DRGPvP_LOTD_Duration = "DRGPvP_LOTD_Duration", 40 | DRGPvP_LOTD_HPValue = "DRGPvP_LOTD_HPValue", 41 | DRGPvP_CS_HP_Threshold = "DRGPvP_CS_HP_Threshold", 42 | DRGPvP_Distance_Threshold = "DRGPvP_Distance_Threshold"; 43 | } 44 | 45 | internal class DRGPvP_Burst : CustomCombo 46 | { 47 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.DRGPvP_Burst; 48 | 49 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 50 | { 51 | if (actionID is RaidenThrust or FangAndClaw or WheelingThrust) 52 | { 53 | bool enemyGuarded = TargetHasEffectAny(PvPCommon.Buffs.Guard); 54 | 55 | if (!enemyGuarded) 56 | { 57 | if (CanWeave(actionID)) 58 | { 59 | if (IsEnabled(CustomComboPreset.DRGPvP_HighJump) && IsOffCooldown(HighJump) && HasEffect(Buffs.LifeOfTheDragon)) 60 | return HighJump; 61 | 62 | if (IsEnabled(CustomComboPreset.DRGPvP_Nastrond) && InMeleeRange()) 63 | { 64 | if (HasEffect(Buffs.LifeOfTheDragon) && PlayerHealthPercentageHp() < GetOptionValue(Config.DRGPvP_LOTD_HPValue) 65 | || HasEffect(Buffs.LifeOfTheDragon) && GetBuffRemainingTime(Buffs.LifeOfTheDragon) < GetOptionValue(Config.DRGPvP_LOTD_Duration)) 66 | return Nastrond; 67 | } 68 | if (IsEnabled(CustomComboPreset.DRGPvP_HorridRoar) && IsOffCooldown(HorridRoar) && InMeleeRange()) 69 | return HorridRoar; 70 | } 71 | if (IsEnabled(CustomComboPreset.DRGPvP_ChaoticSpringSustain) && IsOffCooldown(ChaoticSpring) && PlayerHealthPercentageHp() < GetOptionValue(Config.DRGPvP_CS_HP_Threshold)) 72 | { 73 | if (!HasEffect(Buffs.FirstmindsFocus) && !HasEffect(Buffs.LifeOfTheDragon) && IsOnCooldown(Geirskogul) && IsOnCooldown(ElusiveJump) 74 | || !HasEffect(Buffs.FirstmindsFocus) && HasEffect(Buffs.LifeOfTheDragon) && IsOnCooldown(Geirskogul) && IsOnCooldown(ElusiveJump) && WasLastWeaponskill(HeavensThrust)) 75 | return ChaoticSpring; 76 | } 77 | if (IsEnabled(CustomComboPreset.DRGPvP_Geirskogul) && IsOffCooldown(Geirskogul) && WasLastAbility(ElusiveJump) && HasEffect(Buffs.FirstmindsFocus)) 78 | return Geirskogul; 79 | if (IsEnabled(CustomComboPreset.DRGPvP_WyrmwindThrust) && HasEffect(Buffs.FirstmindsFocus) && GetTargetDistance() >= GetOptionValue(Config.DRGPvP_Distance_Threshold)) 80 | return WyrmwindThrust; 81 | } 82 | } 83 | return actionID; 84 | } 85 | } 86 | internal class DRGPvP_BurstProtection : CustomCombo 87 | { 88 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.DRGPvP_BurstProtection; 89 | 90 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 91 | { 92 | if (actionID is ElusiveJump) 93 | { 94 | if (HasEffect(Buffs.FirstmindsFocus) || IsOnCooldown(Geirskogul)) 95 | { 96 | return 26; 97 | } 98 | } 99 | return actionID; 100 | } 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Data/CustomComboCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using Dalamud.Game; 5 | using Dalamud.Game.ClientState.JobGauge.Types; 6 | using Dalamud.Game.ClientState.Objects.Types; 7 | using Dalamud.Game.ClientState.Objects.SubKinds; 8 | using Dalamud.Plugin.Services; 9 | using DalamudStatus = Dalamud.Game.ClientState.Statuses; // conflicts with structs if not defined 10 | using Status = Dalamud.Game.ClientState.Statuses.IStatus; 11 | using FFXIVClientStructs.FFXIV.Client.Game; 12 | using XIVSlothComboX.Services; 13 | 14 | namespace XIVSlothComboX.Data 15 | { 16 | /// Cached conditional combo logic. 17 | /// Cached conditional combo logic. 18 | internal partial class CustomComboCache : IDisposable 19 | { 20 | private const uint InvalidObjectID = 0xE000_0000; 21 | 22 | // Invalidate these 23 | private readonly ConcurrentDictionary<(uint StatusID, ulong? TargetID, ulong? SourceID), Status?> statusCache = new(); 24 | private readonly ConcurrentDictionary cooldownCache = new(); 25 | 26 | // Do not invalidate these 27 | private readonly ConcurrentDictionary jobGaugeCache = new(); 28 | 29 | /// Initializes a new instance of the class. 30 | public CustomComboCache() => Service.Framework.Update += Framework_Update; 31 | 32 | private delegate IntPtr GetActionCooldownSlotDelegate(IntPtr actionManager, int cooldownGroup); 33 | 34 | /// 35 | public void Dispose() => Service.Framework.Update -= Framework_Update; 36 | 37 | /// Gets a job gauge. 38 | /// Type of job gauge. 39 | /// The job gauge. 40 | internal T GetJobGauge() where T : JobGaugeBase 41 | { 42 | if (!jobGaugeCache.TryGetValue(typeof(T), out JobGaugeBase? gauge)) 43 | gauge = jobGaugeCache[typeof(T)] = Service.JobGauges.Get(); 44 | 45 | return (T)gauge; 46 | } 47 | 48 | /// Finds a status on the given object. 49 | /// Status effect ID. 50 | /// Object to look for effects on. 51 | /// Source object ID. 52 | /// Status object or null. 53 | internal Status? GetStatus(uint statusID, IGameObject? obj, ulong? sourceID) 54 | { 55 | var key = (statusID, obj?.GameObjectId, sourceID); 56 | if (statusCache.TryGetValue(key, out Status? found)) 57 | return found; 58 | 59 | if (obj is null) 60 | return statusCache[key] = null; 61 | 62 | if (obj is not IBattleChara chara) 63 | return statusCache[key] = null; 64 | 65 | foreach (Status? status in chara.StatusList) 66 | { 67 | if (status.StatusId == statusID && (!sourceID.HasValue || status.SourceId == 0 || status.SourceId == InvalidObjectID || status.SourceId == sourceID)) 68 | return statusCache[key] = status; 69 | } 70 | 71 | return statusCache[key] = null; 72 | } 73 | 74 | /// Gets the cooldown data for an action. 75 | /// Action ID to check. 76 | /// Cooldown data. 77 | internal unsafe CooldownData GetCooldown(uint actionID) 78 | { 79 | if (cooldownCache.TryGetValue(actionID, out CooldownData? found)) 80 | return found!; 81 | 82 | CooldownData data = new() 83 | { 84 | ActionID = actionID, 85 | }; 86 | 87 | return cooldownCache[actionID] = data; 88 | } 89 | 90 | /// Get the maximum number of charges for an action. 91 | /// Action ID to check. 92 | /// Max number of charges at current level. 93 | internal unsafe ushort GetMaxCharges(uint actionID) => GetCooldown(actionID).MaxCharges; 94 | 95 | /// Get the resource cost of an action. 96 | /// Action ID to check. 97 | /// Returns the resource cost of an action. 98 | internal static unsafe int GetResourceCost(uint actionID) 99 | { 100 | ActionManager* actionManager = ActionManager.Instance(); 101 | if (actionManager == null) 102 | return 0; 103 | 104 | int cost = ActionManager.GetActionCost(ActionType.Action, actionID, 0, 0, 0, 0); 105 | 106 | return cost; 107 | } 108 | 109 | /// Triggers when the game framework updates. Clears cooldown and status caches. 110 | private unsafe void Framework_Update(IFramework framework) 111 | { 112 | statusCache.Clear(); 113 | cooldownCache.Clear(); 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Combos/PvE/DOL.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.Conditions; 2 | using XIVSlothComboX.CustomComboNS; 3 | 4 | namespace XIVSlothComboX.Combos.PvE 5 | { 6 | internal static class DOL 7 | { 8 | public const byte ClassID = 0; 9 | public const byte JobID = 51; 10 | 11 | internal const uint 12 | //BTN & MIN 13 | AgelessWords = 215, 14 | SolidReason = 232, 15 | MinWiseToTheWorld = 26521, 16 | BtnWiseToTheWorld = 26522, 17 | Prospect = 227, 18 | LayOfTheLand = 228, 19 | LayOfTheLand2 = 291, 20 | TruthOfMountains = 238, 21 | Triangulate = 210, 22 | ArborCall = 211, 23 | ArborCall2 = 290, 24 | TruthOfForests = 221, 25 | //FSH 26 | Cast = 289, 27 | Hook = 296, 28 | Mooch = 297, 29 | MoochII = 268, 30 | CastLight = 2135, 31 | Snagging = 4100, 32 | Chum = 4104, 33 | FishEyes = 4105, 34 | SurfaceSlap = 4595, 35 | //FSH Diving 36 | Gig = 7632, 37 | SharkEye = 7904, 38 | SharkEyeII = 7905, 39 | VeteranTrade = 7906, 40 | NaturesBounty = 7909, 41 | Salvage = 7910, 42 | PrizeCatch = 26806, 43 | VitalSight = 26870, 44 | BaitedBreath = 26871, 45 | ElectricCurrent = 26872; 46 | 47 | internal static class Buffs 48 | { 49 | internal const ushort 50 | TruthOfForests = 221, 51 | TruthOfMountains = 222, 52 | Triangulate = 217, 53 | Prospect = 225, 54 | EurekaMoment = 2765; 55 | } 56 | 57 | internal static class Debuffs 58 | { 59 | internal const ushort 60 | Placeholder = 0; 61 | } 62 | 63 | internal class DOL_Eureka : CustomCombo 64 | { 65 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.DOL_Eureka; 66 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 67 | { 68 | if (actionID is SolidReason && HasEffect(Buffs.EurekaMoment)) return MinWiseToTheWorld; 69 | if (actionID is AgelessWords && HasEffect(Buffs.EurekaMoment)) return BtnWiseToTheWorld; 70 | return actionID; 71 | } 72 | } 73 | 74 | internal class DOL_NodeSearchingBuffs : CustomCombo 75 | { 76 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.DOL_NodeSearchingBuffs; 77 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 78 | { 79 | //MIN 80 | if (actionID is DOL.LayOfTheLand && !HasEffect(Buffs.Prospect)) return DOL.Prospect; 81 | if (actionID is DOL.LayOfTheLand2 && LevelChecked(TruthOfMountains) && !HasEffect(Buffs.TruthOfMountains)) return DOL.TruthOfMountains; 82 | //BTN 83 | if (actionID is DOL.ArborCall && !HasEffect(Buffs.Triangulate)) return DOL.Triangulate; 84 | if (actionID is DOL.ArborCall2 && LevelChecked(TruthOfForests) && !HasEffect(Buffs.TruthOfForests)) return DOL.TruthOfForests; 85 | return actionID; 86 | } 87 | } 88 | 89 | internal class FSH_CastHook : CustomCombo 90 | { 91 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.FSH_CastHook; 92 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 93 | => actionID is Cast && HasCondition(ConditionFlag.Fishing) ? Hook : actionID; 94 | } 95 | 96 | internal class FSH_Swim : CustomCombo 97 | { 98 | protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.FSH_Swim; 99 | protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) 100 | { 101 | if (HasCondition(ConditionFlag.Diving)) 102 | { 103 | if (actionID is Cast && IsEnabled(CustomComboPreset.FSH_CastGig)) return Gig; 104 | if (actionID is SurfaceSlap && IsEnabled(CustomComboPreset.FSH_SurfaceTrade)) return VeteranTrade; 105 | if (actionID is PrizeCatch && IsEnabled(CustomComboPreset.FSH_PrizeBounty)) return NaturesBounty; 106 | if (actionID is Snagging && IsEnabled(CustomComboPreset.FSH_SnaggingSalvage)) return Salvage; 107 | if (actionID is CastLight && IsEnabled(CustomComboPreset.FSH_CastLight_ElectricCurrent)) return ElectricCurrent; 108 | if (IsEnabled(CustomComboPreset.FSH_Mooch_SharkEye)) 109 | { 110 | if (actionID is Mooch) return SharkEye; 111 | if (actionID is MoochII) return SharkEyeII; 112 | } 113 | if (actionID is FishEyes && IsEnabled(CustomComboPreset.FSH_FishEyes_VitalSight)) return VitalSight; 114 | if (actionID is Chum && IsEnabled(CustomComboPreset.FSH_Chum_BaitedBreath)) return BaitedBreath; 115 | } 116 | 117 | return actionID; 118 | } 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /XIVSlothComboX/Data/TmpGauge.cs: -------------------------------------------------------------------------------- 1 | // using System; 2 | // using System.Runtime.InteropServices; 3 | // using Dalamud.Game.ClientState.JobGauge.Enums; 4 | // using Dalamud.Game.ClientState.JobGauge.Types; 5 | // using ECommons.DalamudServices; 6 | // using FFXIVClientStructs.FFXIV.Client.Game.Gauge; 7 | // using XIVSlothComboX.Services; 8 | // using CanvasFlags = FFXIVClientStructs.FFXIV.Client.Game.Gauge.CanvasFlags; 9 | // using CreatureFlags = FFXIVClientStructs.FFXIV.Client.Game.Gauge.CreatureFlags; 10 | // 11 | // 12 | // namespace XIVSlothComboX.Data; 13 | // 14 | // public unsafe class TmpSCHGauge 15 | // { 16 | // public byte Aetherflow => Struct->Aetherflow; 17 | // 18 | // public byte FairyGauge => Struct->FairyGauge; 19 | // 20 | // public short SeraphTimer => Struct->SeraphTimer; 21 | // 22 | // public DismissedFairy DismissedFairy => (DismissedFairy)Struct->DismissedFairy; 23 | // 24 | // private protected TmpScholarGauge* Struct; 25 | // 26 | // public TmpSCHGauge() 27 | // { 28 | // Struct = (TmpScholarGauge*)Service.JobGauges.Get().Address; 29 | // } 30 | // } 31 | // public unsafe class TmpVPRGauge 32 | // { 33 | // public byte RattlingCoilStacks => Struct->RattlingCoilStacks; 34 | // 35 | // public byte SerpentsOfferings => Struct->SerpentsOfferings; 36 | // 37 | // public byte AnguineTribute => Struct->AnguineTribute; 38 | // 39 | // public bool DreadwinderReady => Struct->DreadwinderReady; 40 | // 41 | // public bool HuntersCoilReady => Struct->HuntersCoilReady; 42 | // 43 | // public bool SwiftskinsCoilReady => Struct->SwiftskinsCoilReady; 44 | // 45 | // public bool PitOfDreadReady => Struct->PitOfDreadReady; 46 | // 47 | // public bool HuntersDenReady => Struct->HuntersDenReady; 48 | // 49 | // public bool SwiftskinsDenReady => Struct->SwiftskinsDenReady; 50 | // 51 | // private protected ViperGauge* Struct; 52 | // 53 | // public byte GetOffset(int offset) 54 | // { 55 | // var val = IntPtr.Add(Address, offset); 56 | // return Marshal.ReadByte(val); 57 | // } 58 | // 59 | // private nint Address; 60 | // public TmpVPRGauge() 61 | // { 62 | // Address = Svc.SigScanner.GetStaticAddressFromSig("48 8B 3D ?? ?? ?? ?? 33 ED") + 0x8; 63 | // Struct = (ViperGauge*)Address; 64 | // } 65 | // 66 | // 67 | // } 68 | // 69 | // public unsafe class TmpPCTGauge 70 | // { 71 | // private nint Address; 72 | // private protected PictomancerGauge* Struct; 73 | // public uint 豆子 => Struct->Paint; 74 | // 75 | // public uint 能量 => Struct->PalleteGauge; 76 | // public CanvasFlags CanvasFlags => Struct->CanvasFlags; 77 | // public CreatureFlags CreatureFlags => Struct->CreatureFlags; 78 | // 79 | // public bool 生物画 => Struct->CreatureMotifDrawn; 80 | // public bool 武器画 => Struct->WeaponMotifDrawn; 81 | // public bool 风景画 => Struct->LandscapeMotifDrawn; 82 | // public bool 莫古准备 => Struct->MooglePortraitReady; 83 | // public bool 蔬菜准备 => Struct->MadeenPortraitReady; 84 | // 85 | // 86 | // 87 | // public TmpPCTGauge() 88 | // { 89 | // Address = Svc.SigScanner.GetStaticAddressFromSig("48 8B 3D ?? ?? ?? ?? 33 ED") + 0x8; 90 | // Struct = (PictomancerGauge*)Address; 91 | // } 92 | // } 93 | // 94 | // [StructLayout(LayoutKind.Explicit, Size = 0x10)] 95 | // public struct TmpScholarGauge 96 | // { 97 | // [FieldOffset(0x08)] public byte Aetherflow; 98 | // [FieldOffset(0x09)] public byte FairyGauge; 99 | // [FieldOffset(0x0A)] public short SeraphTimer; 100 | // [FieldOffset(0x0C)] public byte DismissedFairy; 101 | // } 102 | // 103 | // [StructLayout(LayoutKind.Explicit, Size = 0x10)] 104 | // public struct ViperGauge 105 | // { 106 | // [FieldOffset(0x08)] public byte RattlingCoilStacks; 107 | // [FieldOffset(0x0A)] public byte SerpentsOfferings; 108 | // [FieldOffset(0x09)] public byte AnguineTribute; 109 | // [FieldOffset(0x0B)] public DreadwinderPitFlags DreadwinderPitCombo; 110 | // 111 | // public bool DreadwinderReady => DreadwinderPitCombo.HasFlag(DreadwinderPitFlags.Dreadwinder); 112 | // public bool HuntersCoilReady => DreadwinderPitCombo.HasFlag(DreadwinderPitFlags.HuntersCoil); 113 | // public bool SwiftskinsCoilReady => DreadwinderPitCombo.HasFlag(DreadwinderPitFlags.SwiftskinsCoil); 114 | // public bool PitOfDreadReady => DreadwinderPitCombo.HasFlag(DreadwinderPitFlags.PitOfDread); 115 | // public bool HuntersDenReady => DreadwinderPitCombo.HasFlag(DreadwinderPitFlags.HuntersDen); 116 | // public bool SwiftskinsDenReady => DreadwinderPitCombo.HasFlag(DreadwinderPitFlags.SwiftskinsDen); 117 | // 118 | // 119 | // } 120 | // 121 | // [StructLayout(LayoutKind.Explicit, Size = 0x10)] 122 | // public struct PictomancerGauge { 123 | // [FieldOffset(0x08)] public byte PalleteGauge; 124 | // [FieldOffset(0x0A)] public byte Paint; 125 | // [FieldOffset(0x0B)] public CanvasFlags CanvasFlags; 126 | // [FieldOffset(0x0C)] public CreatureFlags CreatureFlags; 127 | // 128 | // public bool CreatureMotifDrawn => CanvasFlags.HasFlag(CanvasFlags.Pom) || CanvasFlags.HasFlag(CanvasFlags.Wing) || CanvasFlags.HasFlag(CanvasFlags.Claw) || CanvasFlags.HasFlag(CanvasFlags.Maw); 129 | // public bool WeaponMotifDrawn => CanvasFlags.HasFlag(CanvasFlags.Weapon); 130 | // public bool LandscapeMotifDrawn => CanvasFlags.HasFlag(CanvasFlags.Landscape); 131 | // public bool MooglePortraitReady => CreatureFlags.HasFlag(CreatureFlags.MooglePortait); 132 | // public bool MadeenPortraitReady => CreatureFlags.HasFlag(CreatureFlags.MadeenPortrait); 133 | // } 134 | // 135 | // [Flags] 136 | // public enum DreadwinderPitFlags : byte 137 | // { 138 | // Dreadwinder = 1, 139 | // HuntersCoil = 2, 140 | // SwiftskinsCoil = 3, 141 | // PitOfDread = 4, 142 | // HuntersDen = 5, 143 | // SwiftskinsDen = 6 144 | // } --------------------------------------------------------------------------------