# Decorators

Decorators are special nodes that wrap a single child and modify its execution behavior. They enable timing control, retry logic, conditional execution, and result inversion without modifying the child node itself.

## What Are Decorators?

Decorators sit between a parent and child node, transforming the child's behavior:

```
Parent
  └─ Decorator
       └─ Child
```

The decorator intercepts the child's execution, potentially:
- Repeating it multiple times
- Timing it out
- Inverting its result
- Guarding its execution
- Forcing a specific result

## Decorator Types

Storybook provides 10 decorator types:

| Decorator | Purpose | Example |
|-----------|---------|---------|
| `repeat` | Loop infinitely | `repeat { Patrol }` |
| `repeat(N)` | Loop N times | `repeat(3) { Check }` |
| `repeat(min..max)` | Loop min to max times | `repeat(2..5) { Search }` |
| `invert` | Flip success/failure | `invert { IsEnemy }` |
| `retry(N)` | Retry on failure (max N times) | `retry(5) { Open }` |
| `timeout(duration)` | Fail after duration | `timeout(10s) { Solve }` |
| `cooldown(duration)` | Run at most once per duration | `cooldown(30s) { Fire }` |
| `if(condition)` | Only run if condition true | `if(health > 50) { Attack }` |
| `succeed_always` | Always return success | `succeed_always { Try }` |
| `fail_always` | Always return failure | `fail_always { Test }` |

## Syntax

```bnf
<decorator> ::= <repeat-decorator>
              | <invert-decorator>
              | <retry-decorator>
              | <timeout-decorator>
              | <cooldown-decorator>
              | <if-decorator>
              | <force-result-decorator>

<repeat-decorator> ::= "repeat" <repeat-spec>? "{" <behavior-node> "}"

<repeat-spec> ::= "(" <number> ")"
                | "(" <number> ".." <number> ")"

<invert-decorator> ::= "invert" "{" <behavior-node> "}"

<retry-decorator> ::= "retry" "(" <number> ")" "{" <behavior-node> "}"

<timeout-decorator> ::= "timeout" "(" <duration-literal> ")" "{" <behavior-node> "}"

<cooldown-decorator> ::= "cooldown" "(" <duration-literal> ")" "{" <behavior-node> "}"

<if-decorator> ::= "if" "(" <expression> ")" "{" <behavior-node> "}"

<force-result-decorator> ::= ("succeed_always" | "fail_always") "{" <behavior-node> "}"

<duration-literal> ::= <number> ("s" | "m" | "h" | "d")
```

## Repeat Decorators

### Infinite Repeat: `repeat`

Loops the child infinitely. The child is re-executed immediately after completing (success or failure).

```storybook
behavior InfinitePatrol {
    repeat {
        PatrolRoute
    }
}
```

**Execution:**
1. Run child
2. Child completes (success or failure)
3. Immediately run child again
4. Go to step 2 (forever)

**Use cases:**
- Perpetual patrols
- Endless background processes
- Continuous monitoring

**Warning:** Infinite loops never return to parent. Ensure they're appropriate for your use case.

### Repeat N Times: `repeat N`

Repeats the child exactly N times, then returns success.

```storybook
behavior CheckThreeTimes {
    repeat(3) {
        CheckDoor
    }
}
```

**Execution:**
1. Counter = 0
2. Run child
3. Counter++
4. If counter < N, go to step 2
5. Return success

**Use cases:**
- Fixed iteration counts
- "Try three times then give up"
- Deterministic looping

### Repeat Range: `repeat min..max`

Repeats the child between min and max times. At runtime, a specific count is selected within the range.

```storybook
behavior SearchRandomly {
    repeat(2..5) {
        SearchArea
    }
}
```

**Execution:**
1. Select count C randomly from [min, max]
2. Repeat child C times (as in `repeat N`)

**Use cases:**
- Variable behavior
- Procedural variation
- Non-deterministic actions

