forked from Simnation/Main
fishing
This commit is contained in:
parent
7e2451be50
commit
176f249885
23 changed files with 1323 additions and 0 deletions
|
@ -0,0 +1,42 @@
|
||||||
|
-- Place in ox_inventory/data/items.lua
|
||||||
|
['tuna'] = {
|
||||||
|
label = 'Tuna',
|
||||||
|
weight = 650,
|
||||||
|
stack = true,
|
||||||
|
close = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
['salmon'] = {
|
||||||
|
label = 'Salmon',
|
||||||
|
weight = 350,
|
||||||
|
stack = true,
|
||||||
|
close = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
['trout'] = {
|
||||||
|
label = 'Trout',
|
||||||
|
weight = 250,
|
||||||
|
stack = true,
|
||||||
|
close = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
['anchovy'] = {
|
||||||
|
label = 'Anchovy',
|
||||||
|
weight = 50,
|
||||||
|
stack = true,
|
||||||
|
close = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
['fishbait'] = {
|
||||||
|
label = 'Fish Bait',
|
||||||
|
weight = 50,
|
||||||
|
stack = true,
|
||||||
|
close = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
['fishingrod'] = {
|
||||||
|
label = 'Fishing Rod',
|
||||||
|
weight = 800,
|
||||||
|
stack = true,
|
||||||
|
close = true,
|
||||||
|
},
|
|
@ -0,0 +1,8 @@
|
||||||
|
INSERT INTO `items` (`name`, `label`, `limit`) VALUES
|
||||||
|
('fishingrod', 'Fishing Rod', 20),
|
||||||
|
('fishbait', 'Fish Bait', 20),
|
||||||
|
('anchovy', 'Anchovy', 20),
|
||||||
|
('trout', 'Trout', 20),
|
||||||
|
('salmon', 'Salmon', 20),
|
||||||
|
('tuna', 'Tuna', 40)
|
||||||
|
;
|
|
@ -0,0 +1,8 @@
|
||||||
|
INSERT INTO `items` (`name`, `label`, `weight`) VALUES
|
||||||
|
('fishingrod', 'Fishing Rod', 20),
|
||||||
|
('fishbait', 'Fish Bait', 20),
|
||||||
|
('anchovy', 'Anchovy', 20),
|
||||||
|
('trout', 'Trout', 20),
|
||||||
|
('salmon', 'Salmon', 20),
|
||||||
|
('tuna', 'Tuna', 40)
|
||||||
|
;
|
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
After Width: | Height: | Size: 4 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,7 @@
|
||||||
|
-- Add to qb-core/shared/items.lua
|
||||||
|
['fishingrod'] = {['name'] = 'fishingrod', ['label'] = 'Fishing Rod', ['weight'] = 250, ['type'] = 'item', ['image'] = 'fishingrod.png', ['unique'] = false, ['useable'] = true, ['shouldClose'] = true, ['combinable'] = nil, ['description'] = 'Use this with bait to catch fish.'},
|
||||||
|
['fishbait'] = {['name'] = 'fishbait', ['label'] = 'Fishing Bait', ['weight'] = 5, ['type'] = 'item', ['image'] = 'fishbait.png', ['unique'] = false, ['useable'] = false, ['shouldClose'] = true, ['combinable'] = true, ['description'] = 'With a fishing rod this can catch some fish.'},
|
||||||
|
['anchovy'] = {['name'] = 'anchovy', ['label'] = 'Anchovy', ['weight'] = 35, ['type'] = 'item', ['image'] = 'anchovy.png', ['unique'] = false, ['useable'] = false, ['shouldClose'] = true, ['combinable'] = true, ['description'] = 'A breed of fish.'},
|
||||||
|
['trout'] = {['name'] = 'trout', ['label'] = 'Trout', ['weight'] = 85, ['type'] = 'item', ['image'] = 'trout.png', ['unique'] = false, ['useable'] = false, ['shouldClose'] = true, ['combinable'] = true, ['description'] = 'A breed of fish.'},
|
||||||
|
['salmon'] = {['name'] = 'salmon', ['label'] = 'Salmon', ['weight'] = 125, ['type'] = 'item', ['image'] = 'salmon.png', ['unique'] = false, ['useable'] = false, ['shouldClose'] = true, ['combinable'] = true, ['description'] = 'A breed of fish.'},
|
||||||
|
['tuna'] = {['name'] = 'tuna', ['label'] = 'Tuna', ['weight'] = 225, ['type'] = 'item', ['image'] = 'tuna.png', ['unique'] = false, ['useable'] = false, ['shouldClose'] = true, ['combinable'] = true, ['description'] = 'A breed of fish.'},
|
|
@ -0,0 +1,42 @@
|
||||||
|
-- Place in ox_inventory/data/items.lua
|
||||||
|
['tuna'] = {
|
||||||
|
label = 'Tuna',
|
||||||
|
weight = 650,
|
||||||
|
stack = true,
|
||||||
|
close = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
['salmon'] = {
|
||||||
|
label = 'Salmon',
|
||||||
|
weight = 350,
|
||||||
|
stack = true,
|
||||||
|
close = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
['trout'] = {
|
||||||
|
label = 'Trout',
|
||||||
|
weight = 250,
|
||||||
|
stack = true,
|
||||||
|
close = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
['anchovy'] = {
|
||||||
|
label = 'Anchovy',
|
||||||
|
weight = 50,
|
||||||
|
stack = true,
|
||||||
|
close = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
['fishbait'] = {
|
||||||
|
label = 'Fish Bait',
|
||||||
|
weight = 50,
|
||||||
|
stack = true,
|
||||||
|
close = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
['fishingrod'] = {
|
||||||
|
label = 'Fishing Rod',
|
||||||
|
weight = 800,
|
||||||
|
stack = true,
|
||||||
|
close = true,
|
||||||
|
},
|
44
resources/[jobs]/[civ]/wasabi_fishing/README.md
Normal file
44
resources/[jobs]/[civ]/wasabi_fishing/README.md
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# wasabi_fishing
|
||||||
|
|
||||||
|
This resource was created as a free interactive fishing script for ESX/QBCore servers.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Out of the box compatible with ESX and QBCore
|
||||||
|
- Optimized 0.00ms usage on idle
|
||||||
|
- Skill-check based success
|
||||||
|
- Full animations and props
|
||||||
|
- Chance of fishing rod breaking upon failing skill-check(Can be changed in config)
|
||||||
|
- Configurable random wait time for getting bite on line
|
||||||
|
- Configurable fishing rewards(4 by default included)
|
||||||
|
- Configurable prices to sell fishing rewards
|
||||||
|
- Configurable skill-check difficulty per fishing reward
|
||||||
|
- Ability to fish from boat, pier, or anywhere with a body of water
|
||||||
|
- No job requirement
|
||||||
|
- Fully configurable fish buyer to sell fish
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- es_extended OR qb-core
|
||||||
|
- ox_lib - https://github.com/overextended/ox_lib/releases
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
- Make sure you have dependencies
|
||||||
|
|
||||||
|
- Make sure items are added to server(Check `Inventory_Items` directory for images/examples
|
||||||
|
|
||||||
|
- Put script in your `resources` directory
|
||||||
|
|
||||||
|
- Add `ensure wasabi_fishing` in your `server.cfg` (*After* dependencies)
|
||||||
|
|
||||||
|
### Extra Information
|
||||||
|
- Make sure `ox_lib` starts before `wasabi_fishing`
|
||||||
|
- Inventory images included in the `Inventory_Items/InventoryImages` directory
|
||||||
|
- You must add the item `fishingrod` and `fishbait` to one of your in-game shops or have a place for your players to obtain.
|
||||||
|
|
||||||
|
## Preview
|
||||||
|
https://www.youtube.com/watch?v=kLLPGJIK3Q0
|
||||||
|
|
||||||
|
|
||||||
|
# Support
|
||||||
|
<a href='https://discord.gg/79zjvy4JMs'></a>
|
65
resources/[jobs]/[civ]/wasabi_fishing/bridge/esx/client.lua
Normal file
65
resources/[jobs]/[civ]/wasabi_fishing/bridge/esx/client.lua
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
-----------------For support, scripts, and more----------------
|
||||||
|
--------------- https://discord.gg/wasabiscripts -------------
|
||||||
|
---------------------------------------------------------------
|
||||||
|
if GetResourceState('es_extended') ~= 'started' then return end
|
||||||
|
ESX = exports['es_extended']:getSharedObject()
|
||||||
|
Framework, PlayerLoaded, PlayerData = 'esx', nil, {}
|
||||||
|
|
||||||
|
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
|
||||||
|
PlayerData = xPlayer
|
||||||
|
PlayerLoaded = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('esx:onPlayerSpawn', function()
|
||||||
|
TriggerEvent('wasabi_fishing:onPlayerSpawn')
|
||||||
|
end)
|
||||||
|
|
||||||
|
AddEventHandler('esx:onPlayerDeath', function(data)
|
||||||
|
TriggerEvent('wasabi_fishing:onPlayerDeath')
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('esx:onPlayerLogout', function()
|
||||||
|
table.wipe(PlayerData)
|
||||||
|
PlayerLoaded = false
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('esx:setJob', function(job)
|
||||||
|
PlayerData.job = job
|
||||||
|
end)
|
||||||
|
|
||||||
|
AddEventHandler('onResourceStart', function(resourceName)
|
||||||
|
if GetCurrentResourceName() ~= resourceName or not ESX.PlayerLoaded then return end
|
||||||
|
PlayerData = ESX.GetPlayerData()
|
||||||
|
PlayerLoaded = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
AddEventHandler('esx:setPlayerData', function(key, value)
|
||||||
|
if GetInvokingResource() ~= 'es_extended' then return end
|
||||||
|
PlayerData[key] = value
|
||||||
|
end)
|
||||||
|
|
||||||
|
function HasGroup(filter)
|
||||||
|
local type = type(filter)
|
||||||
|
|
||||||
|
if type == 'string' then
|
||||||
|
if PlayerData.job.name == filter then
|
||||||
|
return PlayerData.job.name, PlayerData.job.grade
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local tabletype = table.type(filter)
|
||||||
|
|
||||||
|
if tabletype == 'hash' then
|
||||||
|
local grade = filter[PlayerData.job.name]
|
||||||
|
|
||||||
|
if grade and grade <= PlayerData.job.grade then
|
||||||
|
return PlayerData.job.name, PlayerData.job.grade
|
||||||
|
end
|
||||||
|
elseif tabletype == 'array' then
|
||||||
|
for i = 1, #filter do
|
||||||
|
if PlayerData.job.name == filter[i] then
|
||||||
|
return PlayerData.job.name, PlayerData.job.grade
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
85
resources/[jobs]/[civ]/wasabi_fishing/bridge/esx/server.lua
Normal file
85
resources/[jobs]/[civ]/wasabi_fishing/bridge/esx/server.lua
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
if GetResourceState('es_extended') ~= 'started' then return end
|
||||||
|
ESX = exports['es_extended']:getSharedObject()
|
||||||
|
Framework = 'esx'
|
||||||
|
|
||||||
|
function GetPlayer(source)
|
||||||
|
return ESX.GetPlayerFromId(source)
|
||||||
|
end
|
||||||
|
|
||||||
|
function KickPlayer(source, reason)
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
return player.kick(reason)
|
||||||
|
end
|
||||||
|
|
||||||
|
function HasGroup(source, filter)
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
local type = type(filter)
|
||||||
|
|
||||||
|
if type == 'string' then
|
||||||
|
if player.job.name == filter then
|
||||||
|
return player.job.name, player.job.grade
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local tabletype = table.type(filter)
|
||||||
|
|
||||||
|
if tabletype == 'hash' then
|
||||||
|
local grade = filter[player.job.name]
|
||||||
|
|
||||||
|
if grade and grade <= player.job.grade then
|
||||||
|
return player.job.name, player.job.grade
|
||||||
|
end
|
||||||
|
elseif tabletype == 'array' then
|
||||||
|
for i = 1, #filter do
|
||||||
|
if player.job.name == filter[i] then
|
||||||
|
return player.job.name, player.job.grade
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function GetIdentifier(source)
|
||||||
|
local xPlayer = ESX.GetPlayerFromId(source)
|
||||||
|
return xPlayer.identifier
|
||||||
|
end
|
||||||
|
|
||||||
|
function GetName(source)
|
||||||
|
local xPlayer = ESX.GetPlayerFromId(source)
|
||||||
|
return xPlayer.getName()
|
||||||
|
end
|
||||||
|
|
||||||
|
function RegisterUsableItem(item, cb)
|
||||||
|
ESX.RegisterUsableItem(item, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
function HasItem(source, item)
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
local item = player.getInventoryItem(item)
|
||||||
|
if item ~= nil then
|
||||||
|
return item.count
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function AddItem(source, item, count, slot, metadata)
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
return player.addInventoryItem(item, count, metadata, slot)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RemoveItem(source, item, count, slot, metadata)
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
player.removeInventoryItem(item, count, metadata, slot)
|
||||||
|
end
|
||||||
|
|
||||||
|
function AddMoney(source, type, amount)
|
||||||
|
if type == 'cash' then type = 'money' end
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
player.addAccountMoney(type, amount)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RemoveMoney(source, type, amount)
|
||||||
|
if type == 'cash' then type = 'money' end
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
player.removeAccountMoney(type, amount)
|
||||||
|
end
|
78
resources/[jobs]/[civ]/wasabi_fishing/bridge/qb/client.lua
Normal file
78
resources/[jobs]/[civ]/wasabi_fishing/bridge/qb/client.lua
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
if GetResourceState('qb-core') ~= 'started' then return end
|
||||||
|
QBCore = exports['qb-core']:GetCoreObject()
|
||||||
|
Framework, PlayerLoaded, PlayerData = 'qb', nil, {}
|
||||||
|
|
||||||
|
AddStateBagChangeHandler('isLoggedIn', '', function(_bagName, _key, value, _reserved, _replicated)
|
||||||
|
if value then
|
||||||
|
PlayerData = QBCore.Functions.GetPlayerData()
|
||||||
|
else
|
||||||
|
table.wipe(PlayerData)
|
||||||
|
end
|
||||||
|
PlayerLoaded = value
|
||||||
|
end)
|
||||||
|
|
||||||
|
AddEventHandler('onResourceStart', function(resourceName)
|
||||||
|
if GetCurrentResourceName() ~= resourceName or not LocalPlayer.state.isLoggedIn then return end
|
||||||
|
PlayerData = QBCore.Functions.GetPlayerData()
|
||||||
|
PlayerLoaded = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
AddEventHandler('gameEventTriggered', function(event, data)
|
||||||
|
if event ~= 'CEventNetworkEntityDamage' then return end
|
||||||
|
local victim, victimDied = data[1], data[4]
|
||||||
|
if not IsPedAPlayer(victim) then return end
|
||||||
|
local player = PlayerId()
|
||||||
|
if victimDied and NetworkGetPlayerIndexFromPed(victim) == player and (IsPedDeadOrDying(victim, true) or IsPedFatallyInjured(victim)) then
|
||||||
|
TriggerEvent('wasabi_fishing:onPlayerDeath')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
|
||||||
|
TriggerEvent('wasabi_fishing:onPlayerSpawn')
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('QBCore:Player:SetPlayerData', function(newPlayerData)
|
||||||
|
if source ~= '' and GetInvokingResource() ~= 'qb-core' then return end
|
||||||
|
PlayerData = newPlayerData
|
||||||
|
end)
|
||||||
|
|
||||||
|
function HasGroup(filter)
|
||||||
|
local groups = { 'job', 'gang' }
|
||||||
|
local type = type(filter)
|
||||||
|
|
||||||
|
if type == 'string' then
|
||||||
|
for i = 1, #groups do
|
||||||
|
local data = PlayerData[groups[i]]
|
||||||
|
|
||||||
|
if data.name == filter then
|
||||||
|
return data.name, data.grade.level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local tabletype = table.type(filter)
|
||||||
|
|
||||||
|
if tabletype == 'hash' then
|
||||||
|
for i = 1, #groups do
|
||||||
|
local data = PlayerData[groups[i]]
|
||||||
|
local grade = filter[data.name]
|
||||||
|
|
||||||
|
if grade and grade <= data.grade.level then
|
||||||
|
return data.name, data.grade.level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif tabletype == 'array' then
|
||||||
|
for i = 1, #filter do
|
||||||
|
local group = filter[i]
|
||||||
|
|
||||||
|
for j = 1, #groups do
|
||||||
|
local data = PlayerData[groups[j]]
|
||||||
|
|
||||||
|
if data.name == group then
|
||||||
|
return data.name, data.grade.level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
98
resources/[jobs]/[civ]/wasabi_fishing/bridge/qb/server.lua
Normal file
98
resources/[jobs]/[civ]/wasabi_fishing/bridge/qb/server.lua
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
if GetResourceState('qb-core') ~= 'started' then return end
|
||||||
|
QBCore = exports['qb-core']:GetCoreObject()
|
||||||
|
Framework = 'qb'
|
||||||
|
|
||||||
|
function GetPlayer(source)
|
||||||
|
return QBCore.Functions.GetPlayer(source)
|
||||||
|
end
|
||||||
|
|
||||||
|
function KickPlayer(source, reason)
|
||||||
|
QBCore.Functions.Kick(source, reason, true, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
function HasGroup(source, filter)
|
||||||
|
local groups = { 'job', 'gang' }
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
local type = type(filter)
|
||||||
|
|
||||||
|
if type == 'string' then
|
||||||
|
for i = 1, #groups do
|
||||||
|
local data = player.PlayerData[groups[i]]
|
||||||
|
|
||||||
|
if data.name == filter then
|
||||||
|
return data.name, data.grade.level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local tabletype = table.type(filter)
|
||||||
|
|
||||||
|
if tabletype == 'hash' then
|
||||||
|
for i = 1, #groups do
|
||||||
|
local data = player.PlayerData[groups[i]]
|
||||||
|
local grade = filter[data.name]
|
||||||
|
|
||||||
|
if grade and grade <= data.grade.level then
|
||||||
|
return data.name, data.grade.level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif tabletype == 'array' then
|
||||||
|
for i = 1, #filter do
|
||||||
|
local group = filter[i]
|
||||||
|
|
||||||
|
for j = 1, #groups do
|
||||||
|
local data = player.PlayerData[groups[j]]
|
||||||
|
|
||||||
|
if data.name == group then
|
||||||
|
return data.name, data.grade.level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function GetIdentifier(source)
|
||||||
|
local xPlayer = QBCore.Functions.GetPlayer(source)
|
||||||
|
return xPlayer.PlayerData.citizenid
|
||||||
|
end
|
||||||
|
|
||||||
|
function GetName(source)
|
||||||
|
local xPlayer = QBCore.Functions.GetPlayer(source)
|
||||||
|
return xPlayer.PlayerData.charinfo.firstname..' '..xPlayer.PlayerData.charinfo.lastname
|
||||||
|
end
|
||||||
|
|
||||||
|
function RegisterUsableItem(item, cb)
|
||||||
|
QBCore.Functions.CreateUseableItem(item, cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
function HasItem(source, item)
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
local item = player.Functions.GetItemByName(item)
|
||||||
|
if GetResourceState('ox_inventory') == 'started' then
|
||||||
|
return item?.count or 0
|
||||||
|
else
|
||||||
|
return item?.amount or 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function AddItem(source, item, count, slot, metadata)
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
return player.Functions.AddItem(item, count, slot, metadata)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RemoveItem(source, item, count, slot, metadata)
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
player.Functions.RemoveItem(item, count, slot, metadata)
|
||||||
|
end
|
||||||
|
|
||||||
|
function AddMoney(source, type, amount)
|
||||||
|
if type == 'money' then type = 'cash' end
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
player.Functions.AddMoney(type, amount)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RemoveMoney(source, type, amount)
|
||||||
|
if type == 'money' then type = 'cash' end
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
player.Functions.RemoveMoney(type, amount)
|
||||||
|
end
|
303
resources/[jobs]/[civ]/wasabi_fishing/client/client.lua
Normal file
303
resources/[jobs]/[civ]/wasabi_fishing/client/client.lua
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
-----------------For support, scripts, and more----------------
|
||||||
|
--------------- https://discord.gg/wasabiscripts -------------
|
||||||
|
---------------------------------------------------------------
|
||||||
|
local fishing = false
|
||||||
|
|
||||||
|
if Config.sellShop.enabled then
|
||||||
|
CreateThread(function()
|
||||||
|
local ped, textUI
|
||||||
|
CreateBlip(Config.sellShop.coords, 356, 1, Strings.sell_shop_blip, 0.80)
|
||||||
|
local point = lib.points.new({
|
||||||
|
coords = Config.sellShop.coords,
|
||||||
|
distance = 30
|
||||||
|
})
|
||||||
|
|
||||||
|
function point:nearby()
|
||||||
|
if self.currentDistance < self.distance then
|
||||||
|
if not ped then
|
||||||
|
lib.requestAnimDict('mini@strip_club@idles@bouncer@base', 100)
|
||||||
|
lib.requestModel(Config.sellShop.ped, 100)
|
||||||
|
ped = CreatePed(28, Config.sellShop.ped, Config.sellShop.coords.x, Config.sellShop.coords.y, Config.sellShop.coords.z, Config.sellShop.heading, false, false)
|
||||||
|
FreezeEntityPosition(ped, true)
|
||||||
|
SetEntityInvincible(ped, true)
|
||||||
|
SetBlockingOfNonTemporaryEvents(ped, true)
|
||||||
|
TaskPlayAnim(ped, 'mini@strip_club@idles@bouncer@base', 'base', 8.0, 0.0, -1, 1, 0, 0, 0, 0)
|
||||||
|
end
|
||||||
|
if self.currentDistance <= 1.8 then
|
||||||
|
if not textUI then
|
||||||
|
lib.showTextUI(Strings.sell_fish)
|
||||||
|
textUI = true
|
||||||
|
end
|
||||||
|
if IsControlJustReleased(0, 38) then
|
||||||
|
FishingSellItems()
|
||||||
|
end
|
||||||
|
elseif self.currentDistance >= 1.9 and textUI then
|
||||||
|
lib.hideTextUI()
|
||||||
|
textUI = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function point:onExit()
|
||||||
|
if ped then
|
||||||
|
local model = GetEntityModel(ped)
|
||||||
|
SetModelAsNoLongerNeeded(model)
|
||||||
|
DeletePed(ped)
|
||||||
|
SetPedAsNoLongerNeeded(ped)
|
||||||
|
RemoveAnimDict('mini@strip_club@idles@bouncer@base')
|
||||||
|
ped = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to select bait from available options
|
||||||
|
local function SelectBait()
|
||||||
|
local availableBaits = {}
|
||||||
|
|
||||||
|
-- Check all bait types
|
||||||
|
for _, bait in pairs(Config.bait.types) do
|
||||||
|
local hasItem = lib.callback.await('wasabi_fishing:checkItem', 100, bait.itemName)
|
||||||
|
if hasItem then
|
||||||
|
table.insert(availableBaits, bait)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #availableBaits == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If only one bait type is available, use it directly
|
||||||
|
if #availableBaits == 1 then
|
||||||
|
return availableBaits[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create a menu for bait selection
|
||||||
|
local options = {}
|
||||||
|
for _, bait in pairs(availableBaits) do
|
||||||
|
table.insert(options, {
|
||||||
|
title = bait.label,
|
||||||
|
description = 'Use as fishing bait',
|
||||||
|
metadata = {
|
||||||
|
{label = 'Catch Bonus', value = '+' .. bait.catchBonus .. '%'},
|
||||||
|
{label = 'Loss Chance', value = bait.loseChance .. '%'}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local selectedIndex = lib.showContext('bait_selection_menu', {
|
||||||
|
id = 'bait_selection_menu',
|
||||||
|
title = 'Select Fishing Bait',
|
||||||
|
options = options
|
||||||
|
})
|
||||||
|
|
||||||
|
if selectedIndex then
|
||||||
|
return availableBaits[selectedIndex]
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to handle the fishing process
|
||||||
|
local function StartFishingProcess(selectedBait, waterLoc)
|
||||||
|
fishing = true
|
||||||
|
|
||||||
|
-- Create fishing rod prop
|
||||||
|
local model = `prop_fishing_rod_01`
|
||||||
|
lib.requestModel(model, 100)
|
||||||
|
local pole = CreateObject(model, GetEntityCoords(cache.ped), true, false, false)
|
||||||
|
AttachEntityToEntity(pole, cache.ped, GetPedBoneIndex(cache.ped, 18905), 0.1, 0.05, 0, 80.0, 120.0, 160.0, true, true, false, true, 1, true)
|
||||||
|
SetModelAsNoLongerNeeded(model)
|
||||||
|
|
||||||
|
-- Load animations
|
||||||
|
lib.requestAnimDict('mini@tennis', 100)
|
||||||
|
lib.requestAnimDict('amb@world_human_stand_fishing@idle_a', 100)
|
||||||
|
|
||||||
|
-- Initial casting animation
|
||||||
|
TaskPlayAnim(cache.ped, 'mini@tennis', 'forehand_ts_md_far', 1.0, -1.0, 1.0, 48, 0, 0, 0, 0)
|
||||||
|
Wait(3000)
|
||||||
|
TaskPlayAnim(cache.ped, 'amb@world_human_stand_fishing@idle_a', 'idle_c', 1.0, -1.0, 1.0, 11, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
-- Main fishing loop
|
||||||
|
while fishing do
|
||||||
|
Wait(0)
|
||||||
|
local unarmed = `WEAPON_UNARMED`
|
||||||
|
SetCurrentPedWeapon(cache.ped, unarmed)
|
||||||
|
ShowHelp(Strings.intro_instruction)
|
||||||
|
DisableControlAction(0, 24, true)
|
||||||
|
|
||||||
|
-- Cast line
|
||||||
|
if IsDisabledControlJustReleased(0, 24) then
|
||||||
|
-- Casting animation
|
||||||
|
TaskPlayAnim(cache.ped, 'mini@tennis', 'forehand_ts_md_far', 1.0, -1.0, 1.0, 48, 0, 0, 0, 0)
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.waiting_bite, Strings.waiting_bite_desc, 'inform')
|
||||||
|
|
||||||
|
-- Wait for fish to bite
|
||||||
|
local waitTime = math.random(Config.timeForBite.min, Config.timeForBite.max)
|
||||||
|
Wait(waitTime)
|
||||||
|
|
||||||
|
-- Check if player is still fishing
|
||||||
|
if not fishing then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.got_bite, Strings.got_bite_desc, 'inform')
|
||||||
|
Wait(1000)
|
||||||
|
|
||||||
|
-- Get fish data based on selected bait
|
||||||
|
local fishData = lib.callback.await('wasabi_fishing:getFishData', 100, selectedBait.itemName)
|
||||||
|
|
||||||
|
-- Skill check to catch fish
|
||||||
|
if lib.skillCheck(fishData.difficulty) then
|
||||||
|
ClearPedTasks(cache.ped)
|
||||||
|
TryFish(fishData)
|
||||||
|
TaskPlayAnim(cache.ped, 'amb@world_human_stand_fishing@idle_a', 'idle_c', 1.0, -1.0, 1.0, 11, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
-- Check for bait loss
|
||||||
|
local loseChance = math.random(1,100)
|
||||||
|
if loseChance <= selectedBait.loseChance then
|
||||||
|
TriggerServerEvent('wasabi_fishing:loseBait', selectedBait.itemName)
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.bait_lost, Strings.bait_lost_desc, 'error')
|
||||||
|
|
||||||
|
-- Check if we still have bait
|
||||||
|
local hasBait = lib.callback.await('wasabi_fishing:checkItem', 100, selectedBait.itemName)
|
||||||
|
if not hasBait then
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.no_more_bait, Strings.no_more_bait_desc, 'error')
|
||||||
|
|
||||||
|
-- Try to select a new bait
|
||||||
|
local newBait = SelectBait()
|
||||||
|
if newBait then
|
||||||
|
selectedBait = newBait
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.new_bait, string.format(Strings.new_bait_desc, selectedBait.label), 'inform')
|
||||||
|
else
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.no_bait, Strings.no_bait_desc, 'error')
|
||||||
|
fishing = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Failed to catch fish
|
||||||
|
local breakChance = math.random(1,100)
|
||||||
|
if breakChance < Config.fishingRod.breakChance then
|
||||||
|
TriggerServerEvent('wasabi_fishing:rodBroke')
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.rod_broke, Strings.rod_broke_desc, 'error')
|
||||||
|
ClearPedTasks(cache.ped)
|
||||||
|
fishing = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.failed_fish, Strings.failed_fish_desc, 'error')
|
||||||
|
end
|
||||||
|
elseif IsControlJustReleased(0, 194) then
|
||||||
|
-- Cancel fishing with backspace
|
||||||
|
ClearPedTasks(cache.ped)
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.fishing_canceled, Strings.fishing_canceled_desc, 'inform')
|
||||||
|
break
|
||||||
|
elseif #(GetEntityCoords(cache.ped) - waterLoc) > 30 then
|
||||||
|
-- Too far from water
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.too_far, Strings.too_far_desc, 'error')
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clean up
|
||||||
|
fishing = false
|
||||||
|
DeleteObject(pole)
|
||||||
|
RemoveAnimDict('mini@tennis')
|
||||||
|
RemoveAnimDict('amb@world_human_stand_fishing@idle_a')
|
||||||
|
end
|
||||||
|
|
||||||
|
RegisterNetEvent('wasabi_fishing:startFishing', function()
|
||||||
|
-- Check if player is in a valid state to fish
|
||||||
|
if IsPedInAnyVehicle(cache.ped) or IsPedSwimming(cache.ped) then
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.cannot_perform, Strings.cannot_perform_desc, 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if already fishing
|
||||||
|
if fishing then
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.already_fishing, Strings.already_fishing_desc, 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for water
|
||||||
|
local water, waterLoc = WaterCheck()
|
||||||
|
if not water then
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.no_water, Strings.no_water_desc, 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Select bait
|
||||||
|
local selectedBait = SelectBait()
|
||||||
|
if not selectedBait then
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.no_bait, Strings.no_bait_desc, 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start fishing process
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.bait_selected, string.format(Strings.bait_selected_desc, selectedBait.label), 'inform')
|
||||||
|
StartFishingProcess(selectedBait, waterLoc)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Process fish with knife
|
||||||
|
RegisterNetEvent('wasabi_fishing:processFish', function(fishItem)
|
||||||
|
-- Check if player has knife
|
||||||
|
local hasKnife = lib.callback.await('wasabi_fishing:checkItem', 100, Config.processing.knifeItem)
|
||||||
|
if not hasKnife then
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.no_knife, Strings.no_knife_desc, 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find processing data for this fish
|
||||||
|
local processData = nil
|
||||||
|
for _, data in pairs(Config.processing.products) do
|
||||||
|
if data.sourceItem == fishItem then
|
||||||
|
processData = data
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not processData then
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.cannot_process, Strings.cannot_process_desc, 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start processing animation
|
||||||
|
local playerPed = cache.ped
|
||||||
|
local coords = GetEntityCoords(playerPed)
|
||||||
|
|
||||||
|
lib.requestAnimDict('anim@amb@business@coc@coc_unpack_cut@', 100)
|
||||||
|
TaskPlayAnim(playerPed, 'anim@amb@business@coc@coc_unpack_cut@', 'fullcut_cycle_v6_cokecutter', 8.0, -8.0, -1, 1, 0, false, false, false)
|
||||||
|
|
||||||
|
-- Processing progress bar
|
||||||
|
if lib.progressBar({
|
||||||
|
duration = 5000,
|
||||||
|
label = Strings.processing_fish,
|
||||||
|
useWhileDead = false,
|
||||||
|
canCancel = true,
|
||||||
|
disable = {
|
||||||
|
car = true,
|
||||||
|
move = true,
|
||||||
|
combat = true
|
||||||
|
},
|
||||||
|
anim = {
|
||||||
|
dict = 'anim@amb@business@coc@coc_unpack_cut@',
|
||||||
|
clip = 'fullcut_cycle_v6_cokecutter'
|
||||||
|
},
|
||||||
|
}) then
|
||||||
|
-- Success - server handles the actual item processing
|
||||||
|
TriggerServerEvent('wasabi_fishing:processItem', fishItem)
|
||||||
|
else
|
||||||
|
-- Cancelled
|
||||||
|
TriggerEvent('wasabi_fishing:notify', Strings.canceled, Strings.canceled_desc, 'error')
|
||||||
|
end
|
||||||
|
|
||||||
|
ClearPedTasks(playerPed)
|
||||||
|
RemoveAnimDict('anim@amb@business@coc@coc_unpack_cut@')
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('wasabi_fishing:interupt', function()
|
||||||
|
fishing = false
|
||||||
|
ClearPedTasks(cache.ped)
|
||||||
|
end)
|
||||||
|
|
36
resources/[jobs]/[civ]/wasabi_fishing/client/functions.lua
Normal file
36
resources/[jobs]/[civ]/wasabi_fishing/client/functions.lua
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
-----------------For support, scripts, and more----------------
|
||||||
|
--------------- https://discord.gg/wasabiscripts -------------
|
||||||
|
---------------------------------------------------------------
|
||||||
|
|
||||||
|
ShowHelp = function(msg)
|
||||||
|
BeginTextCommandDisplayHelp('STRING')
|
||||||
|
AddTextComponentSubstringPlayerName(msg)
|
||||||
|
EndTextCommandDisplayHelp(0, false, true, -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
WaterCheck = function()
|
||||||
|
local headPos = GetPedBoneCoords(cache.ped, 31086, 0.0, 0.0, 0.0)
|
||||||
|
local offsetPos = GetOffsetFromEntityInWorldCoords(cache.ped, 0.0, 50.0, -25.0)
|
||||||
|
local water, waterPos = TestProbeAgainstWater(headPos.x, headPos.y, headPos.z, offsetPos.x, offsetPos.y, offsetPos.z)
|
||||||
|
return water, waterPos
|
||||||
|
end
|
||||||
|
|
||||||
|
CreateBlip = function(coords, sprite, colour, text, scale)
|
||||||
|
local blip = AddBlipForCoord(coords)
|
||||||
|
SetBlipSprite(blip, sprite)
|
||||||
|
SetBlipColour(blip, colour)
|
||||||
|
SetBlipAsShortRange(blip, true)
|
||||||
|
SetBlipScale(blip, scale)
|
||||||
|
AddTextEntry(text, text)
|
||||||
|
BeginTextCommandSetBlipName(text)
|
||||||
|
EndTextCommandSetBlipName(blip)
|
||||||
|
return blip
|
||||||
|
end
|
||||||
|
|
||||||
|
TryFish = function(data)
|
||||||
|
TriggerServerEvent('wasabi_fishing:tryFish', data)
|
||||||
|
end
|
||||||
|
|
||||||
|
FishingSellItems = function()
|
||||||
|
TriggerServerEvent('wasabi_fishing:sellFish')
|
||||||
|
end
|
146
resources/[jobs]/[civ]/wasabi_fishing/configuration/config.lua
Normal file
146
resources/[jobs]/[civ]/wasabi_fishing/configuration/config.lua
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
-----------------For support, scripts, and more----------------
|
||||||
|
--------------- https://discord.gg/wasabiscripts -------------
|
||||||
|
---------------------------------------------------------------
|
||||||
|
|
||||||
|
local seconds, minutes = 1000, 60000
|
||||||
|
Config = {}
|
||||||
|
|
||||||
|
Config.checkForUpdates = true -- Check for updates?
|
||||||
|
Config.oldESX = false -- Nothing to do with qb / Essentially when set to true it disables the check of if player can carry item
|
||||||
|
|
||||||
|
Config.sellShop = {
|
||||||
|
enabled = true,
|
||||||
|
coords = vec3(-1612.19, -989.18, 13.01-0.9), -- X, Y, Z Coords of where fish buyer will spawn
|
||||||
|
heading = 45.3, -- Heading of fish buyer ped
|
||||||
|
ped = 'cs_old_man2' -- Ped name here
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.bait = {
|
||||||
|
types = {
|
||||||
|
{
|
||||||
|
itemName = 'fishbait',
|
||||||
|
label = 'Standard Bait',
|
||||||
|
loseChance = 65,
|
||||||
|
catchBonus = 0 -- base chance
|
||||||
|
},
|
||||||
|
{
|
||||||
|
itemName = 'worm_bait',
|
||||||
|
label = 'Worm',
|
||||||
|
loseChance = 50,
|
||||||
|
catchBonus = 10 -- 10% better catch chance
|
||||||
|
},
|
||||||
|
{
|
||||||
|
itemName = 'bread_bait',
|
||||||
|
label = 'Bread',
|
||||||
|
loseChance = 75,
|
||||||
|
catchBonus = 5 -- 5% better catch chance
|
||||||
|
},
|
||||||
|
{
|
||||||
|
itemName = 'artificial_lure',
|
||||||
|
label = 'Artificial Lure',
|
||||||
|
loseChance = 30, -- harder to lose
|
||||||
|
catchBonus = 15 -- 15% better catch chance
|
||||||
|
},
|
||||||
|
{
|
||||||
|
itemName = 'young_salmon',
|
||||||
|
label = 'Young Salmon',
|
||||||
|
loseChance = 60,
|
||||||
|
catchBonus = 20, -- 20% better catch chance
|
||||||
|
exclusive = {'pufferfish'} -- can only catch pufferfish with this
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultBait = 'fishbait' -- Default bait item name
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.processing = {
|
||||||
|
knifeItem = 'weapon_knife',
|
||||||
|
products = {
|
||||||
|
{
|
||||||
|
sourceItem = 'tuna',
|
||||||
|
yield = {
|
||||||
|
{ item = 'fish_fillet', amount = {3, 5} },
|
||||||
|
{ item = 'caviar', chance = 5 } -- 5% chance
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceItem = 'salmon',
|
||||||
|
yield = {
|
||||||
|
{ item = 'fish_fillet', amount = {2, 4} },
|
||||||
|
{ item = 'caviar', chance = 15 } -- 15% chance
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceItem = 'trout',
|
||||||
|
yield = {
|
||||||
|
{ item = 'fish_fillet', amount = {1, 3} },
|
||||||
|
{ item = 'caviar', chance = 3 } -- 3% chance
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceItem = 'anchovy',
|
||||||
|
yield = {
|
||||||
|
{ item = 'fish_fillet', amount = {1, 2} }
|
||||||
|
-- No caviar from anchovy
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceItem = 'young_salmon',
|
||||||
|
yield = {
|
||||||
|
{ item = 'fish_fillet', amount = {1, 1} }
|
||||||
|
-- No caviar from young salmon
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceItem = 'pufferfish',
|
||||||
|
yield = {
|
||||||
|
{ item = 'fish_fillet', amount = {1, 3} },
|
||||||
|
{ item = 'caviar', chance = 25 } -- 25% chance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.fish = {
|
||||||
|
{ item = 'tuna', label = 'Tuna', price = {300, 550}, difficulty = {'medium', 'easy', 'easy'} },
|
||||||
|
{ item = 'salmon', label = 'Salmon', price = {235, 300}, difficulty = {'medium', 'easy'} },
|
||||||
|
{ item = 'trout', label = 'Trout', price = {190, 235}, difficulty = {'easy', 'easy'} },
|
||||||
|
{ item = 'anchovy', label = 'Anchovy', price = {100, 190}, difficulty = {'easy'} },
|
||||||
|
{ item = 'young_salmon', label = 'Young Salmon', price = {50, 100}, difficulty = {'easy'} },
|
||||||
|
{ item = 'pufferfish', label = 'Pufferfish', price = {500, 800}, difficulty = {'hard', 'hard', 'medium'} },
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Add prices for processed items
|
||||||
|
Config.processedItems = {
|
||||||
|
{ item = 'fish_fillet', label = 'Fish Fillet', price = {50, 100} },
|
||||||
|
{ item = 'caviar', label = 'Caviar', price = {300, 500} },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Config.fishingRod = {
|
||||||
|
itemName = 'fishingrod', -- Item name of fishing rod
|
||||||
|
breakChance = 25 --Chance of breaking pole when failing skillbar (Setting to 0 means never break)
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.timeForBite = { -- Set min and max random range of time it takes for fish to be on the line.
|
||||||
|
min = 2 * seconds,
|
||||||
|
max = 20 * seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RegisterNetEvent('wasabi_fishing:notify')
|
||||||
|
AddEventHandler('wasabi_fishing:notify', function(title, message, msgType)
|
||||||
|
-- Place notification system info here, ex: exports['mythic_notify']:SendAlert('inform', message)
|
||||||
|
if not msgType then
|
||||||
|
lib.notify({
|
||||||
|
title = title,
|
||||||
|
description = message,
|
||||||
|
type = 'inform'
|
||||||
|
})
|
||||||
|
else
|
||||||
|
lib.notify({
|
||||||
|
title = title,
|
||||||
|
description = message,
|
||||||
|
type = msgType
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
|
@ -0,0 +1,38 @@
|
||||||
|
-----------------For support, scripts, and more----------------
|
||||||
|
--------------- https://discord.gg/wasabiscripts -------------
|
||||||
|
---------------------------------------------------------------
|
||||||
|
Strings = {
|
||||||
|
intro_instruction = 'Press ~INPUT_ATTACK~ to cast line, ~INPUT_FRONTEND_RRIGHT~ to cancel.',
|
||||||
|
rod_broke = 'Rod Snapped',
|
||||||
|
rod_broke_desc = 'You pulled to hard and your fishing rod snapped!',
|
||||||
|
cannot_perform = 'Action Incomplete',
|
||||||
|
cannot_perform_desc = 'You cannot do this right now.',
|
||||||
|
failed = 'Failed',
|
||||||
|
failed_fish = 'You failed to catch fish!',
|
||||||
|
no_water = 'No Water',
|
||||||
|
no_water_desc = 'You are not facing any water.',
|
||||||
|
no_bait = 'Missing Bait',
|
||||||
|
no_bait_desc = 'You don\'t have fishing bait.',
|
||||||
|
bait_lost = 'Bait Lost',
|
||||||
|
bait_lost_desc = 'Fishing bait was lost.',
|
||||||
|
fish_success = 'Caught Fish',
|
||||||
|
fish_success_desc = 'You caught a %s!',
|
||||||
|
sell_shop_blip = 'Fish Market',
|
||||||
|
sell_fish = '[E] - Sell Fish',
|
||||||
|
kicked = 'Nice try, please do not attempt to exploit!',
|
||||||
|
sold_for = 'Sold Fish',
|
||||||
|
sold_for_desc = 'You sold %sx %s for $%s',
|
||||||
|
got_bite = 'Bite Detected',
|
||||||
|
got_bite_desc = 'Your pole has a fish on the line, get ready!',
|
||||||
|
waiting_bite = 'Awaiting Bite',
|
||||||
|
waiting_bite_desc = 'Please wait for a fish to bite your hook.',
|
||||||
|
cannot_carry = 'Cannot Carry',
|
||||||
|
cannot_carry_desc = 'You cannot carry reward!'
|
||||||
|
Strings.no_knife = 'No Knife'
|
||||||
|
Strings.no_knife_desc = 'You need a knife to process this fish.'
|
||||||
|
Strings.processing_success = 'Fish Processed'
|
||||||
|
Strings.processing_success_desc = 'You obtained %d %s from processing the fish.'
|
||||||
|
Strings.caviar_found = 'Caviar Found!'
|
||||||
|
Strings.caviar_found_desc = 'You found some valuable caviar while processing the fish!'
|
||||||
|
|
||||||
|
}
|
19
resources/[jobs]/[civ]/wasabi_fishing/fxmanifest.lua
Normal file
19
resources/[jobs]/[civ]/wasabi_fishing/fxmanifest.lua
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
-----------------For support, scripts, and more----------------
|
||||||
|
--------------- https://discord.gg/wasabiscripts -------------
|
||||||
|
---------------------------------------------------------------
|
||||||
|
|
||||||
|
fx_version 'cerulean'
|
||||||
|
game 'gta5'
|
||||||
|
lua54 'yes'
|
||||||
|
|
||||||
|
version '2.0.7'
|
||||||
|
author 'wasabirobby'
|
||||||
|
description 'Wasabi ESX/QBCore Skill Based Fishing'
|
||||||
|
|
||||||
|
shared_scripts { '@ox_lib/init.lua', 'configuration/*.lua' }
|
||||||
|
|
||||||
|
client_scripts { 'bridge/**/client.lua', 'client/*.lua' }
|
||||||
|
|
||||||
|
server_scripts { 'bridge/**/server.lua', 'server/*.lua' }
|
||||||
|
|
||||||
|
dependencies { 'ox_lib' }
|
241
resources/[jobs]/[civ]/wasabi_fishing/server/server.lua
Normal file
241
resources/[jobs]/[civ]/wasabi_fishing/server/server.lua
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
-----------------For support, scripts, and more----------------
|
||||||
|
--------------- https://discord.gg/wasabiscripts -------------
|
||||||
|
---------------------------------------------------------------
|
||||||
|
local addCommas = function(n)
|
||||||
|
return tostring(math.floor(n)):reverse():gsub("(%d%d%d)","%1,")
|
||||||
|
:gsub(",(%-?)$","%1"):reverse()
|
||||||
|
end
|
||||||
|
|
||||||
|
lib.callback.register('wasabi_fishing:checkItem', function(source, itemname)
|
||||||
|
local item = HasItem(source, itemname)
|
||||||
|
if item >= 1 then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
lib.callback.register('wasabi_fishing:getFishData', function(source)
|
||||||
|
local data = Config.fish[math.random(#Config.fish)]
|
||||||
|
return data
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('wasabi_fishing:rodBroke', function()
|
||||||
|
RemoveItem(source, Config.fishingRod.itemName, 1)
|
||||||
|
TriggerClientEvent('wasabi_fishing:interupt', source)
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('wasabi_fishing:tryFish', function(data)
|
||||||
|
local xPole = HasItem(source, Config.fishingRod.itemName)
|
||||||
|
local xBait = HasItem(source, Config.bait.itemName)
|
||||||
|
if xPole > 0 and xBait > 0 then
|
||||||
|
local chance = math.random(1,100)
|
||||||
|
if chance <= Config.bait.loseChance then
|
||||||
|
RemoveItem(source, Config.bait.itemName, 1)
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.bait_lost, Strings.bait_lost_desc, 'error')
|
||||||
|
end
|
||||||
|
if Framework == 'esx' and not Config.oldESX then
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
if player.canCarryItem(data.item, 1) then
|
||||||
|
AddItem(source, data.item, 1)
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.fish_success, string.format(Strings.fish_success_desc, data.label), 'success')
|
||||||
|
else
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.cannot_carry, Strings.cannot_carry_desc, 'error')
|
||||||
|
end
|
||||||
|
else
|
||||||
|
AddItem(source, data.item, 1)
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.fish_success, string.format(Strings.fish_success_desc, data.label), 'success')
|
||||||
|
end
|
||||||
|
elseif xPole > 0 and xBait < 1 then
|
||||||
|
TriggerClientEvent('wasabi_fishing:interupt', source)
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.no_bait, Strings.no_bait_desc, 'error')
|
||||||
|
elseif xPole < 1 then
|
||||||
|
KickPlayer(source, Strings.kicked)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('wasabi_fishing:sellFish', function()
|
||||||
|
local playerPed = GetPlayerPed(source)
|
||||||
|
local playerCoord = GetEntityCoords(playerPed)
|
||||||
|
local distance = #(playerCoord - Config.sellShop.coords)
|
||||||
|
if distance == nil then
|
||||||
|
KickPlayer(source, Strings.kicked)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if distance > 3 then
|
||||||
|
KickPlayer(source, Strings.kicked)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for i=1, #Config.fish do
|
||||||
|
if HasItem(source, Config.fish[i].item) > 0 then
|
||||||
|
local rewardAmount = 0
|
||||||
|
for j=1, HasItem(source, Config.fish[i].item) do
|
||||||
|
rewardAmount = rewardAmount + math.random(Config.fish[i].price[1], Config.fish[i].price[2])
|
||||||
|
end
|
||||||
|
if rewardAmount > 0 then
|
||||||
|
AddMoney(source, 'money', rewardAmount)
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.sold_for, (Strings.sold_for_desc):format(HasItem(source, Config.fish[i].item), Config.fish[i].label, addCommas(rewardAmount)), 'success')
|
||||||
|
RemoveItem(source, Config.fish[i].item, HasItem(source, Config.fish[i].item))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterUsableItem(Config.fishingRod.itemName, function(source)
|
||||||
|
TriggerClientEvent('wasabi_fishing:startFishing', source)
|
||||||
|
end)
|
||||||
|
-- Register usable items for all fish types for processing
|
||||||
|
for _, fish in pairs(Config.processing.products) do
|
||||||
|
RegisterUsableItem(fish.sourceItem, function(source)
|
||||||
|
local hasKnife = HasItem(source, Config.processing.knifeItem)
|
||||||
|
if hasKnife > 0 then
|
||||||
|
ProcessFish(source, fish)
|
||||||
|
else
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.no_knife, Strings.no_knife_desc, 'error')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to process fish
|
||||||
|
function ProcessFish(source, fishData)
|
||||||
|
-- Remove the fish
|
||||||
|
RemoveItem(source, fishData.sourceItem, 1)
|
||||||
|
|
||||||
|
-- Add fish fillets
|
||||||
|
local filletYield = fishData.yield[1]
|
||||||
|
local filletAmount = math.random(filletYield.amount[1], filletYield.amount[2])
|
||||||
|
|
||||||
|
if Framework == 'esx' and not Config.oldESX then
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
if player.canCarryItem(filletYield.item, filletAmount) then
|
||||||
|
AddItem(source, filletYield.item, filletAmount)
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.processing_success,
|
||||||
|
string.format(Strings.processing_success_desc, filletAmount, GetItemLabel(filletYield.item)), 'success')
|
||||||
|
else
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.cannot_carry, Strings.cannot_carry_desc, 'error')
|
||||||
|
-- Give back the fish if they can't carry the fillets
|
||||||
|
AddItem(source, fishData.sourceItem, 1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
AddItem(source, filletYield.item, filletAmount)
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.processing_success,
|
||||||
|
string.format(Strings.processing_success_desc, filletAmount, GetItemLabel(filletYield.item)), 'success')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for caviar
|
||||||
|
for i=2, #fishData.yield do
|
||||||
|
local extraYield = fishData.yield[i]
|
||||||
|
if extraYield.item == 'caviar' and extraYield.chance then
|
||||||
|
local chance = math.random(1, 100)
|
||||||
|
if chance <= extraYield.chance then
|
||||||
|
if Framework == 'esx' and not Config.oldESX then
|
||||||
|
local player = GetPlayer(source)
|
||||||
|
if player.canCarryItem(extraYield.item, 1) then
|
||||||
|
AddItem(source, extraYield.item, 1)
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.caviar_found, Strings.caviar_found_desc, 'success')
|
||||||
|
end
|
||||||
|
else
|
||||||
|
AddItem(source, extraYield.item, 1)
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.caviar_found, Strings.caviar_found_desc, 'success')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Modify the sellFish event to include processed items
|
||||||
|
RegisterNetEvent('wasabi_fishing:sellFish', function()
|
||||||
|
local playerPed = GetPlayerPed(source)
|
||||||
|
local playerCoord = GetEntityCoords(playerPed)
|
||||||
|
local distance = #(playerCoord - Config.sellShop.coords)
|
||||||
|
if distance == nil or distance > 3 then
|
||||||
|
KickPlayer(source, Strings.kicked)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sell fish
|
||||||
|
for i=1, #Config.fish do
|
||||||
|
SellItem(source, Config.fish[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sell processed items
|
||||||
|
for i=1, #Config.processedItems do
|
||||||
|
SellItem(source, Config.processedItems[i])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Helper function to sell items
|
||||||
|
function SellItem(source, itemData)
|
||||||
|
if HasItem(source, itemData.item) > 0 then
|
||||||
|
local rewardAmount = 0
|
||||||
|
for j=1, HasItem(source, itemData.item) do
|
||||||
|
rewardAmount = rewardAmount + math.random(itemData.price[1], itemData.price[2])
|
||||||
|
end
|
||||||
|
if rewardAmount > 0 then
|
||||||
|
AddMoney(source, 'money', rewardAmount)
|
||||||
|
TriggerClientEvent('wasabi_fishing:notify', source, Strings.sold_for,
|
||||||
|
(Strings.sold_for_desc):format(HasItem(source, itemData.item), itemData.label, addCommas(rewardAmount)), 'success')
|
||||||
|
RemoveItem(source, itemData.item, HasItem(source, itemData.item))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Modify the getFishData callback to handle bait types
|
||||||
|
lib.callback.register('wasabi_fishing:getFishData', function(source, baitType)
|
||||||
|
local baitData = nil
|
||||||
|
|
||||||
|
-- Find the bait data
|
||||||
|
for _, bait in pairs(Config.bait.types) do
|
||||||
|
if bait.itemName == baitType then
|
||||||
|
baitData = bait
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not baitData then
|
||||||
|
-- Use default bait data if the provided bait type is not found
|
||||||
|
for _, bait in pairs(Config.bait.types) do
|
||||||
|
if bait.itemName == Config.bait.defaultBait then
|
||||||
|
baitData = bait
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If using illegal bait, only catch pufferfish
|
||||||
|
if baitData.exclusive then
|
||||||
|
for _, fishType in pairs(baitData.exclusive) do
|
||||||
|
for _, fish in pairs(Config.fish) do
|
||||||
|
if fish.item == fishType then
|
||||||
|
return fish
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Otherwise, random fish with catch bonus
|
||||||
|
local availableFish = {}
|
||||||
|
for _, fish in pairs(Config.fish) do
|
||||||
|
-- Don't include exclusive fish types unless using the right bait
|
||||||
|
local isExclusive = false
|
||||||
|
for _, bait in pairs(Config.bait.types) do
|
||||||
|
if bait.exclusive then
|
||||||
|
for _, exclusiveFish in pairs(bait.exclusive) do
|
||||||
|
if fish.item == exclusiveFish then
|
||||||
|
isExclusive = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if isExclusive then break end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not isExclusive then
|
||||||
|
table.insert(availableFish, fish)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Apply catch bonus logic here if needed
|
||||||
|
return availableFish[math.random(#availableFish)]
|
||||||
|
end)
|
63
resources/[jobs]/[civ]/wasabi_fishing/server/updater.lua
Normal file
63
resources/[jobs]/[civ]/wasabi_fishing/server/updater.lua
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
-----------------For support, scripts, and more----------------
|
||||||
|
--------------- https://discord.gg/wasabiscripts -------------
|
||||||
|
---------------------------------------------------------------
|
||||||
|
|
||||||
|
local curVersion = GetResourceMetadata(GetCurrentResourceName(), "version")
|
||||||
|
local resourceName = "wasabi_fishing"
|
||||||
|
|
||||||
|
if Config.checkForUpdates then
|
||||||
|
CreateThread(function()
|
||||||
|
if GetCurrentResourceName() ~= "wasabi_fishing" then
|
||||||
|
resourceName = "wasabi_fishing (" .. GetCurrentResourceName() .. ")"
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
CreateThread(function()
|
||||||
|
while true do
|
||||||
|
PerformHttpRequest("https://api.github.com/repos/wasabirobby/wasabi_fishing/releases/latest", CheckVersion, "GET")
|
||||||
|
Wait(3600000)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
CheckVersion = function(err, responseText, headers)
|
||||||
|
local repoVersion, repoURL, repoBody = GetRepoInformations()
|
||||||
|
|
||||||
|
CreateThread(function()
|
||||||
|
if curVersion ~= repoVersion then
|
||||||
|
Wait(4000)
|
||||||
|
print("^0[^3WARNING^0] " .. resourceName .. " is ^1NOT ^0up to date!")
|
||||||
|
print("^0[^3WARNING^0] Your Version: ^2" .. curVersion .. "^0")
|
||||||
|
print("^0[^3WARNING^0] Latest Version: ^2" .. repoVersion .. "^0")
|
||||||
|
print("^0[^3WARNING^0] Get the latest Version from: ^2" .. repoURL .. "^0")
|
||||||
|
print("^0[^3WARNING^0] Changelog:^0")
|
||||||
|
print("^1" .. repoBody .. "^0")
|
||||||
|
else
|
||||||
|
Wait(4000)
|
||||||
|
print("^0[^2INFO^0] " .. resourceName .. " is up to date! (^2" .. curVersion .. "^0)")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
GetRepoInformations = function()
|
||||||
|
local repoVersion, repoURL, repoBody = nil, nil, nil
|
||||||
|
|
||||||
|
PerformHttpRequest("https://api.github.com/repos/wasabirobby/wasabi_fishing/releases/latest", function(err, response, headers)
|
||||||
|
if err == 200 then
|
||||||
|
local data = json.decode(response)
|
||||||
|
|
||||||
|
repoVersion = data.tag_name
|
||||||
|
repoURL = data.html_url
|
||||||
|
repoBody = data.body
|
||||||
|
else
|
||||||
|
repoVersion = curVersion
|
||||||
|
repoURL = "https://github.com/wasabirobby/wasabi_fishing"
|
||||||
|
end
|
||||||
|
end, "GET")
|
||||||
|
|
||||||
|
repeat
|
||||||
|
Wait(50)
|
||||||
|
until (repoVersion and repoURL and repoBody)
|
||||||
|
|
||||||
|
return repoVersion, repoURL, repoBody
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Add a link
Reference in a new issue