feat: 完善双版本门控、状态名单分类搜索与YesNo对话自动确认
This commit is contained in:
@@ -22,6 +22,7 @@ public sealed class Configuration : IPluginConfiguration
|
|||||||
public string OpcodeUrl { get; set; } = "https://raw.githubusercontent.com/extrant/FFXIV.EXE/main/Opcode/all_opcodes.json";
|
public string OpcodeUrl { get; set; } = "https://raw.githubusercontent.com/extrant/FFXIV.EXE/main/Opcode/all_opcodes.json";
|
||||||
public bool SmartGroundCastAtTarget { get; set; } = true;
|
public bool SmartGroundCastAtTarget { get; set; } = true;
|
||||||
public bool FastEventFinish { get; set; } = false;
|
public bool FastEventFinish { get; set; } = false;
|
||||||
|
public bool FastSelectYesno { get; set; } = false;
|
||||||
public bool AutoRetainerReventure { get; set; } = true;
|
public bool AutoRetainerReventure { get; set; } = true;
|
||||||
public float Value(int id)
|
public float Value(int id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
private long lastRetainerActionTime;
|
private long lastRetainerActionTime;
|
||||||
private long lastAutoEventFinishTime;
|
private long lastAutoEventFinishTime;
|
||||||
private long lastTalkAdvanceTime;
|
private long lastTalkAdvanceTime;
|
||||||
|
private long lastYesnoTime;
|
||||||
|
private string statusFilterText = "";
|
||||||
private uint pendingEventFinishId;
|
private uint pendingEventFinishId;
|
||||||
private readonly IDisposable eventSubscription;
|
private readonly IDisposable eventSubscription;
|
||||||
|
|
||||||
@@ -206,7 +208,11 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
case "retainer" or "reventure":
|
case "retainer" or "reventure":
|
||||||
ProcessRetainerAutomation();
|
ProcessRetainerAutomation();
|
||||||
chat.Print("[IC Rebuild] 已尝试一键处理当前雇员探险窗口"); break;
|
chat.Print("[IC Rebuild] 已尝试一键处理当前雇员探险窗口"); break;
|
||||||
default: Open(); notice="用法:/icr on|off 编号;stop;pos X Y Z;ground 技能ID [target|self];finish [事件ID];retainer"; break;
|
case "yes":
|
||||||
|
ConfirmYesno(true); break;
|
||||||
|
case "no":
|
||||||
|
ConfirmYesno(false); break;
|
||||||
|
default: Open(); notice="用法:/icr on|off 编号;stop;pos X Y Z;ground 技能ID [target|self];finish [事件ID];retainer;yes;no"; break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -236,6 +242,13 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
var value=config.Value(feature.Id);
|
var value=config.Value(feature.Id);
|
||||||
if(sliders.Float(feature.Id==9 ? "预设高度偏移" : "参数",ref value,feature.Min,feature.Max)) { config.Values[feature.Id]=value; Try(Save); }
|
if(sliders.Float(feature.Id==9 ? "预设高度偏移" : "参数",ref value,feature.Min,feature.Max)) { config.Values[feature.Id]=value; Try(Save); }
|
||||||
}
|
}
|
||||||
|
if(feature.Id==4)
|
||||||
|
{
|
||||||
|
if(ImGui.SmallButton("一键完全防击退 (距离与持续时间归零)##antiKnockZero"))
|
||||||
|
{
|
||||||
|
config.Values[4]=0; config.KnockSeconds=0; Try(Save);
|
||||||
|
}
|
||||||
|
}
|
||||||
if(feature.Id==8) DrawStatusBlock();
|
if(feature.Id==8) DrawStatusBlock();
|
||||||
if(feature.Id==9)
|
if(feature.Id==9)
|
||||||
{
|
{
|
||||||
@@ -254,6 +267,19 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
: "等待当前目标的原生半径读取。");
|
: "等待当前目标的原生半径读取。");
|
||||||
ImGui.TextWrapped($"目标修改 {combat.TargetRadiusChanges} · 原生圈补偿 {combat.NativeRingBonuses} · 距离检查命中 {combat.RadiusBonuses}");
|
ImGui.TextWrapped($"目标修改 {combat.TargetRadiusChanges} · 原生圈补偿 {combat.NativeRingBonuses} · 距离检查命中 {combat.RadiusBonuses}");
|
||||||
}
|
}
|
||||||
|
if(feature.Id==18)
|
||||||
|
{
|
||||||
|
ImGui.TextWrapped("结印技能(天之印、地之印、人之印)复唱归零,结印无需等待 GCD。依赖模块 17。");
|
||||||
|
}
|
||||||
|
if(feature.Id==20)
|
||||||
|
{
|
||||||
|
ImGui.Text("快速预设:"); ImGui.SameLine();
|
||||||
|
if(ImGui.SmallButton("极限 0.5s")) { config.Values[20]=0.5f; Try(Save); }
|
||||||
|
ImGui.SameLine();
|
||||||
|
if(ImGui.SmallButton("平滑 0.6s")) { config.Values[20]=0.6f; Try(Save); }
|
||||||
|
ImGui.SameLine();
|
||||||
|
if(ImGui.SmallButton("默认 0.7s")) { config.Values[20]=0.7f; Try(Save); }
|
||||||
|
}
|
||||||
ImGui.PopID();
|
ImGui.PopID();
|
||||||
}
|
}
|
||||||
switch(group.Key)
|
switch(group.Key)
|
||||||
@@ -278,13 +304,58 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
ImGui.TextWrapped($"已选 {config.BlockedStatuses.Count} 项 · 本次启用拦截 {statusBlock.Blocked} 次 · 最近状态 ID {statusBlock.LastStatus}");
|
ImGui.TextWrapped($"已选 {config.BlockedStatuses.Count} 项 · 本次启用拦截 {statusBlock.Blocked} 次 · 最近状态 ID {statusBlock.LastStatus}");
|
||||||
ImGui.TextWrapped("仅屏蔽本地角色后续收到的已选状态更新;开启前已有状态须等下一次更新。默认选择 1257 强制移动,空名单不屏蔽。录像回放暂停处理。");
|
ImGui.TextWrapped("仅屏蔽本地角色后续收到的已选状态更新;开启前已有状态须等下一次更新。默认选择 1257 强制移动,空名单不屏蔽。录像回放暂停处理。");
|
||||||
ImGui.TextWrapped("关闭后恢复正常接收,已屏蔽状态须等服务端再次同步。本地处理不代表服务端状态被删除;实际移动或机制效果待实测。");
|
ImGui.TextWrapped("关闭后恢复正常接收,已屏蔽状态须等服务端再次同步。本地处理不代表服务端状态被删除;实际移动或机制效果待实测。");
|
||||||
if(!ImGui.CollapsingHeader("状态名单(已审计候选,效果待逐项确认)")) return;
|
if(!ImGui.CollapsingHeader("状态名单管理(分类预设与搜索)")) return;
|
||||||
|
|
||||||
|
ImGui.Text("快捷预设:");
|
||||||
|
ImGui.SameLine();
|
||||||
|
if(ImGui.SmallButton("全选滑冰##selSlippery"))
|
||||||
|
{
|
||||||
|
foreach(var id in StatusCatalog.LegacySlipperyCandidates.Where(StatusCatalog.AuditedBlockCandidates.Contains))
|
||||||
|
config.BlockedStatuses.Add(id);
|
||||||
|
Try(Save);
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
if(ImGui.SmallButton("全选强制移动##selMarch"))
|
||||||
|
{
|
||||||
|
foreach(var id in StatusCatalog.ForcedMarch.Where(StatusCatalog.AuditedBlockCandidates.Contains))
|
||||||
|
config.BlockedStatuses.Add(id);
|
||||||
|
Try(Save);
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
if(ImGui.SmallButton("全选混乱##selConf"))
|
||||||
|
{
|
||||||
|
foreach(var id in StatusCatalog.Confusion.Where(StatusCatalog.AuditedBlockCandidates.Contains))
|
||||||
|
config.BlockedStatuses.Add(id);
|
||||||
|
Try(Save);
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
if(ImGui.SmallButton("全选全部候选##selAll"))
|
||||||
|
{
|
||||||
|
foreach(var id in StatusCatalog.AuditedBlockCandidates)
|
||||||
|
config.BlockedStatuses.Add(id);
|
||||||
|
Try(Save);
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
if(ImGui.SmallButton("清空已选##clearStatus"))
|
||||||
|
{
|
||||||
|
config.BlockedStatuses.Clear();
|
||||||
|
Try(Save);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.InputText("搜索状态名称或 ID##statusSearch",ref statusFilterText,64);
|
||||||
|
|
||||||
var sheet=game.Data.GetExcelSheet<Lumina.Excel.Sheets.Status>();
|
var sheet=game.Data.GetExcelSheet<Lumina.Excel.Sheets.Status>();
|
||||||
foreach(var id in StatusCatalog.AuditedBlockCandidates.Order())
|
foreach(var id in StatusCatalog.AuditedBlockCandidates.Order())
|
||||||
{
|
{
|
||||||
var selected=config.BlockedStatuses.Contains(id);
|
|
||||||
var name=sheet?.GetRowOrDefault(id)?.Name.ToString() ?? "未知状态";
|
var name=sheet?.GetRowOrDefault(id)?.Name.ToString() ?? "未知状态";
|
||||||
if(ImGui.Checkbox($"{id} · {name}",ref selected))
|
if(!string.IsNullOrWhiteSpace(statusFilterText))
|
||||||
|
{
|
||||||
|
if(!id.ToString().Contains(statusFilterText,StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!name.Contains(statusFilterText,StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var selected=config.BlockedStatuses.Contains(id);
|
||||||
|
if(ImGui.Checkbox($"{id} · {name}##status{id}",ref selected))
|
||||||
{
|
{
|
||||||
if(selected) config.BlockedStatuses.Add(id); else config.BlockedStatuses.Remove(id);
|
if(selected) config.BlockedStatuses.Add(id); else config.BlockedStatuses.Remove(id);
|
||||||
Try(Save);
|
Try(Save);
|
||||||
@@ -454,6 +525,13 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
config.FastEventFinish = fastEv;
|
config.FastEventFinish = fastEv;
|
||||||
Try(Save);
|
Try(Save);
|
||||||
}
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
var fastYes = config.FastSelectYesno;
|
||||||
|
if (ImGui.Checkbox("自动确认 Yes/No 提示框 (FastSelectYesno)##fastYesno", ref fastYes))
|
||||||
|
{
|
||||||
|
config.FastSelectYesno = fastYes;
|
||||||
|
Try(Save);
|
||||||
|
}
|
||||||
var evTimeStr = lastEventTime > 0 ? $"{(Environment.TickCount64 - lastEventTime) / 1000} 秒前" : "无";
|
var evTimeStr = lastEventTime > 0 ? $"{(Environment.TickCount64 - lastEventTime) / 1000} 秒前" : "无";
|
||||||
ImGui.TextWrapped($"最近捕获事件:ID 0x{lastEventId:X8} · 目标 0x{lastEventTargetId:X8} · 参数 0x{lastEventParam:X8} · 时间 {evTimeStr}");
|
ImGui.TextWrapped($"最近捕获事件:ID 0x{lastEventId:X8} · 目标 0x{lastEventTargetId:X8} · 参数 0x{lastEventParam:X8} · 时间 {evTimeStr}");
|
||||||
if (ImGui.Button("立即完成当前事件 (UP_EventFinish)")) Try(() => ExecuteEventFinish());
|
if (ImGui.Button("立即完成当前事件 (UP_EventFinish)")) Try(() => ExecuteEventFinish());
|
||||||
@@ -579,7 +657,8 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
var mgr=FFXIVClientStructs.FFXIV.Client.UI.RaptureAtkUnitManager.Instance();
|
var mgr=FFXIVClientStructs.FFXIV.Client.UI.RaptureAtkUnitManager.Instance();
|
||||||
if(mgr==null) return;
|
if(mgr==null) return;
|
||||||
var now=Environment.TickCount64;
|
var now=Environment.TickCount64;
|
||||||
if(now-lastTalkAdvanceTime<250) return;
|
if(now-lastTalkAdvanceTime>=200)
|
||||||
|
{
|
||||||
var talk=mgr->GetAddonByName("Talk",1);
|
var talk=mgr->GetAddonByName("Talk",1);
|
||||||
if(talk!=null && talk->IsVisible && talk->IsReady)
|
if(talk!=null && talk->IsVisible && talk->IsReady)
|
||||||
{
|
{
|
||||||
@@ -589,6 +668,33 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
lastTalkAdvanceTime=now;
|
lastTalkAdvanceTime=now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(config.FastSelectYesno && now-lastYesnoTime>=300)
|
||||||
|
{
|
||||||
|
var yesno=mgr->GetAddonByName("SelectYesno",1);
|
||||||
|
if(yesno!=null && yesno->IsVisible && yesno->IsReady)
|
||||||
|
{
|
||||||
|
var val=stackalloc FFXIVClientStructs.FFXIV.Component.GUI.AtkValue[1];
|
||||||
|
val[0].SetInt(0);
|
||||||
|
yesno->FireCallback(1,val,true);
|
||||||
|
lastYesnoTime=now;
|
||||||
|
notice="已自动确认【SelectYesno】提示框";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private unsafe void ConfirmYesno(bool yes)
|
||||||
|
{
|
||||||
|
var mgr=FFXIVClientStructs.FFXIV.Client.UI.RaptureAtkUnitManager.Instance();
|
||||||
|
if(mgr==null) return;
|
||||||
|
var yesno=mgr->GetAddonByName("SelectYesno",1);
|
||||||
|
if(yesno!=null && yesno->IsVisible && yesno->IsReady)
|
||||||
|
{
|
||||||
|
var val=stackalloc FFXIVClientStructs.FFXIV.Component.GUI.AtkValue[1];
|
||||||
|
val[0].SetInt(yes ? 0 : 1);
|
||||||
|
yesno->FireCallback(1,val,true);
|
||||||
|
chat.Print($"[IC Rebuild] 已手动{(yes ? "确认(Yes)" : "取消(No)")}当前提示框");
|
||||||
|
}
|
||||||
|
else chat.Print("[IC Rebuild] 当前无可见的 Yes/No 提示框");
|
||||||
|
}
|
||||||
private unsafe void ProcessRetainerAutomation()
|
private unsafe void ProcessRetainerAutomation()
|
||||||
{
|
{
|
||||||
var mgr=FFXIVClientStructs.FFXIV.Client.UI.RaptureAtkUnitManager.Instance();
|
var mgr=FFXIVClientStructs.FFXIV.Client.UI.RaptureAtkUnitManager.Instance();
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public sealed unsafe class CombatModules(GameContext game, Configuration config,
|
|||||||
modules.Register(13,AcquireRange);
|
modules.Register(13,AcquireRange);
|
||||||
modules.Register(14, scope =>
|
modules.Register(14, scope =>
|
||||||
{
|
{
|
||||||
if(game.GameVersion!=AccelerationPolicy.AuditedVersion) throw new InvalidOperationException("原生目标圈入口仅核对了 2026.09.15 客户端");
|
if(!AccelerationPolicy.IsAuditedVersion(game.GameVersion)) throw new InvalidOperationException($"原生目标圈入口仅核对了已审计客户端,当前为 {game.GameVersion}。");
|
||||||
Hook<ActionManager.Delegates.GetActionInRangeOrLoS>? rangeCheck = null;
|
Hook<ActionManager.Delegates.GetActionInRangeOrLoS>? rangeCheck = null;
|
||||||
Hook<ActionManager.Delegates.IsActionTargetInRange>? precheck = null;
|
Hook<ActionManager.Delegates.IsActionTargetInRange>? precheck = null;
|
||||||
Hook<TargetRingUpdate>? ringUpdate=null;
|
Hook<TargetRingUpdate>? ringUpdate=null;
|
||||||
@@ -137,7 +137,7 @@ public sealed unsafe class CombatModules(GameContext game, Configuration config,
|
|||||||
});
|
});
|
||||||
modules.Register(16, scope =>
|
modules.Register(16, scope =>
|
||||||
{
|
{
|
||||||
if(game.GameVersion!=AccelerationPolicy.AuditedVersion) throw new InvalidOperationException("技能位移结构仅核对了 2026.09.15 客户端");
|
if(!AccelerationPolicy.IsAuditedVersion(game.GameVersion)) throw new InvalidOperationException($"技能位移结构仅核对了已审计客户端,当前为 {game.GameVersion}。");
|
||||||
Hook<Dash>? hook = null;
|
Hook<Dash>? hook = null;
|
||||||
hook = game.Hook<Dash>(scope,Signatures.ActionMovementStart,(controller,moveType,target,origin,facing,data) =>
|
hook = game.Hook<Dash>(scope,Signatures.ActionMovementStart,(controller,moveType,target,origin,facing,data) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public sealed unsafe class MovementModules(GameContext game, Configuration confi
|
|||||||
});
|
});
|
||||||
modules.Register(2, scope =>
|
modules.Register(2, scope =>
|
||||||
{
|
{
|
||||||
if(game.GameVersion!=AccelerationPolicy.AuditedVersion) throw new InvalidOperationException("最大加速度仅完成 2026.09.15 客户端的字段核对。");
|
if(!AccelerationPolicy.IsAuditedVersion(game.GameVersion)) throw new InvalidOperationException($"最大加速度仅完成已核对客户端的字段核对,当前为 {game.GameVersion}。");
|
||||||
Hook<Acceleration>? hook = null;
|
Hook<Acceleration>? hook = null;
|
||||||
hook = game.Hook<Acceleration>(scope, "40 ?? 48 ?? ?? ?? 80 79 ?? ?? 48 ?? ?? 0f 84 ?? ?? ?? ?? 48 89 7c 24 ?? 48 ?? ?? ??", actor =>
|
hook = game.Hook<Acceleration>(scope, "40 ?? 48 ?? ?? ?? 80 79 ?? ?? 48 ?? ?? 0f 84 ?? ?? ?? ?? 48 89 7c 24 ?? 48 ?? ?? ??", actor =>
|
||||||
{
|
{
|
||||||
@@ -85,7 +85,7 @@ public sealed unsafe class MovementModules(GameContext game, Configuration confi
|
|||||||
});
|
});
|
||||||
modules.Register(7, scope =>
|
modules.Register(7, scope =>
|
||||||
{
|
{
|
||||||
if(game.GameVersion!=AccelerationPolicy.AuditedVersion) throw new InvalidOperationException("强制移动入口参数仅核对了 2026.09.15 客户端");
|
if(!AccelerationPolicy.IsAuditedVersion(game.GameVersion)) throw new InvalidOperationException($"强制移动入口参数仅核对了已审计客户端,当前为 {game.GameVersion}。");
|
||||||
Hook<Forced>? hook = null;
|
Hook<Forced>? hook = null;
|
||||||
hook = game.Hook<Forced>(scope, Signatures.FuckBewithAction, (actor,x,y,z,movementParameter,options) =>
|
hook = game.Hook<Forced>(scope, Signatures.FuckBewithAction, (actor,x,y,z,movementParameter,options) =>
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user