Skip to content
This repository was archived by the owner on Dec 19, 2025. It is now read-only.

Commit f0d480f

Browse files
committed
Use and Manage CustomNodes
1 parent 9bb13fe commit f0d480f

13 files changed

+559
-129
lines changed

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[submodule "KamiToolKit"]
2+
path = KamiToolKit
3+
url = https://github.com/MidoriKami/KamiToolKit.git
4+
[submodule "KamiLib"]
5+
path = KamiLib
6+
url = https://github.com/MidoriKami/KamiLib.git

KamiLib

Submodule KamiLib added at a52eec4

KamiToolKit

Submodule KamiToolKit added at df93e00

MiniCactpotSolver.sln

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Github Actions", "Github Ac
1616
.github\workflows\build.yml = .github\workflows\build.yml
1717
EndProjectSection
1818
EndProject
19+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KamiToolKit", "KamiToolKit\KamiToolKit.csproj", "{78DDC7F0-22CE-4093-BDD3-4DAF7F998CDD}"
20+
EndProject
21+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KamiLib", "KamiLib\KamiLib.csproj", "{95E9749F-3357-454B-9D42-801AB2840627}"
22+
EndProject
1923
Global
2024
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2125
Debug|Any CPU = Debug|Any CPU
@@ -32,6 +36,22 @@ Global
3236
{B5AD85D8-F5F5-42F5-909F-1DB5AAA0140F}.Release|Any CPU.Build.0 = Release|x64
3337
{B5AD85D8-F5F5-42F5-909F-1DB5AAA0140F}.Release|x64.ActiveCfg = Release|x64
3438
{B5AD85D8-F5F5-42F5-909F-1DB5AAA0140F}.Release|x64.Build.0 = Release|x64
39+
{78DDC7F0-22CE-4093-BDD3-4DAF7F998CDD}.Debug|Any CPU.ActiveCfg = Debug|x64
40+
{78DDC7F0-22CE-4093-BDD3-4DAF7F998CDD}.Debug|Any CPU.Build.0 = Debug|x64
41+
{78DDC7F0-22CE-4093-BDD3-4DAF7F998CDD}.Debug|x64.ActiveCfg = Debug|x64
42+
{78DDC7F0-22CE-4093-BDD3-4DAF7F998CDD}.Debug|x64.Build.0 = Debug|x64
43+
{78DDC7F0-22CE-4093-BDD3-4DAF7F998CDD}.Release|Any CPU.ActiveCfg = Release|x64
44+
{78DDC7F0-22CE-4093-BDD3-4DAF7F998CDD}.Release|Any CPU.Build.0 = Release|x64
45+
{78DDC7F0-22CE-4093-BDD3-4DAF7F998CDD}.Release|x64.ActiveCfg = Release|x64
46+
{78DDC7F0-22CE-4093-BDD3-4DAF7F998CDD}.Release|x64.Build.0 = Release|x64
47+
{95E9749F-3357-454B-9D42-801AB2840627}.Debug|Any CPU.ActiveCfg = Debug|x64
48+
{95E9749F-3357-454B-9D42-801AB2840627}.Debug|Any CPU.Build.0 = Debug|x64
49+
{95E9749F-3357-454B-9D42-801AB2840627}.Debug|x64.ActiveCfg = Debug|x64
50+
{95E9749F-3357-454B-9D42-801AB2840627}.Debug|x64.Build.0 = Debug|x64
51+
{95E9749F-3357-454B-9D42-801AB2840627}.Release|Any CPU.ActiveCfg = Release|x64
52+
{95E9749F-3357-454B-9D42-801AB2840627}.Release|Any CPU.Build.0 = Release|x64
53+
{95E9749F-3357-454B-9D42-801AB2840627}.Release|x64.ActiveCfg = Release|x64
54+
{95E9749F-3357-454B-9D42-801AB2840627}.Release|x64.Build.0 = Release|x64
3555
EndGlobalSection
3656
GlobalSection(SolutionProperties) = preSolution
3757
HideSolutionNode = FALSE

