# Permissive Script

### Features

* **Manual state management** - Full control over state lifecycle with class instantiation
* **Enum-based state instances** - Uses `PermissiveStateEnum` interface with class references
* **State iteration** - Automatic state switching with `StateIterator` (inherited from BaseStateScript)
* **Skip conditions** - Conditional state skipping support (inherited from BaseStateScript)
* **Runtime updates** - Change skip conditions at runtime (inherited from BaseStateScript)
* **Backend state handling** - State execution handled by the permissive backend package
* **Custom initialization** - Override `init()` for setup
* **BwuScript compatibility** - Automatically calls `setCurrentState()` on all state changes
* **Permissive DSL integration** - Each state uses the powerful permissive DSL for logic definition

### Complete Example: DooglePicker

<details>

<summary>Click to expand - Complete PermissiveScript Example</summary>

Here's a practical example showing how to use PermissiveScript:

```kotlin
package botwithus

import kotlin.reflect.KClass
import net.botwithus.kxapi.permissive.PermissiveDSL
import net.botwithus.kxapi.permissive.PermissiveStateEnum
import net.botwithus.kxapi.script.PermissiveScript
import net.botwithus.rs3.inventories.events.InventoryEvent
import net.botwithus.scripts.Info
import net.botwithus.xapi.util.BwuMath

// Define the state enum with class references
enum class BotState(override val description: String, override val classz: KClass<out PermissiveDSL<*>>) : PermissiveStateEnum {
    DOOGLE_PICKING("Doogle Picking", DooglePick::class),
    BANKING("Banking", Banking::class);
    
    override fun toString(): String = description
}

@Info(name = "TestScript", description = "Doogle Picking Script", version = "1.0.0", author = "Mark")
class TestScript : PermissiveScript<BotState>(debug = true) {

    val movement: Movement = Movement()
    val selectedLocation = DoogleLocation.GERTRUDES_HOUSE

    private var doogleLeavesPicked = 0
    private var lifetimePicked = 0

    override fun init() {
        botStatInfo.displayInfoMap["Doogle leaves Picked"] =
            " $doogleLeavesPicked (${BwuMath.getUnitsPerHour(STOPWATCH, doogleLeavesPicked)} / hr"

        println("INITTT")
        // Set initial state
        setState(BotState.DOOGLE_PICKING)
    }

    override fun onItemAcquired(event: InventoryEvent) {
        event.newItem().takeIf { it.name == "Doogle leaves" }?.let { item ->
            doogleLeavesPicked += item.quantity
            lifetimePicked += item.quantity
        }
    }
}

// State class for Banking
class Banking(script: TestScript, name: String) : PermissiveDSL<TestScript>(script, name) {

    private companion object {
        const val NEARBY_DISTANCE = 20
    }

    override fun StateBuilder<TestScript>.create() {
        branch(StateNames.IS_BACKPACK_FULL, Backpack::isFull) {
            onSuccess(StateNames.IS_BANK_OPEN)
            onFailure(StateNames.TO_PICKING_STATE_LEAF) {
                script.setState(BotState.DOOGLE_PICKING)
            }
        }

        branch(StateNames.IS_BANK_OPEN, Bank::isOpen) {
            onSuccess(StateNames.DEPOSIT_BACKPACK) {
                script.status = "Depositing items in bank"
                if (Bank.depositAll()) {
                    script.delayUntil(Backpack::isEmpty, Rand.nextInt(10, 14))
                } else {
                    script.debug("Failed to deposit items in bank")
                }
            }
            onFailure(StateNames.IS_NEAR_BANK)
        }

        branch(
            StateNames.IS_NEAR_BANK, Interlock(
            "isNearBank",
            Permissive("bankNearby") { Distance.to(script.selectedLocation.bankArea) < NEARBY_DISTANCE }
        )) {
            onSuccess(StateNames.OPEN_BANK) { script ->
                script.status = "Opening bank"
                SceneObjectQuery().name("Bank booth").results().nearest()?.let { bank ->
                    bank.interact("Bank")
                    script.delayUntil(Bank::isOpen, Rand.nextInt(10, 14))
                } ?: run {
                    script.debug("Failed to open bank")
                    script.delay(6)
                }
            }
            onFailure(StateNames.TRAVERSE_TO_BANK) { script ->
                script.status = "Traversing to bank"
                moveToBankArea()
            }
        }

        root(StateNames.IS_BACKPACK_FULL)
    }
}

// State class for Doogle Picking (example)
class DooglePick(script: TestScript, name: String) : PermissiveDSL<TestScript>(script, name) {
    override fun StateBuilder<TestScript>.create() {
        branch(StateNames.IS_BACKPACK_FULL, Backpack::isFull) {
            onSuccess(StateNames.TO_BANKING_STATE) {
                script.setState(BotState.BANKING)
            }
            onFailure(StateNames.PICK_DOOGLE) {
                script.status = "Picking doogle leaves"
                // Add your picking logic here
            }
        }
        
        root(StateNames.IS_BACKPACK_FULL)
    }
}
```

