# Stash Houses

## Introduction

This script will add the ability for players to rent and manage their very own stash houses, giving your players the ability to hide their supplies without having to buy another house.

{% hint style="danger" %}
**This script is written to work with** [**QBCore**](https://qbcore-framework.github.io/qb-docs/) **and** [**Zap Hosting ESX Pack**](https://github.com/zaphosting/esx_12)

**Requirements:** [**ox\_lib** ](https://github.com/overextended/ox_lib/)**and** [**ox\_target** ](https://github.com/overextended/ox_target)**or** [**qb-target**](https://github.com/qbcore-framework/qb-target)
{% endhint %}

#### Features of Stash Houses:

* Unlimited Stash Houses
* Stash Houses can be created by either staff only or by set jobs (editable in config)
* Stash House Interiors are created automatically using objects
* Storable Cash (max amount editable within config)
* Cash Objects depending on amount stored for added immersion.
* Separate Storage Inventories for each object with a storage inventory.
* Ability to furnish the stash house with a list of furniture (editable within config)
* Ability for Police to Raid Stash Houses (Jobs editable within config)
* Stash Management UI to manage rent and keys

{% hint style="info" %}
**This is compatible with the following inventory systems:**

* [**qb-inventory**](https://github.com/qbcore-framework/qb-inventory/) **(QBCore)**
* [**lj-inventory**](https://github.com/loljoshie/lj-inventory) **(QBCore)**
* [**ox\_inventory**](https://github.com/overextended/ox_inventory) **(ESX)**
* [**chezza-inventory**](https://forum.cfx.re/t/paid-release-chezzas-inventory-esx/2040417) **(ESX)**
  {% endhint %}

{% hint style="warning" %}
**Editable functions file which means this can be converted to any framework.**
{% endhint %}

***

## Installation

* Download the power\_stashhouses from Keymaster
* Run the .sql file in your database.
* Add the files to your resources folder
* Ensure that the resource file will start (either ensure your resource in the server.cfg or inside a folder which is started on server start)
* Edit the config file to your needs
* Restart your server

***

## Configuration

Select the tab below for your framework to see the configuration options available.

{% tabs %}
{% tab title="Configuration Options" %}

```lua
--========================--
--== Framework Settings ==--
--========================--

-- Enable Support for QBCore 
enableQBCore = true
QBCoreResourceName = 'qb-core'

-- QBCore will use qb-inventory or lj-inventory by default.
-- This system also supports ox_inventory 
QBInventory = 'qb-inventory' 

-- Enable QB-Target Support - (False = Using OX_Target, True = Using QB-Target)
enableQBTarget = true 


-- Enable Support for ESX 
enableESX = false

-- ESX Inventory 
-- Compatible with 'ox_inventory' and 'chezza-inventory' and 'mf_inventory'
esxInventory = 'ox_inventory'

-- config to toggleProps for Renewed-Weaponscarry on enter and exit.
renewedWeapons = false

--======================--
--== Command Settings ==--
--======================--

-- The command that is used to create a new stash house.
addStashCommand = 'createstash'

-- This will allow only staff to create a new stash house, if this is disabled then it will use the job whitleist below.
addStashStaffOnly = true 

-- The jobs required to use the addStashCommand, if the staff only setting is disabled.
addStashJobs = {
 ['realestate'] = {2, 3, 4}, -- This will only allow rel estate agents with grade 2, 3 or 4.
 ['realestateagent'] = {1, 2, 3}, 
}

-- Max cash that can be stored in a stash house.
maxStoredCash = 500000
```

{% endtab %}

{% tab title="Language" %}
In this section, you can edit the language if you need to translate to another language or edit the wording.

```lua
Lang = {
 ['stash_created'] = 'Stash Created', 
 ['stash_rented'] = 'Stash Rented', 
 ['stash_unrented'] = 'Stash Unrented', 
 ['cash_withdrawn'] = 'Cash Withdrawn', 
 ['cash_deposited'] = 'Cash Deposited', 
 ['not_staff'] = 'You are not staff', 
 ['not_job'] = 'You do not have the correct job to use this command', 
 ['not_enough_money'] = 'You do not have enough money', 
 ['max_cash'] = 'Stash is full',
 ['key_added'] = 'Key Added',
 ['key_removed'] = 'Key Removed',
 ['days_added'] = 'Days Added',
 ['furniture_deleted'] = 'Furniture Deleted', 
 ['furniture_purchased'] = 'Furniture Purchased', 
 ['not_staff'] = 'You are not staff', 
 ['not_job'] = 'You do not have the correct job',
}
```

{% endtab %}

{% tab title="Interiors" %}
You can edit or add your own interiors here. Default settings are as follows:

```lua
-- This section can be used to add your own interiors, its important to set the right offsets for the enterance and management and money,
-- Money is the location where the player will go to deposit and withdraw money from the stash house, this will spawn a shelf with the money on it.
availableInteriors = {
 ['Default Stashhouse'] = {
  ipl = 'power_stashhouse_shell', 
  enteranceOffset = vector3(-8.5, 0.25, -1.0),
  managementOffset = vector3(-7.4, -1.8, -0.8), 
  moneyOffset = vector3(6.0, -3.65, -1.975),
 },
}
```

{% endtab %}

{% tab title="Furniture" %}
You can edit the available furniture here, change the price and storage amount per piece, and add or remove furniture options. Default settings are as follows:

```lua
-- This section can be used to add your own furniture, you can add as many as you like, the name is the name of the furniture, the model is the model of the furniture, the price is the price of the furniture and the storageSize is the size of the storage that the furniture will have.
availableFurniture = {
 {name = "Locker", model = "p_cs_locker_01_s", price = 200, storageSize = 50},
 {name = "Locker 2", model = "p_cs_locker_02", price = 250, storageSize = 75},
 {name = "Locker 3", model = "p_cs_locker_01", price = 200, storageSize = 50},
 {name = "Lester Crate", model = "prop_cs_lester_crate", price = 300, storageSize = 100},
 {name = "Crate 1", model = "prop_lev_crate_01", price = 350, storageSize = 150},
 {name = "Side Unit", model = "v_res_fh_sidebrdlngb", price = 250, storageSize = 75},
 {name = "Side Unit 2", model = "v_res_fh_sidebrddine", price = 200, storageSize = 50},
 {name = "Side unit", model = "v_res_d_sideunit", price = 200, storageSize = 50},
 {name = "Bed table", model = "v_res_mbbedtable", price = 200, storageSize = 50},
 {name = "TV stand", model = "v_res_j_tvstand", price = 200, storageSize = 50},
 {name = "Dresser", model = "v_res_mbdresser", price = 200, storageSize = 50},
 {name = "Bottoman", model = "v_res_mbottoman", price = 200, storageSize = 50},
 {name = "Console", model = "v_res_mconsolemod", price = 200, storageSize = 50},
 {name = "Cupboard", model = "v_res_mcupboard", price = 200, storageSize = 50},
 {name = "Chest", model = "v_res_mdchest", price = 200, storageSize = 50},
 {name = "Cabinet 3", model = "v_res_msoncabinet", price = 200, storageSize = 50},
 {name = "Cabinet 4", model = "prop_cabinet_02b", price = 200, storageSize = 50},
 {name = "Cabinet 5", model = "prop_cabinet_01b", price = 200, storageSize = 50},
 {name = "Sidetable", model = "v_res_m_sidetable", price = 200, storageSize = 50},
 {name = "Bedsidetable", model = "v_res_tre_bedsidetable", price = 200, storageSize = 50},
 {name = "Bookshelf", model = "v_res_tre_smallbookshelf", price = 200, storageSize = 50},
 {name = "Storage box", model = "v_res_tre_storagebox", price = 200, storageSize = 250},
 {name = "Storage unit", model = "v_res_tre_storageunit", price = 200, storageSize = 50},
 {name = "Sidetable", model = "v_res_m_sidetable", price = 200, storageSize = 50},
 {name = "Woodtable", model = "v_res_tre_wdunitscuz", price = 200, storageSize = 50},
 {name = "Devin Box", model = "prop_devin_box_closed", price = 200, storageSize = 50},
 {name = "Crate 3", model = "prop_mil_crate_01", price = 200, storageSize = 50},
 {name = "Crate 4", model = "prop_mil_crate_02", price = 200, storageSize = 50},
 {name = "Safe 1", model = "prop_ld_int_safe_01", price = 200, storageSize = 50},
 {name = "Safe 2", model = "p_v_43_safe_s", price = 200, storageSize = 50},
 {name = "Safe 3", model = "prop_ld_int_safe_01", price = 200, storageSize = 50},
 {name = "Woodtable", model = "prop_mil_crate_02", price = 200, storageSize = 50},
 {name = "Dressing table", model = "v_res_d_dressingtable", price = 200, storageSize = 50},
 {name = "Cabinet", model = "prop_fbibombfile", price = 200, storageSize = 50},
 {name = "Cabinet 2", model = "v_res_cabinet", price = 200, storageSize = 50},
 {name = "Weapon Box", model = "p_secret_weapon_02", price = 200, storageSize = 50},
 {name = "Gun Case", model = "prop_gun_case_02", price = 200, storageSize = 50},
 {name = "Side Table 2", model = "v_res_mdbedtable", price = 200, storageSize = 50},
 {name = "Used Bedside Table", model = "v_res_tre_bedsidetableb", price = 200, storageSize = 50},
 {name = "Tool Chest", model = "ex_prop_ex_toolchest_01", price = 200, storageSize = 50},
 {name = "Bedside Table 2", model = "mp_b_bed_table_dble_04", price = 200, storageSize = 50},
 {name = "Office Shelf Grey", model = "v_corp_offshelf", price = 200, storageSize = 50},
 {name = "Office Shelf Black", model = "v_corp_offshelfdark", price = 200, storageSize = 50},
 {name = "Hanging Clothes", model = "ex_office_01c_dressing02", price = 200, storageSize = 50},
 {name = "Wooden Box", model = "v_res_m_spanishbox", price = 200, storageSize = 50},
 {name = "Shelf 1", model = "apa_mp_h_str_shelffloorm_02", price = 200, storageSize = 50},
 {name = "Shelf 2", model = "apa_mp_h_str_shelffreel_01", price = 200, storageSize = 50},
 {name = "Shelf 3", model = "apa_mp_h_str_shelfwallm_01", price = 200, storageSize = 50},
 {name = "Shelf 4", model = "hei_int_heist_bed_unit", price = 200, storageSize = 50},
 {name = "Console 9", model = "hei_int_heist_fh_sidebrdlngb", price = 200, storageSize = 50},
 {name = "Coffin", model = "prop_coffin_02b", price = 200, storageSize = 50},
 {name = "Bin 1", model = "prop_cs_bin_02", price = 200, storageSize = 50},
 {name = "Bin 2", model = "prop_cs_bin_03", price = 200, storageSize = 50},
 {name = "Bin 3", model = "prop_fbibombbin", price = 200, storageSize = 50},
 {name = "Bin 4", model = "prop_rub_binbag_sd_01", price = 200, storageSize = 50},
 {name = "Bin 5", model = "prop_rub_binbag_sd_01", price = 200, storageSize = 50},
 {name = "Bin 6", model = "prop_bin_04a", price = 200, storageSize = 50},
 {name = "Bin 7", model = "prop_bin_07a", price = 200, storageSize = 50},
 {name = "Bin 8", model = "prop_bin_06a", price = 200, storageSize = 50},
 {name = "Bin 9", model = "prop_bin_10b", price = 200, storageSize = 50},
 {name = "Bin 10", model = "prop_bin_11b", price = 200, storageSize = 50},
 {name = "Bin 11", model = "prop_bin_11a", price = 200, storageSize = 50},
 {name = "Large bin", model = "prop_bin_13a", price = 200, storageSize = 50},
 {name = "Bin bag", model = "prop_rub_binbag_sd_01", price = 200, storageSize = 50},
 {name = "Sofa", model = "prop_t_sofa_02", price = 200},
 {name = "Sofa 2", model = "prop_t_sofa", price = 200},
 {name = "Sofa 3", model = "p_lev_sofa_s", price = 200},
 {name = "Sofa 4", model = "p_res_sofa_l_s", price = 200},
 {name = "Sofa 5", model = "p_v_med_p_sofa_s", price = 200},
 {name = "Sofa 6", model = "p_yacht_sofa_01_s", price = 200},
 {name = "Sofa 7", model = "v_ilev_m_sofa", price = 200},
}
```

{% endtab %}
{% endtabs %}

***

## Editable files

### functions.lua

#### Inventory Compatibility

If you would like to edit the inventory functions, you can edit them in the functions.lua file. Default functions are as follows:

```lua
function openStorage(stashId, storageAmount)
 if enableQBCore then 
  if QBInventory == 'ox_inventory' then 
   exports.ox_inventory:openInventory('stash', {id = stashId, owner = false})
  else 
   TriggerServerEvent("inventory:server:OpenInventory", "stash", stashId, {maxweight = storageAmount})
   TriggerEvent("inventory:client:SetCurrentStash", stashId)
  end 
 end

 if enableESX then 
  if esxInventory == 'mf-inventory' then 
   exports["mf-inventory"]:openOtherInventory(stashId)
  end 

  if esxInventory == 'ox_inventory' then 
   exports.ox_inventory:openInventory('stash', {id = stashId, owner = false})
  end 

  if esxInventory == 'chezza-inventory' then 
   TriggerEvent('inventory:openStorage', "Stash", stashId, storageAmount, 1000)
  end 

  if esxInventory == 'quasar-inventory' then 
   local other = {}
   other.maxweight = storageAmount
   other.slots = 50 
   TriggerServerEvent("inventory:server:OpenInventory", "stash", stashId, other)
   TriggerEvent("inventory:client:SetCurrentStash", stashId)
  end 
 end 

 -- This is an example for if you would like to use your own framework
 TriggerServerEvent('inventory:getStorageInventory', stashId, storageAmount)
end  
```

#### Pulling character information

If you would like to edit how character information is pulled, you can edit them in the functions.lua file. Default functions are as follows:

```lua
function getCharacterId()
 if enableQBCore then 
  local QBCore = exports['qb-core']:GetCoreObject()
  return QBCore.Functions.GetPlayerData().citizenid
 end

 if enableESX then 
  local ESX = exports['es_extended']:getSharedObject()
  local player = ESX.GetPlayerData()
  return player.identifier
 end

 -- This is an example for if you would like to use your own framework
 return LocalPlayer.state.characterId
end 
```

#### Checking Police Job

If you would like to edit the check for police jobs functions, you can edit them in the functions.lua file. Default functions are as follows:

```lua
function isPolice()
 if enableQBCore then 
  local QBCore = exports['qb-core']:GetCoreObject()
  return QBCore.Functions.GetPlayerData().job.name == 'police'
 end

 if enableESX then 
  local ESX = exports['es_extended']:getSharedObject()
  return ESX.PlayerData.job.name == 'police'
 end

 -- This is an example for if you would like to use your own framework
 return LocalPlayer.state.jobId == 1
end 
```

#### Custom Notifications

If you would like to edit the notifications to add your own custom notifications or to match them to your other server notifications, you can edit them in the functions.lua file. Default notifications are as follows:

```lua
RegisterNetEvent('stashhouses:notification')
AddEventHandler('stashhouses:notification', function(msg)
 TriggerEvent('ox_lib:notify', {description = msg})
end)
```

### server\_functions.lua

#### Handling player data

If you need to change how player data is called, you can change this in the server\_functions.lua. Default functions are as follows:

```lua
function getCharacterId(source)
 if enableQBCore then 
  local QBCore = exports['qb-core']:GetCoreObject()
  local Player = QBCore.Functions.GetPlayer(source)
  return Player.PlayerData.citizenid
 end

 if enableESX then 
  local ESX = exports['es_extended']:getSharedObject()
  local player = ESX.GetPlayerData(source)
  return player.identifier
 end

 -- This is an example for if you would like to use your own framework
 return Player(source).state.characterId
end
```

#### Money Functions

If you need to change how money functions are handled, you can change this in the server\_functions.lua. Default functions are as follows:

```lua
function getMoney(source)
 if enableQBCore then 
  local QBCore = exports['qb-core']:GetCoreObject()  
  local Player = QBCore.Functions.GetPlayer(source)
  return Player.PlayerData.money['cash']
 end

 if enableESX then 
  local ESX = exports['es_extended']:getSharedObject()
  local player = ESX.GetPlayerData(source)
  return player.getMoney()
 end

 -- This is an example for if you would like to use your own framework
 local source = tonumber(source)
 local user = exports['dsrp_core']:getCharacterFromId(source)
 return user.getMoney()
end

function removeMoney(source, amount)
 if enableQBCore then 
  local QBCore = exports['qb-core']:GetCoreObject()
  local Player = QBCore.Functions.GetPlayer(source)
  Player.Functions.RemoveMoney('cash', amount)
  return true 
 end

 if enableESX then 
  local ESX = exports['es_extended']:getSharedObject()
  local player = ESX.GetPlayerData(source)
  player.removeMoney(amount)
  return true 
 end

 -- This is an example for if you would like to use your own framework
 local source = tonumber(source)
 local user = exports['dsrp_core']:getCharacterFromId(source)
 user.removeMoney(amount)
 return true 
end

function addMoney(source, amount)
 if enableQBCore then 
  local QBCore = exports['qb-core']:GetCoreObject()
  local Player = QBCore.Functions.GetPlayer(source)
  Player.Functions.AddMoney('cash', amount)
  return true 
 end

 if enableESX then 
  local ESX = exports['es_extended']:getSharedObject()
  local player = ESX.GetPlayerData(source)
  player.addMoney(amount)
  return true 
 end

 -- This is an example for if you would like to use your own framework
 local source = tonumber(source)
 local user = exports['dsrp_core']:getCharacterFromId(source)
 user.addMoney(amount)
 return true 
end
```

#### Staff Checks

If you need to change how player staff permissions are handled, you can change this in the server\_functions.lua. Default functions are as follows:

```lua
function isStaff(source)
 if enableQBCore then 
  local QBCore = exports['qb-core']:GetCoreObject()
  local Player = QBCore.Functions.GetPlayer(source)
  if QBCore.Functions.HasPermission(source, 'admin') or IsPlayerAceAllowed(source, 'command') then
   return true
  end 
 end

 if enableESX then 
  local ESX = exports['es_extended']:getSharedObject()
  local player = ESX.GetPlayerData(source)
  if player.getGroup() == 'admin' or player.getGroup() == 'superadmin' then 
   return true 
  end
 end

  -- This is an example for if you would like to use your own framework
 return false
end 
```

#### Job Checks

If you need to change how character job permissions are handled, you can change this in the server\_functions.lua. Default functions are as follows:

```lua
function hasJob(source, jobs)
 if enableQBCore then 
  local QBCore = exports['qb-core']:GetCoreObject()
  local Player = QBCore.Functions.GetPlayer(source)
  local job = Player.PlayerData.job.name
  if jobs[job] then 
   return true 
  end
  return false 
 end

 if enableESX then 
  local ESX = exports['es_extended']:getSharedObject()
  local player = ESX.GetPlayerData(source)
  local job = player.job.name
  if jobs[job] then 
   return true 
  end
  return false 
 end

 -- This is an example for if you would like to use your own framework
 return false
end
```

### HTML

HTML files can be edited as needed for additional language support or styling edits.

{% hint style="info" %}
Additional language edits can be applied in **index.html**

Look for the following terms that can be translated:

* Stash House
* Stash Furniture
* Edit Furniture
* Stash Keys
* Add Key
* New Key
* Add Key
* Cancel
* Information
* Days Remaining
* Stash Keys:
* Stored Cash:
* Stash Rent
* Add 7 Days
* Add 28 Days
* Terminate Contract
  {% endhint %}

***


---

# 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.power-scripts.com/stash-houses.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.