**Validation:**
- min ≥ 0
- max ≥ min
- Both must be integers

## Invert Decorator

Inverts the child's return value: success becomes failure, failure becomes success.

```storybook
behavior AvoidEnemies {
    invert {
        IsEnemyNearby  // Success if NOT nearby
    }
}
```

**Truth table:**

| Child returns | Decorator returns |
|---------------|------------------|
| Success | Failure |
| Failure | Success |
| Running | Running (unchanged) |

**Use cases:**
- Negating conditions ("if NOT X")
- Inverting success criteria
- Converting "found" to "not found"

**Example:**
```storybook
behavior SafeExploration {
    choose safe_actions {
        // Only explore if NOT dangerous
        then explore {
            invert { IsDangerous }
            ExploreArea
        }

        // Default: Stay put
        Wait
    }
}
```

## Retry Decorator

Retries the child up to N times on failure. Returns success if any attempt succeeds, failure if all N attempts fail.

```storybook
behavior PersistentDoor {
    retry(5) {
        OpenLockedDoor
    }
}
```

**Execution:**
1. Attempts = 0
2. Run child
3. If child succeeds → return success
4. If child fails:
   - Attempts++
   - If attempts < N, go to step 2
   - Else return failure

**Use cases:**
- Unreliable actions (lockpicking, persuasion)
- Network/resource operations
- Probabilistic success

**Example with context:**
```storybook
behavior Thief_PickLock {
    then attempt_entry {
        // Try to pick lock (may fail)
        retry(3) {
            PickLock
        }

        // If succeeded, enter
        EnterBuilding

        // If failed after 3 tries, give up
    }
}
```

**Validation:**
- N must be ≥ 1

## Timeout Decorator

Fails the child if it doesn't complete within the specified duration.

```storybook
behavior TimeLimitedPuzzle {
    timeout(30s) {
        SolvePuzzle
    }
}
```

**Execution:**
1. Start timer
2. Run child each tick
3. If child completes before timeout → return child's result
4. If timer expires → return failure (interrupt child)

**Use cases:**
- Time-limited actions
- Preventing infinite loops
- Enforcing deadlines

**Duration formats:**
- `5s` - 5 seconds
- `10m` - 10 minutes
- `2h` - 2 hours
- `3d` - 3 days

**Example:**
```storybook
behavior QuickDecision {
    choose timed_choice {
        // Give AI 5 seconds to find optimal move
        timeout(5s) {
            CalculateOptimalStrategy
        }

        // Fallback: Use simple heuristic
        UseQuickHeuristic
    }
}
```

**Notes:**
- Timer starts when decorator is first entered
- Timer resets if decorator exits and re-enters
- Child node should handle interruption gracefully

## Cooldown Decorator

Prevents the child from running more than once per cooldown period. Fails immediately if called within cooldown.

```storybook
behavior SpecialAbility {
    cooldown(30s) {
        FireCannon
    }
}
```

**Execution:**
1. Check last execution time
2. If (current_time - last_time) < cooldown → return failure
3. Else:
   - Run child
   - Record current_time as last_time
   - Return child's result

**Use cases:**
- Rate-limiting abilities
- Resource cooldowns (spells, items)
- Preventing spam

**Example:**
```storybook
behavior Mage_SpellCasting {
    choose spells {
        // Fireball: 10 second cooldown
        cooldown(10s) {
            CastFireball
        }

        // Lightning: 5 second cooldown
        cooldown(5s) {
            CastLightning
        }

        // Basic attack: No cooldown
        MeleeAttack
    }
}
```

**State management:**
- Cooldown state persists across behavior tree ticks
- Each cooldown decorator instance has independent state
- Cooldown timers are per-entity (not global)

## If Decorator

Only runs the child if the condition is true. Fails immediately if condition is false.

```storybook
behavior ConditionalAttack {
    if(health > 50) {
        AggressiveAttack
    }
}
```

