Interaction Menu
Description
A standalone raycast-based interaction menu for FiveM, designed to enhance your interaction with the environment on your FiveM server.
The primary goal is not to replace target scripts; instead, it can be used simultaneously to enhance script interactions within the FiveM. Nonetheless, the main factor is not just features it's because this script uses sprites and DUI, making it more demanding on resources compared to NUI-based scripts.
WIP project
Since this is a WIP project, some of these exports, features might change or removed in future.
Make sure to review them whenever the script is updated.
Preview
Usage
create()
To create all types of interactions we're just going to use create()
(or Create()
) to generate the interactions.
- We achieve this by using a
type
field in each interaction menu. - The nice thing is, script will automatically detect the type and there's no need for us to type it.
exports['interactionMenu']:create ( { Properties } )
exports['interactionMenu']:Create ( { Properties } )
List Of Properties (create)
Property | Type | Description |
---|---|---|
id | String | A unique identifier assigned to the menu. (If not provided, script will assign a random id) |
type | String | Type of interaction (auto assign) |
theme | String | Theme name (default, red, green, yellow) |
glow | Boolean | Whether the menu has a glowing border around it |
suppressGlobals | Boolean | Control the visibility of other menus, ignoring other menus |
static | Boolean | If the entity is static and its position remains unchanged, using this option can have a better performance |
zone | Vector3 | Defines the zone-based interaction |
position | Vector3 | Defines the position-based interaction |
rotation | Vector3 | Defines the rotation of interaction menu (3d) (works while either position or zone present) |
entity | String | Defines an interaction based on a specific entity |
model | String | Defines an interaction on all entities of the same model |
vehicle | String | Used in interactions within vehicles |
bone | String | Defines an interactions within a vehicle parts (e.g. wheels) |
maxDistance | Table | The maximum distance at which the menu is accessible |
extra | Table | Additional properties for the menu |
indicator | Table | Menu indicator |
options | Table | List of options available in the menu |
Automatic dark theme
The interaction menu features an automatic dark theme selection based on the time of day.
- This is implemented to address visibility issues with the default theme, especially when there is too much light.
Light Theme | Dark Theme |
triggers
By using events or actions we can trigger part of our code when player interacted with and specifid option in our interaction menu.
action
Property | Type | Description |
---|---|---|
type | String | Type of action. Possible values: sync ,async |
func | Function | The action function |
sync
actions will put the interaction menu into loading/exuting mode and won't be usable till out action is finished.
-- other options
{
label = 'Drink',
action = {
type = 'sync',
func = function(entity, distance, coords, name, bone)
print(entity, distance, coords, name, bone)
end
}
}
-- other options
event
Property | Type | Description |
---|---|---|
type | String | Type of action. Possible values: client ,server |
name | String | Event name |
payload | Any (except function) | Payload |
-- other options
{
label = 'Drink',
event = {
type = 'client',
name = 'test:event:drink',
payload = 'something'
}
}
-- other options
AddEventHandler('test:event:drink', function(payload, data)
print(payload) -- 'something'
Util.print_table(data)
-- data => {
-- entity,
-- distance,
-- coords,
-- name,
-- bone
-- }
end)
zone
So, imagine you're in a game world, right? Now, think of a "zone" as an invisible area within that world. This zone is there to define certain rules or trigger certain actions when players or objects enter or leave it.
Property | Type | Description |
---|---|---|
type | String | Type of zone. Possible values: boxZone ,entityZone , circleZone , polyZone , comboZone |
name | String | The name of the zone |
position | Object | The position of the zone |
heading | Number | The heading of the zone |
width | Number | The width of the zone |
length | Number | The length of the zone. |
debugPoly | Boolean | Whether to display debug zone |
minZ | Number | The minimum Z coordinate of the zone |
maxZ | Number | The maximum Z coordinate of the zone |
Types
At the current version v0.5.0, the only acceptable type is boxZone
. Other types are still in the works, so you can't use them just yet.
But hey, the boxZone is pretty cool too, right?
box zone + rotation = 3d |
exports['interactionMenu']:Create {
id = 'ZoneTest',
rotation = vector3(-40, 0, 240),
position = vector4(-1965.7, 3188.65, 32.81, 58.08),
scale = 1,
zone = {
type = 'boxZone',
name = "onZoneTest",
position = p,
heading = p.w,
width = 4.0,
length = 6.0,
debugPoly = true,
minZ = p.z - 1,
maxZ = p.z + 1,
},
options = { ... }
}
static
The static menus won't update the entities's positions on runtime, even though this will offer better performance, choose it carefully and make sure those entities won't move.
static = false (Default) |
static = true |
glow
You can choose to make the menu have a glowing border.
glow = true |
exports['interactionMenu']:Create {
...
glow = true --
...
}
Indicator glow
Indicators can have a glow effect to.
glow = true |
exports['interactionMenu']:Create {
...
indicator = {
prompt = 'Press Enter',
glow = true --
},
...
}
suppressGlobals
You have the option to create a unique menu on the entity and remove all global menus.
suppressGlobals = false | suppressGlobals = true |
indicator
Menus can have custom indicators with custom key triggers that players must press to initiate actions and events.
- The default trigger is
E
Property | Type | Description |
---|---|---|
prompt | String | Prompt string |
glow | Boolean | Glow effect |
keyPress | keyPress | Controls used to trigger events and actions |
keyPress
Check these two links for more information Controls Reference, Natives Reference
Property | Type | Description |
---|---|---|
padIndex | String | The control system instance to use |
control | Table | The control ID to check |
Example
exports['interactionMenu']:Create {
...
indicator = {
prompt = 'Custom indicator',
keyPress = {
padIndex = 0,
control = 38 -- press E
}
},
...
}
extra
Property | Type | Description |
---|---|---|
onSeen | String | Triggers the onSeen function upon seeing the menu |
onExit | Table | Triggers the onExit function upon exiting the menu |
onTrigger | Table | Triggers the onTrigger function upon triggering the triggers |
job | Table | Job check (currently not working) |
Example
exports['interactionMenu']:Create {
...
extra = {
onSeen = function(data)
print('onSeen')
end,
onExit = function(data)
print('onExit')
end,
onTrigger = function(data)
print('onTrigger')
end
},
...
}
Create an interaction on a specific position.
FOV
There's an optimization layer with a field of view
function placed in script, meaning script is going to show up only when we are directly looking at it.
local controlPoint = vector4(-1996.67, 3155.48, 31.81, 95.00) -- vector 3 is enough
local menuId = exports['interactionMenu']:Create {
id = 'uniqueId',
type = 'position', -- technically, we don't need it
position = controlPoint,
maxDistance = 2.0,
options = { ... } -- Replace this with your actual options
}
print(menuId) -- uniqueId
option
Here we describe each property that you can use in each option to create interactions on each menu.
Property | Type | Description |
---|---|---|
label | String | Label |
icon | String | Optional icon (fontawesome) |
canInteract | Function | reutrn false to hide the option |
action | Table | Action triggered after the option is selected by the player |
event | Table | Event triggered after the option is selected by the player |
video | Table | Video property |
style | Table | Custom style |
progress | Table | Show a progress bar |
bind | Table | Bind the value of progress to a function |
picture | Table | Show a picture |
dynamic | Boolean | Set the option as dynamic (value can be updated in runtime) |
Let's create a simple menu as an example and continue working on that.
Here we'll spawn a new entity and add an interaction to it.
Globals
The first option (Debug) is a test/debug option, which I'll discuss in the globals section.
Example
local controlPoint = vector4(-1996.67, 3155.48, 31.81, 95.00)
local ent = Util.spawnObject(`prop_watercooler`, controlPoint)
exports['interactionMenu']:Create {
entity = ent,
maxDistance = 2.0,
options = {
{
label = 'Drink',
event = {
type = 'client',
name = 'test:event:drink',
payload = 'something'
}
}
}
}
By using actions
:
Actions
The type
can be either sync
or async
. To use the sync
function, follow this format:
- Please note that this format is subject to change.
- Interactions have to wait until the execution of functions is finished, which is not ideal as it uses system resources.
action = {
type = 'sync',
name = function(entity, distance, coords, name, bone)
print(entity, distance, coords, name, bone)
end
}
local controlPoint = vector4(-1996.67, 3155.48, 31.81, 95.00)
local ent = Util.spawnObject(`prop_watercooler`, controlPoint)
exports['interactionMenu']:Create {
entity = ent,
maxDistance = 2.0,
options = {
{
label = 'Drink',
action = {
type = 'sync',
name = function(entity, distance, coords, name, bone)
print(entity, distance, coords, name, bone)
end
}
}
}
}
canInteract
A function which you can use to hide options.
Example
-- other options
{
label = 'Drink',
style = {
color = {
label = 'rgb(0, 255, 0)',
}
},
event = {
type = 'client',
name = 'test:event:drink',
payload = 'something'
},
canInteract = function(entity, distance, coords, name, bone)
return true -- show option
end
}
-- other options
style
These properties are style properties we can use on each individual option.
Property | Type | Description |
---|---|---|
color.background | String | Background color |
color.label | String | Label color |
color.labelSelected | String | Selected label color |
color.backgroundSelected | String | Background color when selected |
text.labelFontSize | String | Font size for labels |
Example
-- other options
{
label = 'Drink',
style = {
color = {
label = 'rgb(0, 255, 0)',
}
},
event = {
type = 'client',
name = 'test:event:drink',
payload = 'something'
}
}
-- other options
video
Each option can serve a role more than triggering actions and events. Here, we want to just display a video to players when they see the menu.
Property | Type | Description |
---|---|---|
url | String | URL of the video |
opacity | Number | Opacity of the video |
currentTime | Number | Current playback time |
autoplay | Boolean | Autoplay the video |
loop | Boolean | Loop the video |
progress | Boolean | Show progress bar |
percent | Boolean | Show percentage progress |
timecycle | Boolean | Enable time cycle |
volume | Number | Volume level |
Example
-- other options
{
video = {
url = 'http://127.0.0.1:8080/TEST VIDEO.mp4',
volume = 0,
currentTime = 100,
progress = true,
autoplay = true,
loop = true
}
},
-- other options
WARNING
We can't use YouTube and other streaming services. Only videos hosted on servers with streaming enabled. You can host them on your game server or use FiveM to load them within your scripts and display videos locally.
picture
Same as the previous option where we could show videos, we can also display images. Here are the properties we can use:
Property | Type | Description |
---|---|---|
url | String | URL of the picture |
opacity | Number | Opacity of the picture |
width | Number | Width of the picture |
height | Number | Height of the picture |
border | BorderType | Type of border around picture (dash, solid, double, none) |
Example
-- other options
{
picture = {
url = 'http://127.0.0.1:8080/00221-1775208258.png'
}
},
-- other options
progress
You can display a progress bar within the menu to represent values between 0-100. Also, you have the option to bind a function and update the value from there.
- The progress value is updated whenever bound value changes.
Property | Type | Description |
---|---|---|
type | String | Type of progress |
value | Number | Value of the progress |
percent | Boolean | Show percentage progress |
Example
-- other options
{
label = "Health",
progress = {
type = "info",
value = 0,
percent = true
},
bind = {
func = function(entity, distance, coords, name, bone)
return GetEntityHealth(entity) / 10
end
}
},
-- other options
createGlobal()
Global Interactions, Here, instead of using create()
, we use createGlobal()
(or CreateGlobal()
) to create global interactions.
- Global interactions can be applied to various types such as
peds
,player
,entities
,bones
, andvehicles
.
exports['interactionMenu']:createGlobal { Properties }
exports['interactionMenu']:CreateGlobal { Properties }
TIP
Properties are essentially the same properties we have in create()
.
exports['interactionMenu']:createGlobal {
type = 'entities',
offset = vec3(0, 0, 0),
maxDistance = 1.0,
options = {
{
label = '[Debug] On All Entities',
icon = 'fa fa-bug',
action = {
type = 'sync',
func = function(entity, distance, coords, name, bone)
end
}
}
}
}
exports['interactionMenu']:createGlobal {
type = 'peds',
offset = vec3(0, 0, 0),
maxDistance = 1.0,
options = {
{
label = '[Debug] On All Peds',
icon = 'fa fa-person',
action = {
type = 'sync',
func = function(entity, distance, coords, name, bone)
end
}
}
}
}
exports['interactionMenu']:createGlobal {
type = 'vehicles',
offset = vec3(0, 0, 0),
maxDistance = 1.0,
options = {
{
label = '[Debug] On All Vehicles',
icon = 'fa fa-car',
action = {
type = 'sync',
func = function(entity, distance, coords, name, bone)
end
}
}
}
}
exports['interactionMenu']:createGlobal {
type = 'players',
offset = vec3(0, 0, 0),
maxDistance = 1.0,
options = {
{
label = '[Debug] On All Players',
icon = 'fa fa-person',
action = {
type = 'sync',
func = function(entity, distance, coords, name, bone)
end
}
}
}
}
exports['interactionMenu']:createGlobal {
type = 'bones',
bone = 'platelight',
offset = vec3(0, 0, 0),
maxDistance = 1.0,
options = {
{
label = '[Debug] On All plates',
icon = 'fa fa-rectangle-ad',
action = {
type = 'sync',
func = function(entity, distance, coords, name, bone)
end
}
}
}
}
set()
With set()
or setValue()
function, you'll be able to adjust various properties of a menu at runtime.
For example, you can hide a menu, change its position (position-based menus), update labels, or modify progress value.
Property | Type | Description |
---|---|---|
menuId | String | The ID of the menu to be modified |
type | String | The type of property to be modified ('hide' , 'position' , 'label' , or 'progress' ) |
option | Number | (Optional) The index of the option within the menu to be modified |
value | any | The new value to be set for the specified property |
local id = exports['interactionMenu']:Create {
entity = ent2,
offset = vec3(0, 0, 1),
options = {
{
label = "Above Menu",
icon = "fas fa-heartbeat",
}
}
}
if this is our menu we can use its id
and hide
it by updating hide
peropety of the menu.
exports['interactionMenu']:set {
menuId = id,
type = 'hide',
value = true
}
However, if we only want to hide a single option, we can provide the option
, which will hide that specific option for us.
exports['interactionMenu']:set {
menuId = id,
option = 2,
type = 'hide',
value = true
}
We can also update the position of the menu, label, and progress value of options.
Dynamic
To update the progress or label value, option must be a dynamic
.
{
label = "Health",
icon = "fas fa-heartbeat",
dynamic = true, --
progress = {
type = "error",
value = 50,
percent = true
}
},
For example, updating the label:
also, we can update the position of position-based interactions too
exports['interactionMenu']:set {
menuId = id,
type = 'position',
value = vector4(-1981.69, 3188.51, 32.81, 111.91)
}
remove()
After adding numerous menus, it's always beneficial to remove some of those. In such cases, we can utilize the remove
function to delete an interaction by using its id
.
exports['interactionMenu']:remove(id)
Property | Type | Description |
---|---|---|
id | String | The ID of the menu to be modified |
Support
If you need any help or have any questions about our products, please join our discord channel: https://discord.gg/ccMArCwrPV