# TMC 2.1

## **Base**

### PlayerData Replication

* **Reworked how `PlayerData` is saved to player state bags.**
  * Reduced state bag sync frequency to avoid potential issues with high player counts.
  * Only essential or infrequently updated values are now replicated.
  * Configurable via:

    ```lua
    core/config.server.lua > Config.Server.PlayerDataToSaveToStateBags
    ```
  * No changes are required for **our** internal scripts. However, if your external scripts rely on remote state bag access, you may need to adjust accordingly or enable replication for those values manually.
  * All original `PlayerData` remains accessible on the local client side.

***

### 💵 Cash Denominations System

See thread - <https://discord.com/channels/1072182006584184843/1346952944251174962>\
Enabled via: (this is the new default behaviour for using cash as items)

```lua
TMCConfig.Money.UseCashAsItem = true
```

#### Configuration

* Moved config:

  ```lua
  core/config.gta5.lua > Config.Money.UseCashAsItem
  now in
  core/config.lua > TMCConfig.Money.UseCashAsItem
  ```
* Toggle debugging:

  ```
  /toggledenomdebugging [playerId] [0|1]
  ```

  * Enables server-side denomination logic debugging.
  * Must be run from the server console.
* Denomination value setup:

  ```lua
  core/config.lua > TMCConfig.Money.CashDenominations
  ```

  * Default: US coin denominations.
  * UK denominations included as commented examples.
  * You can expand this to include bills/notes.
  * `cash` always equals the base unit (e.g. $1, £1).
* Item configuration:

  ```lua
  core/shared.gta5.lua > denomConfig
  ```

  * Matches the items to the defined denominations.
  * UK denominations included as commented examples.
* Banking has been updated to accept fraction values in input dialogs. Enabled by default
  * Can be toggled in `Config.AllowFractions`

***

#### Coin Exchange System

* Added coin exchange and purchase prompt at all bank location
* Players can now:
  * Pay a fee (`Config.CoinExchangeFee`) to convert all cash on hand into the largest denomination combination possible.
  * Pay a separate fee (`Config.ChangePurchaseFee`) to purchase coin denominations (change).
* This feature is enabled by default but can be toggled via:

```lua
  Config.CoinExchangeEnabled
```

***

#### Wallet System

* **New item:** `wallet`
  * Only accepts denomination items as defined in the config.
  * Incoming cash goes into the wallet if the item exists.
  * If no wallet is present, cash is received in the standard inventory.

***

#### 🧾 Command Updates

* `/cash`
  * Displays total cash with decimal precision.
* `/cashb` *new*
  * Shows breakdown of denominations across inventory and wallet.
* `/givecash`
  * Requires **exact denomination match** to complete transaction.
  * Example:

> Player A has $1.23 (1x $1, 1x $0.20, 3x $0.01)\
> Player A **cannot** give $1.13 unless they have the exact change.

***

### ⚙️ Inventory & Item Performance Optimizations

We're making an effort to improve on stability of processing large number of items, especially with stackable items.

* Player weight calculation reworked for improved performance during player objection transactions.
* Shop purchase/sell logic optimized for handling stackable item transactions.
* Improved `AddItem` logic for inbound stackable items.
* Inventory syncs to the client are now throttled and batched. This reduces network load, especially during high volumes of add/remove item transactions.

***

### Dead Player Tracking

* Added:

  ```lua
  TMC.Player.GetDeadCharacters
  ```

  * Returns a table of dead characters currently online:

    ```lua
    {
      [1] = "ABCDE123", -- serverId = CSN
      [5] = "FGHIJ123",
    }
    ```
* You can reference it with:

  ```lua
  if TMC.Player.DeadCharacters[source] then
    -- do something
  end
  ```
* No longer necessary to manually loop through `TMC.Players` and check metadata.

***

### **Garages - Misc**

**Added stored procedure check in order to display warning when an invalid SQL setup is detected.**

***

### Item Display

* Added a new client event for showing bulk item boxes.
  * Example usage:

```lua
    local itemArray = {
        { name = "apple", amount = 2, info = { _label = "Some New Label" } },
        { name = "water_bottle", amount = 3 }
    }

    TriggerClientEvent("inventory:client:itemBoxBulk", source, itemArray, "add" or "remove")
```

***

### Bundled Items

* Added support for the `bundled_item` item type.
  * When used, it gives the player multiple items specified in its metadata.
  * Example usage:

```lua
    local player = TMC.Functions.GetPlayer(source)
    player.Functions.AddItem("bundled_item", 1, nil, {
        _label = "Packed Lunch",
        items = {
            { name = "apple", amount = 2 },
            { name = "water_bottle", amount = 1 }
        }
    })
```

* Can also be sold in shops using standard `buy` config with metadata for included items.

***

### Shop System Enhancements

* Added new fields for 'sell' shop configuration:
  * `requireMetadata`
  * `requireRep`
  * `sellAmount`
  * Example:

```lua
    {
        name = 'log',
        price = 1,
        sellAmount = 5,
        info = { _label = "5x Douglas Log" },
        requireMetadata = { species = "p_tree_douglasfir_snow_05" },
        requireRep = { name = "lumberjack", amount = 10 }
    }
```

* These options allow conditional sales based on metadata, reputation, and required quantity.

***

### Shop Access Conditions

* Added support for `condition` functions to restrict shop access globally or per location.
  * Example configuration:

```lua
['pawnshop'] = {
    label = 'Pawn Shop',
    blipInfo = {
        type = 272,
        color = 4
    },
    type = 'sell',
    condition = function()
        if TMC.Functions.HasJob("pawnshop") then
            return true
        end
        return false
    end,
    locations = {
        { -- Southside
            center = vector3(182.6522, -1319.378, 29.323),
            length = 2.0,
            width = 1.6,
            options = {
                heading = 333,
                minZ = 28.0,
                maxZ = 31.0
            },
            condition = function()
                if TMC.Functions.HasItem("pawn_shop_key_southside") then
                    return true
                end
                return false
            end
        },
    }
}
```