**Execution:**
1. Evaluate condition expression
2. If true → run child and return its result
3. If false → return failure (do not run child)

**Use cases:**
- Preconditions ("only if X")
- Resource checks ("only if have mana")
- Safety checks ("only if safe")

**Expression syntax:**
See [Expression Language](./17-expressions.md) for complete syntax.

**Examples:**

```storybook
behavior GuardedActions {
    choose options {
        // Only attack if have weapon and enemy close
        if(has_weapon and distance < 10) {
            Attack
        }

        // Only heal if health below 50%
        if(health < (max_health * 0.5)) {
            Heal
        }

        // Only flee if outnumbered
        if(enemy_count > ally_count) {
            Flee
        }
    }
}
```

**Comparison with bare `if` conditions:**

```storybook
// Using bare 'if' condition (checks every tick, no body)
then approach_and_attack {
    if(enemy_nearby)
    Approach
    Attack
}

// Using 'if' decorator with body (precondition check, fails fast)
if(enemy_nearby) {
    then do_attack {
        Approach
        Attack
    }
}
```

The `if` decorator with a body is more efficient for gating expensive subtrees.

## Force Result Decorators

### `succeed_always`

Always returns success, regardless of child's actual result.

```storybook
behavior TryOptionalTask {
    succeed_always {
        AttemptBonus  // Even if fails, we don't care
    }
}
```

**Use cases:**
- Optional tasks that shouldn't block progress
- Logging/monitoring actions
- Best-effort operations

**Example:**
```storybook
behavior QuestSequence {
    then main_quest {
        TalkToNPC

        // Try to find secret, but don't fail quest if not found
        succeed_always {
            SearchForSecretDoor
        }

        ReturnToQuestGiver
    }
}
```

### `fail_always`

Always returns failure, regardless of child's actual result.

```storybook
behavior TestFailure {
    fail_always {
        AlwaysSucceedsAction  // But we force it to fail
    }
}
```

**Use cases:**
- Testing/debugging
- Forcing alternative paths
- Disabling branches temporarily

**Example:**
```storybook
behavior UnderConstruction {
    choose abilities {
        // Temporarily disabled feature
        fail_always {
            NewExperimentalAbility
        }

        // Fallback to old ability
        ClassicAbility
    }
}
```

## Combining Decorators

Decorators can nest to create complex behaviors:

```storybook
behavior ComplexPattern {
    // Repeat 3 times, each with 10 second timeout
    repeat(3) {
        timeout(10s) {
            SolveSubproblem
        }
    }
}
```

**More complex nesting:**
```storybook
behavior ResilientAction {
    // If: Only if health > 30
    if(health > 30) {
        // Timeout: Must complete in 20 seconds
        timeout(20s) {
            // Retry: Try up to 5 times
            retry(5) {
                // Cooldown: Can only run once per minute
                cooldown(1m) {
                    PerformComplexAction
                }
            }
        }
    }
}
```

**Execution order:** Outside → Inside
1. If checks condition
2. Timeout starts timer
3. Retry begins first attempt
4. Cooldown checks last execution time
5. Child action runs

## Duration Syntax

Timeout and cooldown decorators use duration literals:

```bnf
<duration-literal> ::= <number> <unit>

<unit> ::= "s"    // seconds
         | "m"    // minutes
         | "h"    // hours
         | "d"    // days

<number> ::= <digit>+
```

**Examples:**
- `5s` - 5 seconds
- `30s` - 30 seconds
- `2m` - 2 minutes
- `10m` - 10 minutes
- `1h` - 1 hour
- `24h` - 24 hours
- `7d` - 7 days

**Validation:**
- Number must be positive integer
- No compound durations (use `120s` not `2m` if runtime expects seconds)
- No fractional units (`1.5m` not allowed; use `90s`)

## Comparison Table

