Developers
The money laundering client and server exports, its notification event, the hooks in its open files and the integrations you can edit, with signatures and Lua examples.
Every export is called on exports.lation_laundering. Server exports that act on a player take their server id as source; client exports work on the local player. A player's identifier is the ESX identifier on ESX and the citizenid on Qbox and QBCore.
Client exports
| Export | Returns |
|---|---|
OpenMenu() | Nothing |
IsLaundering() | boolean |
GetData(type?) | The local player's record, or one field of it |
GetBalance(moneyType) | number |
PoliceCount() | number |
GetTimeRemaining() | Hours, minutes and seconds |
OpenMenu
Client export
exports.lation_laundering:OpenMenu()Returns
- Nothing
Opens the main menu wherever the player is, after CanOpenMenu and the item Config.Start.require asks for. Use it with Config.Start.spawnPed = false to start runs from your own NPC, item or phone app. It waits for the server, so call it from a thread, a command or an event handler.
Contracts start from anywhere. The contract car spawns at the nearest Config.Vehicle.locations entry, and with teleport on the player is put in it there. The car deposit, its refund and the warehouse key are only handled within 15 m of a Config.Start.locations entry, so list the places you open it from there; elsewhere the car is handed out without its deposit, and buying a key does nothing.
RegisterCommand('launder', function()
exports.lation_laundering:OpenMenu()
end, false)IsLaundering
Client export
local running = exports.lation_laundering:IsLaundering()Returns
booleantruewhile the player has a buyer to meet
It goes false for a moment at the end of each deal, before the next buyer is marked, and stays false once the run stops.
GetData (client)
Client export
local data = exports.lation_laundering:GetData(type)Parameters
typestringoptionaldefaultnil- One field:
level,experience,contracts_completed,total_money_cleaned,pendingortime_remaining. The whole record when left out.
Returns
table | number- The record from the server, shaped like the server export's, or the value of
type.0when there is nothing to return.
It asks the server, so call it from a thread.
CreateThread(function()
local level = exports.lation_laundering:GetData('level')
print(('Laundering level %d'):format(level))
end)GetBalance
Client export
local amount = exports.lation_laundering:GetBalance(moneyType)Parameters
moneyTypestring- An item such as
markedbills, or an account:cash,bank, orblack_moneyon ESX
Returns
number- What the local player holds, counted the way buyers count it (with
Config.QBCoreon QBCore and Qbox).0when there is nothing.
It asks the server, so call it from a thread.
PoliceCount
Client export
local police = exports.lation_laundering:PoliceCount()Returns
number- Police in the city, counted as for
Config.Police.require: players on duty inConfig.Police.jobson QBCore and Qbox, every player with one of those jobs on ESX
GetTimeRemaining
Client export
local hours, minutes, seconds = exports.lation_laundering:GetTimeRemaining()Returns
number, number, number- The time left on the local player's wash.
0, 0, 0when no wash is running or it's done.
local h, m, s = exports.lation_laundering:GetTimeRemaining()
if h + m + s > 0 then
print(('Wash ready in %dh %dm %ds'):format(h, m, s))
endServer exports
| Export | Returns |
|---|---|
GetData(source, type?) | A player's record, or one field of it |
AddData(source, amount, dataType, action) | Nothing |
CheckLevel(source) | Nothing |
IsLaundering(source) | boolean |
GetData
Server export
local data = exports.lation_laundering:GetData(source, type)Parameters
sourcenumber- The player's server id
typestringoptionaldefaultnil- One field of the record. The whole record when left out.
Returns
table | any | nil- The record (below), or the value of
type.nilwhen the player isn't loaded in your framework.
The first time a player is looked up, their row is read from the lation_laundering table, or created at level 1. The record is the cached one, kept in memory until they leave, so treat it as read-only and change it with AddData. At the last level, experience reads as Config.LevelUp.
{
player_identifier = 'ABC12345', -- once read from the database
level = 3,
experience = 42000, -- XP earned at the current level
contracts_completed = 318,
total_money_cleaned = 412750, -- dirty money laundered, lifetime
pending = 0, -- dirty money in the washing machine
time_remaining = 0, -- milliseconds left on that wash
}local level = exports.lation_laundering:GetData(source, 'level')
if level and level >= 3 then
-- unlock something
endAddData
Server export
exports.lation_laundering:AddData(source, amount, dataType, action)Parameters
sourcenumber- The player's server id
amountnumber- The amount, 0 or more
dataTypestring- The field:
level,experience,contracts_completedortotal_money_cleaned actionstring'add'to addamount,'set'to replace the field with it
Returns
- Nothing
Changes the cached record. XP is refused at the last level. Adding XP doesn't level the player up on its own: call CheckLevel after it. Nothing is written straight away; the record is saved when the player leaves, when the resource stops and when txAdmin shuts the server down.
exports.lation_laundering:AddData(source, 5000, 'experience', 'add')
exports.lation_laundering:CheckLevel(source)CheckLevel
Server export
exports.lation_laundering:CheckLevel(source)Parameters
sourcenumber- The player's server id
Returns
- Nothing
Moves the player up a level for every Config.LevelUp of XP they hold, keeps the leftover, and sends the levelUp log. Does nothing at the last level. The player isn't told, and the warehouse blip waits for their next login or a key.
IsLaundering (server)
Server export
local running = exports.lation_laundering:IsLaundering(source)Parameters
sourcenumber- The player's server id
Returns
boolean- The player's client
IsLaundering
It asks the player's client and waits for the answer, so call it from a thread or an event handler.
Events
lation_laundering:Notify
Client event
TriggerClientEvent('lation_laundering:Notify', source, message, type)Parameters
messagestring- The text
typestringsuccess,error,warningorinform, as your notification system reads them
Send it from the server to show a notification through Config.Notify, under the script's Money Laundering title where the system shows one.
TriggerClientEvent('lation_laundering:Notify', source, 'The launderer wants a word', 'inform')Hooks
Hooks are plain functions in the files that ship open. Edit them in place.
Client hooks
In client/functions.lua.
| Hook | Called | Default |
|---|---|---|
CanOpenMenu() | Before the main menu opens, at the launderer or through OpenMenu | Refuses dead players |
CanBuyKey() | When a player picks Warehouse in the menu, before the confirm | Allows everyone |
CanEnterWarehouse() | When a player uses the warehouse door | The key, police and level checks |
StartedLaundering() | After each contract is assigned, so once per buyer | Empty |
StoppedLaundering() | When a run is stopped with Stop Contracts, or ends because the dirty money ran out | Empty |
EnteredWarehouse() | After a player is taken inside | Empty |
ExitedWarehouse() | After a player is taken back out | Empty |
StartedCounting() | When counting starts | Locks ox_inventory, qb-inventory or qs-inventory |
StoppedCounting() | When counting ends | Unlocks them |
CanOpenMenu
Client hook
CanOpenMenu = function() endReturns
booleanfalseto keep the menu closed
Tell the player why when you refuse, with ShowNotification(message, 'error').
CanBuyKey
Client hook
CanBuyKey = function() endReturns
booleanfalseto refuse the key
The file's example keeps keys to gang members, with a placeholder that lets everyone through:
CanBuyKey = function()
local inGang = true -- replace with your gang script's check
if not inGang then
ShowNotification('You must be a gang member to purchase this', 'error')
return false
end
return true
endCanEnterWarehouse
Client hook
CanEnterWarehouse = function() endReturns
booleanfalseto keep the player out
The default is the warehouse's whole entry check: the player needs Config.Items.key and, with Config.Warehouse.requireLvl, the unlock level; police jobs need neither while Config.Police.warehouseAccess is on. A refused player is told "You are not authorized to enter". Add your own conditions before its return, and keep the rest unless you mean to replace it.
Server hooks
In server/functions.lua. Both run before the money moves.
| Hook | Called with | When |
|---|---|---|
TaxPaid(source, amount, type) | The tax kept, and 'contracts' or 'warehouse' | At every deal, and when a wash is picked up |
MoneyLaundered(source, amount, type) | The clean cash paid for a deal, or the uncounted money a wash gives, and 'contracts' or 'warehouse' | The same moments |
function TaxPaid(source, amount, type)
-- the tax goes to a job's account in lation banking
exports.lation_banking:AddAccountMoney('government', amount, 'Laundering tax')
endIntegrations
Each system the config lets you pick is a function in client/functions.lua, with a branch to fill in for 'custom':
| To change | Edit |
|---|---|
Notifications (Config.Notify = 'custom') | ShowNotification(message, type) |
Progress bars (Config.Progress = 'custom') | ProgressBar(data), which returns true when it completes |
| Text prompts | The textui setting at the top, and ShowTextUI(text, icon) and HideTextUI(label) |
A target (Config.Target = 'custom') | AddCircleZone, AddTargetEntity, RemoveCircleZone and RemoveTargetEntity |
Dispatch (Config.Police.dispatchSystem = 'custom') | PoliceDispatch(dispatch), given coords and street |
Phone (Config.Phone = 'custom') | SendPhoneMessage(), and the lation_laundering:phoneMessage handler in server/functions.lua |
| Fuel and keys for the contract car | SetFuel(vehicle) and GiveKeys(vehicle) |
| Item checks on the client | HasItem(item, amount) and ItemCount(item) |
Frameworks live in bridge/client.lua and bridge/server.lua: each function has a branch marked for a custom framework.
Still stuck? Ask in our Discord: the people who write the scripts answer.
Last updated