Christmas Event - Santa’s Present Hunt

:gift: Christmas Event - Santa’s Present Hunt

Bring the magic of Christmas to your server with the Santa’s Present Hunt event script! This fun and festive script lets players go on a scavenger hunt to gather presents scattered around the map. As they collect presents, they contribute to the community goal, unlocking rewards for everyone once the target is met!

:shopping_cart: Get it here: gamzkystore.com

:question: Support: Discord

:movie_camera: Detailed Preview: Youtube

Key Features

:gift: Gift Collection: Presents will randomly appear in the world around the player, with adjustable spawn distances from the player, roads, and other presents.
:dart: Community Goal: Everyone works together to reach the community-wide goal (Configurable). Players need to collect a set minimum number of gifts to be eligible for the prize.
:shield: Anti-Abuse System: Built-in server-side checks detect and block any invalid collection attempts. If abuse is detected, the player is temporarily prevented from contributing to the community goal.
:gear: Customizable Settings: Tweak spawn rates, distances, pickup effects, and more to suit your server.
:santa: Santa & Blip: Santa Claus is by default located at Legion Square where players can claim their rewards, marked by a blip on the map.
:floppy_disk: Save System: Progress is saved in JSON format, so no progress gets lost during the event.

Resmon: 0.00ms

How it works

  1. By default, a max of 3 presents spawn at random spots in a configurable area around the player. (Presents automatically despawn when out of range.)
  2. Players can pickup presents. As they collect the presents they contribute to the community goal.
  3. Once the goal is reached and a player has collected the minimum required number of presents, they can visit Santa to claim their reward!

Installation

The script is pretty much drag and drop and works for ESX or QBCore by default. You can easily adapt to other frameworks through the bridge functions. No database tables have to be created.
If you need help setting up and configuring the script, feel free to contact us.

The code snippets below are accessible in the encrypted version:

Config
Config = {
    debug = false,       -- When enabled, draws a 3D text on the present entities.

    communityGoal = 500, -- The amount of presents that need to be collected to reach the community goal.
    minimumPresents = 3, -- The minimum amount of presents that a player needs to collect to be able to claim the reward.
    rewards = {          -- The rewards that can be claimed by players once the community goal is reached.
        { type = 'item',  item = 'your_item_here', amount = 1 },
        { type = 'money', amount = 7500 },
    },

    -- Spawn present settings
    presentModel = `xm3_prop_xm3_present_01a`,
    spawnRange = 60.0,                                             -- The maximum range from the player where the present can spawn.
    maxPresentObjects = 3,                                         -- The maximum amount of present objects that can be spawned at the same time.
    distFromPlayer = 15.0,                                         -- Minimum distance from the player.
    distFromRoads = 30.0,                                          -- Minimum distance from roads. (To make it harder to find presents in the open. For example on sidewalks)
    distBetweenPresents = 30.0,                                    -- Minimum distance between two presents.
    ignoredZones = {                                               -- The zone where the presents will not spawn
        { coords = vector3(169.63, -975.51, 29.09), size = 80.0 }, -- Don't spawn near santa ped
    },
    enableParticles = true,                                        -- Enable particles when a present is picked up.
    pickupParticles = { dict = 'scr_indep_fireworks', name = 'scr_indep_firework_shotburst', size = 0.4 },

    -- Save data settings
    saveInterval = 30, -- Interval in seconds to save the data.

    -- Miscellaneous settings
    timeBetweenFinds = 5,         -- If a player finds multiple presents within this time (in seconds), it will be considered not legit.
    overlayUpdateInterval = 5000, -- The interval in milliseconds to update the overlay with the most recent data. (Only works if the overlay is open)

    -- Used when there is no target script and the drawtext function is used.
    drawText = {
        key = 38,
        keyLabel = '[~b~E~s~] ',
    },

    santa = {
        blip = {
            sprite = 304,
            color = 35,
            scale = 1.2,
            label = 'Santa\'s Present Hunt',
        },
        ped = {
            coords = vector4(169.63, -975.51, 29.09, 135.15),
            distance = 7.5,
            model = `mp_m_freemode_01`,
            componentVariations = {
                { componentId = 11, drawableId = 116, textureId = 0 }, -- Torso
                { componentId = 1,  drawableId = 8,   textureId = 0 }, -- Mask
                { componentId = 3,  drawableId = 14,  textureId = 0 }, -- Arms
                { componentId = 4,  drawableId = 57,  textureId = 0 }, -- Legs
                { componentId = 6,  drawableId = 39,  textureId = 0 }, -- Shoes
                { componentId = 8,  drawableId = 15,  textureId = 0 }, -- Shirt
            }
        }
    },
}

