# Distillery

A **job-based distillery production system** for RedM, featuring:

* A **book-style NUI** menu for recipes + batch management
* A **batch pipeline**: Mash → Ferment → Distill → (optional) Age → Bottle
* **Job-shared capacity** with upgrade requirements
* Optional **Barrel Care** gameplay (temperature + top-up) for aged recipes

***

### Escrow / What customers can edit

This resource is protected by the **CFX Escrow system**.

✅ **Editable (escrow ignored):**

* `config.lua`
* `shared/recipes.lua`

❌ **Not editable:**

* All other client/server code and NUI files (they are escrowed/encrypted)

This documentation is written so a customer can understand **what the script does** and **how to use/configure it** using only the editable files.

***

### Requirements / Dependencies

#### Required resource

* **`codex_core`**

The server code loads the Codex server library via:

* `exports['codex_core']:getLibServer()`

The resource relies on CodexCore features such as:

* inventory counts / add / remove
* job name retrieval
* character identifier retrieval
* server callbacks
* SQL execution helper

#### Database

The script persists batches, job upgrades, and job statistics using SQL.

You do **not** need to import tables manually if your version includes the shipped DB setup logic; the server uses SQL calls for reads/writes and a cache layer that flushes to DB.

***

### What the script does (player experience)

#### 1) Open the Distillery UI

Players open the UI at the configured location:

* `Config.CodexCFG.JobData[JOB].OpenJobMenu`

If `OpenJobMenu` is set to `vector3(0,0,0)`, players will **not** be able to realistically reach the open prompt.

#### 2) Pick a recipe and start mash

In the UI (left panel), players:

* select a recipe
* click **Start Mash**

When they start mash:

* the script checks the player inventory for the recipe `mash` items
* it removes the required items
* it creates a new **batch** in the **fermenting** stage

#### 3) Fermentation → ready to distill

Batches remain **fermenting** until their internal timer expires.

Players use the UI action:

* **Collect Fermentation**

This moves all fermenting batches that are finished into:

* **ready\_to\_distill**

#### 4) Distill

For each batch that is ready, the UI will show **Distill**.

Distilling:

* computes a new quality from the stored batch quality and a distill score (0–100)
* generates output items using `recipe.output` (min/max yield)
* if the recipe has **aging enabled**, the batch moves into **aging** instead of instantly outputting

#### 5) Aging (optional)

Only recipes that have `aging = { seconds = ... }` will enter the aging system.

When a batch enters aging:

* it is assigned barrel meta values like temperature and top-up progress
* the world will show spawned **barrel props** near your configured Barrel/Still anchor station

#### 6) Barrel Care (optional gameplay)

If `Config.CodexCFG.BarrelCare.enabled = true`, the server runs a periodic “aging tick”:

* temperature drifts over time
* temperature states can be shown (hot/cold) for UI display
* if temperature stays too high, the barrel can receive **strikes**
* if strikes reach `maxStrikes` and `destroyOnFail` is enabled, the barrel/batch fails

Players can interact with aging barrels using prompts to:

* **cool** the barrel (consumes `BarrelCare.temp.coolItem`)
* **top up** the barrel (consumes `BarrelCare.topup.item`)

#### 7) Collect aging → bottle

In the UI:

* **Collect Aging** moves finished aging batches to **ready\_to\_bottle**
* **Bottle** gives the final output items and removes the batch record

***

### Configuration guide (config.lua)

All settings below are in `config.lua`.

#### Job locations

Jobs are configured under:

```lua
Config.CodexCFG.JobData = {
  ["job_name_here"] = {
    stationName = "...",
    Stations = {
      Mash   = { { name="...", coords=vector3(...), radius=2.0 }, },
      Still  = { { name="...", coords=vector3(...), radius=2.0 }, },
      Barrel = { { name="...", coords=vector3(...), radius=3.0 }, }
    },
    OpenJobMenu = vector3(...),
  }
}
```

**Important behaviors:**

* `OpenJobMenu` controls where the “Open Distillery” prompt appears.
* `Stations.Barrel` is used as the preferred anchor for spawning barrel props.
* If `Stations.Barrel` is missing, the script may fall back to the Still station anchor.

The shipped config includes at least:

* `distiller_Valentine` with a valid `OpenJobMenu`
* `distiller_Blackwater` with `OpenJobMenu = vector3(0,0,0)` (meaning it won’t be reachable until you change it)

#### Business level requirements (inventory-based)

`BaseLevelReq` defines the inventory thresholds used to compute “business level”:

```lua
BaseLevelReq = {
  bourbon   = 60,
  moonshine = 150,
  absinthe  = 70
}
```

The script checks the player’s inventory counts of:

* `bourbon`, `moonshine`, `absinthe`

And applies a multiplier by level:

* level 1 = x1
* level 2 = x2
* level 3 = x4

#### Job upgrades (job storage-based)

Upgrade requirements are defined as:

```lua
UpgradeRequirements = {
  base = {
    bourbon   = 150,
    moonshine = 340,
    absinthe  = 200,
  },
  multipliers = {
    [1] = 1,
    [2] = 2,
    [3] = 4,
  }
}
```

To buy an upgrade, the job must have enough items in **job storage**.

In the UI, the Job Details area includes:

