1
0
Fork 0
forked from Simnation/Main
This commit is contained in:
Nordi98 2025-07-11 19:52:23 +02:00
parent 3ec53b72ff
commit fdd888d405
13 changed files with 1114 additions and 4 deletions

View file

@ -2701,7 +2701,7 @@ CodeStudio.Shops = {
vector4(1697.87, 4922.96, 42.06, 324.71), -- Grapeseed Main St
vector4(2005.3311, 3784.2839, 32.2014, 119.1002), -- Marina Dr
vector4(650.0884, 260.1908, 103.3210, 58.0985), -- Globe Oil Vinewood
vector4(241.1867, -904.0213, 29.6234, 66.2385) -- Stadtpark
vector4(238.0815, -916.4984, 29.7251, 72.5867) -- Stadtpark
@ -2937,7 +2937,7 @@ CodeStudio.Shops = {
vector4(-691.5956, -862.3833, 23.7687, 3), -- Little Seoul 2
vector4(-583.2180, -1016.3259, 22.3382, 3), -- Little Seoul 3
vector4(189.7391, -28.8968, 69.9315, 3), -- Spanish Ave
vector4(243.5098, -899.9354, 29.6234, 3), -- Stadtpark
vector4(240.3683, -912.3829, 29.7251, 3), -- Stadtpark
vector4(647.2603, 259.4702, 103.3210, 3) -- Globe Oil Vinewood
},
@ -2968,7 +2968,7 @@ CodeStudio.Shops = {
},
[13] = {
ShopName = 'Mega Mall',
ShopName = 'Baumarkt',
Category = {
[1] = 'hardware',
@ -2980,7 +2980,7 @@ CodeStudio.Shops = {
},
Interaction = {
targetIcon = 'fas fa-shopping-basket',
targetLabel = 'Mega Mall öffnen',
targetLabel = 'Baumarkt öffnen',
spawnPed = 'IG_Manuel', -- Put false if you dont need ped
scenario = 'WORLD_HUMAN_STAND_MOBILE', --Ped Scenario Animation put false if you dont need
radius = 2.5,

View 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'>![Discord Banner 2](https://discordapp.com/api/guilds/1025493337031049358/widget.png?style=banner2)</a>

View 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

View 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

View 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

View 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

View file

@ -0,0 +1,263 @@
-----------------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 check for available baits and select the best one
local function GetBestAvailableBait()
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
-- Sort baits by catch bonus (highest first)
table.sort(availableBaits, function(a, b)
return (a.catchBonus or 0) > (b.catchBonus or 0)
end)
-- Return the best bait
return availableBaits[1]
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 this bait
local hasBait = lib.callback.await('wasabi_fishing:checkItem', 100, selectedBait.itemName)
if not hasBait then
-- Try to get a new bait
local newBait = GetBestAvailableBait()
if newBait then
selectedBait = newBait
TriggerEvent('wasabi_fishing:notify', 'New Bait', 'Using ' .. selectedBait.label .. ' as bait', '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', 'Fishing Canceled', 'You stopped fishing', 'inform')
break
elseif #(GetEntityCoords(cache.ped) - waterLoc) > 30 then
-- Too far from water
TriggerEvent('wasabi_fishing:notify', 'Too Far', 'You moved too far from the water', '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', 'Already Fishing', 'You are already fishing', '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 = GetBestAvailableBait()
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', 'Bait Selected', 'Using ' .. selectedBait.label .. ' as bait', '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', 'No Knife', 'You need a knife to process fish', 'error')
return
end
-- Start processing animation
local playerPed = cache.ped
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 = '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', 'Canceled', 'Fish processing canceled', 'error')
end
ClearPedTasks(playerPed)
RemoveAnimDict('anim@amb@business@coc@coc_unpack_cut@')
end)
RegisterNetEvent('wasabi_fishing:interupt', function()
fishing = false
ClearPedTasks(cache.ped)
end)

View 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

View file

@ -0,0 +1,86 @@
-----------------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(-1597.5862, 5202.3809, 3.3590), -- X, Y, Z Coords of where buyer will spawn
heading = 225.9852, -- Heading of buyer ped
ped = 'cs_old_man2' -- Ped name here
}
Config.magnets = {
types = {
{
itemName = 'basic_magnet',
label = 'Basic Magnet',
loseChance = 25,
catchBonus = 0 -- base chance
},
{
itemName = 'strong_magnet',
label = 'Strong Magnet',
loseChance = 15,
catchBonus = 15 -- 15% better catch chance
},
{
itemName = 'neodymium_magnet',
label = 'Neodymium Magnet',
loseChance = 10,
catchBonus = 25 -- 25% better catch chance
},
{
itemName = 'rare_earth_magnet',
label = 'Rare Earth Magnet',
loseChance = 5, -- very hard to lose
catchBonus = 35, -- 35% better catch chance
exclusive = {'treasure'} -- can only catch treasure with this
}
},
defaultMagnet = 'basic_magnet'
}
Config.magnetRope = {
itemName = 'magnet_rope', -- Item name of magnet rope
breakChance = 5 --Chance of breaking rope when failing skillbar (Setting to 0 means never break)
}
Config.timeForFind = { -- Set min and max random range of time it takes for something to be on the magnet.
min = 2 * seconds,
max = 20 * seconds
}
Config.finds = {
{ item = 'scrap_metal', label = 'Scrap Metal', price = {50, 100}, difficulty = {'easy'} },
{ item = 'old_coin', label = 'Old Coin', price = {100, 200}, difficulty = {'easy', 'easy'} },
{ item = 'rusty_knife', label = 'Rusty Knife', price = {150, 250}, difficulty = {'medium', 'easy'} },
{ item = 'bicycle_parts', label = 'Bicycle Parts', price = {200, 300}, difficulty = {'medium'} },
{ item = 'metal_debris', label = 'Metal Debris', price = {75, 150}, difficulty = {'easy'} },
{ item = 'antique_item', label = 'Antique Item', price = {300, 600}, difficulty = {'medium', 'hard'} },
{ item = 'safe', label = 'Safe', price = {500, 1000}, difficulty = {'hard', 'hard', 'medium'} },
}
RegisterNetEvent('wasabi_magnet:notify')
AddEventHandler('wasabi_magnet: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)

View file

@ -0,0 +1,37 @@
-----------------For support, scripts, and more----------------
--------------- https://discord.gg/wasabiscripts -------------
---------------------------------------------------------------
Strings = {
intro_instruction = 'Drücke ~INPUT_ATTACK~ zu auswerfen, ~INPUT_FRONTEND_RRIGHT~ um abzubrechen.',
rod_broke = 'Kaputt.',
rod_broke_desc = 'Oha, zu fest gezogen! Die Angel ist kaputt.',
cannot_perform = 'Nope.',
cannot_perform_desc = 'Das geht gerade nicht der Fisch macht Pause.',
failed = 'Oops, das war nix.',
failed_fish = 'Nix gefangen der Fisch war schneller.',
no_water = 'Error 404: Wasser nicht gefunden.',
no_water_desc = 'Deine Angel verheddert sich im trockenen Gras. Bravo.',
no_bait = 'Kein Köder.',
no_bait_desc = 'Ohne Köder bist du nur ein sehr geduldiger Spaziergänger am Wasser.',
bait_lost = 'Köder abgehauen.',
bait_lost_desc = 'Der Köder hat sich selbstständig gemacht..',
fish_success = 'Fisch im Kasten das Abendessen ist gesichert.',
fish_success_desc = 'Du hast eine/n %s!',
sell_shop_blip = 'Fish Market',
sell_fish = '[E] - Fisch verkaufen',
kicked = 'Du kannsts versuchen, aber das klappt hier nicht.',
sold_for = 'Fisch verkauft.',
sold_for_desc = 'Du hast %sx %s für $%s verkauft.',
got_bite = 'Achtung, da zappelt was!',
got_bite_desc = 'Der Fisch ist am Haken, gleich wirds sportlich!',
waiting_bite = 'Geduld, der Fisch überlegt noch.',
waiting_bite_desc = 'Ein bisschen Geduld der Fisch ist gleich da.',
cannot_carry = 'Kein Platz!',
cannot_carry_desc = 'Du schleppst schon genug mehr passt nicht rein!',
no_knife = 'Kein Messer',
no_knife_desc = 'Ohne Messer ist der Fisch nur Deko.',
processing_success = 'Fang verarbeitet nächster Schritt: Grill an!',
processing_success_desc = '%d %s frisch aus der Fischverarbeitung!',
caviar_found = 'Kaviar entdeckt Fisch deluxe!',
caviar_found_desc = 'Wow, wertvoller Kaviar im Fisch das hat sich gelohnt!'
}

View 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 'wasabi/ edit by nordi'
description 'QBCore Skill Based Fishing edit'
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' }

View file

@ -0,0 +1,236 @@
-----------------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, baitType)
-- Find the bait data
local baitData = nil
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)
RegisterNetEvent('wasabi_fishing:rodBroke', function()
RemoveItem(source, Config.fishingRod.itemName, 1)
TriggerClientEvent('wasabi_fishing:interupt', source)
end)
RegisterNetEvent('wasabi_fishing:loseBait', function(baitType)
RemoveItem(source, baitType, 1)
end)
RegisterNetEvent('wasabi_fishing:tryFish', function(data)
local xPole = HasItem(source, Config.fishingRod.itemName)
local xBait = false
-- Check if player has any type of bait
for _, bait in pairs(Config.bait.types) do
if HasItem(source, bait.itemName) > 0 then
xBait = true
break
end
end
if xPole > 0 and xBait then
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 not xBait 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:processItem', function(fishItem)
-- 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
TriggerClientEvent('wasabi_fishing:notify', source, 'Cannot Process', 'This fish cannot be processed', 'error')
return
end
-- Check if player has the fish and knife
local hasFish = HasItem(source, fishItem)
local hasKnife = HasItem(source, Config.processing.knifeItem)
if hasFish < 1 or hasKnife < 1 then
TriggerClientEvent('wasabi_fishing:notify', source, 'Missing Items', 'You need both the fish and a knife', 'error')
return
end
-- Remove the fish
RemoveItem(source, fishItem, 1)
-- Add fish fillets
local filletYield = processData.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, 'Processing Success',
'You obtained ' .. filletAmount .. ' fish fillets', '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, fishItem, 1)
return
end
else
AddItem(source, filletYield.item, filletAmount)
TriggerClientEvent('wasabi_fishing:notify', source, 'Processing Success',
'You obtained ' .. filletAmount .. ' fish fillets', 'success')
end
-- Check for caviar
for i=2, #processData.yield do
local extraYield = processData.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, 'Caviar Found', 'You found some valuable caviar!', 'success')
end
else
AddItem(source, extraYield.item, 1)
TriggerClientEvent('wasabi_fishing:notify', source, 'Caviar Found', 'You found some valuable caviar!', 'success')
end
end
end
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
-- 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
-- Register usable items for all fish types for processing
for _, fish in pairs(Config.fish) do
RegisterUsableItem(fish.item, function(source)
TriggerClientEvent('wasabi_fishing:processFish', source, fish.item)
end)
end
RegisterUsableItem(Config.fishingRod.itemName, function(source)
TriggerClientEvent('wasabi_fishing:startFishing', source)
end)

View 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