Tutorial 2026-05-12

FiveM Boat & Deep Sea Fishing System

TDYSKY

TDYSKY

Founder & Lead Developer at Agency Scripts

Deep Sea Fishing as a Gameplay System

Deep sea fishing extends the standard fishing mechanic found on most FiveM servers by taking players offshore into open water where they battle larger fish species, navigate ocean hazards, and manage boat fuel and storage capacity. While shore fishing is a passive activity where players cast a line and wait, deep sea fishing is an expedition that requires preparation, investment, and teamwork. Players rent or purchase boats, buy bait and tackle, navigate to fishing grounds marked on the nautical chart, and use sonar equipment to locate fish schools beneath the surface. The catch is more valuable than shore fish, but the risks are higher: storms can damage boats, fuel can run out far from shore, and rare trophy fish require extended fights that test the player's patience and skill. This risk-reward balance creates genuinely exciting gameplay moments when a player hooks a massive tuna after investing thirty minutes sailing to the right spot, and the social dynamic of fishing trips with friends creates memories that keep players coming back.

Boat Configuration and Fleet Management

Define multiple boat classes ranging from small dinghies for nearshore fishing to large charter vessels for deep water expeditions. Each boat should have configurable properties including speed, fuel capacity, storage slots for caught fish, rod holder count determining how many lines can be cast simultaneously, and a durability rating that degrades with use and storm exposure. Store boat ownership in the database and provide a marina location where players purchase, upgrade, and maintain their vessels. Allow boat owners to invite other players aboard as crew members who can fish from the deck, creating a cooperative experience where the captain navigates while crew members manage multiple lines:

Config.Boats = {
    ['dinghy'] = {
        label = 'Fishing Dinghy',
        model = 'dinghy',
        price = 15000,
        fuelCapacity = 60,
        storageSlots = 10,
        maxWeight = 200,
        rodHolders = 1,
        maxCrew = 2,
        durability = 100,
        speed = 'slow',
        fishingZones = { 'nearshore' },
    },
    ['marquis'] = {
        label = 'Sport Fisher',
        model = 'marquis',
        price = 85000,
        fuelCapacity = 200,
        storageSlots = 30,
        maxWeight = 800,
        rodHolders = 4,
        maxCrew = 6,
        durability = 250,
        speed = 'medium',
        fishingZones = { 'nearshore', 'offshore', 'deep_sea' },
    },
    ['tug'] = {
        label = 'Commercial Trawler',
        model = 'tug',
        price = 250000,
        fuelCapacity = 500,
        storageSlots = 60,
        maxWeight = 3000,
        rodHolders = 6,
        maxCrew = 8,
        durability = 500,
        speed = 'slow',
        fishingZones = { 'nearshore', 'offshore', 'deep_sea', 'abyssal' },
    },
}

Config.FishingZones = {
    ['nearshore'] = {
        areas = {
            { center = vector3(-1850.0, -1250.0, 0.0), radius = 300.0 },
            { center = vector3(-3420.0, 970.0, 0.0), radius = 400.0 },
        },
        fish = {
            { item = 'bass',     weight = { 1, 8 },   chance = 35, price = 15 },
            { item = 'mackerel', weight = { 0.5, 4 },  chance = 40, price = 10 },
            { item = 'snapper',  weight = { 2, 12 },   chance = 20, price = 25 },
            { item = 'grouper',  weight = { 5, 25 },   chance = 5,  price = 40 },
        },
    },
    ['deep_sea'] = {
        areas = {
            { center = vector3(-4200.0, -800.0, 0.0), radius = 600.0 },
            { center = vector3(-3800.0, 2200.0, 0.0), radius = 500.0 },
        },
        fish = {
            { item = 'tuna',      weight = { 20, 120 },  chance = 25, price = 80 },
            { item = 'swordfish', weight = { 30, 200 },  chance = 15, price = 120 },
            { item = 'marlin',    weight = { 50, 350 },  chance = 8,  price = 200 },
            { item = 'shark',     weight = { 80, 500 },  chance = 2,  price = 500 },
        },
    },
}

The zone system restricts which fish species are available in each area, forcing players to venture further from shore for more valuable catches. Small boats can only access nearshore zones, while larger vessels unlock offshore and deep sea zones where the most profitable fish swim. This creates a natural progression path where players earn money from small catches to fund better boats that access premium fishing grounds.

Sonar and Fish Finding Mechanics

Add a sonar display to boats that shows fish density in the surrounding water. Implement the sonar as an NUI overlay that appears when the player activates their fish finder equipment from the boat's control panel. The sonar should display a circular sweep with blips representing fish schools at various depths, updating every few seconds with slight position changes to simulate fish movement. Each blip's size indicates the school density and potential catch quality. Players must navigate their boat to position themselves over a school before casting their lines, adding a navigation minigame element before the fishing itself begins. The sonar data should be generated server-side based on the boat's actual position relative to configured fishing zones, with randomized school positions that shift over time so players cannot simply memorize fixed hotspots.

Fishing Minigame and Fight Mechanics

When a fish bites, initiate a fishing fight minigame that scales in difficulty based on the fish species and weight. The fight should involve managing line tension through a dynamic bar that moves based on the fish's behavior. The player reels in by holding a key, but must release when the fish surges to prevent the line from snapping. Larger fish fight harder and longer, with deep sea species like marlin and sharks requiring several minutes of careful tension management. Display the estimated fish weight, species silhouette, and fight progress to maintain engagement during longer battles. Add environmental factors like current strength and weather conditions that affect fight difficulty, making calm weather ideal for fishing while storms increase the challenge significantly:

local fishFight = {
    active = false,
    tension = 50,       -- 0-100, snaps at 100
    progress = 0,       -- 0-100, fish landed at 100
    fishStrength = 0,
    fishBehavior = 'steady',
    reeling = false,
}

function StartFishFight(fishData)
    fishFight.active = true
    fishFight.tension = 30
    fishFight.progress = 0
    fishFight.fishStrength = fishData.weight * 0.5

    SendNUIMessage({
        action = 'showFishFight',
        species = fishData.item,
        estimatedWeight = fishData.weight,
    })

    CreateThread(function()
        while fishFight.active do
            -- Fish behavior changes randomly
            local behaviorRoll = math.random(100)
            if behaviorRoll < 15 then
                fishFight.fishBehavior = 'surge'    -- strong pull
            elseif behaviorRoll < 40 then
                fishFight.fishBehavior = 'resist'   -- moderate pull
            else
                fishFight.fishBehavior = 'steady'   -- light pull
            end

            -- Apply fish force
            local force = fishFight.fishStrength * 0.02
            if fishFight.fishBehavior == 'surge' then
                force = force * 3.0
                fishFight.progress = math.max(0, fishFight.progress - 2)
            elseif fishFight.fishBehavior == 'resist' then
                force = force * 1.5
            end

            -- Reeling input
            if IsControlPressed(0, 38) then -- E key
                fishFight.reeling = true
                fishFight.progress = fishFight.progress + 0.8
                fishFight.tension = fishFight.tension + force + 1.5
            else
                fishFight.reeling = false
                fishFight.tension = math.max(0, fishFight.tension - 3)
            end

            -- Check win/lose conditions
            if fishFight.tension >= 100 then
                EndFishFight(false, 'Line snapped!')
                return
            end

            if fishFight.progress >= 100 then
                EndFishFight(true, fishData)
                return
            end

            -- Update HUD
            SendNUIMessage({
                action = 'updateFishFight',
                tension = fishFight.tension,
                progress = fishFight.progress,
                behavior = fishFight.fishBehavior,
            })

            Wait(100)
        end
    end)
end

Weather and Ocean Conditions

Integrate your fishing system with the server's weather to create dynamic ocean conditions that affect gameplay. Calm weather provides ideal fishing conditions with low wave intensity and maximum sonar accuracy. Storms increase wave height, reduce sonar effectiveness, damage boats over time, and cause fish to dive deeper where they are harder to catch. Implement a sea state system that gradually transitions between calm, moderate, rough, and storm conditions based on the current weather. During storms, display visual effects like increased wave height using native water level adjustments, spray particles on the boat deck, and screen shake that intensifies with wave severity. Give players weather forecast information at the marina so they can plan fishing trips around favorable conditions, and add a storm warning system that alerts boats at sea when weather is deteriorating so they can head back to port before conditions become dangerous.

Fish Market and Economy Integration

Create a fish market at the marina where players sell their catch at prices that fluctuate based on supply and demand. When many players sell tuna, the tuna price drops. When nobody has sold swordfish in a while, the price rises. This dynamic pricing encourages players to diversify their catches and target less popular species when the market is saturated. Track total fish sold per species over rolling time windows and adjust prices within configured minimum and maximum bounds. Display current market prices on a board at the marina so players can make informed decisions about which species to target before heading out. For advanced integration, allow restaurants on the server to place bulk orders for specific fish species at premium prices, creating a direct supply chain between fishing players and restaurant operators that enriches both gameplay loops.

Performance and Entity Management

Boats and ocean environments present unique performance challenges because water rendering is already expensive in GTA V, and adding boat entities, fish fight effects, and sonar overlays increases the load further. Spawn fish entities only during active fights and only visible to the fighting player to avoid creating dozens of fish objects visible to everyone on the server. Use particle effects sparingly for water splashes and fight indicators, disabling them for distant observers through scope-based rendering. Boat fuel and storage state should be tracked server-side with the client requesting updates only when the player opens the relevant UI panel rather than syncing continuously. When players dock their boat at the marina, despawn the boat entity and store its state in the database including fuel level, durability, and stored fish. This prevents marinas from filling up with persistent boat entities that consume server resources even when their owners are offline. Test your system in multiplayer scenarios where multiple boats fish in the same zone simultaneously, ensuring that fish school data, sonar displays, and fight instances remain properly isolated per player.

Share this article

Ready to upgrade your server?

Check out our premium FiveM scripts in the Agency Scripts store or join our Discord community for support and updates.