* a dropdown (Absinthe / Bourbon / Moonshine)
* a **STOCK** button

**STOCK deposits 1 item at a time** from the player into the job’s shared storage.

#### Item labels (UI display names)

The UI displays nicer names using:

```lua
ItemLabels = {
  craft_wheat             = "Wheat",
  craft_corn_floursack    = "Corn Flour Sack",
  consumable_alcohol      = "Alcohol",
  consumable_water_bottle = "Water Bottle",
  craft_herb_wild_mint    = "Mint",
  craft_herb_basil        = "Basil",
  consumable_sugarcube    = "Sugar Cube"
}
```

If an item is not in `ItemLabels`, the UI may display the raw item name.

#### BarrelCare tuning (aging gameplay)

BarrelCare is configured under:

```lua
BarrelCare = {
  enabled = true,
  temp = {
    startMin = 50,
    startMax = 70,
    hotAt = 90,
    coldAt = 25,

    driftEverySeconds = 30,
    driftMin = -2,
    driftMax = 4,

    spikeChance = 0.12,
    spikeAddMin = 6,
    spikeAddMax = 14,

    coolItem = "cooling_fun",
    coolAmount = 20,
  },

  topup = {
    item = "high_moonshine",
    amountEach = 1,
    required = 2,
    intervalSeconds = 6 * 60,
  },

  checks = {
    required = 3,
    intervalSeconds = 5 * 60,
  },
}
```

**Confirmed behavior notes:**

* Cooling consumes exactly **1** `coolItem` per action and subtracts `coolAmount` from temperature.
* Top-up consumes exactly **1** `topup.item` per action and increments `topup_done` by 1 until it reaches `topup.required`.
* `topup.amountEach` exists in config, but the top-up server logic removes **1 item** per action.

***

### Recipes (shared/recipes.lua)

Recipes are defined like:

```lua
Recipes = {
  recipe_key = {
    label = "Display Name",
    difficulty = 1,
    mash = { itemA = 1, itemB = 2 },
    -- optional aging:
    -- aging = { seconds = 20 * 60 },
    output = { item = "output_item", min = 1, max = 2 }
  }
}
```

#### Shipped recipes

**Absinthe**

* Difficulty: 3
* Mash requires:
  * `craft_herb_wild_mint` x1
  * `craft_herb_basil` x1
  * `consumable_water_bottle` x1
  * `consumable_alcohol` x1
* Output: `absinthe` (min 1, max 2)
* Aging: **disabled** by default (commented out)

**Moonshine**

* Difficulty: 1
* Mash requires:
  * `craft_corn_floursack` x1
  * `consumable_sugarcube` x1
  * `consumable_water_bottle` x1
  * `consumable_alcohol` x1
* Output: `moonshine` (min 1, max 3)
* Aging: **disabled** by default (commented out)

**Bourbon**

* Difficulty: 2
* Mash requires:
  * `craft_corn_floursack` x1
  * `craft_wheat` x1
  * `consumable_water_bottle` x1
  * `consumable_alcohol` x1
* Output: `bourbon` (min 1, max 2)
* Aging: **enabled** by default

> Note: In the shipped recipes file, Bourbon’s aging seconds is set to `1 * 10` (10 seconds). The comment next to it says “20 minutes”, but the actual value is 10 seconds.

***

### Examples (copy/paste)

In `config.lua`, the shipped Blackwater job uses `OpenJobMenu = vector3(0,0,0)`.&#x20;

Replace it with a real location, for example:

```lua
Config.CodexCFG.JobData["distiller_Blackwater"].OpenJobMenu = vector3(-255.0, 743.0, 118.0)
```

(Use the exact coords you want the prompt to appear at.)

#### &#x20;Enable aging for Moonshine

In `shared/recipes.lua`, uncomment aging:

```lua
moonshine = {
  label = "Moonshine",
  difficulty = 1,

  mash = {
    craft_corn_floursack    = 1,
    consumable_sugarcube    = 1,
    consumable_water_bottle = 1,
    consumable_alcohol      = 1,
  },

  aging = {
    seconds = 20 * 60, -- 20 minutes
  },

  output = {
    item = "moonshine",
    min = 1,
    max = 3,
  },
},
```

#### Make barrels “riskier”

Lower `hotAt` so overheating happens sooner:

```lua
Config.CodexCFG.BarrelCare.temp.hotAt = 75
```

With a lower hot threshold, barrels will accumulate strikes more often.

#### Change cooling item

```lua
Config.CodexCFG.BarrelCare.temp.coolItem = "cooling_fun"
Config.CodexCFG.BarrelCare.temp.coolAmount = 25
```

***

### Troubleshooting checklist

#### UI doesn’t open

* Confirm the player’s job name matches a key in `Config.CodexCFG.JobData`.
* Confirm `OpenJobMenu` is not `vector3(0,0,0)` and is reachable.

#### Can’t start mash

* The player must have every required item from the recipe `mash` table in inventory.

#### Aging barrels never appear

* Only recipes with `aging = { seconds = ... }` will enter the aging system.
* Ensure your job has a Barrel station (or at least a Still station) so barrel props have an anchor.

#### Top-up seems to ignore amountEach

* This is expected: the server top-up logic consumes **1 item per action** and increments top-up progress by 1 each time.
