├── 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 | // }
--------------------------------------------------------------------------------