| Decorator | Affects Success | Affects Failure | Affects Running | Stateful |
|-----------|----------------|----------------|----------------|----------|
| `repeat` | Repeat | Repeat | Wait | Yes (counter) |
| `repeat N` | Repeat | Repeat | Wait | Yes (counter) |
| `repeat min..max` | Repeat | Repeat | Wait | Yes (counter) |
| `invert` | → Failure | → Success | Unchanged | No |
| `retry N` | → Success | Retry or fail | Wait | Yes (attempts) |
| `timeout dur` | → Success | → Success | → Failure if expired | Yes (timer) |
| `cooldown dur` | → Success | → Success | → Success | Yes (last time) |
| `if(expr)` | → Success | → Success | → Success | No |
| `succeed_always` | → Success | → Success | → Success | No |
| `fail_always` | → Failure | → Failure | → Failure | No |

**Stateful decorators** maintain state across ticks. **Stateless decorators** evaluate fresh every tick.

## Validation Rules

1. **Child required**: All decorators must have exactly one child node
2. **Repeat count**: `repeat N` requires N ≥ 0
3. **Repeat range**: `repeat min..max` requires 0 ≤ min ≤ max
4. **Retry count**: `retry N` requires N ≥ 1
5. **Duration positive**: Timeout/cooldown durations must be > 0
6. **Duration format**: Must match `<number><unit>` (e.g., `10s`, `5m`)
7. **Guard expression**: Guard condition must be valid expression
8. **No empty decorators**: `repeat { }` is invalid (missing child)

## Use Cases by Category

### Timing Control
- **timeout**: Prevent infinite loops, enforce time limits
- **cooldown**: Rate-limit abilities, prevent spam
- **repeat**: Continuous processes, patrols

### Reliability
- **retry**: Handle unreliable actions, probabilistic success
- **if**: Precondition checks, resource validation
- **succeed_always**: Optional tasks, best-effort

### Logic Control
- **invert**: Negate conditions, flip results
- **fail_always**: Disable branches, testing

### Iteration
- **repeat N**: Fixed loops, deterministic behavior
- **repeat min..max**: Variable loops, procedural variation

## Best Practices

### 1. Use Guards for Expensive Checks

**Avoid:**
```storybook
then expensive {
    if(complex_condition)
    ExpensiveOperation
}
```

**Prefer:**
```storybook
if(complex_condition) {
    ExpensiveOperation  // Only runs if condition passes
}
```

### 2. Combine Timeout with Retry

**Avoid:** Infinite retry loops

**Prefer:**
```storybook
timeout(30s) {
    retry(5) {
        UnreliableAction
    }
}
```

### 3. Use Cooldowns for Rate Limiting

**Avoid:** Manual timing in actions

**Prefer:**
```storybook
cooldown(10s) {
    FireCannon
}
```

### 4. Invert for Readable Conditions

**Avoid:**
```storybook
choose options {
    then branch_a {
        if(not is_dangerous)
        Explore
    }
}
```

**Prefer:**
```storybook
choose options {
    then branch_a {
        invert { IsDangerous }
        Explore
    }
}
```

### 5. succeed_always for Optional Tasks

**Avoid:**
```storybook
then quest {
    MainTask
    choose optional {
        BonusTask
        DoNothing  // Awkward fallback
    }
    NextTask
}
```

**Prefer:**
```storybook
then quest {
    MainTask
    succeed_always { BonusTask }  // Try bonus, don't fail quest
    NextTask
}
```

## Cross-References

- [Behavior Trees](./11-behavior-trees.md) - Using decorators in behavior trees
- [Expression Language](./17-expressions.md) - Guard condition syntax
- [Value Types](./18-value-types.md) - Duration literals
- [Design Patterns](../advanced/20-patterns.md) - Common decorator patterns

## Related Concepts

- **Composability**: Decorators can nest for complex control flow
- **Separation of concerns**: Decorators handle control flow, children handle logic
- **State management**: Stateful decorators (repeat, retry, timeout, cooldown) persist across ticks
- **Performance**: Guards prevent unnecessary child execution
