# Editing Statuses

### Effect Manager

The effect manager controls what happens to a player on the **client side** when a status crosses a threshold. Blurry vision, camera shaking, walking styles, screen overlays, stumbling, reactions - all of these are configured through the effect system.

Effects are driven by your **status configs**. You define thresholds, attach effect keys with values, and the system takes care of the rest. When multiple effects compete (e.g. two different walking styles from stress and alcohol), the system automatically resolves which one wins.

***

#### Table of Contents

* [How It Works](#how-it-works)
* [Configuring Effects](#configuring-effects)
* [Effect Keys](#effect-keys)
* [Notifications](#notifications)
* [Reactions (Animations & Sounds)](#reactions-animations-and-sounds)
* [Damage](#damage)
* [FAQ](#faq)

***

#### How It Works

1. **Each status** has an `effect` array in its config file (`statuses/<status>/config.lua`).
2. **Each entry** in that array has a `threshold` and one or more effect keys.
3. **Every second**, the system checks each status value against its thresholds. When a threshold is crossed, the effects attached to it activate. When the value drops back below, they deactivate.
4. **If multiple statuses** try to control the same effect key (e.g. two different `walkingStyle` values), the system picks the most severe one automatically.

You configure everything in the status config. No code changes are needed.

***

#### Configuring Effects

Effects live inside the `effect` table of a status config. Each entry is a threshold with one or more effect keys attached to it.

**Basic Example (Drunk)**

```lua
Config.Status.drunk = {
    ["base"] = {
        value = {
            drain = 0.1
        },
        effect = {
            {threshold = 10.0, walkingStyle = "move_m@buzzed"},
            {threshold = 20.0, screenEffect = "BeastLaunch02", walkingStyle = "move_m@drunk@slightlydrunk"},
            {threshold = 30.0, screenEffect = "BikerFilter", walkingStyle = "move_m@drunk@a", blurryVision = true},
            {threshold = 50.0, screenEffect = "DaxTrip03", walkingStyle = "move_m@drunk@verydrunk"},
        },
    }
}
```

* At `10.0`, the player starts walking wobbly.
* At `20.0`, a screen effect kicks in and the walk gets worse.
* At `30.0`, blur effects start on top.
* At `50.0`, the screen effect gets more intense and the walking style is heavily impaired.

When the value drops below a threshold, its effects are automatically cleaned up.

**Threshold Stacking**

Thresholds are cumulative. When a player is at value `35.0` with the config above, thresholds at `10`, `20`, and `30` are **all** active simultaneously. The system picks the dominant value per effect key, so only one `walkingStyle` plays at a time (the highest threshold wins).

**Combining Multiple Effects**

You can attach as many effect keys as you want to a single threshold:

```lua
{threshold = 30.0, screenEffect = "BikerFilter", walkingStyle = "move_m@drunk@a", blurryVision = true, cameraShaking = "DRUNK_SHAKE"}
```

**Simple vs Rich Values**

Some effect keys accept either a simple value or a table with extra options:

```lua
-- Simple: just the name, uses default settings
screenEffect = "BarryFadeOut"

-- Rich: name with an intensity value
screenEffect = {value = "BarryFadeOut", intensity = 0.3}
```

```lua
-- Simple
cameraShaking = "DRUNK_SHAKE"

-- Rich
cameraShaking = {value = "DRUNK_SHAKE", intensity = 0.05}
```

Both forms work. The simple form just uses a default intensity of `1.0`.

***

#### Effect Keys

Here are all the available effect keys you can use in your threshold configs:

| Key              | Value Type                       | Description                                              |
| ---------------- | -------------------------------- | -------------------------------------------------------- |
| `screenEffect`   | `string` or `{value, intensity}` | Applies a timecycle modifier as a screen overlay         |
| `cameraShaking`  | `string` or `{value, intensity}` | Shakes the gameplay camera                               |
| `walkingStyle`   | `string`                         | Changes the player's movement clipset (walk animation)   |
| `blurryVision`   | `boolean`                        | Periodically fades a blur overlay in and out             |
| `blockJumping`   | `boolean`                        | Prevents the player from jumping                         |
| `blockSprinting` | `boolean`                        | Prevents the player from sprinting                       |
| `movementSpeed`  | `number`                         | Scales run/sprint speed (`1.0` = normal, lower = slower) |
| `strength`       | `number`                         | Scales unarmed melee damage (`1.0` = normal)             |
| `stumble`        | `number`                         | Chance-based stumbling/ragdoll (higher = more frequent)  |
| `reaction`       | `table`                          | Synchronized animation + sound (see below)               |
| `damage`         | `number`                         | Deals damage per tick (see below)                        |
| `notification`   | `table`                          | Shows a notification once (see below)                    |

**Notes**

* **`movementSpeed`**: We recommend not going below `0.8` as it starts to look weird.
* **`stumble`**: The value is a multiplier on the base chance. Running and sprinting significantly increase the stumble chance.
* **`walkingStyle`**: Won't override crouching.

***

#### Notifications

Notifications fire **once** when a threshold is first crossed. They are not queued effects, so they don't compete for dominance.

```lua
{
    threshold = 10.0,
    notification = {value = "stress1", play = "start"},
}
```

| Field   | Type       | Description                                                              |
| ------- | ---------- | ------------------------------------------------------------------------ |
| `value` | `string`   | Translation key (from `locales/`) or a raw message string                |
| `play`  | `string`   | When to show it. Use `"start"` (when the threshold is hit)               |
| `type`  | `string?`  | Optional notification type (e.g. `"warning"`)                            |
| `force` | `boolean?` | If `true`, always shows even when surpassing multiple thresholds at once |

**Anti-Spam**

When a player jumps past multiple thresholds at once (e.g. from 0 to 60), only the **highest** threshold's notification plays. Use `force = true` on a specific notification if it should always play regardless:

```lua
notification = {value = "stress1", play = "start", force = true}
```

**Translation Keys vs Direct Messages**

If the `value` matches a key in your locale files, the translation is used. Otherwise the string is shown directly:

```lua
-- Uses the "stress1" translation
notification = {value = "stress1", play = "start"}

-- Shows this exact text
notification = {value = "You are feeling critically stressed.", type = "warning", play = "start"}
```

***

#### Reactions (Animations & Sounds)

Reactions combine animations and/or sounds into one synchronized effect. They can play once or loop continuously.

**Full Example (Stress Coughing)**

```lua
{
    threshold = 10.0,
    reaction = {
        animation = {
            dict = "timetable@gardener@smoking_joint",
            clip = "idle_cough",
            flag = 49,
            start = 800,
            stop = 2000,
            blendInSpeed = 4.0,
            blendOutSpeed = 1.0,
        },
        sound = {
            name = {
                male = {"cough_m1.wav", "cough_m2.ogg", "cough_m3.ogg"},
                female = {"cough_f1.wav", "cough_f2.wav", "cough_f3.wav"},
            },
            volume = 0.2,
            distance = 2.0,
        },
        loop = {
            delay = 13500,
        }
    }
}
```

You don't need both `animation` and `sound`, either one on its own works fine.

**Animation Fields**

| Field           | Type      | Default | Description                                      |
| --------------- | --------- | ------- | ------------------------------------------------ |
| `dict`          | `string`  |         | Animation dictionary                             |
| `clip`          | `string`  |         | Animation clip name                              |
| `flag`          | `integer` | `49`    | Animation flag (49 = upper body, doesn't freeze) |
| `start`         | `integer` | `nil`   | Start time in ms (skip the beginning)            |
| `stop`          | `integer` | `nil`   | Stop time in ms (cut the animation short)        |
| `blendInSpeed`  | `number`  | `1.0`   | How fast the animation blends in                 |
| `blendOutSpeed` | `number`  | `1.0`   | How fast the animation blends out                |
| `forceAnim`     | `boolean` | `false` | Force replay even if already playing             |
| `speed`         | `number`  | `1.0`   | Playback speed                                   |

**Sound Fields**

| Field      | Type                             | Default | Description                                                             |
| ---------- | -------------------------------- | ------- | ----------------------------------------------------------------------- |
| `name`     | `string`, `string[]`, or `table` |         | Sound file(s). Supports `{male = ..., female = ...}` for gendered audio |
| `volume`   | `number`                         | `0.3`   | Playback volume                                                         |
| `distance` | `number`                         | `10.0`  | How far other players can hear it                                       |

> Sounds require `zyke_sounds` to be running. If it's not available, the sound part is silently skipped.

**Loop Settings**

| Field   | Type                      | Description                                                  |
| ------- | ------------------------- | ------------------------------------------------------------ |
| `delay` | `integer` or `{min, max}` | Delay in ms before repeating. Use a table for a random range |

```lua
loop = {delay = 5000}           -- Fixed 5 second delay
loop = {delay = {3000, 7000}}   -- Random between 3 and 7 seconds
```

If `loop` is not defined, the reaction plays once and does not repeat.

**Anti-Spam**

Same as notifications. When surpassing multiple thresholds at once, only the highest threshold's reaction plays. You can override this on the threshold entry with `force = true`.

***

#### Damage

Damage is not queued and **does** stack. If two statuses both deal damage, the player takes both.

```lua
effect = {
    {threshold = 50.0, damage = 0.5},
    {threshold = 80.0, damage = 1.5},
}
```

At value `85.0`, both thresholds are active, so the player takes `0.5 + 1.5 = 2.0` damage per tick. The value is automatically scaled by time so it stays consistent.

***

#### FAQ

**Q: Where do I configure effects?** In the status config file at `statuses/<status>/config.lua`, inside the `effect` array.

**Q: Can one threshold have multiple effect keys?** Yes. Combine as many as you want on a single threshold.

**Q: What happens when two statuses use the same effect key?** The system picks the most severe value automatically. They don't stack, only one `walkingStyle` or `screenEffect` plays at a time.

**Q: Does damage work the same way?** No. Damage is the exception, it stacks across all active thresholds and all statuses.

**Q: Do I need to restart after changing a config?** Yes. Restart the resource for config changes to take effect.

**Q: What if I set `movementSpeed` to a very low value?** Anything below `0.8` starts to look and feel weird. We recommend staying at `0.8` or above.

**Q: How does the `intensity` field work on `screenEffect` and `cameraShaking`?** Higher intensity = stronger visual effect. If you don't specify it, it defaults to `1.0`. Use lower values for subtle effects at early thresholds and ramp it up at higher ones.