MiniCactpotSolver/ConfigWindow.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System.Numerics;
2+
using ImGuiNET;
3+
using KamiLib.Classes;
4+
using KamiLib.Window;
5+
6+
namespace MiniCactpotSolver;
7+
8+
public class ConfigWindow() : Window("EzMiniCactpot", new Vector2(400.0f, 500.0f)) {
9+
10+
protected override void DrawContents() {
11+
DrawAnimationConfig();
12+
DrawIconConfig();
13+
DrawColorConfig();
14+
}
15+
16+
private static void DrawAnimationConfig() {
17+
ImGuiTweaks.Header("Animations");
18+
19+
if (ImGui.Checkbox("Enable Animations", ref Service.Config.EnableAnimations)) {
20+
if (Service.Config.EnableAnimations) {
21+
Service.AddonController.EnableAnimations();
22+
}
23+
else {
24+
Service.AddonController.DisableAnimations();
25+
}
26+
}
27+
}
28+
29+
private void DrawIconConfig() {
30+
ImGuiTweaks.Header("Icon");
31+
32+
if (ImGuiTweaks.GameIconButton(Service.TextureProvider, 61332)) {
33+
Service.Config.CustomIconId = 61332;
34+
UpdateIcons();
35+
}
36+
37+
ImGui.SameLine();
38+
39+
if (ImGuiTweaks.GameIconButton(Service.TextureProvider, 90452)) {
40+
Service.Config.CustomIconId = 90452;
41+
UpdateIcons();
42+
}
43+
44+
ImGui.SameLine();
45+
46+
if (ImGuiTweaks.GameIconButton(Service.TextureProvider, 234008)) {
47+
Service.Config.CustomIconId = 234008;
48+
UpdateIcons();
49+
}
50+
51+
ImGui.Spacing();
52+
53+
if (ImGui.Checkbox("Use Custom Icon", ref Service.Config.UseCustomIcon)) {
54+
UpdateIcons();
55+
}
56+
57+
if (Service.Config.UseCustomIcon) {
58+
var iconId = (int) Service.Config.CustomIconId;
59+
if (ImGui.InputInt("IconId", ref iconId)) {
60+
Service.Config.CustomIconId = (uint) iconId;
61+
UpdateIcons();
62+
}
63+
}
64+
}
65+
66+
private void DrawColorConfig() {
67+
ImGuiTweaks.Header("Colors");
68+
69+
if (ImGui.ColorEdit4("Button Colors", ref Service.Config.ButtonColor, ImGuiColorEditFlags.AlphaPreviewHalf)) {
70+
UpdateColors();
71+
}
72+
73+
if (ImGui.ColorEdit4("Lane Colors", ref Service.Config.LaneColor, ImGuiColorEditFlags.AlphaPreviewHalf)) {
74+
UpdateColors();
75+
}
76+
}
77+
78+
public override void OnClose()
79+
=> Service.Config.Save();
80+
81+
private void UpdateIcons()
82+
=> Service.AddonController.UpdateIcons(Service.Config.CustomIconId);
83+
84+
private void UpdateColors() {
85+
Service.AddonController.UpdateButtonColors(Service.Config.ButtonColor);
86+
Service.AddonController.UpdateLaneColors(Service.Config.LaneColor);
87+
}
88+
}

MiniCactpotSolver/Configuration.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Numerics;
2+
using KamiLib.Configuration;
3+
4+
namespace MiniCactpotSolver;
5+
6+
public class Configuration {
7+
public bool EnableAnimations = true;
8+
9+
public Vector4 ButtonColor = new(1.0f, 1.0f, 1.0f, 0.80f);
10+
public Vector4 LaneColor = new(1.0f, 1.0f, 1.0f, 1.0f);
11+
12+
public bool UseCustomIcon = true;
13+
14+
public uint CustomIconId = 61332;
15+
16+
public static Configuration Load()
17+
=> Service.PluginInterface.LoadConfigFile("EzMiniCactpot.config.json", () => new Configuration());
18+
19+
public void Save()
20+
=> Service.PluginInterface.SaveConfigFile("EzMiniCactpot.config.json", this);
21+
}

