OrigenNetwork
Docs

Custom · origen_phone

text
custom/
├── shared/
│   └── custom.lua
├── client/
│   └── notifications.lua
├── server/
│   ├── money.lua
│   └── permissions.lua
└── bridge/
    ├── banking/server.lua
    ├── business/server.lua
    ├── dispatch/server.lua
    ├── framework/client.lua
    ├── framework/server.lua
    ├── fuel/client.lua
    ├── garage/server.lua
    ├── housing/server.lua
    ├── inventory/client.lua
    ├── inventory/server.lua
    ├── notify/client.lua
    ├── uploader/server.lua
    ├── vehiclekeys/client.lua
    ├── voice/client.lua
    ├── voice/server.lua
    └── weather/server.lua

Everything in custom/ is listed in escrow_ignore, so it stays open when the resource is encrypted, and it is separate from the core, so phone updates do not overwrite it. The provider files in bridge/ are also open: you can read them or copy one as a starting point.

Every Custom hook returns nil by default, which means "do the default thing". Return anything else and your code takes over.


custom/client/notifications.lua

Custom.ShowNotification(data) → boolean|nil

How a notification looks while the phone is put away. By default the phone peeks up from the bottom of the screen. With the phone open, notifications always show inside it.

ParameterTypeDescription
datatable{ app, title, content, avatar, thumbnail }
ReturnBehaviour
nilThe phone peeks up as usual
trueYou already showed it; the phone does nothing

Use origen_notify instead of the phone:

lua
function Custom.ShowNotification(data)
    exports.origen_notify:ShowNotification(data.content, 'info', 5000)
    return true
end

Only calls and messages use the phone, the rest your notify:

lua
function Custom.ShowNotification(data)
    if data.app == 'phone' or data.app == 'messages' then
        return nil
    end
 
    exports.origen_notify:ShowNotification(data.content, 'info', 5000)
    return true
end

The hook runs inside pcall. If it errors, the error is printed and the phone shows the notification as usual.


custom/server/money.lua

Every charge and payout the phone makes goes through here: ads, taxi fares, bills, transfers, dating subscriptions, line recovery. By default the phone uses the Banking bridge.

Custom.Charge(source, amount, reason, account) → boolean|nil, string|nil

ParameterTypeDescription
sourcenumberPlayer server ID
amountnumberAlways positive
reasonstringAlready translated to the player's language
accountstring|nilAccount the player picked, if the app lets them
ReturnBehaviour
nilCharge through the banking bridge
trueCharged, continue
false, 'reason'Failed; the text is shown to the player

Charge cash instead of bank (Qbox):

lua
function Custom.Charge(source, amount, reason)
    local player = exports.qbx_core:GetPlayer(source)
    if not player then return false, 'Player not found' end
 
    if player.Functions.RemoveMoney('cash', amount, reason) then
        return true
    end
 
    return false, 'You do not have enough cash'
end

Free ads for reporters:

lua
function Custom.Charge(source, amount, reason)
    local job = Framework.GetJob(source)
    if reason:find('Ad') and job and job.name == 'reporter' then
        return true
    end
 
    return nil
end

Custom.Deposit(source, amount, reason) → boolean|nil, string|nil

Pays a player: driver and courier earnings, marketplace sales and refunds. Same return rules as Custom.Charge.

ParameterTypeDescription
sourcenumberPlayer server ID
amountnumberAmount to pay
reasonstringAlready translated to the player's language

custom/server/permissions.lua

Custom.CanUseApp(source, appId) → boolean|nil

Your own veto on an app, for conditions that do not fit a panel rule. It runs after the panel rules and can only take access away, never grant it.

ParameterTypeDescription
sourcenumberPlayer server ID
appIdstring'bank', 'darkchat', 'blackmarket'...
ReturnBehaviour
nilWhatever the panel says
falseDenied, no matter what
lua
function Custom.CanUseApp(source, appId)
    if appId == 'bank' and MyResource.IsJailed(source) then
        return false
    end
 
    return nil
end

This runs every time a phone opens, once per app (over thirty times in a row). Do not query the database here: keep what you need in memory.


custom/bridge

Every category has a template with the exact functions it needs. Fill it in and set the category to 'custom' in config/bridge.lua:

lua
BridgeConfig.Banking = 'custom'

Each template registers itself with Bridge.Register(category, 'custom', { ... }) and is only used when its category is set to 'custom'.


custom/bridge/framework

Server

lua
Bridge.Register('Framework', 'custom', {
    RegisterUsableItem = function(name, cb) end,
    GetPlayer = function(source) return nil end,
    Identifier = function(player) return nil end,
    Name = function(player) return nil end,
    Job = function(player) return nil end,
    Gang = function(player) return nil end,
})
MethodReturnsDescription
RegisterUsableItem(name, cb)Registers the phone item as usable. cb(source, item)
GetPlayer(source)table|nilYour framework's player object
Identifier(player)string|nilStable character identifier (phone owner)
Name(player)string|nil'Firstname Lastname'
Job(player)table|nil{ name, label, type, grade, gradeLabel, onduty }
Gang(player)table|nil{ name, label, grade, gradeLabel, isBoss }

Client

lua
Bridge.Register('Framework', 'custom', {
    PlayerLoadedEvent = nil,
    IsDead = function() return false end,
    IsCuffed = function() return false end,
})
FieldReturnsDescription
PlayerLoadedEventstring|nilClient event your framework fires once the character is loaded
IsDead()booleanDead or downed: the phone cannot be used
IsCuffed()booleanCuffed: the phone cannot be used

custom/bridge/inventory

Server

MethodReturnsDescription
Items(source, name)table[]|nil{ { slot, metadata } } for that item. nil when the inventory is not loaded yet
SetMetadata(source, slot, meta)Writes metadata to the item in that slot
All(source)table[]Every item: { name, label, count, slot, metadata }
Remove(source, name, count)booleanRemoves items
Add(target, name, count, meta)booleanAdds items to a player source or a stash id
CanCarry(source, name, count)booleanWhether the player can carry the items

Client

MethodReturnsDescription
Items()table[]|nilThe local player's items, each with name and metadata

custom/bridge/banking/server.lua

MethodReturnsDescription
Accounts(citizenid)table[]{ { id, name, type, balance } }
Charge(source, amount, reason)boolean, string|nilSuccess, and the error shown to the player
Deposit(source, amount, reason)booleanPays the player
Transactions(accountId)table[]{ { id, amount (negative = outgoing), description, type, createdAt (ms) } }
AddToSociety(account, amount, reason)booleanDeposits a paid bill into the society or business account that issued it
lua
Bridge.Register('Banking', 'custom', {
    Charge = function(source, amount, reason)
        return exports.my_bank:RemoveMoney(source, amount, reason) == true
    end,
    -- ...
})

custom/bridge/business/server.lua

Every method is optional: leave out what your business resource does not support.

MethodReturnsDescription
BusinessesOf(citizenid)table[]{ { id, name, label, type, onDuty } } businesses this character works for
JobList()table[]{ { name, label } } business job names, for the admin panel
DeliverOrder(businessId, orderId)booleanDelivers a supply order. false when the order no longer exists
AddIncome(businessId, amount, kind, source, meta)booleanAdds income to the business till
AddExpense(businessId, amount, kind, source, meta)Registers an expense

custom/bridge/dispatch/server.lua

MethodReturnsDescription
SendAlert(alert)booleanalert: { coords (vector3), title, message, job, source, number }. number is nil when anonymous
CentralPlayers(job)number[]|nilSources answering that job's central. nil counts on-duty players instead

custom/bridge/garage/server.lua

MethodReturnsDescription
List(citizenid)table[]{ { plate, model, garage, state, fuel, engine, body, depotPrice } }. state: 0 out, 1 stored, 2 impounded. engine and body are 0 to 100
Find(citizenid, plate)table|nil{ plate, model, mods (vehicle properties JSON), label, garage, stored (boolean) }
Locate(garage)table|nil{ label, coords = { x, y } }
TakeOut(plate)booleanMarks the vehicle as out of the garage (valet)

custom/bridge/housing/server.lua

MethodReturnsDescription
List(citizenid)table[]{ { id, name, mine (boolean), price, kind, keys (count), where = { x, y, z } } }
Get(id, citizenid)table|nilSame shape as each List entry
HasKey(citizenid, id)booleanWhether the character has a key to that property

custom/bridge/uploader/server.lua

MethodReturnsDescription
Upload(name, base64)string|nil url, string|nil errorUploads a file. name includes the extension
Capture(source, options)string|nil url, string|nil errorCaptures the player's screen. options: { maxWidth, maxHeight }

custom/bridge/voice

Server

MethodReturnsDescription
SetCall(source, channel)Puts the player in a call channel. 0 leaves every call
GetCall(source)numberCurrent call channel, 0 when in none

Client

MethodReturnsDescription
SetCallVolume(volume)Call volume, 5 to 100
RestoreCallEffect(serverId)Restores the call voice effect for that player

custom/bridge/weather/server.lua

MethodReturnsDescription
Get()table|nil{ weather (GTA weather type, e.g. 'CLEAR'), remaining (ms, optional), blackout, hour, minute }

custom/bridge/notify/client.lua

lua
Bridge.Register('Notify', 'custom', {
    Send = function(message, kind)
        lib.notify({ description = message, type = kind == 'info' and 'inform' or kind })
    end,
})
ParameterTypeDescription
messagestringNotification text
kindstring'info', 'success', 'error' or 'warning'

custom/bridge/vehiclekeys/client.lua

lua
Bridge.Register('VehicleKeys', 'custom', {
    GiveKeys = function(vehicle, plate)
    end,
})

Called by the valet when it delivers a vehicle. vehicle is the entity handle.


custom/bridge/fuel/client.lua

lua
Bridge.Register('Fuel', 'custom', {
    SetFuel = function(vehicle, level)
        SetVehicleFuelLevel(vehicle, level + 0.0)
    end,
})

Called by the valet to restore the stored fuel level (0 to 100).