diff --git a/api/src/main/java/com/lunarclient/apollo/ApolloPlatform.java b/api/src/main/java/com/lunarclient/apollo/ApolloPlatform.java
index f63d6cf4..4679d873 100644
--- a/api/src/main/java/com/lunarclient/apollo/ApolloPlatform.java
+++ b/api/src/main/java/com/lunarclient/apollo/ApolloPlatform.java
@@ -25,6 +25,7 @@
import com.lunarclient.apollo.option.Options;
import com.lunarclient.apollo.stats.ApolloStats;
+import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import org.jetbrains.annotations.ApiStatus;
@@ -92,6 +93,14 @@ public interface ApolloPlatform {
*/
Object getPlugin();
+ /**
+ * Returns the platform {@link Scheduler}.
+ *
+ * @return the platform scheduler
+ * @since 1.2.9
+ */
+ Scheduler getScheduler();
+
/**
* Represents the kind of server a platform is.
*
@@ -115,4 +124,26 @@ enum Platform {
VELOCITY
}
+ /**
+ * Represents the platform scheduler, running tasks through the
+ * platform plugin's own scheduler.
+ *
+ * @since 1.2.9
+ */
+ interface Scheduler {
+
+ /**
+ * Schedules a task to run asynchronously every period, first running
+ * after the given delay.
+ *
+ * @param task the task to run
+ * @param delay the delay before the first run
+ * @param period the period between runs
+ * @param unit the unit of the delay and period
+ * @since 1.2.9
+ */
+ void scheduleAsyncRepeating(Runnable task, long delay, long period, TimeUnit unit);
+
+ }
+
}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButton.java b/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButton.java
new file mode 100644
index 00000000..3dbe830e
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButton.java
@@ -0,0 +1,169 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button;
+
+import com.lunarclient.apollo.common.button.action.ApolloButtonAction;
+import com.lunarclient.apollo.common.button.action.ApolloButtonClientAction;
+import com.lunarclient.apollo.common.button.content.ApolloButtonContent;
+import com.lunarclient.apollo.common.location.HudPosition;
+import java.awt.Color;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.experimental.SuperBuilder;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Represents a button which can be shown on the client.
+ *
+ * @since 1.2.9
+ */
+@Getter
+@SuperBuilder(toBuilder = true)
+@ApiStatus.NonExtendable
+public abstract class ApolloButton {
+
+ /**
+ * Returns the button {@link String} id.
+ *
+ *
Displaying another button with the same id replaces the
+ * previous one.
+ *
+ * @return the button id
+ * @since 1.2.9
+ */
+ @NotNull String id;
+
+ /**
+ * Returns the {@link HudPosition} of this button, relative to the
+ * top-left corner of the container it is placed in.
+ *
+ * The button must fit inside the container: {@code 0 <= x},
+ * {@code 0 <= y}, {@code x + width <= container width} and
+ * {@code y + height <= container height}. See the surface type for
+ * its container dimensions (e.g. {@code InventoryButton#BOX_WIDTH}).
+ *
+ * @return the button position
+ * @since 1.2.9
+ */
+ @NotNull HudPosition position;
+
+ /**
+ * Returns the {@link ApolloButtonSize} of this button.
+ *
+ * Use one of the surface's suggested sizes (e.g.
+ * {@code InventoryButton.SIZE_MEDIUM}) or a fully custom
+ * {@link ApolloButtonSize#of(float, float)}.
+ *
+ * @return the button size
+ * @since 1.2.9
+ */
+ @NotNull ApolloButtonSize size;
+
+ /**
+ * Returns the {@link ApolloButtonShape} of this button.
+ *
+ * @return the button shape
+ * @since 1.2.9
+ */
+ @NotNull ApolloButtonShape shape;
+
+ /**
+ * Returns the {@link ApolloButtonContent} rendered inside this button.
+ *
+ * Built via {@code ApolloButtonContent.builder()}, appending static
+ * components, icons or live per-player parts that are re-resolved
+ * whenever the content is sent (see the owning module's live button
+ * broadcast option, e.g. {@code InventoryModule#BROADCAST_LIVE_BUTTONS}).
+ *
+ * @return the button content
+ * @since 1.2.9
+ */
+ @NotNull ApolloButtonContent content;
+
+ /**
+ * Returns the {@link ApolloButtonTooltip} shown while this button is hovered.
+ *
+ * Built via {@code ApolloButtonTooltip.of(...)} for static lines,
+ * or {@code ApolloButtonTooltip.live(...)} for per-player lines that
+ * are re-resolved whenever the button content is sent (see the owning
+ * module's live button broadcast option, e.g.
+ * {@code InventoryModule#BROADCAST_LIVE_BUTTONS}).
+ *
+ * @return the tooltip, or {@code null} for no tooltip
+ * @since 1.2.9
+ */
+ @Builder.Default
+ @Nullable ApolloButtonTooltip tooltip = null;
+
+ /**
+ * Returns the {@link ApolloButtonAction} executed when this button
+ * is clicked.
+ *
+ * Built via {@link ApolloButtonAction#runCommand(String)},
+ * {@link ApolloButtonAction#openUrl(String)} or
+ * {@link ApolloButtonAction#clientAction(ApolloButtonClientAction)}.
+ *
+ * @return the click action, or {@code null}
+ * @since 1.2.9
+ */
+ @Builder.Default
+ @Nullable ApolloButtonAction onClick = null;
+
+ /**
+ * Returns the background {@link Color} used while this button is hovered.
+ *
+ * @return the hovered background color, or {@code null}
+ * @since 1.2.9
+ */
+ @Builder.Default
+ @Nullable Color hoveredBackgroundColor = null;
+
+ /**
+ * Returns the border {@link Color} used while this button is hovered.
+ *
+ * @return the hovered border color, or {@code null}
+ * @since 1.2.9
+ */
+ @Builder.Default
+ @Nullable Color hoveredBorderColor = null;
+
+ /**
+ * Returns the background {@link Color} of this button.
+ *
+ * @return the background color
+ * @since 1.2.9
+ */
+ public abstract Color getBackgroundColor();
+
+ /**
+ * Returns the border {@link Color} of this button.
+ *
+ * @return the border color
+ * @since 1.2.9
+ */
+ public abstract Color getBorderColor();
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButtonShape.java b/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButtonShape.java
new file mode 100644
index 00000000..6ba14b6a
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButtonShape.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button;
+
+/**
+ * Represents the shape of a button.
+ *
+ * @since 1.2.9
+ */
+public enum ApolloButtonShape {
+ ROUNDED_SQUARE,
+ CIRCLE
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButtonSize.java b/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButtonSize.java
new file mode 100644
index 00000000..6ed3d954
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButtonSize.java
@@ -0,0 +1,86 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Represents the size of an {@link ApolloButton}, in GUI-scaled pixels.
+ *
+ * Surface types ship suggested sizes tuned to their container
+ * dimensions (e.g. {@code InventoryButton.SIZE_MEDIUM}); use
+ * {@link #of(float, float)} for a fully custom size.
+ *
+ * @since 1.2.9
+ */
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public final class ApolloButtonSize {
+
+ /**
+ * Creates a new {@link ApolloButtonSize} with the given dimensions.
+ *
+ * @param width the button width, must be finite and greater than 0
+ * @param height the button height, must be finite and greater than 0
+ * @return the button size
+ * @since 1.2.9
+ */
+ public static ApolloButtonSize of(float width, float height) {
+ if (!(width > 0.0F) || !(height > 0.0F) || !Float.isFinite(width) || !Float.isFinite(height)) {
+ throw new IllegalArgumentException("ApolloButtonSize dimensions must be finite and greater than 0");
+ }
+
+ return new ApolloButtonSize(width, height);
+ }
+
+ /**
+ * Creates a new square {@link ApolloButtonSize}.
+ *
+ * @param size the button width and height, must be finite and greater than 0
+ * @return the button size
+ * @since 1.2.9
+ */
+ public static ApolloButtonSize of(float size) {
+ return of(size, size);
+ }
+
+ /**
+ * Returns the {@code float} button width.
+ *
+ * @return the button width
+ * @since 1.2.9
+ */
+ private final float width;
+
+ /**
+ * Returns the {@code float} button height.
+ *
+ * @return the button height
+ * @since 1.2.9
+ */
+ private final float height;
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButtonTooltip.java b/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButtonTooltip.java
new file mode 100644
index 00000000..4f5fb3a3
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/ApolloButtonTooltip.java
@@ -0,0 +1,151 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button;
+
+import com.lunarclient.apollo.player.ApolloPlayer;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+import lombok.Getter;
+import lombok.NonNull;
+import net.kyori.adventure.text.Component;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Represents the tooltip of an {@link ApolloButton}, either a static
+ * list of lines or a live per-player resolver.
+ *
+ * @since 1.2.9
+ */
+@Getter
+public final class ApolloButtonTooltip {
+
+ /**
+ * The maximum number of tooltip lines.
+ *
+ * @since 1.2.9
+ */
+ public static final int MAX_LINES = 100;
+
+ /**
+ * Creates a static tooltip from the given lines.
+ *
+ * @param lines the tooltip lines
+ * @return the tooltip
+ * @since 1.2.9
+ */
+ public static ApolloButtonTooltip of(@NonNull Component... lines) {
+ return of(Arrays.asList(lines));
+ }
+
+ /**
+ * Creates a static tooltip from the given lines.
+ *
+ * @param lines the tooltip lines
+ * @return the tooltip
+ * @since 1.2.9
+ */
+ public static ApolloButtonTooltip of(@NonNull List lines) {
+ if (lines.size() > MAX_LINES) {
+ throw new IllegalArgumentException("ApolloButtonTooltip supports at most " + MAX_LINES + " lines");
+ }
+
+ for (Component line : lines) {
+ if (line == null) {
+ throw new IllegalArgumentException("ApolloButtonTooltip lines must not contain null");
+ }
+ }
+
+ return new ApolloButtonTooltip(lines, null, null);
+ }
+
+ /**
+ * Creates a live tooltip, resolved into lines for each viewing
+ * {@link ApolloPlayer} and refreshed at the given interval.
+ *
+ * The interval must be positive and is quantized to server ticks
+ * (50ms); anything below one tick refreshes every tick.
+ *
+ * @param resolver the per-player tooltip resolver
+ * @param updateInterval the refresh interval
+ * @return the tooltip
+ * @since 1.2.9
+ */
+ public static ApolloButtonTooltip live(@NonNull Function> resolver,
+ @NonNull Duration updateInterval) {
+ if (updateInterval.isNegative() || updateInterval.isZero()) {
+ throw new IllegalArgumentException("ApolloButtonTooltip#updateInterval must be positive");
+ }
+
+ return new ApolloButtonTooltip(null, resolver, updateInterval);
+ }
+
+ /**
+ * Returns the static tooltip lines, or {@code null} if this tooltip is live.
+ *
+ * @return the tooltip lines, or {@code null}
+ * @since 1.2.9
+ */
+ private final @Nullable List lines;
+
+ /**
+ * Returns the per-player live tooltip resolver, or {@code null} if this
+ * tooltip is static.
+ *
+ * @return the live tooltip resolver, or {@code null}
+ * @since 1.2.9
+ */
+ private final @Nullable Function> resolver;
+
+ /**
+ * Returns the refresh interval of a live tooltip, or {@code null} if
+ * this tooltip is static.
+ *
+ * @return the update interval, or {@code null}
+ * @since 1.2.9
+ */
+ private final @Nullable Duration updateInterval;
+
+ private ApolloButtonTooltip(@Nullable List lines,
+ @Nullable Function> resolver,
+ @Nullable Duration updateInterval) {
+ this.lines = lines == null ? null : Collections.unmodifiableList(new ArrayList<>(lines));
+ this.resolver = resolver;
+ this.updateInterval = updateInterval;
+ }
+
+ /**
+ * Returns whether this tooltip is live.
+ *
+ * @return whether this tooltip is live
+ * @since 1.2.9
+ */
+ public boolean isLive() {
+ return this.resolver != null;
+ }
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/action/ApolloButtonAction.java b/api/src/main/java/com/lunarclient/apollo/common/button/action/ApolloButtonAction.java
new file mode 100644
index 00000000..1ec44ac0
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/action/ApolloButtonAction.java
@@ -0,0 +1,81 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button.action;
+
+import com.lunarclient.apollo.common.button.ApolloButton;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * The abstract base class for the action executed when an
+ * {@link ApolloButton} is clicked.
+ *
+ * Each action is one of {@link RunCommandAction}, {@link OpenUrlAction}
+ * or {@link ClientAction}.
+ *
+ * @since 1.2.9
+ */
+@NoArgsConstructor(access = AccessLevel.PACKAGE)
+@ApiStatus.NonExtendable
+public abstract class ApolloButtonAction {
+
+ /**
+ * Creates an action that runs the given command as the player.
+ *
+ * Must start with {@code /}.
+ *
+ * @param command the command to run
+ * @return the run command action
+ * @since 1.2.9
+ */
+ public static RunCommandAction runCommand(@NonNull String command) {
+ return new RunCommandAction(command);
+ }
+
+ /**
+ * Creates an action that opens the given URL.
+ *
+ * @param url the url to open
+ * @return the open url action
+ * @since 1.2.9
+ */
+ public static OpenUrlAction openUrl(@NonNull String url) {
+ return new OpenUrlAction(url);
+ }
+
+ /**
+ * Creates an action that executes a built-in
+ * {@link ApolloButtonClientAction}.
+ *
+ * @param action the client action to execute
+ * @return the client action
+ * @since 1.2.9
+ */
+ public static ClientAction clientAction(@NonNull ApolloButtonClientAction action) {
+ return new ClientAction(action);
+ }
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/action/ApolloButtonClientAction.java b/api/src/main/java/com/lunarclient/apollo/common/button/action/ApolloButtonClientAction.java
new file mode 100644
index 00000000..76d8c57c
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/action/ApolloButtonClientAction.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button.action;
+
+/**
+ * Represents a built-in client action executed when a button is clicked.
+ *
+ * @since 1.2.9
+ */
+public enum ApolloButtonClientAction {
+
+ /**
+ * Opens the fullscreen minimap view.
+ *
+ * Does nothing when the player has the MiniMap mod disabled.
+ *
+ * @since 1.2.9
+ */
+ OPEN_MINIMAP_VIEW,
+
+ /**
+ * Opens the waypoints menu.
+ *
+ * @since 1.2.9
+ */
+ OPEN_WAYPOINTS_MENU
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/action/ClientAction.java b/api/src/main/java/com/lunarclient/apollo/common/button/action/ClientAction.java
new file mode 100644
index 00000000..c9049773
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/action/ClientAction.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button.action;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Represents a button action that executes a built-in
+ * {@link ApolloButtonClientAction}.
+ *
+ * @since 1.2.9
+ */
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
+public final class ClientAction extends ApolloButtonAction {
+
+ /**
+ * Returns the built-in client action executed when the button
+ * is clicked.
+ *
+ * @return the client action
+ * @since 1.2.9
+ */
+ private final ApolloButtonClientAction action;
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/action/OpenUrlAction.java b/api/src/main/java/com/lunarclient/apollo/common/button/action/OpenUrlAction.java
new file mode 100644
index 00000000..d28e1d83
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/action/OpenUrlAction.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button.action;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Represents a button action that opens a URL.
+ *
+ * @since 1.2.9
+ */
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
+public final class OpenUrlAction extends ApolloButtonAction {
+
+ /**
+ * Returns the URL opened when the button is clicked.
+ *
+ * @return the url
+ * @since 1.2.9
+ */
+ private final String url;
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/action/RunCommandAction.java b/api/src/main/java/com/lunarclient/apollo/common/button/action/RunCommandAction.java
new file mode 100644
index 00000000..65d4f709
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/action/RunCommandAction.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button.action;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Represents a button action that runs a command as the player.
+ *
+ * @since 1.2.9
+ */
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
+public final class RunCommandAction extends ApolloButtonAction {
+
+ /**
+ * Returns the command run as the player when the button is clicked.
+ *
+ * @return the command
+ * @since 1.2.9
+ */
+ private final String command;
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/content/ApolloButtonContent.java b/api/src/main/java/com/lunarclient/apollo/common/button/content/ApolloButtonContent.java
new file mode 100644
index 00000000..edf468d8
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/content/ApolloButtonContent.java
@@ -0,0 +1,210 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button.content;
+
+import com.lunarclient.apollo.common.button.ApolloButton;
+import com.lunarclient.apollo.common.icon.Icon;
+import com.lunarclient.apollo.player.ApolloPlayer;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+import lombok.Getter;
+import lombok.NonNull;
+import net.kyori.adventure.text.Component;
+
+/**
+ * Represents the content of an {@link ApolloButton}.
+ *
+ * @since 1.2.9
+ */
+@Getter
+public final class ApolloButtonContent {
+
+ /**
+ * The maximum number of content parts per button.
+ *
+ * @since 1.2.9
+ */
+ public static final int MAX_PARTS = 30;
+
+ /**
+ * The minimum content {@link #scale}.
+ *
+ * @since 1.2.9
+ */
+ public static final float MIN_SCALE = 0.25F;
+
+ /**
+ * The maximum content {@link #scale}.
+ *
+ * @since 1.2.9
+ */
+ public static final float MAX_SCALE = 4.0F;
+
+ /**
+ * Creates a new {@link Builder} for building button content.
+ *
+ * @return the content builder
+ * @since 1.2.9
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Returns an unmodifiable {@link List} of the content
+ * {@link ApolloButtonContentPart}s, in render order.
+ *
+ * @return the content parts
+ * @since 1.2.9
+ */
+ private final List parts;
+
+ /**
+ * Returns the {@code float} scale factor applied to the content row.
+ *
+ * @return the content scale
+ * @since 1.2.9
+ */
+ private final float scale;
+
+ /**
+ * Returns whether any part of this content is live.
+ *
+ * @return whether this content has live parts
+ * @since 1.2.9
+ */
+ private final boolean live;
+
+ private ApolloButtonContent(List parts, float scale, boolean live) {
+ this.parts = Collections.unmodifiableList(parts);
+ this.scale = scale;
+ this.live = live;
+ }
+
+ /**
+ * Represents a builder for {@link ApolloButtonContent}.
+ *
+ * @since 1.2.9
+ */
+ public static final class Builder {
+
+ private final List parts = new ArrayList<>();
+ private float scale = 1.0F;
+ private boolean live;
+
+ private Builder() {
+ }
+
+ /**
+ * Appends an adventure {@link Component} to the content.
+ *
+ * @param component the component to append
+ * @return this builder
+ * @since 1.2.9
+ */
+ public Builder append(@NonNull Component component) {
+ return this.append(ApolloButtonContentPart.component(component));
+ }
+
+ /**
+ * Appends an {@link Icon} to the content; see {@link IconPart}.
+ *
+ * @param icon the icon to append
+ * @return this builder
+ * @since 1.2.9
+ */
+ public Builder append(@NonNull Icon icon) {
+ return this.append(ApolloButtonContentPart.icon(icon));
+ }
+
+ /**
+ * Appends a live part, resolved into an adventure {@link Component}
+ * for each viewing {@link ApolloPlayer} and refreshed at the given
+ * interval.
+ *
+ * @param liveComponent the per-player component resolver to append
+ * @param updateInterval the refresh interval
+ * @return this builder
+ * @since 1.2.9
+ */
+ public Builder append(@NonNull Function liveComponent,
+ @NonNull Duration updateInterval) {
+ return this.append(ApolloButtonContentPart.live(liveComponent, updateInterval));
+ }
+
+ /**
+ * Appends a pre-built content part; see the
+ * {@link ApolloButtonContentPart} factories.
+ *
+ * @param part the content part to append
+ * @return this builder
+ * @since 1.2.9
+ */
+ public Builder append(@NonNull ApolloButtonContentPart part) {
+ this.parts.add(part);
+ this.live |= part.isLive();
+ return this;
+ }
+
+ /**
+ * Sets the {@code float} scale factor applied to the content row.
+ * Defaults to {@code 1.0}.
+ *
+ * @param scale the content scale, between {@link #MIN_SCALE} and {@link #MAX_SCALE}
+ * @return this builder
+ * @since 1.2.9
+ */
+ public Builder scale(float scale) {
+ if (!(scale >= MIN_SCALE && scale <= MAX_SCALE)) {
+ throw new IllegalArgumentException("ApolloButtonContent#scale must be between " + MIN_SCALE + " and " + MAX_SCALE);
+ }
+
+ this.scale = scale;
+ return this;
+ }
+
+ /**
+ * Builds the {@link ApolloButtonContent}.
+ *
+ * @return the built content
+ * @since 1.2.9
+ */
+ public ApolloButtonContent build() {
+ if (this.parts.isEmpty()) {
+ throw new IllegalArgumentException("ApolloButtonContent requires at least one part");
+ }
+
+ if (this.parts.size() > MAX_PARTS) {
+ throw new IllegalArgumentException("ApolloButtonContent supports at most " + MAX_PARTS + " parts");
+ }
+
+ return new ApolloButtonContent(new ArrayList<>(this.parts), this.scale, this.live);
+ }
+
+ }
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/content/ApolloButtonContentPart.java b/api/src/main/java/com/lunarclient/apollo/common/button/content/ApolloButtonContentPart.java
new file mode 100644
index 00000000..d9181a2b
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/content/ApolloButtonContentPart.java
@@ -0,0 +1,102 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button.content;
+
+import com.lunarclient.apollo.common.icon.Icon;
+import com.lunarclient.apollo.player.ApolloPlayer;
+import java.time.Duration;
+import java.util.function.Function;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+import net.kyori.adventure.text.Component;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * The abstract base class for a single {@link ApolloButtonContent} part.
+ *
+ * Each part is one of {@link ComponentPart}, {@link IconPart} or
+ * {@link LiveComponentPart}, created through the factory methods on this
+ * class or the {@link ApolloButtonContent.Builder} append overloads.
+ *
+ * @since 1.2.9
+ */
+@NoArgsConstructor(access = AccessLevel.PACKAGE)
+@ApiStatus.NonExtendable
+public abstract class ApolloButtonContentPart {
+
+ /**
+ * Creates a static text part from the given adventure {@link Component}.
+ *
+ * @param component the component to render
+ * @return the component part
+ * @since 1.2.9
+ */
+ public static ComponentPart component(@NonNull Component component) {
+ return new ComponentPart(component);
+ }
+
+ /**
+ * Creates an icon part from the given {@link Icon}.
+ *
+ * @param icon the icon to render
+ * @return the icon part
+ * @since 1.2.9
+ */
+ public static IconPart icon(@NonNull Icon icon) {
+ return new IconPart(icon);
+ }
+
+ /**
+ * Creates a live part, resolved into an adventure {@link Component} for
+ * each viewing {@link ApolloPlayer} and refreshed at the given interval.
+ *
+ * The interval must be positive and is quantized to server ticks
+ * (50ms); anything below one tick refreshes every tick.
+ *
+ * @param resolver the per-player component resolver
+ * @param updateInterval the refresh interval
+ * @return the live component part
+ * @since 1.2.9
+ */
+ public static LiveComponentPart live(@NonNull Function resolver,
+ @NonNull Duration updateInterval) {
+ if (updateInterval.isNegative() || updateInterval.isZero()) {
+ throw new IllegalArgumentException("LiveComponentPart#updateInterval must be positive");
+ }
+
+ return new LiveComponentPart(resolver, updateInterval);
+ }
+
+ /**
+ * Returns whether this part is live.
+ *
+ * @return whether this part is live
+ * @since 1.2.9
+ */
+ public boolean isLive() {
+ return false;
+ }
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/content/ComponentPart.java b/api/src/main/java/com/lunarclient/apollo/common/button/content/ComponentPart.java
new file mode 100644
index 00000000..e2955e22
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/content/ComponentPart.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button.content;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.kyori.adventure.text.Component;
+
+/**
+ * Represents a static text content part.
+ *
+ * @since 1.2.9
+ */
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
+public final class ComponentPart extends ApolloButtonContentPart {
+
+ /**
+ * Returns the adventure {@link Component} rendered by this part.
+ *
+ * @return the component
+ * @since 1.2.9
+ */
+ private final Component component;
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/content/IconPart.java b/api/src/main/java/com/lunarclient/apollo/common/button/content/IconPart.java
new file mode 100644
index 00000000..fbb4caca
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/content/IconPart.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button.content;
+
+import com.lunarclient.apollo.common.icon.Icon;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Represents an icon content part.
+ *
+ * Can be any of the icons found in the {@link com.lunarclient.apollo.common.icon} package;
+ * for the most common use case, use {@link com.lunarclient.apollo.common.icon.ItemStackIcon}.
+ *
+ * @since 1.2.9
+ */
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
+public final class IconPart extends ApolloButtonContentPart {
+
+ /**
+ * Returns the {@link Icon} rendered by this part.
+ *
+ * @return the icon
+ * @since 1.2.9
+ */
+ private final Icon icon;
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/button/content/LiveComponentPart.java b/api/src/main/java/com/lunarclient/apollo/common/button/content/LiveComponentPart.java
new file mode 100644
index 00000000..faab4d7a
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/button/content/LiveComponentPart.java
@@ -0,0 +1,66 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.button.content;
+
+import com.lunarclient.apollo.player.ApolloPlayer;
+import java.time.Duration;
+import java.util.function.Function;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.kyori.adventure.text.Component;
+
+/**
+ * Represents a live content part: a per-player {@link Component} resolver
+ * with an update interval controlling how often the module's live button
+ * broadcast re-resolves and re-sends it.
+ *
+ * @since 1.2.9
+ */
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
+public final class LiveComponentPart extends ApolloButtonContentPart {
+
+ /**
+ * Returns the per-player live {@link Component} resolver.
+ *
+ * @return the component resolver
+ * @since 1.2.9
+ */
+ private final Function resolver;
+
+ /**
+ * Returns the refresh interval.
+ *
+ * @return the update interval
+ * @since 1.2.9
+ */
+ private final Duration updateInterval;
+
+ @Override
+ public boolean isLive() {
+ return true;
+ }
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/icon/ItemStackIcon.java b/api/src/main/java/com/lunarclient/apollo/common/icon/ItemStackIcon.java
index 3913eee7..ff1fffe7 100644
--- a/api/src/main/java/com/lunarclient/apollo/common/icon/ItemStackIcon.java
+++ b/api/src/main/java/com/lunarclient/apollo/common/icon/ItemStackIcon.java
@@ -79,4 +79,12 @@ public final class ItemStackIcon extends Icon {
*/
@Nullable Profile profile;
+ /**
+ * Returns the icon {@link String} potion id (e.g. {@code "healing"}).
+ *
+ * @return the icon potion id
+ * @since 1.2.9
+ */
+ @Nullable String potion;
+
}
diff --git a/api/src/main/java/com/lunarclient/apollo/common/location/HudPosition.java b/api/src/main/java/com/lunarclient/apollo/common/location/HudPosition.java
index c6f7f4fa..4d3f7a49 100644
--- a/api/src/main/java/com/lunarclient/apollo/common/location/HudPosition.java
+++ b/api/src/main/java/com/lunarclient/apollo/common/location/HudPosition.java
@@ -35,6 +35,18 @@
@Builder
public final class HudPosition {
+ /**
+ * Creates a new {@link HudPosition} with the given coordinates.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @return the hud position
+ * @since 1.2.9
+ */
+ public static HudPosition of(float x, float y) {
+ return new HudPosition(x, y);
+ }
+
/**
* Returns the {@code float} X coordinate for this HUD position.
*
diff --git a/api/src/main/java/com/lunarclient/apollo/event/packetenrichment/inventory/ApolloPlayerInventoryCloseEvent.java b/api/src/main/java/com/lunarclient/apollo/event/packetenrichment/inventory/ApolloPlayerInventoryCloseEvent.java
new file mode 100644
index 00000000..ab47b864
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/event/packetenrichment/inventory/ApolloPlayerInventoryCloseEvent.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.event.packetenrichment.inventory;
+
+import com.lunarclient.apollo.event.Event;
+import com.lunarclient.apollo.module.packetenrichment.PlayerInfo;
+import com.lunarclient.apollo.player.ApolloPlayer;
+import lombok.Value;
+
+/**
+ * Represents an event that is fired when the player closes their inventory.
+ *
+ * @since 1.2.9
+ */
+@Value
+public class ApolloPlayerInventoryCloseEvent implements Event {
+
+ /**
+ * The player that sent the packet.
+ *
+ * @return the player
+ * @since 1.2.9
+ */
+ ApolloPlayer player;
+
+ /**
+ * The {@code long} representing the unix timestamp
+ * when the packet was created.
+ *
+ * @return the unix timestamp
+ * @since 1.2.9
+ */
+ long instantiationTimeMs;
+
+ /**
+ * The player's {@link PlayerInfo} information.
+ *
+ * @return the player's player info
+ * @since 1.2.9
+ */
+ PlayerInfo playerInfo;
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/event/packetenrichment/inventory/ApolloPlayerInventoryOpenEvent.java b/api/src/main/java/com/lunarclient/apollo/event/packetenrichment/inventory/ApolloPlayerInventoryOpenEvent.java
new file mode 100644
index 00000000..9d0c3dde
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/event/packetenrichment/inventory/ApolloPlayerInventoryOpenEvent.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.event.packetenrichment.inventory;
+
+import com.lunarclient.apollo.event.Event;
+import com.lunarclient.apollo.module.packetenrichment.PlayerInfo;
+import com.lunarclient.apollo.player.ApolloPlayer;
+import lombok.Value;
+
+/**
+ * Represents an event that is fired when the player opens their inventory.
+ *
+ * @since 1.2.9
+ */
+@Value
+public class ApolloPlayerInventoryOpenEvent implements Event {
+
+ /**
+ * The player that sent the packet.
+ *
+ * @return the player
+ * @since 1.2.9
+ */
+ ApolloPlayer player;
+
+ /**
+ * The {@code long} representing the unix timestamp
+ * when the packet was created.
+ *
+ * @return the unix timestamp
+ * @since 1.2.9
+ */
+ long instantiationTimeMs;
+
+ /**
+ * The player's {@link PlayerInfo} information.
+ *
+ * @return the player's player info
+ * @since 1.2.9
+ */
+ PlayerInfo playerInfo;
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/Mods.java b/api/src/main/java/com/lunarclient/apollo/mods/Mods.java
index 344016d6..98e66355 100644
--- a/api/src/main/java/com/lunarclient/apollo/mods/Mods.java
+++ b/api/src/main/java/com/lunarclient/apollo/mods/Mods.java
@@ -25,6 +25,7 @@
import com.lunarclient.apollo.mods.impl.Mod2dItems;
import com.lunarclient.apollo.mods.impl.Mod3dSkins;
+import com.lunarclient.apollo.mods.impl.ModActionBar;
import com.lunarclient.apollo.mods.impl.ModArmorstatus;
import com.lunarclient.apollo.mods.impl.ModAttackIndicator;
import com.lunarclient.apollo.mods.impl.ModAudioSubtitles;
@@ -64,6 +65,8 @@
import com.lunarclient.apollo.mods.impl.ModItemTracker;
import com.lunarclient.apollo.mods.impl.ModKeystrokes;
import com.lunarclient.apollo.mods.impl.ModKillSounds;
+import com.lunarclient.apollo.mods.impl.ModKnockbackTrainer;
+import com.lunarclient.apollo.mods.impl.ModLightOverlay;
import com.lunarclient.apollo.mods.impl.ModLighting;
import com.lunarclient.apollo.mods.impl.ModMarkers;
import com.lunarclient.apollo.mods.impl.ModMemory;
@@ -83,6 +86,7 @@
import com.lunarclient.apollo.mods.impl.ModParticleChanger;
import com.lunarclient.apollo.mods.impl.ModPing;
import com.lunarclient.apollo.mods.impl.ModPlaytime;
+import com.lunarclient.apollo.mods.impl.ModPotionCounter;
import com.lunarclient.apollo.mods.impl.ModPotionEffects;
import com.lunarclient.apollo.mods.impl.ModPvpInfo;
import com.lunarclient.apollo.mods.impl.ModQuickplay;
@@ -155,6 +159,7 @@ public final class Mods {
ModScoreboard.class,
ModTitles.class,
ModItemCounter.class,
+ ModPotionCounter.class,
ModPing.class,
ModMotionBlur.class,
ModPackOrganizer.class,
@@ -218,15 +223,18 @@ public final class Mods {
ModOverlayMod.class,
ModRewind.class,
ModAudioSubtitles.class,
+ ModActionBar.class,
ModShields.class,
+ ModLightOverlay.class,
ModKillSounds.class,
ModInventoryMod.class,
ModF3Display.class,
ModGuiScale.class,
+ ModKnockbackTrainer.class,
ModRadio.class,
- ModSba.class,
ModUhcOverlay.class,
- ModNeu.class
+ ModNeu.class,
+ ModSba.class
);
private Mods() {
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModActionBar.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModActionBar.java
new file mode 100644
index 00000000..d416060f
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModActionBar.java
@@ -0,0 +1,63 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.mods.impl;
+
+import com.lunarclient.apollo.option.NumberOption;
+import com.lunarclient.apollo.option.SimpleOption;
+import io.leangen.geantyref.TypeToken;
+
+/**
+ * Allows you to move and scale Minecraft's action bar.
+ *
+ * @since 1.2.9
+ */
+public final class ModActionBar {
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption ENABLED = SimpleOption.builder()
+ .node("action-bar", "enabled").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption SCALE = NumberOption.number()
+ .node("action-bar", "scale").type(TypeToken.get(Float.class))
+ .min(0.25F).max(5.0F)
+ .defaultValue(1.0F)
+ .notifyClient()
+ .build();
+
+ private ModActionBar() {
+ }
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModChat.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModChat.java
index 231f2f69..b428e8c2 100644
--- a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModChat.java
+++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModChat.java
@@ -207,6 +207,66 @@ public final class ModChat {
.notifyClient()
.build();
+ /**
+ * Displays the player's Minecraft head before their message in chat.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption CHAT_HEADS = SimpleOption.builder()
+ .comment("Displays the player's Minecraft head before their message in chat.")
+ .node("chat", "chat-heads").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * Looks up skins for chat senders that aren't in the tab list, like players on other servers of a network. Head detection may be less accurate.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption CHAT_HEADS_FETCH_UNKNOWN = SimpleOption.builder()
+ .comment("Looks up skins for chat senders that aren't in the tab list, like players on other servers of a network. Head detection may be less accurate.")
+ .node("chat", "chat-heads-fetch-unknown").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * Adds emojis to chat. Shortcodes like :smile: autocomplete as you type, and emojis in messages show as colorful Twemoji.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption CHAT_EMOJI = SimpleOption.builder()
+ .comment("Adds emojis to chat. Shortcodes like :smile: autocomplete as you type, and emojis in messages show as colorful Twemoji.")
+ .node("chat", "chat-emoji").type(TypeToken.get(Boolean.class))
+ .defaultValue(true)
+ .notifyClient()
+ .build();
+
+ /**
+ * Shows emojis with Minecraft's built-in font instead of Twemoji images. Shortcodes the game font can't display are left as text.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption CHAT_EMOJI_UNICODE_ONLY = SimpleOption.builder()
+ .comment("Shows emojis with Minecraft's built-in font instead of Twemoji images. Shortcodes the game font can't display are left as text.")
+ .node("chat", "chat-emoji-unicode-only").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * Turns shortcodes like :skull: in other players' messages into emojis. When disabled, only emojis sent as actual characters are shown.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption CHAT_EMOJI_CONVERT_SHORTCODES = SimpleOption.builder()
+ .comment("Turns shortcodes like :skull: in other players' messages into emojis. When disabled, only emojis sent as actual characters are shown.")
+ .node("chat", "chat-emoji-convert-shortcodes").type(TypeToken.get(Boolean.class))
+ .defaultValue(true)
+ .notifyClient()
+ .build();
+
/**
* Copies the hovered chat message when holding the keybind and clicking.
*
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModHitbox.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModHitbox.java
index 715abbd2..66109a77 100644
--- a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModHitbox.java
+++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModHitbox.java
@@ -104,6 +104,62 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PLAYER_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-player-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PLAYER_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-player-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * Only show the hitbox when the entity is within reach and under your crosshair.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PLAYER_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .comment("Only show the hitbox when the entity is within reach and under your crosshair")
+ .node("hitbox", "hitbox-player-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PLAYER_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-player-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PLAYER_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-player-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -149,6 +205,61 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ITEM_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-item-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ITEM_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-item-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ITEM_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .node("hitbox", "hitbox-item-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ITEM_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-item-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ITEM_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-item-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -194,6 +305,61 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_EXP_ORB_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-exp-orb-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_EXP_ORB_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-exp-orb-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_EXP_ORB_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .node("hitbox", "hitbox-exp-orb-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_EXP_ORB_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-exp-orb-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_EXP_ORB_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-exp-orb-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -239,6 +405,61 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ITEM_FRAME_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-item-frame-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ITEM_FRAME_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-item-frame-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ITEM_FRAME_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .node("hitbox", "hitbox-item-frame-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ITEM_FRAME_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-item-frame-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ITEM_FRAME_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-item-frame-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -284,6 +505,61 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_FIREWORK_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-firework-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_FIREWORK_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-firework-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_FIREWORK_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .node("hitbox", "hitbox-firework-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_FIREWORK_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-firework-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_FIREWORK_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-firework-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -329,6 +605,61 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_WITHER_SKULL_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-wither-skull-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_WITHER_SKULL_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-wither-skull-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_WITHER_SKULL_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .node("hitbox", "hitbox-wither-skull-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_WITHER_SKULL_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-wither-skull-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_WITHER_SKULL_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-wither-skull-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -374,6 +705,61 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_SNOWBALL_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-snowball-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_SNOWBALL_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-snowball-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_SNOWBALL_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .node("hitbox", "hitbox-snowball-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_SNOWBALL_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-snowball-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_SNOWBALL_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-snowball-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -419,6 +805,61 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_FIREBALL_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-fireball-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_FIREBALL_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-fireball-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_FIREBALL_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .node("hitbox", "hitbox-fireball-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_FIREBALL_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-fireball-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_FIREBALL_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-fireball-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -464,6 +905,61 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ARROW_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-arrow-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ARROW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-arrow-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ARROW_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .node("hitbox", "hitbox-arrow-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ARROW_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-arrow-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_ARROW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-arrow-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -509,6 +1005,61 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PROJECTILE_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-projectile-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PROJECTILE_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-projectile-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PROJECTILE_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .node("hitbox", "hitbox-projectile-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PROJECTILE_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-projectile-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PROJECTILE_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-projectile-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -554,6 +1105,62 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_MONSTER_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-monster-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_MONSTER_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-monster-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * Only show the hitbox when the entity is within reach and under your crosshair.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_MONSTER_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .comment("Only show the hitbox when the entity is within reach and under your crosshair")
+ .node("hitbox", "hitbox-monster-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_MONSTER_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-monster-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_MONSTER_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-monster-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -599,6 +1206,62 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PASSIVE_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-passive-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PASSIVE_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-passive-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * Only show the hitbox when the entity is within reach and under your crosshair.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PASSIVE_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .comment("Only show the hitbox when the entity is within reach and under your crosshair")
+ .node("hitbox", "hitbox-passive-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PASSIVE_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-passive-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_PASSIVE_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-passive-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -644,6 +1307,61 @@ public final class ModHitbox {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_OTHER_SHOW_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-other-show-hittable-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_OTHER_HITTABLE_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-other-hittable-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_OTHER_ONLY_SHOW_HITTABLE = SimpleOption.builder()
+ .node("hitbox", "hitbox-other-only-show-hittable").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_OTHER_SHOW_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-other-show-damaged-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HITBOX_OTHER_DAMAGED_COLOR = SimpleOption.builder()
+ .node("hitbox", "hitbox-other-damaged-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModKnockbackTrainer.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModKnockbackTrainer.java
new file mode 100644
index 00000000..1a82c014
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModKnockbackTrainer.java
@@ -0,0 +1,98 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.mods.impl;
+
+import com.lunarclient.apollo.option.NumberOption;
+import com.lunarclient.apollo.option.SimpleOption;
+import io.leangen.geantyref.TypeToken;
+
+/**
+ * Train jump resets: how close your jump press lands to the tick you were hit.
+ *
+ * @since 1.2.9
+ */
+public final class ModKnockbackTrainer {
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption ENABLED = SimpleOption.builder()
+ .node("knockback-trainer", "enabled").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption TARGET_TICKS = NumberOption.number()
+ .node("knockback-trainer", "target-ticks").type(TypeToken.get(Integer.class))
+ .min(0).max(6)
+ .defaultValue(0)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption WINDOW_TICKS = NumberOption.number()
+ .node("knockback-trainer", "window-ticks").type(TypeToken.get(Integer.class))
+ .min(0).max(10)
+ .defaultValue(5)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption FALLING_HIT_SOUND = SimpleOption.builder()
+ .node("knockback-trainer", "falling-hit-sound").type(TypeToken.get(Boolean.class))
+ .defaultValue(true)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption FALLING_HIT_SOUND_VOLUME = NumberOption.number()
+ .node("knockback-trainer", "falling-hit-sound-volume").type(TypeToken.get(Float.class))
+ .min(0.0F).max(1.0F)
+ .defaultValue(1.0F)
+ .notifyClient()
+ .build();
+
+ private ModKnockbackTrainer() {
+ }
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModLightOverlay.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModLightOverlay.java
new file mode 100644
index 00000000..07d415e1
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModLightOverlay.java
@@ -0,0 +1,208 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.mods.impl;
+
+import com.lunarclient.apollo.option.NumberOption;
+import com.lunarclient.apollo.option.SimpleOption;
+import io.leangen.geantyref.TypeToken;
+import java.awt.Color;
+
+/**
+ * Shows the light levels of blocks where mobs can spawn.
+ *
+ * @since 1.2.9
+ */
+public final class ModLightOverlay {
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption ENABLED = SimpleOption.builder()
+ .node("light-overlay", "enabled").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * The rendering limit (in chunks) for light overlay values. Higher values might lower your FPS.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption RENDER_RANGE_LIMIT = NumberOption.number()
+ .comment("The rendering limit (in chunks) for light overlay values. Higher values might lower your FPS.")
+ .node("light-overlay", "render-range-limit").type(TypeToken.get(Integer.class))
+ .min(1).max(12)
+ .defaultValue(2)
+ .notifyClient()
+ .build();
+
+ /**
+ * With this option on, the overlay updates aren't deferred, and happen as fast as possible.This looks nicer (less "pop in"), but turning this option on comes at a potential performance cost.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption FAST_UPDATES = SimpleOption.builder()
+ .comment("With this option on, the overlay updates aren't deferred, and happen as fast as possible.This looks nicer (less \"pop in\"), but turning this option on comes at a potential performance cost.")
+ .node("light-overlay", "fast-updates").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * Reduce the amount of overlays that are rendered, increasing performance, especially at higher rendering ranges.This comes at the expense of overlays at the edges of the screen sometimes popping in and out as you look around.It's highly recommended that this option is kept on, especially if using higher rendering ranges.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption CULLING = SimpleOption.builder()
+ .comment("Reduce the amount of overlays that are rendered, increasing performance, especially at higher rendering ranges.This comes at the expense of overlays at the edges of the screen sometimes popping in and out as you look around.It's highly recommended that this option is kept on, especially if using higher rendering ranges.")
+ .node("light-overlay", "culling").type(TypeToken.get(Boolean.class))
+ .defaultValue(true)
+ .notifyClient()
+ .build();
+
+ /**
+ * With this option on, the real, effective light value is displayed. With this option off, only the block light is used, completely ignoring the sky/day light.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption INCLUDE_SKY_LIGHT = SimpleOption.builder()
+ .comment("With this option on, the real, effective light value is displayed. With this option off, only the block light is used, completely ignoring the sky/day light.")
+ .node("light-overlay", "include-sky-light").type(TypeToken.get(Boolean.class))
+ .defaultValue(true)
+ .notifyClient()
+ .build();
+
+ /**
+ * Only show on blocks which have light levels that hostile mobs can spawn on.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HIDE_UNSPAWNABLE_LIGHT = SimpleOption.builder()
+ .comment("Only show on blocks which have light levels that hostile mobs can spawn on")
+ .node("light-overlay", "hide-unspawnable-light").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * Set a custom threshold for Light Check. This overrides the vanilla hostile mob spawning value, and shows the light overlay on all blocks on which the light level falls below this threshold.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption CUSTOM_LIGHT_THRESHOLD = SimpleOption.builder()
+ .comment("Set a custom threshold for Light Check. This overrides the vanilla hostile mob spawning value, and shows the light overlay on all blocks on which the light level falls below this threshold")
+ .node("light-overlay", "custom-light-threshold").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption THRESHOLD = NumberOption.number()
+ .node("light-overlay", "threshold").type(TypeToken.get(Integer.class))
+ .min(0).max(15)
+ .defaultValue(15)
+ .notifyClient()
+ .build();
+
+ /**
+ * Show a number for the light value of each block.Turning this option on might lower your FPS.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption SHOW_LIGHT_VALUE = SimpleOption.builder()
+ .comment("Show a number for the light value of each block.Turning this option on might lower your FPS.")
+ .node("light-overlay", "show-light-value").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption CROSS_THICKNESS = NumberOption.number()
+ .node("light-overlay", "cross-thickness").type(TypeToken.get(Float.class))
+ .min(0.5F).max(10.0F)
+ .defaultValue(2.0F)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption TEXT_COLOR = SimpleOption.builder()
+ .node("light-overlay", "text-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 255, 255))
+ .notifyClient()
+ .build();
+
+ /**
+ * Color where hostile mobs can't spawn.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption BRIGHT_COLOR = SimpleOption.builder()
+ .comment("Color where hostile mobs can't spawn")
+ .node("light-overlay", "bright-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 255, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * Color where hostile mobs can spawn.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption DARK_COLOR = SimpleOption.builder()
+ .comment("Color where hostile mobs can spawn")
+ .node("light-overlay", "dark-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 0, 0))
+ .notifyClient()
+ .build();
+
+ /**
+ * When this is off, the color is picked based on the light level allowing hostile mobs to spawn. When this is on, the color is interpolated smoothly.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption LIGHT_OVERLAY_DYNAMIC_COLOR = SimpleOption.builder()
+ .comment("When this is off, the color is picked based on the light level allowing hostile mobs to spawn. When this is on, the color is interpolated smoothly.")
+ .node("light-overlay", "light-overlay-dynamic-color").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ private ModLightOverlay() {
+ }
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModNametag.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModNametag.java
index 64e724b6..1ab58554 100644
--- a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModNametag.java
+++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModNametag.java
@@ -104,6 +104,18 @@ public final class ModNametag {
.notifyClient()
.build();
+ /**
+ * With this option enabled, your nametag will be hidden in third person when there is a passenger attached to you.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption HIDE_NAMETAG_F5PASSENGERS = SimpleOption.builder()
+ .comment("With this option enabled, your nametag will be hidden in third person when there is a passenger attached to you.")
+ .node("nametag", "hide-nametag-f5-passengers").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModOverlayMod.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModOverlayMod.java
index 21f6786f..b04034dd 100644
--- a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModOverlayMod.java
+++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModOverlayMod.java
@@ -70,6 +70,18 @@ public final class ModOverlayMod {
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption FIRE_BLOCK_HEIGHT = NumberOption.number()
+ .node("overlay-mod", "fire-block-height").type(TypeToken.get(Float.class))
+ .min(0.0F).max(2.0F)
+ .defaultValue(1.0F)
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPing.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPing.java
index 059e6312..3e46e2bf 100644
--- a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPing.java
+++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPing.java
@@ -47,15 +47,15 @@ public final class ModPing {
.build();
/**
- * Faster updates may impact performance.
+ * How often ping is measured. If you have trouble connecting to a server, try increasing this.
*
* @since 1.0.0
*/
public static final NumberOption UPDATE_INTERVAL_SEC = NumberOption.number()
- .comment("Faster updates may impact performance")
+ .comment("How often ping is measured. If you have trouble connecting to a server, try increasing this.")
.node("ping", "update-interval-sec").type(TypeToken.get(Integer.class))
.min(1).max(120)
- .defaultValue(20)
+ .defaultValue(5)
.notifyClient()
.build();
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPotionCounter.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPotionCounter.java
new file mode 100644
index 00000000..02dac49d
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPotionCounter.java
@@ -0,0 +1,213 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.mods.impl;
+
+import com.lunarclient.apollo.option.NumberOption;
+import com.lunarclient.apollo.option.SimpleOption;
+import io.leangen.geantyref.TypeToken;
+import java.awt.Color;
+
+/**
+ * Displays how many healing potions or soups are currently in your inventory on the HUD.
+ *
+ * @since 1.2.9
+ */
+public final class ModPotionCounter {
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption ENABLED = SimpleOption.builder()
+ .node("potion-counter", "enabled").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption SCALE = NumberOption.number()
+ .node("potion-counter", "scale").type(TypeToken.get(Float.class))
+ .min(0.25F).max(5.0F)
+ .defaultValue(1.0F)
+ .notifyClient()
+ .build();
+
+ /**
+ * Adds a shadow to text.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption TEXT_SHADOW = SimpleOption.builder()
+ .comment("Adds a shadow to text")
+ .node("potion-counter", "text-shadow").type(TypeToken.get(Boolean.class))
+ .defaultValue(true)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption BRACKETS = SimpleOption.builder()
+ .node("potion-counter", "brackets").type(TypeToken.get(Boolean.class))
+ .defaultValue(true)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption BRACKET_COLOR = SimpleOption.builder()
+ .node("potion-counter", "bracket-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 255, 255))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption BACKGROUND = SimpleOption.builder()
+ .node("potion-counter", "background").type(TypeToken.get(Boolean.class))
+ .defaultValue(true)
+ .notifyClient()
+ .build();
+
+ /**
+ * If this is disabled the background will change size with the text.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption STATIC_BACKGROUND_WIDTH = SimpleOption.builder()
+ .comment("If this is disabled the background will change size with the text.")
+ .node("potion-counter", "static-background-width").type(TypeToken.get(Boolean.class))
+ .defaultValue(true)
+ .notifyClient()
+ .build();
+
+ /**
+ * If this is disabled the background will change size with the text.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption STATIC_BACKGROUND_HEIGHT = SimpleOption.builder()
+ .comment("If this is disabled the background will change size with the text.")
+ .node("potion-counter", "static-background-height").type(TypeToken.get(Boolean.class))
+ .defaultValue(true)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption BACKGROUND_WIDTH = NumberOption.number()
+ .node("potion-counter", "background-width").type(TypeToken.get(Integer.class))
+ .min(40).max(62)
+ .defaultValue(56)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption BACKGROUND_HEIGHT = NumberOption.number()
+ .node("potion-counter", "background-height").type(TypeToken.get(Integer.class))
+ .min(10).max(22)
+ .defaultValue(18)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption BORDER = SimpleOption.builder()
+ .node("potion-counter", "border").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption BORDER_THICKNESS = NumberOption.number()
+ .node("potion-counter", "border-thickness").type(TypeToken.get(Float.class))
+ .min(0.5F).max(3.0F)
+ .defaultValue(0.5F)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption BORDER_COLOR = SimpleOption.builder()
+ .node("potion-counter", "border-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 0, 0, 159))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption BACKGROUND_COLOR = SimpleOption.builder()
+ .node("potion-counter", "background-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(0, 0, 0, 111))
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final SimpleOption TEXT_COLOR = SimpleOption.builder()
+ .node("potion-counter", "text-color").type(TypeToken.get(Color.class))
+ .defaultValue(new Color(255, 255, 255))
+ .notifyClient()
+ .build();
+
+ private ModPotionCounter() {
+ }
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPotionEffects.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPotionEffects.java
index 93be05cc..649d2ee1 100644
--- a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPotionEffects.java
+++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModPotionEffects.java
@@ -49,22 +49,22 @@ public final class ModPotionEffects {
/**
* No documentation available.
*
- * @since 1.0.0
+ * @since 1.2.9
*/
- public static final NumberOption SCALE = NumberOption.number()
- .node("potion-effects", "scale").type(TypeToken.get(Float.class))
- .min(0.25F).max(5.0F)
- .defaultValue(1.0F)
+ public static final SimpleOption HIDE_AMBIENT_DURATION = SimpleOption.builder()
+ .node("potion-effects", "hide-ambient-duration").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
.notifyClient()
.build();
/**
- * No documentation available.
+ * Put the good effects and the bad effects on separate lines.
*
- * @since 1.0.0
+ * @since 1.2.9
*/
- public static final SimpleOption SHOW_IN_INVENTORY = SimpleOption.builder()
- .node("potion-effects", "show-in-inventory").type(TypeToken.get(Boolean.class))
+ public static final SimpleOption VANILLA_GROUP_EFFECTS = SimpleOption.builder()
+ .comment("Put the good effects and the bad effects on separate lines")
+ .node("potion-effects", "vanilla-group-effects").type(TypeToken.get(Boolean.class))
.defaultValue(true)
.notifyClient()
.build();
@@ -72,14 +72,49 @@ public final class ModPotionEffects {
/**
* No documentation available.
*
- * @since 1.0.0
+ * @since 1.2.9
*/
- public static final SimpleOption SHOW_WHILE_TYPING = SimpleOption.builder()
- .node("potion-effects", "show-while-typing").type(TypeToken.get(Boolean.class))
+ public static final SimpleOption VANILLA_MODE_HORIZONTAL = SimpleOption.builder()
+ .node("potion-effects", "vanilla-mode-horizontal").type(TypeToken.get(Boolean.class))
.defaultValue(true)
.notifyClient()
.build();
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption VANILLA_MODE_TILES_PER_LINE = NumberOption.number()
+ .node("potion-effects", "vanilla-mode-tiles-per-line").type(TypeToken.get(Integer.class))
+ .min(1).max(10)
+ .defaultValue(10)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.1.9
+ */
+ public static final SimpleOption MINIMAL_MODE_HORIZONTAL = SimpleOption.builder()
+ .node("potion-effects", "minimal-mode-horizontal").type(TypeToken.get(Boolean.class))
+ .defaultValue(false)
+ .notifyClient()
+ .build();
+
+ /**
+ * No documentation available.
+ *
+ * @since 1.2.9
+ */
+ public static final NumberOption MINIMAL_MODE_TILES_PER_LINE = NumberOption.number()
+ .node("potion-effects", "minimal-mode-tiles-per-line").type(TypeToken.get(Integer.class))
+ .min(1).max(10)
+ .defaultValue(4)
+ .notifyClient()
+ .build();
+
/**
* No documentation available.
*
@@ -92,13 +127,12 @@ public final class ModPotionEffects {
.build();
/**
- * Adds a shadow to text.
+ * No documentation available.
*
- * @since 1.0.0
+ * @since 1.2.9
*/
- public static final SimpleOption TEXT_SHADOW = SimpleOption.builder()
- .comment("Adds a shadow to text")
- .node("potion-effects", "text-shadow").type(TypeToken.get(Boolean.class))
+ public static final SimpleOption EFFECT_DURATION = SimpleOption.builder()
+ .node("potion-effects", "effect-duration").type(TypeToken.get(Boolean.class))
.defaultValue(true)
.notifyClient()
.build();
@@ -106,10 +140,10 @@ public final class ModPotionEffects {
/**
* No documentation available.
*
- * @since 1.1.9
+ * @since 1.2.9
*/
- public static final SimpleOption BACKGROUND = SimpleOption.