MiniCactpotSolver/GameGrid.cs

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
using System;
2+
using System.Linq;
3+
using System.Numerics;
4+
using FFXIVClientStructs.FFXIV.Component.GUI;
5+
using KamiToolKit.Classes.TimelineBuilding;
6+
using KamiToolKit.Nodes;
7+
using KamiToolKit.Nodes.Image;
8+
9+
namespace MiniCactpotSolver;
10+
11+
public class GameGrid : ResNode {
12+
13+
private readonly IconImageNode[] buttonImages;
14+
private readonly ImageNode[] laneImages;
15+
16+
public GameGrid() {
17+
var gameGridOffset = new Vector2(28.0f, 44.0f);
18+
var buttonsOffset = gameGridOffset + new Vector2(32.0f, 32.0f);
19+
20+
uint selectedIcon = Service.Config.CustomIconId;
21+
22+
AddTimeline(new TimelineBuilder()
23+
.BeginFrameSet(1, 130)
24+
.AddLabel(1, 200, AtkTimelineJumpBehavior.Start, 0)
25+
.AddLabel(120, 0, AtkTimelineJumpBehavior.LoopForever, 200)
26+
.AddLabel(121, 201, AtkTimelineJumpBehavior.Start, 0)
27+
.EndFrameSet()
28+
.Build());
29+
30+
buttonImages = new IconImageNode[9];
31+
32+
foreach(var yIndex in Enumerable.Range(0, 3))
33+
foreach (var xIndex in Enumerable.Range(0, 3)) {
34+
var imageNode = new IconImageNode {
35+
Position = new Vector2(54.0f * xIndex, 54.0f * yIndex - 1.0f) + buttonsOffset,
36+
Size = new Vector2(54.0f, 54.0f),
37+
Origin = new Vector2(27.0f, 27.0f),
38+
Scale = new Vector2(0.8f, 0.8f),
39+
IsVisible = true,
40+
IconId = selectedIcon,
41+
Color = Service.Config.ButtonColor,
42+
};
43+
44+
imageNode.AddTimeline(new TimelineBuilder()
45+
.BeginFrameSet(1, 120)
46+
.AddFrame(1, scale: new Vector2(0.8f, 0.8f), rotation: 0.0f)
47+
.AddFrame(60, scale: new Vector2(0.7f, 0.7f), rotation: MathF.PI)
48+
.AddFrame(120, scale: new Vector2(0.8f, 0.8f), rotation: 2.0f * MathF.PI)
49+
.EndFrameSet()
50+
.BeginFrameSet(121, 130)
51+
.AddFrame(121, scale: new Vector2(0.8f, 0.8f), rotation: 0.0f)
52+
.EndFrameSet()
53+
.Build());
54+
55+
buttonImages[xIndex + yIndex * 3] = imageNode;
56+
57+
Service.NativeController.AttachNode(imageNode, this);
58+
}
59+
60+
laneImages = new ImageNode[8];
61+
62+
laneImages[0] = new IconImageNode {
63+
Position = new Vector2(0.0f, 40.0f) + gameGridOffset,
64+
Size = new Vector2(34.0f, 34.0f),
65+
Origin = new Vector2(17.0f, 17.0f),
66+
Color = Service.Config.LaneColor,
67+
IsVisible = true,
68+
IconId = 60934,
69+
};
70+
71+
AddLaneNodeTimeline(laneImages[0], MathF.PI / 2.0f);
72+
Service.NativeController.AttachNode(laneImages[0], this);
73+
74+
laneImages[1] = new IconImageNode {
75+
Position = new Vector2(0.0f, 94.0f) + gameGridOffset,
76+
Size = new Vector2(34.0f, 34.0f),
77+
Origin = new Vector2(17.0f, 17.0f),
78+
Color = Service.Config.LaneColor,
79+
IsVisible = true,
80+
IconId = 60934,
81+
};
82+
83+
AddLaneNodeTimeline(laneImages[1], MathF.PI / 2.0f);
84+
Service.NativeController.AttachNode(laneImages[1], this);
85+
86+
laneImages[2] = new IconImageNode {
87+
Position = new Vector2(0.0f, 148.0f) + gameGridOffset,
88+
Size = new Vector2(34.0f, 34.0f),
89+
Origin = new Vector2(17.0f, 17.0f),
90+
Color = Service.Config.LaneColor,
91+
IsVisible = true,
92+
IconId = 60934,
93+
};
94+
95+
AddLaneNodeTimeline(laneImages[2], MathF.PI / 2.0f);
96+
Service.NativeController.AttachNode(laneImages[2], this);
97+
98+
laneImages[3] = new IconImageNode {
99+
Position = new Vector2(42.0f, 0.0f) + gameGridOffset,
100+
Size = new Vector2(34.0f, 34.0f),
101+
Origin = new Vector2(17.0f, 17.0f),
102+
Color = Service.Config.LaneColor,
103+
IsVisible = true,
104+
IconId = 60934,
105+
};
106+
107+
AddLaneNodeTimeline(laneImages[3], MathF.PI);
108+
Service.NativeController.AttachNode(laneImages[3], this);
109+
110+
laneImages[4] = new IconImageNode {
111+
Position = new Vector2(96.0f, 0.0f) + gameGridOffset,
112+
Size = new Vector2(34.0f, 34.0f),
113+
Origin = new Vector2(17.0f, 17.0f),
114+
Color = Service.Config.LaneColor,
115+
IsVisible = true,
116+
IconId = 60934,
117+
};
118+
119+
AddLaneNodeTimeline(laneImages[4], MathF.PI);
120+
Service.NativeController.AttachNode(laneImages[4], this);
121+
122+
laneImages[5] = new IconImageNode {
123+
Position = new Vector2(150.0f, 0.0f) + gameGridOffset,
124+
Size = new Vector2(34.0f, 34.0f),
125+
Origin = new Vector2(17.0f, 17.0f),
126+
Color = Service.Config.LaneColor,
127+
IsVisible = true,
128+
IconId = 60934,
129+
};
130+
131+
AddLaneNodeTimeline(laneImages[5], MathF.PI);
132+
Service.NativeController.AttachNode(laneImages[5], this);
133+
134+
laneImages[6] = new IconImageNode {
135+
Position = new Vector2(0.0f, 0.0f) + gameGridOffset,
136+
Size = new Vector2(34.0f, 34.0f),
137+
Origin = new Vector2(17.0f, 17.0f),
138+
Color = Service.Config.LaneColor,
139+
IsVisible = true,
140+
IconId = 60934,
141+
};
142+
143+
AddLaneNodeTimeline(laneImages[6], MathF.PI * 3.0f / 4.0f);
144+
Service.NativeController.AttachNode(laneImages[6], this);
145+
146+
laneImages[7] = new IconImageNode {
147+
Position = new Vector2(190.0f, 0.0f) + gameGridOffset,
148+
Size = new Vector2(34.0f, 34.0f),
149+
Origin = new Vector2(17.0f, 17.0f),
150+
Color = Service.Config.LaneColor,
151+
IsVisible = true,
152+
IconId = 60934,
153+
};
154+
155+
AddLaneNodeTimeline(laneImages[7], MathF.PI + MathF.PI / 4.0f);
156+
Service.NativeController.AttachNode(laneImages[7], this);
157+
158+
Timeline?.StartAnimation(Service.Config.EnableAnimations ? 200 : 201);
159+
}
160+
161+
protected override void Dispose(bool disposing) {
162+
if (disposing) {
163+
foreach (var node in buttonImages) {
164+
node.Dispose();
165+
}
166+
167+
foreach (var lane in laneImages) {
168+
lane.Dispose();
169+
}
170+
171+
base.Dispose(disposing);
172+
}
173+
}
174+
175+
public void SetActiveButtons(params int[]? indexes) {
176+
#if DEBUG
177+
return;
178+
#endif
179+
180+
foreach (var image in buttonImages) {
181+
image.IsVisible = false;
182+
}
183+
184+
if (indexes is null) return;
185+
186+
foreach (var index in indexes) {
187+
buttonImages[index].IsVisible = true;
188+
}
189+
}
190+
191+
public void SetActiveLanes(params int[]? indexes) {
192+
#if DEBUG
193+
return;
194+
#endif
195+
196+
foreach (var lane in laneImages) {
197+
lane.IsVisible = false;
198+
}
199+
200+
if (indexes is null) return;
201+
202+
foreach (var index in indexes) {
203+
laneImages[index].IsVisible = true;
204+
}
205+
}
206+
207+
private void AddLaneNodeTimeline(ImageNode imageNode, float rotation) {
208+
imageNode.AddTimeline(new TimelineBuilder()
209+
.BeginFrameSet(1, 120)
210+
.AddFrame(1, scale: new Vector2(1.4f, 1.4f), rotation: rotation)
211+
.AddFrame(60, scale: new Vector2(1.0f, 1.0f), rotation: rotation)
212+
.AddFrame(120, scale: new Vector2(1.4f, 1.4f), rotation: rotation)
213+
.EndFrameSet()
214+
.BeginFrameSet(121, 130)
215+
.AddFrame(121, scale: new Vector2(1.0f, 1.0f), rotation: rotation)
216+
.EndFrameSet()
217+
.Build());
218+
}
219+
220+
public void UpdateIcons(uint icon) {
221+
foreach (var image in buttonImages) {
222+
image.IconId = icon;
223+
}
224+
}
225+
226+
public void UpdateButtonColors(Vector4 color) {
227+
foreach (var image in buttonImages) {
228+
image.Color = color;
229+
}
230+
}
231+
232+
public void UpdateLaneColors(Vector4 color) {
233+
foreach (var image in laneImages) {
234+
image.Color = color;
235+
}
236+
}
237+
}

0 commit comments

Comments
 (0)