* These conditions are optional but allow greater control over who can access shops.

***

### Item Tagging

* Introduced logic for `item_tag` items.
  * Used to apply `_label` metadata to items.
  * Displays in inventory as a uniquely named item.
  * Not applicable to weapons or currency items.
  * Usage is logged under the `itemtag` category.

***

### Carriable Item Metadata Requirements

* `Config.CarriableConfig` now supports `MetadataRequired`.
  * Example:

```lua
    ['sand_sack'] = {
        ObjectModel = `p_sandbags03x`,
        MetadataRequired = { full = true }
    }
```

***

### Item Exchanges

* Added `Config.ItemExchanges` for trade-based interactions.
  * Example: 20x water bottles for 2x apples.
  * Supports reputation requirements and per-character limits.
  * Character limits reset on script/server restart.

***

### Internal Events

* Added internal server-side events:

```lua
  AddEventHandler("inventory:server:itemAdded", function(stashId, itemName, slot, itemInfo, amount, originSource) end)
  AddEventHandler("inventory:server:itemRemoved", function(stashId, itemName, slot, slotIndex, amount, originSource) end)
```

***

### Stash Restrictions

* New system to apply whitelist/blacklist restrictions on stashes.
* Also allows setting a maximum item amount per slot.
* Example usage:

```lua
  exports.inventory:SetStashItemRestriction("food_stash", "whitelist", { "bread", "water" }, 10)
  exports.inventory:SetStashItemRestriction("safe_stash", "blacklist", { "gun", "ammo" })
```

* New exports:
  * `GetStashItemRestriction(stashId)`
  * `ClearStashItemRestriction(stashId)`
  * Example:

```lua
    local restriction = GetStashItemRestriction("food_stash")
    if restriction then
        print("Type:", restriction.type)
        print("Items:", table.concat(restriction.items, ", "))
        if restriction.maxAmount then
            print("Max:", restriction.maxAmount)
        end
    end

    ClearStashItemRestriction("food_stash")
```

***

### Player Shops & Market Stalls

* Updated player shops to support **multiple stackable items** per stock slot.
* Menus now support decimal value prices for items.
* Added feedback when updating market stalls to improve user clarity.
* Additional general improvements made to stock management logic

***

### Misc

* Added `TMC.Functions.StopNotify` for **server-side** use.
  * Works similarly to `SimpleNotify`.
  * Usage:

    ```lua
    TMC.Functions.StopNotify(source, "perist_notify_id")
    ```
* Improved the logic within the core `playerDropped` function.
* `TMC.Common.FormatCashString` *new*
  * Formats a number to a readable string with 2 decimal places.
* `TMC.Common.RoundToPenny`*new*
  * Rounds a number to 2 decimal places.

***

## **Gangs**

### Gang Member Management

* Added two new exports for better gang management:
  * `GetOnlineGangMembers`
  * `IsPlayerInGang`
* Refer to the `README.md` for usage examples and documentation.
* Removed unused debug print statements.

***

## **Territories**

### Actioned Influence Enhancements

* Added **radius-based influence gain** for actioned influence.
  * Example provided in:

    ```lua
    serverConfig.lua > ActionedInfluenceConfig > example_radius_action
    ```
  * Influence gain is scaled down the further it gets from the center hexagon. The scaling is configurable
* Each action in `ActionedInfluenceConfig` now supports an `enabled` toggle.
  * **Ensure you update your config:**

    ```lua
    serverConfig.lua > ActionedInfluenceConfig
    ```

***

### Influence System Improvements

* Added influence gain through **player presence** in zones.
  * Configurable via:

    ```lua
    serverConfig.lua > ActionedInfluenceConfig > player_in_zone
    ```
  * **Note:** For balance reasons, players will **only gain influence through presence in zones that already exist**
    * To create a new zone, one of the other influence actions must be performed first
    * Once a zone is created, players can then begin earning influence in it simply by being at that location
* Refactored passive influence loss to streamline performance.
* Improved `ModifyZoneInfluence` to prevent repeated save/sync calls on failed attempts.
* Added logs for zone contest and claim timeouts.

***

### General Updates

* Improved debug output for **overdue upkeep** and **zone discovery**.
* Updated permission sync for staff view access to reduce network load.
* Removed unnecessary debug prints.

***

## Changed Files

### Base

```
banking/
|-  client.lua
|-  config.lua
|-  server.lua
|-  html/
|-  |-  REPLACE EVERYTHING
```

```
core/
|-  config.gta5.lua
|-  config.lua
|-  config.server.lua
|-  shared.gta5.lua
|-  client/
|-  |-  events.lua
|-  common/
|-  |-  functions.lua
|-  server/
|-  |-  events.lua
|-  |-  functions.lua
|-  |-  main.lua
|-  |-  player.lua
```

```
core_game/
|-  server/
|-  |-  server.lua
```

```
inventory/
|-  client.lua
|-  config.lua
|-  inventories.lua
|-  server.lua
```

```
garages/
|-  proc.sql
|-  server/
|-  |-  main.lua
```

```
tmc_queue/
|-  server.js
```

### Addon

```
gangs/
|- server.lua
|- README.md
```

```
playershops/
|-  client.lua
|-  server.lua
|-  marketstalls/
|-  |-  server.lua
```

```
territories/
|- serverConfig.lua
|- server.lua
|- utils.lua
```


---

# 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://docs.tmc.bj/release-notes/tmc-2.1.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.
