feat: 增加对话列表快捷选择指令与PVP动画锁实时状态显示

This commit is contained in:
2026-10-14 01:23:03 +08:00
parent c68f5f743a
commit 7eaa536efb
2 changed files with 35 additions and 4 deletions
+34 -3
View File
@@ -103,7 +103,7 @@ public sealed class Plugin : IDalamudPlugin
serviceBar=dtrBar.Get("IC Rebuild · 高度与迷宫"); serviceBar=dtrBar.Get("IC Rebuild · 高度与迷宫");
serviceBar.Shown=false; serviceBar.Shown=false;
serviceBar.OnClick=_=>Open(); serviceBar.OnClick=_=>Open();
commands.AddHandler("/icr",new CommandInfo(Command) { HelpMessage="IC Rebuild 设置。on/off 编号;stop;pos X Y Z;ground 技能ID [target|self];finish [事件ID];retainer;face 弧度;scale 倍率;export [文件名]。" }); commands.AddHandler("/icr",new CommandInfo(Command) { HelpMessage="IC Rebuild 设置。on/off 编号;stop;pos X Y Z;ground 技能ID [target|self];finish [事件ID];retainer;yes;no;select 索引;face 弧度;scale 倍率;export [文件名]。" });
pi.UiBuilder.Draw+=Draw; pi.UiBuilder.OpenConfigUi+=Open; pi.UiBuilder.OpenMainUi+=Open; pi.UiBuilder.Draw+=Draw; pi.UiBuilder.OpenConfigUi+=Open; pi.UiBuilder.OpenMainUi+=Open;
framework.Update+=Update; framework.Update+=Update;
} }
@@ -212,11 +212,14 @@ public sealed class Plugin : IDalamudPlugin
ConfirmYesno(true); break; ConfirmYesno(true); break;
case "no": case "no":
ConfirmYesno(false); break; ConfirmYesno(false); break;
default: Open(); notice="用法:/icr on|off 编号;stop;pos X Y Z;ground 技能ID [target|self];finish [事件ID];retainer;yes;no"; break; case "select" or "choose" when args.Length==2:
var optIndex=int.Parse(args[1],CultureInfo.InvariantCulture);
SelectDialogOption(optIndex); break;
default: Open(); notice="用法:/icr on|off 编号;stop;pos X Y Z;ground 技能ID [target|self];finish [事件ID];retainer;yes;no;select 索引"; break;
} }
}); });
} }
private void Draw() private unsafe void Draw()
{ {
if(!open) return; if(!open) return;
ImGui.SetNextWindowSize(new(790,650),ImGuiCond.FirstUseEver); ImGui.SetNextWindowSize(new(790,650),ImGuiCond.FirstUseEver);
@@ -273,6 +276,10 @@ public sealed class Plugin : IDalamudPlugin
} }
if(feature.Id==20) if(feature.Id==20)
{ {
var pvpState=game.IsPvP ? "是 (生效中)" : "否 (仅PvP区域生效)";
var mgr=FFXIVClientStructs.FFXIV.Client.Game.ActionManager.Instance();
var currentLock=mgr!=null ? mgr->AnimationLock : 0f;
ImGui.Text($"PvP 区域:【{pvpState}】 · 当前动画锁:{currentLock:F3}s");
ImGui.Text("快速预设:"); ImGui.SameLine(); ImGui.Text("快速预设:"); ImGui.SameLine();
if(ImGui.SmallButton("极限 0.5s")) { config.Values[20]=0.5f; Try(Save); } if(ImGui.SmallButton("极限 0.5s")) { config.Values[20]=0.5f; Try(Save); }
ImGui.SameLine(); ImGui.SameLine();
@@ -695,6 +702,30 @@ public sealed class Plugin : IDalamudPlugin
} }
else chat.Print("[IC Rebuild] 当前无可见的 Yes/No 提示框"); else chat.Print("[IC Rebuild] 当前无可见的 Yes/No 提示框");
} }
private unsafe void SelectDialogOption(int index)
{
var mgr=FFXIVClientStructs.FFXIV.Client.UI.RaptureAtkUnitManager.Instance();
if(mgr==null) return;
var selectString=mgr->GetAddonByName("SelectString",1);
if(selectString!=null && selectString->IsVisible && selectString->IsReady)
{
var val=stackalloc FFXIVClientStructs.FFXIV.Component.GUI.AtkValue[1];
val[0].SetInt(index);
selectString->FireCallback(1,val,true);
chat.Print($"[IC Rebuild] 已选择【SelectString】第 {index + 1} 项 (索引 {index})");
return;
}
var selectIconString=mgr->GetAddonByName("SelectIconString",1);
if(selectIconString!=null && selectIconString->IsVisible && selectIconString->IsReady)
{
var val=stackalloc FFXIVClientStructs.FFXIV.Component.GUI.AtkValue[1];
val[0].SetInt(index);
selectIconString->FireCallback(1,val,true);
chat.Print($"[IC Rebuild] 已选择【SelectIconString】第 {index + 1} 项 (索引 {index})");
return;
}
chat.Print("[IC Rebuild] 当前无可见的选择列表 (SelectString / SelectIconString)");
}
private unsafe void ProcessRetainerAutomation() private unsafe void ProcessRetainerAutomation()
{ {
var mgr=FFXIVClientStructs.FFXIV.Client.UI.RaptureAtkUnitManager.Instance(); var mgr=FFXIVClientStructs.FFXIV.Client.UI.RaptureAtkUnitManager.Instance();
+1 -1
View File
@@ -21,7 +21,7 @@ public sealed class StatusBlockModule(GameContext game, Configuration config, Mo
public void Register() => modules.Register(8, scope => public void Register() => modules.Register(8, scope =>
{ {
if(!StatusBlockPolicy.IsAuditedVersion(game.GameVersion)) if(!StatusBlockPolicy.IsAuditedVersion(game.GameVersion))
throw new InvalidOperationException($"状态屏蔽仅适配国际服已审计版本,当前为 {game.GameVersion}。"); throw new InvalidOperationException($"状态屏蔽仅适配已核对客户端(国服/国际服),当前为 {game.GameVersion}。");
if(Marshal.OffsetOf<BattleChara>(nameof(BattleChara.StatusManager)).ToInt32() != StatusBlockPolicy.ManagerOffset || if(Marshal.OffsetOf<BattleChara>(nameof(BattleChara.StatusManager)).ToInt32() != StatusBlockPolicy.ManagerOffset ||
Marshal.OffsetOf<GameObject>(nameof(GameObject.EntityId)).ToInt32() != StatusBlockPolicy.EntityIdOffset) Marshal.OffsetOf<GameObject>(nameof(GameObject.EntityId)).ToInt32() != StatusBlockPolicy.EntityIdOffset)
throw new InvalidOperationException("当前 SDK 的角色布局与已核对的状态入口不一致。"); throw new InvalidOperationException("当前 SDK 的角色布局与已核对的状态入口不一致。");