</details>

### Key Features Demonstrated

#### 1. **State Enum with Class References**

```kotlin
enum class BotState(override val description: String, override val classz: KClass<out PermissiveDSL<*>>) : PermissiveStateEnum {
    DOOGLE_PICKING("Doogle Picking", DooglePick::class),
    BANKING("Banking", Banking::class);
    
    override fun toString(): String = description
}
```

#### 2. **Individual State Classes with Permissive DSL**

```kotlin
class Banking(script: TestScript, name: String) : PermissiveDSL<TestScript>(script, name) {
    override fun StateBuilder<TestScript>.create() {
        branch(StateNames.IS_BACKPACK_FULL, Backpack::isFull) {
            onSuccess(StateNames.IS_BANK_OPEN)
            onFailure(StateNames.TO_PICKING_STATE_LEAF) {
                script.setState(BotState.DOOGLE_PICKING)
            }
        }
        // ... more permissive DSL logic
        root(StateNames.IS_BACKPACK_FULL)
    }
}
```

#### 3. **Script Initialization**

```kotlin
override fun init() {
    botStatInfo.displayInfoMap["Doogle leaves Picked"] =
        " $doogleLeavesPicked (${BwuMath.getUnitsPerHour(STOPWATCH, doogleLeavesPicked)} / hr"
    
    println("INITTT")
    // Set initial state
    setState(BotState.DOOGLE_PICKING)
}
```

#### 4. **State Switching with BwuScript Compatibility**

```kotlin
setState(BotState.DOOGLE_PICKING)  // Set state and update BwuScript currentState
setState(BotState.BANKING)         // Switch to banking state
```

#### 5. **State Iterator Management**

```kotlin
setState(BotState.DOOGLE_PICKING)  // Set specific state
nextState()                        // Move to next state
resetStateIterator()               // Reset to beginning
```

#### 6. **Skip Conditions (Optional)**

```kotlin
override fun getSkipConditions(): Map<BotState, () -> Boolean> = mapOf(
    BotState.DOOGLE_PICKING to { !hasDoogleLeavesNearby() },
    BotState.BANKING to { !Backpack.isFull() }
)
```

#### 7. **State Monitoring**

```kotlin
val currentState = getStateIterator()?.getCurrentState()
val iterator = getStateIterator()
println("Current State: $currentState")
println("Has Next: ${iterator?.hasNext()}")
```

### Constructor Options

```kotlin
// Enable debug mode (recommended for development)
class MyScript : PermissiveScript<MyState>(debug = true)

// Disable debug mode (for production)
class MyScript : PermissiveScript<MyState>(debug = false)
```

### When to Use

Use PermissiveScript when:

* You need explicit class instantiation for each state
* You want manual control over state lifecycle with permissive DSL
* You need integration with backend permissive package
* You want both manual and iterator-based state switching options
* You have complex state classes that extend `PermissiveDSL`
* You need BwuScript compatibility with automatic `setCurrentState()` calls

### Key Methods

#### **Required Methods**

* `init()` - Override for custom initialization

#### **State Management Methods**

* `setState(state)` - Set specific state (automatically calls `setCurrentState()`)
* `getState(stateEnum)` - Get state instance by enum
* `getStateInstance(stateEnum)` - Alias for getState
* `isStateActive(stateEnum)` - Check if state is currently active

#### **State Iterator Methods (inherited from BaseStateScript)**

* `getStateIterator()` - Get the current state iterator
* `nextState()` - Move to the next state using iterator
* `resetStateIterator()` - Reset to the first state
* `getSkipConditions()` - Override to set initial skip conditions
* `updateSkipConditions()` - Change skip conditions at runtime

#### **BwuScript Integration**

* Automatic `setCurrentState(stateEnum.description)` calls on all state changes
* Automatic `status` updates with state descriptions
* Full compatibility with BwuScript lifecycle methods


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://kxapi.gitbook.io/kxapi/scripting/script-types/permissive-script.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
