Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/main/kotlin/com/lambda/config/Configurable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.lambda.Lambda.LOG
import com.lambda.config.Configuration.Companion.configurables
import com.lambda.config.settings.CharSetting
import com.lambda.config.settings.FunctionSetting
import com.lambda.config.settings.GuiButton
import com.lambda.config.settings.StringSetting
import com.lambda.config.settings.collections.BlockCollectionSetting
import com.lambda.config.settings.collections.ClassCollectionSetting
Expand Down Expand Up @@ -66,6 +67,7 @@ abstract class Configurable(
val configuration: Configuration,
) : Jsonable, Nameable {
val settings = mutableListOf<Setting<*, *>>()
val otherElements = mutableListOf<LayoutBuildable>()
val settingGroups = mutableListOf<SettingGroup>()

init {
Expand All @@ -84,6 +86,10 @@ abstract class Configurable(
settings.add(this)
}

fun LayoutBuildable.register() = apply {
otherElements.add(this)
}

override fun toJson() =
JsonObject().apply {
settings.forEach { setting ->
Expand All @@ -102,6 +108,12 @@ abstract class Configurable(
}
}

fun button(
name: String,
description: String = "",
onPress: () -> Unit
) = GuiButton(name, description, onPress).register()

fun setting(
name: String,
defaultValue: Boolean,
Expand Down
23 changes: 23 additions & 0 deletions src/main/kotlin/com/lambda/config/LayoutBuildable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2026 Lambda
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.lambda.config


interface LayoutBuildable {
fun buildLayout()
}
35 changes: 35 additions & 0 deletions src/main/kotlin/com/lambda/config/settings/GuiButton.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2026 Lambda
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.lambda.config.settings

import com.lambda.config.LayoutBuildable
import com.lambda.gui.dsl.ImGuiBuilder.button
import com.lambda.util.Describable
import com.lambda.util.Nameable

class GuiButton(
override val name: String,
override val description: String,
val clickConsumer: () -> Unit?
) : Nameable, Describable, LayoutBuildable {
override fun buildLayout() {
button(name) {
clickConsumer()
}
}
}
191 changes: 97 additions & 94 deletions src/main/kotlin/com/lambda/gui/components/SettingsWidget.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,104 +31,107 @@ import imgui.flag.ImGuiPopupFlags
import imgui.flag.ImGuiTabBarFlags

object SettingsWidget {
/**
* Builds the settings context popup content for the given configurable.
*/
fun ImGuiBuilder.buildConfigSettingsContext(config: Configurable) {
group {
if (config is Module) {
with(config.keybindSetting) { buildLayout() }
with(config.disableOnReleaseSetting) { buildLayout() }
with(config.drawSetting) { buildLayout() }
}
smallButton("Reset") {
config.settings.forEach { it.reset(silent = true) }
}
lambdaTooltip("Resets all settings for this module to their default values")
if (config is MutableAutomationConfig && config.automationConfig !== AutomationConfig.Companion.DEFAULT) {
button("Automation Config") {
ImGui.openPopup("##automation-config-popup-${config.name}")
}
if (config.backingAutomationConfig !== config.defaultAutomationConfig) {
sameLine()
text("(${config.backingAutomationConfig.name})")
}
ImGui.setNextWindowSizeConstraints(0f, 0f, Float.MAX_VALUE, io.displaySize.y * 0.5f)
popupContextItem("##automation-config-popup-${config.name}", ImGuiPopupFlags.None) {
combo("##LinkedConfig", preview = "Linked Config: ${config.backingAutomationConfig.name}") {
val addItem: (Configurable) -> Unit = { item ->
val selected = item === config.backingAutomationConfig
/**
* Builds the settings context popup content for the given configurable.
*/
fun ImGuiBuilder.buildConfigSettingsContext(config: Configurable) {
group {
if (config is Module) {
with(config.keybindSetting) { buildLayout() }
with(config.disableOnReleaseSetting) { buildLayout() }
with(config.drawSetting) { buildLayout() }
}
smallButton("Reset") {
config.settings.forEach { it.reset(silent = true) }
}
lambdaTooltip("Resets all settings for this module to their default values")
if (config is MutableAutomationConfig && config.automationConfig !== AutomationConfig.Companion.DEFAULT) {
button("Automation Config") {
ImGui.openPopup("##automation-config-popup-${config.name}")
}
if (config.backingAutomationConfig !== config.defaultAutomationConfig) {
sameLine()
text("(${config.backingAutomationConfig.name})")
}
ImGui.setNextWindowSizeConstraints(0f, 0f, Float.MAX_VALUE, io.displaySize.y * 0.5f)
popupContextItem("##automation-config-popup-${config.name}", ImGuiPopupFlags.None) {
combo("##LinkedConfig", preview = "Linked Config: ${config.backingAutomationConfig.name}") {
val addItem: (Configurable) -> Unit = { item ->
val selected = item === config.backingAutomationConfig

selectable(item.name, selected) {
if (!selected) {
(config.backingAutomationConfig as? UserAutomationConfig)?.linkedModules?.value?.remove(config.name)
(item as? UserAutomationConfig)?.linkedModules?.value?.add(config.name)
config.automationConfig = item as? AutomationConfig ?: return@selectable
}
}
}
addItem(config.defaultAutomationConfig)
selectable(item.name, selected) {
if (!selected) {
(config.backingAutomationConfig as? UserAutomationConfig)?.linkedModules?.value?.remove(config.name)
(item as? UserAutomationConfig)?.linkedModules?.value?.add(config.name)
config.automationConfig = item as? AutomationConfig ?: return@selectable
}
}
}
addItem(config.defaultAutomationConfig)
UserAutomationConfigs.configurables.forEach { addItem(it) }
}
buildConfigSettingsContext(config.automationConfig)
}
}
}
val toIgnoreSettings =
when (config) {
is Module -> setOf(config.keybindSetting, config.disableOnReleaseSetting, config.drawSetting)
is UserAutomationConfig -> setOf(config.linkedModules)
else -> emptySet()
}
val visibleSettings = config.settings.filter { it.visibility() } - toIgnoreSettings
if (visibleSettings.isEmpty()) return
else separator()
val (grouped, ungrouped) = visibleSettings.partition { it.groups.isNotEmpty() }
ungrouped.forEach {
it.withDisabled { buildLayout() }
}
renderGroup(grouped, emptyList(), config)
}
}
buildConfigSettingsContext(config.automationConfig)
}
}
}
val toIgnoreSettings =
when (config) {
is Module -> setOf(config.keybindSetting, config.disableOnReleaseSetting, config.drawSetting)
is UserAutomationConfig -> setOf(config.linkedModules)
else -> emptySet()
}
val visibleSettings = config.settings.filter { it.visibility() } - toIgnoreSettings
if (visibleSettings.isEmpty() && config.otherElements.isEmpty()) return
else separator()
val (grouped, ungrouped) = visibleSettings.partition { it.groups.isNotEmpty() }
ungrouped.forEach {
it.withDisabled { buildLayout() }
}
config.otherElements.forEach {
it.buildLayout()
}
renderGroup(grouped, emptyList(), config)
}

private fun Setting<*, *>.withDisabled(block: Setting<*, *>.() -> Unit) {
if (disabled()) ImGui.beginDisabled()
block()
if (disabled()) ImGui.endDisabled()
}
private fun Setting<*, *>.withDisabled(block: Setting<*, *>.() -> Unit) {
if (disabled()) ImGui.beginDisabled()
block()
if (disabled()) ImGui.endDisabled()
}

private fun ImGuiBuilder.renderGroup(
settings: List<Setting<*, *>>,
parentPath: List<NamedEnum>,
config: Configurable
) {
settings.filter { it.groups.contains(parentPath) }.forEach {
it.withDisabled { buildLayout() }
}
private fun ImGuiBuilder.renderGroup(
settings: List<Setting<*, *>>,
parentPath: List<NamedEnum>,
config: Configurable
) {
settings.filter { it.groups.contains(parentPath) }.forEach {
it.withDisabled { buildLayout() }
}

val subGroupSettings = settings.filter { s ->
s.groups.any { it.size > parentPath.size && it.subList(0, parentPath.size) == parentPath }
}
val subTabs = subGroupSettings
.flatMap { s ->
s.groups.mapNotNull { path ->
if (path.size > parentPath.size && path.subList(0, parentPath.size) == parentPath)
path[parentPath.size] else null
}
}.distinct()
val subGroupSettings = settings.filter { s ->
s.groups.any { it.size > parentPath.size && it.subList(0, parentPath.size) == parentPath }
}
val subTabs = subGroupSettings
.flatMap { s ->
s.groups.mapNotNull { path ->
if (path.size > parentPath.size && path.subList(0, parentPath.size) == parentPath)
path[parentPath.size] else null
}
}.distinct()

if (subTabs.isNotEmpty()) {
val id = "##${config.name}-tabs-${parentPath.joinToString("-") { it.displayName }}"
tabBar(id, ImGuiTabBarFlags.FittingPolicyResizeDown) {
subTabs.forEach { tab ->
tabItem(tab.displayName) {
val newParentPath = parentPath + tab
val settingsForSubGroup = subGroupSettings.filter { s ->
s.groups.any { it.size >= newParentPath.size && it.subList(0, newParentPath.size) == newParentPath }
}
renderGroup(settingsForSubGroup, newParentPath, config)
}
}
}
}
}
if (subTabs.isNotEmpty()) {
val id = "##${config.name}-tabs-${parentPath.joinToString("-") { it.displayName }}"
tabBar(id, ImGuiTabBarFlags.FittingPolicyResizeDown) {
subTabs.forEach { tab ->
tabItem(tab.displayName) {
val newParentPath = parentPath + tab
val settingsForSubGroup = subGroupSettings.filter { s ->
s.groups.any { it.size >= newParentPath.size && it.subList(0, newParentPath.size) == newParentPath }
}
renderGroup(settingsForSubGroup, newParentPath, config)
}
}
}
}
}
}
17 changes: 16 additions & 1 deletion src/main/kotlin/com/lambda/interaction/BaritoneManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,8 @@ object BaritoneManager : Configurable(LambdaConfig), Automated by AutomationConf
get() = isBaritoneLoaded &&
(primary?.customGoalProcess?.isActive == true ||
primary?.pathingBehavior?.isPathing == true ||
primary?.pathingControlManager?.mostRecentInControl()?.orElse(null)?.isActive == true)
primary?.pathingControlManager?.mostRecentInControl()?.orElse(null)?.isActive == true ||
primary?.elytraProcess?.isActive == true)

/**
* Sets the current Baritone goal and starts pathing
Expand All @@ -365,11 +366,25 @@ object BaritoneManager : Configurable(LambdaConfig), Automated by AutomationConf
primary?.customGoalProcess?.setGoalAndPath(goal)
}

/**
* Sets the current Baritone goal without starting pathing
*/
fun setGoal(goal: Goal) {
if (!isBaritoneLoaded || primary?.elytraProcess?.isLoaded == false) return
primary?.customGoalProcess?.goal = goal
}

fun setGoalAndElytraPath(goal: Goal) {
if (!isBaritoneLoaded || primary?.elytraProcess?.isLoaded == false) return
primary?.elytraProcess?.pathTo(goal)
}

/**
* Force cancel Baritone
*/
fun cancel() {
if (!isBaritoneLoaded) return
primary?.pathingBehavior?.cancelEverything()
primary?.elytraProcess?.resetState()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ interface IRotationRequest : Automated {
@RotationRequestDsl
fun yaw(yaw: Float) { yawBuilder = { yaw.toDouble() } }

@RotationRequestDsl
fun yaw(rotation: Rotation) { yawBuilder = { rotation.yaw } }

@JvmName("pitchBuilder1")
@RotationRequestDsl
fun pitch(builder: SafeContext.() -> Double) { pitchBuilder = builder }
Expand All @@ -159,6 +162,9 @@ interface IRotationRequest : Automated {
@RotationRequestDsl
fun pitch(pitch: Float) { pitchBuilder = { pitch.toDouble() } }

@RotationRequestDsl
fun pitch(rotation: Rotation) { pitchBuilder = { rotation.pitch } }

@RotationRequestDsl
fun rotation(builder: SafeContext.() -> Rotation) { rotationBuilder = builder }

Expand Down
Loading