Config.Locales = {
    ['pickup_present'] = 'Pickup present',
    ['claim_reward'] = 'Claim reward',
    ['present_picked_up'] = '~g~Present successfully picked up.',
    ['community_goal_not_reached'] = '~r~The community goal has not been reached yet.',
    ['not_enough_presents'] = '~r~You do not have enough presents to claim the reward.',
    ['reward_already_claimed'] = '~r~You have already claimed your reward.',
    ['reward_claimed'] = '~g~You have claimed your reward.',
}
Accessible Client Bridge Functions
ESX = nil
QBCore = nil

if (GetResourceState('es_extended') == 'started') then
    ESX = exports['es_extended']:getSharedObject()
elseif (GetResourceState('qb-core') == 'started') then
    QBCore = exports['qb-core']:GetCoreObject()
end

Functions = {}

Functions.Notify = function(message)
    if ESX then
        ESX.ShowNotification(message, 'info', 5000)
    elseif QBCore then
        QBCore.Functions.Notify(message, 'primary', 5000)
    end
end

-- Used to check if the player can use any of the target interactions
Functions.CanInteract = function()
    local playerPed = PlayerPedId()

    if IsPedDeadOrDying(playerPed, true) then
        return false
    end

    if IsPedInAnyVehicle(playerPed, false) then
        return false
    end

    return true
end

Functions.AddTarget = function(targetEntity, icon, label, distance, canInteract, onSelect)
    if (GetResourceState('ox_target') == 'started') then
        exports.ox_target:addLocalEntity(targetEntity, { {
            icon = icon,
            label = label,
            distance = distance,
            canInteract = canInteract,
            onSelect = onSelect
        } })
        return
    end

    -- Fallback in case you don't want to use a target script
    AddTextTarget(targetEntity, label, distance, canInteract, onSelect)
end
Accessible Server Bridge Functions
ESX = nil
QBCore = nil

if (GetResourceState('es_extended') == 'started') then
    ESX = exports['es_extended']:getSharedObject()
elseif (GetResourceState('qb-core') == 'started') then
    QBCore = exports['qb-core']:GetCoreObject()
end

Functions = {}

Functions.GiveReward = function(playerId)
    if ESX then
        local xPlayer = ESX.GetPlayerFromId(playerId)
        for i = 1, #Config.rewards do
            local reward = Config.rewards[i]
            if reward.type == 'item' then
                xPlayer.addInventoryItem(reward.item, reward.amount)
            elseif reward.type == 'money' then
                xPlayer.addAccountMoney('bank', reward.amount)
            end
        end
    elseif QBCore then
        local Player = QBCore.Functions.GetPlayer(playerId)
        for i = 1, #Config.rewards do
            local reward = Config.rewards[i]
            if reward.type == 'item' then
                Player.Functions.AddItem(reward.item, reward.amount)
            elseif reward.type == 'money' then
                Player.Functions.AddMoney('bank', reward.amount)
            end
        end
    end

    TriggerClientEvent('gs_presenthunt:Notify', playerId, Config.Locales['reward_claimed'])
end

Functions.GetIdentifier = function(playerId)
    if ESX then
        local xPlayer = ESX.GetPlayerFromId(playerId)
        return xPlayer.identifier
    elseif QBCore then
        local Player = QBCore.Functions.GetPlayer(playerId)
        return Player.PlayerData.citizenid
    end
end

-- Don't worry about performance. The names will be cached.
Functions.GetCharacterName = function(identifier)
    if ESX then
        local character = MySQL.single.await('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?', { identifier })
        return ('%s %s'):format(character.firstname, character.lastname)
    elseif QBCore then
        local result = MySQL.scalar.await('SELECT `charinfo` FROM `players` WHERE `citizenid` = ?', { identifier })
        local charinfo = json_decode(result)
        return ('%s %s'):format(charinfo.firstname, charinfo.lastname)
    end
end

AddEventHandler('gs_presenthunt:OnPresentFound', function(data)
    local playerId = data.playerId
    local count = data.count           -- The players present count
    local totalCount = data.totalCount -- The total amount of presents found across all players

    -- For example: Give an extra reward to the player
end)

AddEventHandler('gs_presenthunt:OnSuspiciousActivity', function(data)
    local playerId = data.playerId
    local reason = data.reason

    if (reason == 'too_fast') then
        -- Found 2 presents in x seconds
    elseif (reason == 'same_location') then
        -- Found a present at the same location in 60 seconds
    end
end)

Code is accessible No, but there is an unencrypted version available
Subscription-based No
Lines (approximately) 1144
Requirements ox_lib
Support Yes
1 Like

Can I use the text function with ox_lib or must I use target because you only require ox_lib?

A target script is not required. If you don’t use a target script, the built-in drawtext function will be used. (The keys and text are configurable.)
Example: