Copy
return {
-- ⚠️ WARNING: When you are working with this script, never do "restart lation_diving"
-- ⚠️ This will cause issues, data loss & more! You must restart the script like this:
-- ⚠️ "stop lation_diving" ..wait a couple seconds.. then "ensure lation_diving"
----------------------------------------------
-- 🛠️ Setup the basics below
----------------------------------------------
setup = {
-- Use only if needed, directed by support or know what you're doing
-- Notice: enabling debug features will significantly increase resmon
-- And should always be disabled in production
debug = false,
-- Set your interaction system below
-- Available options are: 'ox_target', 'qb-target', 'interact' & 'custom'
-- 'custom' needs to be added to client/functions.lua
-- We also provide support for various textUI: ox_lib, jg-textui, okokTextUI & qbcore
-- Go to client/functions.lua line 3 to choose which textUI to use
interact = 'ox_target',
-- Set your notification system below
-- Available options are: 'lation_ui', 'ox_lib', 'esx', 'qb', 'okok', 'sd-notify', 'wasabi_notify' & 'custom'
-- 'custom' needs to be added to client/functions.lua
notify = 'ox_lib',
-- Set your progress bar system below
-- Available options are: 'lation_ui', 'ox_lib', 'qbcore' & 'custom'
-- 'custom' needs to be added to client/functions.lua
-- Any custom progress bar must also support animations
progress = 'ox_lib',
-- Set your context menu system below
-- Available options are: 'lation_ui', 'ox_lib' & 'custom'
menu = 'ox_lib',
-- Set your alert & input dialog system below
-- Available options are: 'lation_ui', 'ox_lib' & 'custom'
dialogs = 'ox_lib',
-- Do you want to hide player names in the group menu?
-- If true, names will instead be replaced with their Player IDs
-- If false it will display their character names as normal
hideNames = false,
-- Do you want to be notified via server console if an update is available?
-- True if yes, false if no
version = true,
-- You have the option to set a job requirement to Scuba Dive if you wish
-- Just change the false below to your job name, e.g. job = 'job_name'
job = false,
-- When a player is diving, they can see their current depth underwater.
-- Do you want to show this depth in meters or feet?
-- If meteres, set metric to true. If feet, set metric to false.
metric = false,
-- ⚠️ WARNING: Do not alter the number below unless directed by support
-- ⚠️ And/or understand what you are doing - "SetPedMaxTimeUnderWater" value
max = 1800.0
},
----------------------------------------------
-- 📍 Activity start settings
----------------------------------------------
start = {
-- Where to spawn the main ped to start diving
-- If you wish to disable the starting ped, set coords = false
coords = vec4(-289.3029, -2770.0029, 2.1953, 48.9145),
-- The ped model used
-- More models: https://docs.fivem.net/docs/game-references/ped-models/
model = 's_m_y_shop_mask',
-- The scenario assigned to the ped (or scenario = false for no scenario)
-- More scenarios: https://github.com/DioneB/gtav-scenarios
scenario = 'WORLD_HUMAN_CLIPBOARD',
-- You can limit the hours at which the ped is available here
-- By default, this ped is available 24/7
-- Min is the earliest the ped is available (in 24hr format)
-- Max is the latest the ped is available (in 24hr format)
-- For example, if you want the ped only available during daytime set min = 6 & max = 21
hours = { min = 0, max = 24 },
-- When a group owner selects "Start Diving", this is how long it takes
-- (in seconds) to receive an assigned location on the map.
cooldown = { min = 15, max = 30 },
-- When a diving job is completed, do you want to display the "Continue Diving?"
-- Dialog to the group owner? True if yes, false if no
continue = true,
},
----------------------------------------------
-- 📈 Customize the XP system
----------------------------------------------
experience = {
-- The number in these [brackets] are the level
-- The number after = is the exp required to reach that level
-- Be sure levels *always* start at level 1 with 0 exp
[1] = 0,
[2] = 2500,
[3] = 10000,
[4] = 20000,
[5] = 50000,
-- You can add or remove levels as you wish
},
----------------------------------------------
-- 🤿 Setup your Scuba Gear
----------------------------------------------
gear = {
-- Customize the various Scuba Gear tanks available
tanks = {
-- The number in [brackets] is the level required to use this tank
-- item: is the actual scuba tank item spawn name
-- degrade: is how much the tank degrades every 10 seconds underwater
-- Level 1 lasts roughly a total of 5 minutes underwater before needing a refill
[1] = { item = 'ls_scuba_gear_1', degrade = 3.34 },
-- Level 2 lasts roughly a total of 10 minutes underwater before needing a refill
[2] = { item = 'ls_scuba_gear_2', degrade = 1.67 },
-- Level 3 lasts roughly a total of 15 minutes underwater before needing a refill
[3] = { item = 'ls_scuba_gear_3', degrade = 1.11 },
-- Level 4 lasts roughly a total of 20 minutes underwater before needing a refill
[4] = { item = 'ls_scuba_gear_4', degrade = 0.84 },
-- Level 5 lasts roughly a total of 25 minutes underwater before needing a refill
[5] = { item = 'ls_scuba_gear_5', degrade = 0.66 },
-- The formula to get degradation value based on total time is:
-- 100 ÷ (Total time in seconds desired ÷ 10) = degrade
},
-- The item spawn name for oxygen refill tanks
refill = 'ls_oxygen_tank',
-- The mask is an object, object spawn name:
mask = 'p_d_scuba_mask_s',
-- The tank is an object, object spawn name:
tank = 'p_s_scuba_tank_s',
-- Optional clothing to be worn when scuba diving
-- Don't want to change clothes? Set all drawableId's to 0!
-- ⚠️ Do not alter componentId
clothes = {
mask = {
-- The compononent id you want to set
componentId = 1,
-- The drawableId and textureId for males
male = { drawableId = 0, textureId = 0 },
-- The drawableId and textureId for females
female = { drawableId = 0, textureId = 0 }
},
upperBody = {
componentId = 3,
male = { drawableId = 8, textureId = 0 },
female = { drawableId = 3, textureId = 0 }
},
lowerBody = {
componentId = 4,
male = { drawableId = 94, textureId = 0 },
female = { drawableId = 97, textureId = 0 }
},
bags = {
componentId = 5,
male = { drawableId = 0, textureId = 0 },
female = { drawableId = 0, textureId = 0 }
},
shoes = {
componentId = 6,
male = { drawableId = 67, textureId = 0 },
female = { drawableId = 70, textureId = 0 }
},
shirt = {
componentId = 8,
male = { drawableId = 15, textureId = 0 },
female = { drawableId = 15, textureId = 0 }
},
bodyArmor = {
componentId = 9,
male = { drawableId = 0, textureId = 0 },
female = { drawableId = 0, textureId = 0 }
},
decals = {
componentId = 10,
male = { drawableId = 0, textureId = 0 },
female = { drawableId = 0, textureId = 0 }
},
jackets = {
componentId = 11,
male = { drawableId = 243, textureId = 0 },
female = { drawableId = 251, textureId = 0 }
}
}
},
----------------------------------------------
-- 🛒 Setup your shops
----------------------------------------------
shop = {
gear = {
-- Do you want to enable the scuba gear shop?
-- This shop is added to the main diving menu
enable = true,
-- The account that will be used for purchases
-- Available options are: 'cash' or 'bank'
account = 'cash',
-- The shop items available for purchase
items = {
-- item: item spawn name
-- price: price of item
-- icon: icon for item
-- level: optional player level requirement to purchase item
-- metadata: optional durability level assigned at purchase
-- metadata: ⚠️ remove this option if selling non-metadata items
[1] = { item = 'ls_oxygen_tank', price = 100, icon = 'fas fa-repeat', metadata = 100 },
[2] = { item = 'ls_scuba_gear_1', price = 250, icon = 'fas fa-lungs', level = 1, metadata = 100 },
[3] = { item = 'ls_scuba_gear_2', price = 500, icon = 'fas fa-lungs', level = 2, metadata = 100 },
[4] = { item = 'ls_scuba_gear_3', price = 1000, icon = 'fas fa-lungs', level = 3, metadata = 100 },
[5] = { item = 'ls_scuba_gear_4', price = 2000, icon = 'fas fa-lungs', level = 4, metadata = 100 },
[6] = { item = 'ls_scuba_gear_5', price = 5000, icon = 'fas fa-lungs', level = 5, metadata = 100 },
[7] = { item = 'burger', price = 5, icon = 'fas fa-burger' },
[8] = { item = 'water', price = 5, icon = 'fas fa-glass-water' },
-- You can add or remove items as you wish
}
},
pawn = {
-- Do you want to enable the scuba gear pawn/sell shop?
-- This shop is added to the main diving menu
enable = true,
-- The account that will be used to pay the player
-- Available options are 'cash' or 'bank'
account = 'cash',
-- The shop items available to sell
items = {
-- item: item spawn name
-- price: how much item sells for
-- icon: icon for item
[1] = { item = 'ls_old_boot', price = 5, icon = 'fas fa-hand-holding-dollar' },
[2] = { item = 'ls_wood_plank', price = 5, icon = 'fas fa-hand-holding-dollar' },
[3] = { item = 'ls_rusted_padlock', price = 5, icon = 'fas fa-hand-holding-dollar' },
[4] = { item = 'ls_rusted_key', price = 5, icon = 'fas fa-hand-holding-dollar' },
[5] = { item = 'ls_rusted_gear', price = 5, icon = 'fas fa-hand-holding-dollar' },
[6] = { item = 'ls_seashell', price = 15, icon = 'fas fa-hand-holding-dollar' },
[7] = { item = 'ls_seaweed', price = 15, icon = 'fas fa-hand-holding-dollar' },
[8] = { item = 'ls_rusted_muffler', price = 15, icon = 'fas fa-hand-holding-dollar' },
[9] = { item = 'ls_broken_cd', price = 15, icon = 'fas fa-hand-holding-dollar' },
[10] = { item = 'ls_diving_goggles', price = 15, icon = 'fas fa-hand-holding-dollar' },
[11] = { item = 'ls_fishing_net', price = 25, icon = 'fas fa-hand-holding-dollar' },
[12] = { item = 'ls_old_camera', price = 25, icon = 'fas fa-hand-holding-dollar' },
[13] = { item = 'ls_military_helmet', price = 25, icon = 'fas fa-hand-holding-dollar' },
[14] = { item = 'ls_old_compass', price = 50, icon = 'fas fa-hand-holding-dollar' },
[15] = { item = 'ls_old_watch', price = 50, icon = 'fas fa-hand-holding-dollar' },
[16] = { item = 'ls_conch_shell', price = 50, icon = 'fas fa-hand-holding-dollar' },
-- You can add or remove items as you wish
}
}
},
----------------------------------------------
-- 🛥️ Customize boat rentals
----------------------------------------------
rental = {
-- Do you want to offer boat rentals?
enable = true,
-- The account that will be used for rentals
-- Available options are: 'cash' or 'bank'
account = 'cash',
-- Do you want to require payment for boat rentals?
-- If not, set deposit = 0
deposit = 500,
-- Do you want to refund some money when the boat is returned?
-- If not, set returnDeposit = 0
returnDeposit = 250,
-- The script will spawn a random boat from the list below
-- If you only want one boat, leave just one in the list
models = { 'dinghy' },
-- When a boat is rented, do you want to teleport the player into it?
teleport = false,
-- Where to spawn the rental boat?
coords = vec4(-291.7570, -2766.1208, 0.6787, 267.4251),
-- Do you want to add a boat anchor option?
anchor = true,
},
----------------------------------------------
-- ⛏️ Build the diving zones
----------------------------------------------
zones = {
[1] = {
-- The center location of this zone
location = vec3(1086.9691, -3575.9382, -50.1214),
-- Models selected at random for each crate spawned here
models = { 'xm3_prop_xm3_crate_supp_01a' },
-- The radius of this zone
radius = 150.0,
-- Optional highlighting on crates when a player is nearby
-- enable: true/false - enable or disable highlighting when nearby
-- distance: how close the player must be for the highlight to be visible
-- color: the color of the highlight (https://rgbacolorpicker.com/)
highlight = { enable = true, distance = 30, color = { r = 106, g = 226, b = 119 } },
-- How many crates can be spawned in this zone?
crates = { min = 2, max = 6 },
-- How much XP is rewarded for crates found in this zone?
xp = { min = 1, max = 5 },
-- The exact coords the crates will spawn inside this zone
coords = {
vec3(1094.7655, -3577.4995, -52.5339),
vec3(1086.7635, -3537.7588, -43.1057),
vec3(1106.2019, -3583.6062, -17.5417),
vec3(1091.3970, -3618.1091, -44.4066),
vec3(974.6331, -3567.4514, -47.2678),
vec3(1161.8893, -3630.9204, -22.7693),
vec3(1066.5341, -3559.0664, -20.8264),
vec3(1138.0339, -3581.6411, -39.0230),
vec3(1203.7281, -3524.0046, -33.9104),
vec3(1191.8622, -3531.3965, -24.0956),
vec3(1066.8358, -3485.3047, -32.4555),
vec3(1034.0037, -3651.6384, -49.3254),
vec3(1108.3530, -3662.9187, -41.3706),
vec3(1180.6683, -3558.5405, -38.9220),
vec3(1035.1763, -3586.1721, -45.7417),
-- Add or remove spawns from this zone
}
},
[2] = {
location = vec3(3085.2043, -1853.3246, -14.3668),
models = { 'xm3_prop_xm3_crate_supp_01a' },
radius = 150.0,
highlight = { enable = true, distance = 30, color = { r = 106, g = 226, b = 119 } },
crates = { min = 2, max = 6 },
xp = { min = 1, max = 5 },
coords = {
vec3(3085.2043, -1853.3246, -16.0565),
vec3(3029.1843, -1820.8015, -24.6999),
vec3(2993.0427, -1818.4943, -34.2077),
vec3(2997.8711, -1753.7402, -11.0640),
vec3(2982.0010, -1912.0287, -28.9325),
vec3(3051.9775, -1926.9736, -54.6551),
vec3(3011.4712, -1866.9753, -48.1732),
vec3(3056.2656, -1761.5493, -43.3554),
vec3(2950.1038, -1839.1633, -35.2352),
vec3(3019.7561, -1913.2930, -32.4005),
vec3(3061.5286, -1866.6348, -35.7668),
vec3(3040.8525, -1879.8348, -57.6607),
vec3(2974.8582, -1785.8473, -13.5562),
}
},
[3] = {
location = vec3(1818.7792, -2984.2068, -41.2811),
models = { 'xm3_prop_xm3_crate_supp_01a' },
radius = 150.0,
highlight = { enable = true, distance = 30, color = { r = 106, g = 226, b = 119 } },
crates = { min = 2, max = 6 },
xp = { min = 1, max = 5 },
coords = {
vec3(1815.4813, -2981.9314, -42.1416),
vec3(1820.6042, -2943.6421, -39.3650),
vec3(1738.9772, -2948.8298, -23.5561),
vec3(1732.7568, -3042.6794, -58.3799),
vec3(1902.0460, -3026.5271, -45.4950),
vec3(1760.1697, -2977.9131, -47.1564),
vec3(1760.2402, -3093.1196, -66.6896),
vec3(1820.7825, -3068.2268, -39.7288),
vec3(1929.2610, -2969.8464, -36.5976),
vec3(1777.1892, -2915.6821, -30.6168),
vec3(1709.7538, -2952.2861, -24.4798),
vec3(1849.2987, -2965.3936, -52.7471),
vec3(1891.3480, -2918.0266, -22.7276),
}
},
[4] = {
location = vec3(3150.8074, -973.6499, -26.9572),
models = { 'xm3_prop_xm3_crate_supp_01a' },
radius = 150.0,
highlight = { enable = true, distance = 30, color = { r = 106, g = 226, b = 119 } },
crates = { min = 2, max = 6 },
xp = { min = 1, max = 5 },
coords = {
vec3(3150.8074, -973.6499, -26.9572),
vec3(3132.8245, -1026.0867, -42.3399),
vec3(3085.2126, -970.5078, -17.9598),
vec3(3088.5186, -859.4744, -36.9759),
vec3(3129.6709, -944.6890, -39.5992),
vec3(3191.7087, -984.1827, -21.3900),
vec3(3112.4922, -1061.3378, -47.8674),
vec3(3089.1252, -1030.5328, -43.6450),
vec3(3050.0190, -946.1809, -23.7141),
vec3(3184.7986, -870.0077, -65.7725),
vec3(3125.7749, -890.1265, -43.6622),
vec3(3075.5540, -898.0163, -22.6175),
}
},
[5] = {
location = vec3(2383.6611, -2451.3289, -32.9255),
models = { 'xm3_prop_xm3_crate_supp_01a' },
radius = 150.0,
highlight = { enable = true, distance = 30, color = { r = 106, g = 226, b = 119 } },
crates = { min = 2, max = 6 },
xp = { min = 1, max = 5 },
coords = {
vec3(2383.6611, -2451.3289, -32.9255),
vec3(2437.7466, -2411.5706, -32.8800),
vec3(2385.5486, -2347.8923, -16.6751),
vec3(2313.7725, -2344.6472, -8.4371),
vec3(2270.5879, -2393.7388, -26.9309),
vec3(2299.5146, -2433.6165, -19.7982),
vec3(2301.1694, -2404.3206, -25.4087),
vec3(2393.1316, -2410.4976, -32.1512),
vec3(2353.0732, -2517.1201, -29.7120),
vec3(2442.2646, -2456.6064, -7.9306),
}
},
[6] = {
location = vec3(280.3442, -3492.5969, -29.2227),
models = { 'xm3_prop_xm3_crate_supp_01a' },
radius = 150.0,
highlight = { enable = true, distance = 30, color = { r = 106, g = 226, b = 119 } },
crates = { min = 2, max = 6 },
xp = { min = 1, max = 5 },
coords = {
vec3(280.3442, -3492.5969, -29.2227),
vec3(361.8796, -3468.6265, -26.7427),
vec3(313.8169, -3376.5999, -24.5615),
vec3(214.2739, -3467.3735, -14.0912),
vec3(230.4904, -3558.5493, -50.8256),
vec3(229.1392, -3405.6541, -19.9491),
vec3(263.5007, -3446.9626, -25.7515),
vec3(332.7290, -3455.4980, -28.1874),
vec3(260.9624, -3362.8247, -18.8871),
vec3(183.2011, -3419.6165, -18.7913),
}
},
[7] = {
location = vec3(-2080.0508, -876.7839, -38.1838),
models = { 'xm3_prop_xm3_crate_supp_01a' },
radius = 150.0,
highlight = { enable = true, distance = 30, color = { r = 106, g = 226, b = 119 } },
crates = { min = 2, max = 6 },
xp = { min = 1, max = 5 },
coords = {
vec3(-2080.0508, -876.7839, -38.1838),
vec3(-2013.5044, -984.5208, -15.5007),
vec3(-2169.8203, -962.9402, -70.6583),
vec3(-2081.6479, -939.0807, -40.9644),
vec3(-2142.8997, -880.9988, -38.1233),
vec3(-2051.6763, -874.8913, -4.9137),
vec3(-2060.8647, -843.3949, -24.2824),
vec3(-2101.0276, -856.7371, -32.4969),
vec3(-2187.1812, -831.8537, -36.8464),
vec3(-2090.7273, -1001.5419, -43.5531),
vec3(-2000.4624, -917.0707, -14.9745),
vec3(-2035.0875, -833.5734, -14.8460),
vec3(-2041.9478, -818.1243, -5.5512),
vec3(-2161.8516, -776.5375, -22.6451),
vec3(-2142.2932, -839.6798, -32.2059),
vec3(-2049.0518, -920.3878, -32.7276),
}
},
[8] = {
location = vec3(-2837.9504, -494.8672, -45.7883),
models = { 'xm3_prop_xm3_crate_supp_01a' },
radius = 150.0,
highlight = { enable = true, distance = 30, color = { r = 106, g = 226, b = 119 } },
crates = { min = 2, max = 6 },
xp = { min = 1, max = 5 },
coords = {
vec3(-2837.9504, -494.8672, -45.7883),
vec3(-2789.4587, -623.3177, -47.6618),
vec3(-2854.2878, -578.2092, -75.3579),
vec3(-2836.2771, -528.2178, -66.3296),
vec3(-2814.6587, -561.5555, -71.3785),
vec3(-2749.4819, -543.8317, -46.9659),
vec3(-2749.5925, -462.8193, -37.6869),
vec3(-2833.0474, -462.1175, -18.8996),
vec3(-2921.6694, -434.5992, -37.3384),
vec3(-2819.7563, -591.3509, -58.2769),
vec3(-2760.0801, -581.4194, -47.1628),
vec3(-2808.8740, -409.1779, -38.3897),
vec3(-2847.6389, -380.1435, -40.4797),
vec3(-2881.4072, -439.8226, -38.2622),
vec3(-2785.4697, -524.0317, -54.6307),
}
},
[9] = {
location = vec3(-3550.5359, 646.9001, -66.3281),
models = { 'xm3_prop_xm3_crate_supp_01a' },
radius = 150.0,
highlight = { enable = true, distance = 30, color = { r = 106, g = 226, b = 119 } },
crates = { min = 2, max = 6 },
xp = { min = 1, max = 5 },
coords = {
vec3(-3550.5359, 646.9001, -66.3281),
vec3(-3562.6257, 622.5925, -31.0750),
vec3(-3514.4497, 629.0671, -31.9019),
vec3(-3464.5229, 618.9487, -39.9314),
vec3(-3468.6663, 546.3879, -46.4398),
vec3(-3532.4519, 540.3656, -55.3767),
vec3(-3576.2854, 742.5957, -47.0631),
vec3(-3530.5144, 767.2094, -36.7547),
vec3(-3489.9067, 699.1218, -50.1850),
vec3(-3503.2341, 591.9188, -46.4599),
vec3(-3589.1309, 596.7454, -61.6465),
vec3(-3594.2913, 689.3352, -64.4904),
}
},
[10] = {
location = vec3(-3416.8613, 1701.7200, -61.6626),
models = { 'xm3_prop_xm3_crate_supp_01a' },
radius = 150.0,
highlight = { enable = true, distance = 30, color = { r = 106, g = 226, b = 119 } },
crates = { min = 2, max = 6 },
xp = { min = 1, max = 5 },
coords = {
vec3(-3416.8613, 1701.7200, -61.6626),
vec3(-3417.9424, 1653.9034, -42.2417),
vec3(-3410.1980, 1589.5227, -42.9441),
vec3(-3303.7632, 1705.6891, -23.4423),
vec3(-3494.1262, 1706.0807, -65.6907),
vec3(-3465.9348, 1698.5503, -45.7951),
vec3(-3372.7073, 1669.8127, -45.0799),
vec3(-3331.2153, 1686.3544, -42.6237),
vec3(-3406.0732, 1780.2509, -37.4121),
vec3(-3450.4163, 1663.0955, -40.5404),
vec3(-3336.1089, 1611.4885, -40.8837),
}
}
},
----------------------------------------------
-- 📦 Customize your crates
----------------------------------------------
crates = {
-- The item spawn name for the crate
item = 'ls_diving_crate',
-- The quantity of crates given to the player when picked up
quantity = { min = 1, max = 1 },
-- The loot table when opening crates
-- ⚠️ The way this loot table works is pretty unique - please read :)
-- ⚠️ Each number in [brackets] is the level required to get a chance at that loot
-- ⚠️ Each item is rolled individually, so you can get multiple items from one crate
-- ⚠️ If you are level 1 in Diving, you only get a chance at items from section [1]
-- ⚠️ If you are level 3 in Diving, you get a chance at items from sections [1] to [3]
-- ⚠️ This gives you the ability to scale rewards based on the players diving experience
-- ⚠️ This section is intended to be built by YOU for your specific server and item(s)
-- ⚠️ We only provide a base loot table to help get you started with some filler items
loot = {
[1] = {
-- These item(s) are only rolled for players level 1 and above
-- item: spawn name | quantity = min/max | chance = 1-100 | metadata (optional): {key = value}
{ item = 'ls_old_boot', quantity = { min = 1, max = 1 }, chance = 100 },
{ item = 'ls_wood_plank', quantity = { min = 1, max = 1 }, chance = 80 },
{ item = 'ls_rusted_padlock', quantity = { min = 1, max = 1 }, chance = 60 },
{ item = 'ls_rusted_key', quantity = { min = 1, max = 1 }, chance = 40 },
{ item = 'ls_rusted_gear', quantity = { min = 1, max = 1 }, chance = 30 },
{ item = 'ls_seashell', quantity = { min = 1, max = 1 }, chance = 30 },
{ item = 'ls_seaweed', quantity = { min = 1, max = 1 }, chance = 25 },
{ item = 'ls_rusted_muffler', quantity = { min = 1, max = 1 }, chance = 25 },
{ item = 'ls_broken_cd', quantity = { min = 1, max = 1 }, chance = 20 },
{ item = 'ls_diving_goggles', quantity = { min = 1, max = 1 }, chance = 20 },
{ item = 'ls_fishing_net', quantity = { min = 1, max = 1 }, chance = 15 },
{ item = 'ls_old_camera', quantity = { min = 1, max = 1 }, chance = 15 },
{ item = 'ls_military_helmet', quantity = { min = 1, max = 1 }, chance = 10 },
{ item = 'ls_old_compass', quantity = { min = 1, max = 1 }, chance = 10 },
{ item = 'ls_old_watch', quantity = { min = 1, max = 1 }, chance = 5 },
{ item = 'ls_conch_shell', quantity = { min = 1, max = 1 }, chance = 5 },
-- Add or remove items as you wish
},
[2] = {
-- These item(s) are only rolled for players level 2 and above
{ item = 'ls_bottle_of_rum', quantity = { min = 1, max = 1 }, chance = 50 },
},
[3] = {
-- These item(s) are only rolled for players level 3 and above
{ item = 'ls_treasure_map', quantity = { min = 1, max = 1 }, chance = 50 },
},
[4] = {
-- These item(s) are only rolled for players level 4 and above
{ item = 'ls_silver_bracelet', quantity = { min = 1, max = 1 }, chance = 50 },
},
[5] = {
-- These item(s) are only rolled for players level 5 and above
{ item = 'ls_ancient_artifact', quantity = { min = 1, max = 1 }, chance = 50 },
}
}
}
}
Copy
-- Initialize global variables to store framework & inventory
Framework, Inventory = nil, nil
-- Initialize global player variables
PlayerLoaded, PlayerData = false, {}
-- Get framework
local function InitializeFramework()
if GetResourceState('es_extended') == 'started' then
ESX = exports['es_extended']:getSharedObject()
Framework = 'esx'
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
PlayerData = xPlayer
PlayerLoaded = true
TriggerEvent('lation_diving:playerLoaded')
end)
RegisterNetEvent('esx:onPlayerLogout', function()
table.wipe(PlayerData)
PlayerLoaded = false
TriggerEvent('lation_diving:playerDropped')
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_diving:playerLoaded')
end)
elseif GetResourceState('qbx_core') == 'started' then
Framework = 'qbx'
AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_diving:playerLoaded')
end)
RegisterNetEvent('qbx_core:client:playerLoggedOut', function()
table.wipe(PlayerData)
PlayerLoaded = false
TriggerEvent('lation_diving:playerDropped')
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_diving:playerLoaded')
end)
elseif GetResourceState('qb-core') == 'started' then
QBCore = exports['qb-core']:GetCoreObject()
Framework = 'qb'
AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_diving:playerLoaded')
end)
RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()
table.wipe(PlayerData)
PlayerLoaded = false
TriggerEvent('lation_diving:playerDropped')
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_diving:playerLoaded')
end)
elseif GetResourceState('ox_core') == 'started' then
Ox = require '@ox_core.lib.init'
Framework = 'ox'
AddEventHandler('ox:playerLoaded', function()
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_diving:playerLoaded')
end)
AddEventHandler('ox:playerLogout', function()
table.wipe(PlayerData)
PlayerLoaded = false
TriggerEvent('lation_diving:playerDropped')
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_diving:playerLoaded')
end)
else
-- Add custom framework here
end
end
-- Get inventory
local function InitializeInventory()
if GetResourceState('ox_inventory') == 'started' then
Inventory = 'ox_inventory'
elseif GetResourceState('qb-inventory') == 'started' then
Inventory = 'qb-inventory'
elseif GetResourceState('qs-inventory') == 'started' then
Inventory = 'qs-inventory'
elseif GetResourceState('ps-inventory') == 'started' then
Inventory = 'ps-inventory'
elseif GetResourceState('origen_inventory') == 'started' then
Inventory = 'origen_inventory'
elseif GetResourceState('codem-inventory') == 'started' then
Inventory = 'codem-inventory'
elseif GetResourceState('core_inventory') == 'started' then
Inventory = 'core_inventory'
else
-- Add custom inventory here
end
end
-- Returns player data
function GetPlayerData()
if Framework == 'esx' then
return ESX.GetPlayerData()
elseif Framework == 'qb' then
return QBCore.Functions.GetPlayerData()
elseif Framework == 'qbx' then
return exports.qbx_core:GetPlayerData()
elseif Framework == 'ox' then
return Ox.GetPlayer()
else
-- Add custom framework here
end
end
-- Returns player job
function GetJob()
local player = GetPlayerData()
if not player then return end
if Framework == 'esx' then
return player?.job?.name
elseif Framework == 'qb' then
return player.job.name
elseif Framework == 'qbx' then
return player.job.name
elseif Framework == 'ox' then
return player.getGroupByType('job')
else
-- Add custom framework here
end
return 'unemployed'
end
-- Return data for item
--- @param item string
function GetItemData(item)
if not item then return end
if Inventory then
if Inventory == 'ox_inventory' then
return exports[Inventory]:Items(item)
elseif Inventory == 'qb-inventory' or Inventory == 'ps-inventory' then
return QBCore.Shared.Items[item]
elseif Inventory == 'qs-inventory' then
local items = exports[Inventory]:GetItemList()
if not items then return end
return items[item]
elseif Inventory == 'origen_inventory' then
local items = exports[Inventory]:GetItems()
if not items then return end
return items[item]
elseif Inventory == 'codem-inventory' then
local items = exports[Inventory]:GetItemList()
if not items then return end
return items[item]
elseif Inventory == 'core_inventory' then
-- No available client-side export to get item list
if Framework == 'qb' then
return QBCore.Shared.Items[item]
else
print('^1[ERROR]: An issue has occured, please contact support at: https://discord.gg/9EbY4nM5uu^0')
end
else
-- Add custom inventory here
end
else
if Framework == 'esx' then
-- Unlikely to need anything here but.. just in case..
print('^1[ERROR]: An error has occured with lation_diving - please contact support^0')
elseif Framework == 'qb' then
return QBCore.Shared.Items[item]
elseif Framework == 'qbx' then
-- Unlikely to need anything here but.. just in case..
print('^1[ERROR]: Are you really not using ox_inventory? Contact support please lul.^0')
end
end
end
-- Returns boolean if player has specified amount of item
--- @param item string
--- @param amount number
--- @return boolean
function HasItem(item, amount)
if not item or not amount then return false end
if Inventory then
if Inventory == 'ox_inventory' then
return exports[Inventory]:Search('count', item) >= amount
elseif Inventory == 'core_inventory' then
return exports[Inventory]:hasItem(item, amount)
else
return exports[Inventory]:HasItem(item, amount)
end
else
local playerData = GetPlayerData()
if not playerData then return false end
local inventory = Framework == 'esx' and playerData.inventory or playerData.items
if not inventory then return false end
for _, itemData in pairs(inventory) do
if itemData and itemData.name == item then
local count = itemData.amount or itemData.count or 0
if count >= amount then
return true
end
end
end
return false
end
end
-- Disables access to open/view inventory
function DisableInventory()
if Inventory == 'ox_inventory' then
LocalPlayer.state.invBusy = true
elseif Inventory == 'qb-inventory' then
LocalPlayer.state.inv_busy = true
elseif Inventory == 'qs-inventory' then
exports[Inventory]:setInventoryDisabled(true)
elseif Inventory == 'core_inventory' then
exports[Inventory]:lockInventory()
else
-- Add custom inventory here
end
end
-- Enables access to open/view inventory
function EnableInventory()
if Inventory == 'ox_inventory' then
LocalPlayer.state.invBusy = false
elseif Inventory == 'qb-inventory' then
LocalPlayer.state.inv_busy = false
elseif Inventory == 'qs-inventory' then
exports[Inventory]:setInventoryDisabled(false)
elseif Inventory == 'core_inventory' then
exports[Inventory]:unlockInventory()
else
-- Add custom inventory here
end
end
-- Initialize defaults
InitializeFramework()
InitializeInventory()
Copy
-- Initialize global variable to store phone, keys & disaptch
Phone, Keys, Fuel = nil, nil, nil
-- Initialize config(s)
local shared = require 'config.shared'
local client = require 'config.client'
-- Localize export
local diving = exports.lation_diving
-- You can change the textUI script here
-- Options: 'lation_ui', 'ox_lib', 'jg-textui', 'okokTextUI', 'qbcore' & 'custom'
local textui = 'ox_lib'
-- Get phone
local function InitializePhone()
if GetResourceState('lb-phone') == 'started' then
Phone = 'lb-phone'
elseif GetResourceState('qb-phone') == 'started' then
Phone = 'qb-phone'
elseif GetResourceState('qs-smartphone-pro') == 'started' then
Phone = 'qs-smartphone-pro'
elseif GetResourceState('qs-smartphone') == 'started' then
Phone = 'qs-smartphone'
elseif GetResourceState('gksphone') == 'started' then
Phone = 'gksphone'
elseif GetResourceState('roadphone') == 'started' then
Phone = 'roadphone'
elseif GetResourceState('npwd') == 'started' then
Phone = 'npwd'
elseif GetResourceState('yseries') == 'started' then
Phone = 'yseries'
elseif GetResourceState('okokPhone') == 'started' then
Phone = 'okokPhone'
else
-- Add custom phone here
end
end
-- Get keys
local function InitializeKeys()
if GetResourceState('qb-vehiclekeys') == 'started' then
Keys = 'qb-vehiclekeys'
elseif GetResourceState('cd_garage') == 'started' then
Keys = 'cd_garage'
elseif GetResourceState('wasabi_carlock') == 'started' then
Keys = 'wasabi_carlock'
elseif GetResourceState('qbx_vehiclekeys') == 'started' then
Keys = 'qbx_vehiclekeys'
elseif GetResourceState('vehicles_keys') == 'started' then
Keys = 'vehicles_keys'
elseif GetResourceState('okokGarage') == 'started' then
Keys = 'okokGarage'
elseif GetResourceState('qs-vehiclekeys') == 'started' then
Keys = 'qs-vehiclekeys'
elseif GetResourceState('Renewed-Vehiclekeys') == 'started' then
Keys = 'Renewed-Vehiclekeys'
elseif GetResourceState('t1ger_keys') == 'started' then
Keys = 't1ger_keys'
else
-- Add custom keys here
end
end
-- Get fuel
local function InitializeFuel()
if GetResourceState('LegacyFuel') == 'started' then
Fuel = 'LegacyFuel'
elseif GetResourceState('ox_fuel') == 'started' then
Fuel = 'ox_fuel'
elseif GetResourceState('cdn-fuel') == 'started' then
Fuel = 'cdn-fuel'
elseif GetResourceState('x-fuel') == 'started' then
Fuel = 'x-fuel'
elseif GetResourceState('okokGasStation') == 'started' then
Fuel = 'okokGasStation'
elseif GetResourceState('rcore_fuel') == 'started' then
Fuel = 'rcore_fuel'
elseif GetResourceState('qs-fuelstations') == 'started' then
Fuel = 'qs-fuelstations'
elseif GetResourceState('ps-fuel') == 'started' then
Fuel = 'ps-fuel'
else
-- Add custom fuel system here
end
end
-- Display a notification
--- @param message string
--- @param type any
function ShowNotification(message, type)
if shared.setup.notify == 'lation_ui' then
exports.lation_ui:notify({ title = 'Scuba Diving', message = message, type = type or 'info', icon = 'fas fa-anchor' })
elseif shared.setup.notify == 'ox_lib' then
lib.notify({ description = message, type = type or 'inform', position = 'top', icon = 'fas fa-anchor' })
elseif shared.setup.notify == 'esx' then
ESX.ShowNotification(message)
elseif shared.setup.notify == 'qb' then
QBCore.Functions.Notify(message, type or 'primary')
elseif shared.setup.notify == 'okok' then
exports['okokNotify']:Alert('Scuba Diving', message, 5000, type or 'info', false)
elseif shared.setup.notify == 'sd-notify' then
exports['sd-notify']:Notify('Scuba Diving', message, type or 'primary')
elseif shared.setup.notify == 'wasabi_notify' then
exports.wasabi_notify:notify('Scuba Diving', message, 5000, type or 'info', false, 'fas fa-anchor')
elseif shared.setup.notify == 'custom' then
-- Add custom notification export/event here
end
end
-- Display notifications from server
--- @param message string
--- @param type string
RegisterNetEvent('lation_diving:notify', function(message, type)
ShowNotification(message, type)
end)
-- Set fuel
--- @param vehicle number Entity ID
function SetFuel(vehicle)
if not Fuel then print('^1[ERROR]: No fuel system detected - cannot set fuel^0') return end
if not vehicle or not DoesEntityExist(vehicle) then return end
if Fuel == 'LegacyFuel' then
exports[Fuel]:SetFuel(vehicle, 100.0)
elseif Fuel == 'ox_fuel' then
Entity(vehicle).state.fuel = 100.0
elseif Fuel == 'cdn-fuel' then
exports[Fuel]:SetFuel(vehicle, 100.0)
elseif Fuel == 'x-fuel' then
exports[Fuel]:SetFuel(vehicle, 100.0)
elseif Fuel == 'okokGasStation' then
exports[Fuel]:SetFuel(vehicle, 100.0)
elseif Fuel == 'rcore_fuel' then
exports[Fuel]:SetVehicleFuel(vehicle, 100.0)
elseif Fuel == 'qs-fuelstations' then
exports[Fuel]:SetFuel(vehicle, 100.0)
elseif Fuel == 'ps-fuel' then
exports[Fuel]:SetFuel(vehicle, 100.0)
else
-- Add custom fuel system here
end
end
-- Give keys
--- @param vehicle number Entity ID
function GiveKeys(vehicle)
if not Keys then print('^1[ERROR]: No vehicle keys detected - cannot give keys^0') return end
if not vehicle or not DoesEntityExist(vehicle) then return end
local plate = GetVehicleNumberPlateText(vehicle)
local model = GetDisplayNameFromVehicleModel(GetEntityModel(vehicle))
if not plate or not model then return end
if Keys == 'qb-vehiclekeys' then
TriggerEvent('qb-vehiclekeys:client:AddKeys', plate)
elseif Keys == 'cd_garage' then
TriggerEvent('cd_garage:AddKeys', plate)
elseif Keys == 'wasabi_carlock' then
exports[Keys]:GiveKey(plate)
elseif Keys == 'qbx_vehiclekeys' then
-- @qbox: doesn't have a native client-side export, #sad
TriggerEvent('qb-vehiclekeys:client:AddKeys', plate)
elseif Keys == 'vehicles_keys' then
TriggerServerEvent("vehicles_keys:selfGiveVehicleKeys", plate)
elseif Keys == 'okokGarage' then
TriggerServerEvent("okokGarage:GiveKeys", plate)
elseif Keys == 'qs-vehiclekeys' then
exports[Keys]:GiveKeys(plate, model, true)
elseif Keys == 'Renewed-Vehiclekeys' then
exports[Keys]:addKey(plate)
elseif Keys == 't1ger_keys' then
TriggerServerEvent('t1ger_keys:updateOwnedKeys', plate, true)
else
-- Add custom vehicle keys
end
end
-- Remove keys
--- @param vehicle number Entity ID
function RemoveKeys(vehicle)
if not Keys then print('^1[ERROR]: No vehicle keys detected - cannot remove keys^0') return end
if not vehicle or not DoesEntityExist(vehicle) then return end
local plate = GetVehicleNumberPlateText(vehicle)
local model = GetDisplayNameFromVehicleModel(GetEntityModel(vehicle))
if not plate or not model then return end
if Keys == 'qb-vehiclekeys' then
TriggerEvent('qb-vehiclekeys:client:RemoveKeys', plate)
elseif Keys == 'cd_garage' then
-- @cd_garage: no known way to remove keys
elseif Keys == 'wasabi_carlock' then
exports[Keys]:RemoveKey(plate)
elseif Keys == 'qbx_vehiclekeys' then
-- @qbox: doesn't have a native client-side export, #dumb
TriggerEvent('qb-vehiclekeys:client:RemoveKeys', plate)
elseif Keys == 'vehicles_keys' then
TriggerServerEvent("vehicles_keys:selfRemoveKeys", plate)
elseif Keys == 'okokGarage' then
-- @okokGarage: source as a second param? weird, but it's from the docs
TriggerServerEvent("okokGarage:RemoveKeys", plate, cache.serverId)
elseif Keys == 'qs-vehiclekeys' then
exports[Keys]:RemoveKeys(plate, model)
elseif Keys == 'Renewed-Vehiclekeys' then
exports[Keys]:removeKey(plate)
elseif Keys == 't1ger_keys' then
TriggerServerEvent('t1ger_keys:updateOwnedKeys', plate, false)
else
-- Add custom vehicle keys
end
end
-- Send an email
--- @param message string
RegisterNetEvent('lation_diving:sendEmail', function(message)
if not message then return end
if Phone == 'qs-smartphone' then
TriggerServerEvent('qs-smartphone:server:sendNewMail', {
sender = 'Diving Expert',
subject = 'Scuba Diving',
message = message
})
elseif Phone == 'roadphone' then
exports[Phone]:sendMail({
sender = 'Diving Expert',
subject = 'Scuba Diving',
message = message
})
else
ShowAlert({
header = 'Scuba Diving',
content = message,
centered = true,
cancel = true
})
end
end)
-- Change player clothes
--- @param isDiving boolean
function ChangeClothes(isDiving)
if isDiving then
local isMale = IsPedModel(cache.ped, `mp_m_freemode_01`)
for _, clothes in pairs(shared.gear.clothes) do
local drawableId = isMale and clothes.male.drawableId or clothes.female.drawableId
local textureId = isMale and clothes.male.textureId or clothes.female.textureId
SetPedComponentVariation(cache.ped, clothes.componentId, drawableId, textureId, 0)
end
else
if Framework == 'esx' then
ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin)
TriggerEvent('skinchanger:loadSkin', skin)
end)
elseif Framework == 'qb' then
TriggerServerEvent('qb-clothes:loadPlayerSkin')
end
TriggerEvent('illenium-appearance:client:reloadSkin')
TriggerEvent('fivem-appearance:client:reloadSkin')
end
end
-- Return an inventory's "durability/quality" types
function GetDurabilityType()
if Inventory == 'ox_inventory' then
return 'durability'
else
return 'quality'
end
end
-- Returns an items metadata
--- @param item string Item name
function GetMetadata(item)
return lib.callback.await('lation_diving:getMetadata', false, item)
end
-- Display a progress bar
--- @param data table
function ProgressBar(data)
if shared.setup.progress == 'lation_ui' then
if exports.lation_ui:progressBar({
label = data.label,
description = data.description or nil,
icon = data.icon or nil,
duration = data.duration,
useWhileDead = data.useWhileDead,
allowSwimming = data.allowSwimming or false,
canCancel = data.canCancel,
steps = data.steps or nil,
disable = data.disable,
anim = {
dict = data.anim.dict or nil,
clip = data.anim.clip or nil,
flag = data.anim.flag or nil
},
prop = {
model = data.prop.model or nil,
bone = data.prop.bone or nil,
pos = data.prop.pos or nil,
rot = data.prop.rot or nil
}
}) then
return true
end
return false
elseif shared.setup.progress == 'ox_lib' then
-- Want to use ox_lib's progress circle instead of bar?
-- Change "progressBar" to "progressCircle" below & done!
if lib.progressBar({
label = data.label,
duration = data.duration,
position = data.position or 'bottom',
useWhileDead = data.useWhileDead,
allowSwimming = data.allowSwimming or false,
canCancel = data.canCancel,
disable = data.disable,
anim = {
dict = data.anim.dict or nil,
clip = data.anim.clip or nil,
flag = data.anim.flag or nil
},
prop = {
model = data.prop.model or nil,
bone = data.prop.bone or nil,
pos = data.prop.pos or nil,
rot = data.prop.rot or nil
}
}) then
return true
end
return false
elseif shared.setup.progress == 'qbcore' then
local p = promise.new()
QBCore.Functions.Progressbar(data.label, data.label, data.duration, data.useWhileDead, data.canCancel, {
disableMovement = data.disable.move,
disableCarMovement = data.disable.car,
disableMouse = false,
disableCombat = data.disable.combat
}, {
animDict = data.anim.dict or nil,
anim = data.anim.clip or nil,
flags = data.anim.flag or nil
}, {
model = data.prop.model or nil,
bone = data.prop.bone or nil,
coords = data.prop.pos or nil,
rotation = data.prop.rot or nil
}, {},
function()
ClearPedTasks(cache.ped)
p:resolve(true)
end,
function()
ClearPedTasks(cache.ped)
p:resolve(false)
end)
return Citizen.Await(p)
else
-- Add 'custom' progress bar here
end
end
-- Register menu
--- @param data table
function RegisterMenu(data)
if shared.setup.menu == 'lation_ui' then
exports.lation_ui:registerMenu(data)
elseif shared.setup.menu == 'ox_lib' then
lib.registerContext(data)
elseif shared.setup.menu == 'custom' then
-- Add 'custom' menu system here
end
end
-- Show menu
--- @param menuId string
function ShowMenu(menuId)
if shared.setup.menu == 'lation_ui' then
exports.lation_ui:showMenu(menuId)
elseif shared.setup.menu == 'ox_lib' then
lib.showContext(menuId)
elseif shared.setup.menu == 'custom' then
-- Add 'custom' menu system here
end
end
-- Display an alert dialog
--- @param data table
function ShowAlert(data)
if shared.setup.dialogs == 'lation_ui' then
return exports.lation_ui:alert(data)
elseif shared.setup.dialogs == 'ox_lib' then
return lib.alertDialog(data)
elseif shared.setup.dialogs == 'custom' then
-- Add your custom alert dialog here
end
end
-- Display an input dialog
--- @param data table
function ShowInput(data)
if shared.setup.dialogs == 'lation_ui' then
return exports.lation_ui:input({ title = data.title, options = data.options })
elseif shared.setup.dialogs == 'ox_lib' then
return lib.inputDialog(data.title, data.options)
elseif shared.setup.dialogss == 'custom' then
-- Add your custom input dialog here
end
end
-- Display TextUI
--- @param text string
--- @param icon string
function ShowTextUI(text, icon)
if textui == 'lation_ui' then
exports.lation_ui:showText({
description = text,
icon = icon,
iconAnimation = 'beat'
})
elseif textui == 'ox_lib' then
lib.showTextUI(text, {
position = 'left-center',
icon = icon
})
elseif textui == 'jg-textui' then
exports['jg-textui']:DrawText(text)
elseif textui == 'okokTextUI' then
exports['okokTextUI']:Open(text, 'lightblue ', 'left', false)
elseif textui == 'qbcore' then
exports['qb-core']:DrawText(text, 'left')
else
-- Add custom textUI here
end
end
-- Hide TextUI
--- @param label string
function HideTextUI(label)
if textui == 'lation_ui' then
local isOpen, text = exports.lation_ui:isOpen()
if isOpen and text == label then
exports.lation_ui:hideText()
end
elseif textui == 'ox_lib' then
local isOpen, text = lib.isTextUIOpen()
if isOpen and text == label then
lib.hideTextUI()
end
elseif textui == 'jg-textui' then
exports['jg-textui']:HideText()
elseif textui == 'okokTextUI' then
exports['okokTextUI']:Close()
elseif textui == 'qbcore' then
exports['qb-core']:HideText()
else
-- Add custom textUI here
end
end
-- Add entity target
--- @param entity number Entity number
--- @param data table Options table
function AddTargetEntity(entity, data)
if shared.setup.interact == 'ox_target' then
exports.ox_target:addLocalEntity(entity, data)
elseif shared.setup.interact == 'qb-target' then
exports['qb-target']:AddTargetEntity(entity, {options = data, distance = 5})
elseif shared.setup.interact == 'interact' then
exports.interact:AddLocalEntityInteraction({
entity = entity,
interactDst = 5.0,
offset = vec3(0.0, 0.0, 1.0),
options = data
})
elseif shared.setup.interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system defined in the config/shared.lua file^0')
end
end
-- Add circle target zones
--- @param data table
function AddCircleZone(data)
if shared.setup.interact == 'ox_target' then
exports.ox_target:addSphereZone(data)
elseif shared.setup.interact == 'qb-target' then
exports['qb-target']:AddCircleZone(data.name, data.coords, data.radius, {
name = data.name,
debugPoly = shared.setup.debug}, {
options = data.options,
distance = 2,
})
elseif shared.setup.interact == 'interact' then
exports.interact:AddInteraction({
coords = data.coords,
interactDst = 2.0,
id = data.name,
options = data.options
})
elseif shared.setup.interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system defined in the config/shared.lua file^0')
end
end
-- Remove target from entity
--- @param entity any|number
--- @param data table|string
function RemoveTargetEntity(entity, data)
if shared.setup.interact == 'ox_target' then
exports.ox_target:removeLocalEntity(entity, data)
elseif shared.setup.interact == 'qb-target' then
exports['qb-target']:RemoveTargetEntity(entity, data)
elseif shared.setup.interact == 'interact' then
exports.interact:RemoveLocalEntityInteraction(entity, data)
elseif shared.setup.interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system defined in the config/shared.lua file^0')
end
end
-- Remove circle zone
--- @param name string
function RemoveCircleZone(name)
if shared.setup.interact == 'ox_target' then
exports.ox_target:removeZone(name)
elseif shared.setup.interact == 'qb-target' then
exports['qb-target']:RemoveZone(name)
elseif shared.setup.interact == 'interact' then
exports.interact:RemoveInteraction(name)
elseif shared.setup.interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system defined in the config/shared.lua file^0')
end
end
-- Function to spawn NPCs
--- @param model string
--- @param position vector4
function SpawnPed(model, position)
lib.requestModel(model)
while not HasModelLoaded(model) do Wait(0) end
local ped = CreatePed(0, model, position.x, position.y, position.z - 1.0, position.w, false, true)
FreezeEntityPosition(ped, true)
SetBlockingOfNonTemporaryEvents(ped, true)
SetEntityInvincible(ped, true)
return ped
end
-- Play anim on crate open
local function openCrate()
if ProgressBar(client.anims.openCrate) then
return true
end
return false
end
-- Use oxygen refill item
local function useRefill()
local tankMetadata, metatype = GetMetadata(shared.gear.refill), GetDurabilityType()
if not tankMetadata or not metatype or not tankMetadata[metatype] then
print('^1[ERROR]: Missing or invalid metadata on oxygen tank item^0')
print('^1[ERROR]: These items cannot be spawned - you must purchase from the shop^0')
print('^1[ERROR]: Visit https://docs.lationscripts.com/scuba-diving/common for more info^0')
return
end
local tankDurability = tankMetadata[metatype]
if tankDurability <= 0 then
ShowNotification(locale('notify.no-durability'), 'error')
return
end
local level = diving:getPlayerData('level')
if not level then return end
local hasGear, gearItem, gearMetadata, gearDurability = false, nil, nil, 0
for requiredLevel, gear in pairs(shared.gear.tanks) do
if level >= requiredLevel and HasItem(gear.item, 1) then
gearMetadata = GetMetadata(gear.item)
if gearMetadata and gearMetadata[metatype] then
gearDurability = gearMetadata[metatype]
hasGear = true
gearItem = gear.item
break
end
end
end
if not hasGear then
ShowNotification(locale('notify.no-scuba-gear'), 'error')
return
end
local maxTransfer = math.min(tankDurability, 100 - gearDurability)
---@diagnostic disable-next-line: undefined-field
maxTransfer = math.round(maxTransfer, 2)
if maxTransfer <= 0 then
ShowNotification(locale('notify.scuba-gear-full'), 'error')
return
end
local input = ShowInput({
title = 'Refill Tank',
options = {
{ type = 'slider', label = locale('Select how much oxygen to transfer'), required = true, min = 0, max = maxTransfer, step = 0.01 }
}
})
if not input or not input[1] then return end
local transferAmount = input[1]
gearDurability = math.min(gearDurability + transferAmount, 100)
---@diagnostic disable-next-line: undefined-field
gearDurability = math.round(gearDurability, 2)
tankDurability = tankDurability - transferAmount
---@diagnostic disable-next-line: undefined-field
tankDurability = math.round(tankDurability, 2)
TriggerServerEvent('lation_diving:setMetadata', cache.serverId, gearItem, metatype, gearDurability)
TriggerServerEvent('lation_diving:setMetadata', cache.serverId, shared.gear.refill, metatype, tankDurability)
ShowNotification(locale('notify.refill-success'), 'success')
end
if shared.setup.debug then
RegisterCommand('debugzones', function()
local crates = {}
for id, zone in pairs(shared.zones) do
lib.zones.sphere({
coords = zone.location,
radius = zone.radius,
onEnter = function()
print(('You entered zoneId %s'):format(id))
for _, coords in pairs(zone.coords) do
local model = zone.models and zone.models[math.random(#zone.models)] or `prop_box_wood02a`
lib.requestModel(model)
local crate = CreateObject(model, coords.x, coords.y, coords.z, false, true, false)
PlaceObjectOnGroundProperly(crate)
FreezeEntityPosition(crate, true)
SetModelAsNoLongerNeeded(model)
crates[#crates + 1] = crate
AddCircleZone({
coords = GetEntityCoords(crate),
name = 'debug-crate' ..crate,
radius = 1.5,
debug = shared.setup.debug,
options = {
label = 'Debug crate ' .. id,
distance = 5.0,
icon = 'fas fa-bug',
onSelect = function()
print(('Crate coords: %s'):format(coords))
end,
action = function()
print(('Crate coords: %s'):format(coords))
end
}
})
end
end,
onExit = function()
print(('You exited zoneId %s'):format(id))
for _, crate in ipairs(crates) do
RemoveCircleZone('debug-crate' ..crate)
if DoesEntityExist(crate) then
DeleteEntity(crate)
end
end
crates = {}
end,
debug = shared.setup.debug
})
end
end, false)
end
-- Register callback(s)
lib.callback.register('lation_diving:openCrate', openCrate)
-- Register net event(s)
RegisterNetEvent('lation_diving:useRefill', useRefill)
-- Initialize default(s)
InitializePhone()
InitializeKeys()
InitializeFuel()
