24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Blip Manager: Blips with info popups
2 |
3 | #### ⭐ Check out our other resources on [gamzkystore.com](https://gamzkystore.com/) or in our [Discord](https://discord.com/invite/sjFP3HrWc3).
4 | #### 📼 Preview video: [Streamable](https://streamable.com/la3460)
5 |
6 | ## Features
7 | - Create blips with properties such as coordinates, sprite, label, and more.
8 | - Update and delete existing blips.
9 | - Handle blip overlays that provide additional information when hovering over or selecting blips.
10 | - Automatically clean up blips when the resource is stopped.
11 | - Assign blips to categories to group them together. (Max 2 unique categories, as per limit of GTA 5)
12 |
13 | ## Installation
14 | 1. Place the script in your resource directory.
15 | 2. Ensure your resource is started in the server configuration.
16 | 3. Use the provided exports to interact with the blip system in your other scripts.
17 |
18 | ## Documentation
19 |
20 | ### `exports.gs_blips:CreateBlip(params)`
21 | Creates a new blip on the map.
22 | #### Parameters
23 | - `params`: A table containing:
24 | - `coords` (required): Coordinates for the blip. Preferably a vector3, but any coordinate type will work.
25 | - `sprite` (required): The sprite ID for the blip. See [here](https://docs.fivem.net/game-references/blips/) for a list of sprites.
26 | - `label` (required): The label for the blip.
27 | - `scale` (optional): The scale of the blip (default is `1.0`).
28 | - `color` (optional): The color of the blip (default is `0`). See [here](https://docs.fivem.net/docs/game-references/blips/#blip-colors) for a list of colors.
29 | - `category` (optional): The category label of the blip.
30 | - `data` (optional): A table containing additional data for the blip.
31 | - `title` (optional): The title of the blip.
32 | - `description` (optional): The description of the blip.
33 | - `display` (optional): Display option for the blip (default is `4`).
34 |
35 | #### Returns
36 | A blip object with methods to modify or delete the blip.
37 |
38 | ### Methods of Blip Object
39 | - `setData(newData)`: Updates the data associated with the blip.
40 | - `setTitle(title)`: Sets the title of the blip.
41 | - `setDescription(description)`: Sets the description of the blip.
42 | - `setDisplayHandler(fn)`: Sets a custom display function for the blip.
43 | - `delete()`: Deletes the blip from the map.
44 |
45 | ## Usage Examples
46 | ```lua
47 | -- Example blip with default properties
48 | exports.gs_blips:CreateBlip({
49 | coords = vector3(436.28, -993.07, 43.69),
50 | sprite = 60,
51 | scale = 1.3,
52 | color = 38,
53 | label = 'Police Station',
54 | data = {
55 | title = '👮🏽 Police Station',
56 | description = 'Mission Row Police Station is a key center for city police operations, equipped with holding cells, offices, and an impound lot.',
57 | },
58 | })
59 | ```
60 |
61 | ```lua
62 | -- Example blip with display handler
63 | local blip = exports.gs_blips:CreateBlip({
64 | coords = vector3(189.69, -937.84, 30.69),
65 | sprite = 306,
66 | scale = 1.5,
67 | color = 5,
68 | label = 'Example blip',
69 | data = {
70 | title = 'Example blip',
71 | description = '',
72 | },
73 | })
74 |
75 | -- Example: Sets a random number each time the info box is opened
76 | blip.setDisplayHandler(function()
77 | blip.setDescription('Random number: ' .. math.random(1, 100))
78 | end)
79 |
80 | -- Example: Delete the blip
81 | blip.delete()
82 | ```
83 |
84 | ```lua
85 | -- Example: Create blips with a category
86 | for i = 1, 10 do
87 | exports.gs_blips:CreateBlip({
88 | coords = vector3(i * 50, i * 50, i * 50),
89 | sprite = 1,
90 | scale = 1.0,
91 | color = 0,
92 | label = 'Example blip: #' .. i,
93 | category = 'Example category',
94 | data = {
95 | title = 'Example blip',
96 | description = 'This blip is grouped under the "Example category" category.',
97 | },
98 | })
99 | end
100 | ```
101 |
102 | ```lua
103 | -- Delete a blip created with the CreateBlip export
104 | exports.gs_blips:DeleteBlip(blipHandle)
105 |
106 | -- Example: Get a blip object
107 | local blip = exports.gs_blips:GetBlip(blipHandle)
108 | ```
109 |
--------------------------------------------------------------------------------
/client/cl_main.lua:
--------------------------------------------------------------------------------
1 | local createdBlips = {}
2 | local blipData = {}
3 | local currentBlip = nil
4 | local isOverlayOpen = false
5 | local blipIndex = 0
6 |
7 | local blipCategories = {
8 | { id = 'BLIP_PROPCAT', index = 10, label = nil },
9 | { id = 'BLIP_APARTCAT', index = 11, label = nil },
10 | }
11 |
12 | function CreateBlip(params)
13 | local invokingResource = GetInvokingResource()
14 |
15 | -- Validate params
16 | if not params.coords then
17 | print(('^1[%s] Error while creating blip: coords is required'):format(invokingResource))
18 | return
19 | end
20 | if not params.sprite then
21 | print(('^1[%s] Error while creating blip: sprite is required'):format(invokingResource))
22 | return
23 | end
24 | if not params.label then
25 | print(('^1[%s] Error while creating blip: label is required'):format(invokingResource))
26 | return
27 | end
28 |
29 | local coords = ParseCoords(params.coords)
30 | local sprite = params.sprite
31 | local scale = params.scale or 1.0
32 | local color = params.color or 0
33 | local label = params.label
34 | local data = params.data or { title = label }
35 | local display = params.display or 4
36 |
37 | local blipHandle = AddBlipForCoord(coords.x, coords.y, coords.z)
38 |
39 | SetBlipSprite(blipHandle, sprite)
40 | SetBlipScale(blipHandle, scale)
41 | SetBlipColour(blipHandle, color)
42 | SetBlipDisplay(blipHandle, display)
43 | SetBlipAsShortRange(blipHandle, true)
44 | SetBlipHighDetail(blipHandle, true)
45 | SetBlipAsMissionCreatorBlip(blipHandle, true)
46 |
47 | -- Set blip category
48 | if params.category then
49 | local categoryIndex = AssignLabelToCategory(params.category)
50 | if categoryIndex then
51 | SetBlipCategory(blipHandle, categoryIndex)
52 | else
53 | print(('^1[%s] Error while creating blip: No available category for label %s'):format(invokingResource, params.category))
54 | end
55 | end
56 |
57 | blipIndex = blipIndex + 1
58 | local blipTextEntry = 'GS_BLIP_' .. blipIndex
59 | AddTextEntry(blipTextEntry, label)
60 | BeginTextCommandSetBlipName(blipTextEntry)
61 | AddTextComponentSubstringPlayerName('me')
62 | EndTextCommandSetBlipName(blipHandle)
63 |
64 | if data then
65 | blipData[blipHandle] = data
66 | end
67 |
68 | createdBlips[invokingResource] = createdBlips[invokingResource] or {}
69 | table.insert(createdBlips[invokingResource], blipHandle)
70 |
71 | local blipObject = {
72 | handle = blipHandle,
73 |
74 | setData = function(newData)
75 | blipData[blipHandle] = newData
76 | UpdateBlipOverlay(blipHandle)
77 | end,
78 |
79 | setTitle = function(title)
80 | blipData[blipHandle].title = title
81 | UpdateBlipOverlay(blipHandle)
82 | end,
83 |
84 | setDescription = function(description)
85 | blipData[blipHandle].description = description
86 | UpdateBlipOverlay(blipHandle)
87 | end,
88 |
89 | setDisplayHandler = function(fn)
90 | blipData[blipHandle].onDisplay = fn
91 | end,
92 |
93 | delete = function()
94 | DeleteBlip(blipHandle)
95 | end,
96 | }
97 |
98 | return blipObject
99 | end
100 |
101 | function DeleteBlip(blipHandle)
102 | if blipData[blipHandle] then
103 | blipData[blipHandle] = nil
104 | end
105 | if DoesBlipExist(blipHandle) then
106 | RemoveBlip(blipHandle)
107 | end
108 | end
109 |
110 | function GetBlip(blipHandle)
111 | return blipData[blipHandle]
112 | end
113 |
114 | function ParseCoords(input)
115 | local inputType = type(input)
116 |
117 | -- Convert table to vector3
118 | if (inputType == 'table') then
119 | if input.x and input.y and input.z then
120 | return vector3(input.x, input.y, input.z)
121 | else
122 | return vector3(input[1], input[2], input[3])
123 | end
124 | end
125 |
126 | return input
127 | end
128 |
129 | function AssignLabelToCategory(label)
130 | for _, category in ipairs(blipCategories) do
131 | if (category.label == label) then
132 | -- If the label already matches, use this category
133 | return category.index
134 | elseif (category.label == nil) then
135 | -- If the category is available (no label assigned), use it and set the label
136 | category.label = label
137 | AddTextEntry(category.id, label)
138 | return category.index
139 | end
140 | end
141 |
142 | -- No available category
143 | return nil
144 | end
145 |
146 | CreateThread(function()
147 | while true do
148 | Wait(100)
149 | if IsFrontendReadyForControl() then
150 | if IsHoveringOverMissionCreatorBlip() then
151 | local blipHandle = GetNewSelectedMissionCreatorBlip()
152 | if DoesBlipExist(blipHandle) then
153 | if currentBlip ~= blipHandle then
154 | currentBlip = blipHandle
155 | if blipData[blipHandle] then
156 | ShowBlipOverlay(blipHandle)
157 | if blipData[blipHandle].onDisplay then
158 | blipData[blipHandle].onDisplay()
159 | end
160 | else
161 | HideBlipOverlay()
162 | end
163 | end
164 | end
165 | else
166 | if currentBlip then currentBlip = nil end
167 | if isOverlayOpen then HideBlipOverlay() end
168 | end
169 | end
170 | end
171 | end)
172 |
173 | AddEventHandler('onClientResourceStop', function(resourceName)
174 | if resourceName == GetCurrentResourceName() then return end
175 |
176 | if createdBlips[resourceName] then
177 | -- Delete blips created by this resource
178 | for i = 1, #createdBlips[resourceName] do
179 | local blipHandle = createdBlips[resourceName][i]
180 | if DoesBlipExist(blipHandle) then
181 | RemoveBlip(blipHandle)
182 | end
183 | end
184 |
185 | createdBlips[resourceName] = nil
186 | end
187 | end)
188 |
189 | function ShowBlipOverlay(blipHandle)
190 | isOverlayOpen = true
191 | SendNUIMessage({ action = 'show', data = blipData[blipHandle] })
192 | end
193 |
194 | function HideBlipOverlay()
195 | isOverlayOpen = false
196 | SendNUIMessage({ action = 'hide' })
197 | end
198 |
199 | function UpdateBlipOverlay(blip)
200 | SendNUIMessage({ action = 'update', data = blipData[blip] })
201 | end
202 |
203 | -- Exports
204 | exports('CreateBlip', CreateBlip)
205 | exports('DeleteBlip', DeleteBlip)
206 | exports('GetBlip', GetBlip)
207 |
--------------------------------------------------------------------------------