> For the complete documentation index, see [llms.txt](https://5scripts-1.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://5scripts-1.gitbook.io/docs/assets/5s_tuningmenu/editable.md).

# Editable

## Editable

> **Note:** This documentation describes the functions in the `editable` folders. You are free to modify, extend, or adapt to fit your server's needs.

***

## Client Side (`client/editable/`)

### 1. `events.lua`

**Tuning Menu Exit Handling**

* **handleExitEvent()**
  * Safely closes the tuning menu when certain conditions are met.
  * **Behavior:**
    * Returns immediately if `TuningDataStore.tuningIsActive` is `false` or `nil`.
    * Calls `closeTuningMenu(true)` to close the menu and perform any cleanup.
  * **Usage:**
    * Used internally by death/vehicle cache events; can be reused if you add more exit triggers.
* **Death Events**
  * **`baseevents:onPlayerDied`**
    * Triggered when the player dies.\[1]
    * **Behavior:** Calls `handleExitEvent()` to ensure the tuning menu is closed on death.
  * **`izzy-ambulance:playerDied`**
    * Custom death event from the `izzy-ambulance` resource.
    * **Behavior:** Calls `handleExitEvent()` when this death event fires.
* **Vehicle Cache Listener**
  * **lib.onCache('vehicle', function(value, oldValue) ... end)**
    * Listens for changes to the cached vehicle value in `ox_lib`.
    * **Behavior:**
      * If `value` is `0` or `false` (player no longer in a valid vehicle), it calls `handleExitEvent()`.
    * **Usage:**
      * Ensures the tuning menu closes automatically when the player leaves or loses their vehicle.

**Tuning Menu Hooks**

* **onTuningMenuOpened()**
  * Called when the tuning menu is opened.
  * **Usage:**
    * Add custom behavior such as disabling the HUD, disabling certain controls, or pausing other UI while the menu is active.
* **onTuningMenuExit()**
  * Called when the tuning menu is closed.
  * **Usage:**
    * Restore things modified in `onTuningMenuOpened()`, for example re-enabling HUD, controls, or other UI.

***

### 2. `priceCalculation.lua`

**Price Calculation Helpers**

* **calculateModPrice(modCategory, num, vehiclePrice)**
  * Calculates the price for a performance/visual mod based on its category and the vehicle’s base price.
  * **Arguments:**
    * `modCategory` *(string)*: Name of the mod category (e.g. `"modEngine"`, `"modTransmission"`).
    * `num` *(number)*: Mod index/level (usually the mod number being installed).
    * `vehiclePrice` *(number)*: Base price of the current vehicle.
  * **Behavior:**
    * Splits `vehiclePrice` into 6 equal parts for calculations (`vehiclePrice / 6`).
    * Sets a default base mod price of `2500`.
    * If `ModPrices[modCategory]` exists, uses that as the base price instead.
    * If `modCategory` is listed in `affectedByVehiclePrice`, the price scales with vehicle price and mod level:
      * Computes `modMultiplier = (num + 1) * 0.25`.
      * Replaces `modPrice` with `vehiclePriceSplit * modMultiplier`.
    * Returns the final price rounded up with `math.ceil`.
  * **Returns:**
    * Final mod price *(number)*.
* **calculateToggleModPrice(modCategory, vehiclePrice)**
  * Calculates install/uninstall prices for toggle-type mods (e.g. extras that are either on or off).
  * **Arguments:**
    * `modCategory` *(string)*: Name of the mod category.
    * `vehiclePrice` *(number)*: Base price of the current vehicle (unused in the default implementation, but available for customization).
  * **Behavior:**
    * Returns a table with two fixed prices:
      * `uninstall = 1000`
      * `install = 4000`
  * **Returns:**
    * Table `{ uninstall = number, install = number }`.
  * **Usage:**
    * Customize this function if you want toggle mod prices to depend on `vehiclePrice` or `modCategory`.

***

### 3. `vehicleReaction.lua`

**Vehicle Reaction Logic**

* **handleCategoryChange(modCategory, mainVehicle, cloneVehicle)**
  * Reacts when the user switches between mod categories in the tuning menu.
  * **Arguments:**
    * `modCategory` *(string)*: The currently selected mod category (e.g. `"modXenon"`).
    * `mainVehicle` *(vehicle handle)*: The player’s actual vehicle entity.
    * `cloneVehicle` *(vehicle handle)*: The preview/clone vehicle used in the tuning UI.
  * **Behavior:**
    * If `modCategory == "modXenon"`:
      * Calls `SetVehicleLights(mainVehicle, 2)` and `SetVehicleLights(cloneVehicle, 2)` to turn the lights on for preview.
    * For any other category:
      * Calls `SetVehicleLights(mainVehicle, 0)` and `SetVehicleLights(cloneVehicle, 0)` to restore lights to default/off.
  * **Usage:**
    * Extend this to add category-specific reactions (e.g., show neon, change doors, etc.).
* **handleModChange(modCategory, mainVehicle, cloneVehicle)**
  * Intended to react when an actual mod value changes within a category.
  * **Arguments:**
    * `modCategory` *(string)*: The category of the mod changed.
    * `mainVehicle` *(vehicle handle)*: The player’s actual vehicle.
    * `cloneVehicle` *(vehicle handle)*: The preview vehicle.
  * **Behavior:**
    * Empty by default; no reaction is defined.
  * **Usage:**
    * Implement visual or physical reactions when a specific mod value is changed, such as updating engine sound, suspension height, or temporary effects in the preview.

***

## Server Side (`server/editable/`)

### 1. `invoicePayment.lua`

**Money Check**

* **hasMoney(target, total)**
  * Checks if a player has enough money to pay for a tuning invoice.
  * **Arguments:**
    * `target` *(number)*: Server ID of the paying player (customer).
    * `total` *(number)*: Total invoice amount.
  * **Behavior:**
    * Uses `fs.framework.getMoney(target)` to get the player’s balance.
    * Returns `true` if the balance is greater than or equal to `total`, otherwise `false`.
  * **Returns:**
    * `true` if the player can afford the invoice, otherwise `false`.

**Invoice Billing**

* **billPlayer(sender, senderJob, target, total, basket)**
  * Handles payment flow when a mechanic bills a player for tuning work.
  * **Arguments:**
    * `sender` *(number)*: Server ID of the mechanic issuing the invoice.
    * `senderJob` *(string)*: Job name or business identifier of the mechanic.
    * `target` *(number)*: Server ID of the customer being billed.
    * `total` *(number)*: Total invoice amount to be paid.
    * `basket` *(table)*: Details of the items/mods being billed (not used in default implementation but available for customization).
  * **Behavior:**
    * If `total <= 0`, returns `true` immediately (nothing to charge).
    * If `hasMoney(target, total)` is `false`, returns `false` (payment fails).
    * Removes `total` from the customer using `fs.framework.removeMoney(target, total)`.
    * Splits the payment between the business and the mechanic:
      * `firmPrice = math.floor(total * (ConfigSv.BusinessPercent / 100))`
      * `mechanicPrice = math.ceil(total * (ConfigSv.WorkerPercent / 100))`
    * Credits the business with `firmPrice` via `fs.framework.addBusinessMoney(senderJob, firmPrice)`.
    * Credits the mechanic with `mechanicPrice` via `fs.framework.addMoney(sender, mechanicPrice)`.
    * Sends notifications:
      * To `sender` using `fs.utils.notify(sender, locale('invoicePaidSender', mechanicPrice))`.
      * To `target` using `fs.utils.notify(target, locale('invoicePaidTarget', total, sender))`.
    * Returns `true` when the transaction succeeds.
  * **Returns:**
    * `true` on successful billing or when `total <= 0`, otherwise `false`.
  * **Usage:**
    * Adapt this to integrate with different frameworks or to change how the revenue split and notifications work.

***

> All files in the `editable` folders are intended for easy customization without affecting the core logic. For further customization, refer to the comments and structure in each file.
